//----------------------------| // Jeff Smith, Music 220a // HW #4, Nov 4, 2007 // // Composed entirely of concrete sounds, the piece juxtaposes man and // machine. I set up a pretty neat drone by layering a buffer of a // recording of my washing machine spinning. I 'spin' three of these // buffers concurrently, each offset by a slice of time to introduce a clear // meter. Hence, the drone moves to different meters/speeds. I find it // effective. When the drone shifts speeds, I preface the shift with a // buffer of my son's geotrax train going down a track. // // The human sounds are my kids, my three-year-old singing a few songs // in Chinese, my son playing his piano piece (which I find to be very // much a human sound that is getting lost in all of the noise of these // machines), and finally my son telling his daddy something in Chinese. // // I create a few classes here to simplify buffer processing and the // drone creation. // // note that this shred automatically outputs file 'hw4x.wav', the 'x' // to avoid overwriting someone's homework. i'll try and remember // to comment this out for the final submission. // the following data files are required. "data/charlie singing 1.wav" => string singing1; "data/charlie singing 2.wav" => string singing2; "data/noah chinese.wav" => string talking; "data/noah piano.wav" => string playing; "data/washing machine clean.wav" => string drone_wav; "data/train clean.wav" => string train_wav; class PitchData { SndBuf my_buf; PitShift ps; public void open (string file_name) { file_name => my_buf.read; end(); } public void end() { my_buf.samples() => my_buf.pos; } public int at_end() { return my_buf.pos() >= my_buf.samples(); } public void connect( UGen ugen, float gain ) { my_buf => ps => ugen; gain => my_buf.gain; // 200::ms => e.duration; } public void play( int buf_pos ) { buf_pos => my_buf.pos; } public void play() { play(0); } public void playfromcurrentpos() { play(my_buf.pos()); } public void UpdatePitch(int pitch) { // negative means turn off pitch-shift if (pitch < 0) { 0.0 => ps.shift; } else { // quarter-tones only, sharp or flat if (pitch % 2 ) { (Std.mtof(pitch) + Std.mtof(pitch + 1)) / 2.0 => ps.shift; } else { (Std.mtof(pitch) + Std.mtof(pitch - 1)) / 2.0 => ps.shift; } } } } // class PitchData class DroneData extends PitchData { 0::ms => dur drone_meter; 0 => int buf_pos; Echo ec; 0.5 => ec.gain; JCRev jcr; .7 => jcr.mix; public void connect( UGen ugen, float gain ) { my_buf => ps => jcr => ec => ugen; gain => ec.gain => my_buf.gain; 300::ms => ec.max => ec.delay; } public void DronePlay(int buf_pos) { // if play event would go past end of buffer, move to buffer beginning if (drone_meter > 0::ms && buf_pos > 0) { my_buf.samples() => float f; buf_pos / f => f; f * my_buf.length() => dur used_length; my_buf.length() - used_length => dur remaining_length; if (drone_meter > remaining_length) { 0 => buf_pos; } } play(buf_pos); } public void UpdateGain( float gain ) { gain => ec.gain => my_buf.gain; } public void Drone(dur meter_time) { UpdateMeter(meter_time); while (true) { DronePlay(40); drone_meter => now; } } public void UpdateMeter(dur meter_time) { if (meter_time < 3::ms ) <<< "meter time too short for envelope" >>> ; drone_meter + Std.rand2(31, 145)::ms => now; meter_time => drone_meter; meter_time + Std.rand2(19, 155)::ms => now; } } // class DroneData class MultiDrone { DroneData drones[3]; public void connect(UGen ugen, float gain) { 0 => int i; while ( i < drones.cap() - 1 ) { drones[i].connect(ugen, gain); ++i; } // last drone louder drones[i].connect(ugen, gain + 0.1); } public void open(string path) { for (0 => int i; i < drones.cap(); i++) { drones[i].open(path); } } public void Drone(dur meter, int slice) { // spoke the drones, each with a slice-ly reduced meter for (0 => int i; i < drones.cap(); i++) { meter / (slice - i) => dur current; spork ~drones[i].Drone(current); } } public void UpdateMeter(dur meter, int slice) { for (0 => int i; i < drones.cap(); i++) { meter / (slice - i) => dur current; drones[i].UpdateMeter(current); } } public void UpdatePitch(int drone_index, int pitch) { drones[drone_index].UpdatePitch(pitch); } public void UpdateGain(float gain) { for (0 => int i; i < drones.cap(); i++) { drones[i].UpdateGain(gain); } } } // MultiDrone // how we manage the sound buffers of people singing & playing; some concurrency is allowed class MultiPitchPlayer { 3 => int max_players; PitchData players[max_players]; int player_playing[max_players]; for (0 => int i; i < max_players; i++) { false => player_playing[i]; } private int FindPlayer() { 0 => int i; while (i < max_players && player_playing[i]) { i++; } return i; } private void ConnectAndPlay(int i, string buf_name, UGen ugen, float gain) { players[i].connect(ugen, gain); players[i].open(buf_name); players[i].play(); } public void AddSyncPlayer(string buf_name, UGen ugen, float gain) { FindPlayer() => int i; // no room at the inn. do nothing if (i == max_players) { return; } ConnectAndPlay(i, buf_name, ugen, gain); } public void AddPlayer(string buf_name, UGen ugen, float gain) { FindPlayer() => int i; // no room at the inn. do nothing if (i == max_players) { return; } true => player_playing[i]; ConnectAndPlay(i, buf_name, ugen, gain); while (!players[i].at_end()) { 100::ms => now; } false => player_playing[i]; } } // pull samples from the dac Gain g => WvOut w => dac; .5 => g.gain; // this is the output file name // "hw4x.wav" => w.wavFilename; // <<<"writing to file:", "'" + w.filename() + "'">>>; 0 => int current_pitch_index; DroneData train; MultiDrone drones; MultiPitchPlayer players; PitchData piano, speaking; .7 => float volume; .1 => float min_volume; .9 => float max_volume; drones.connect(g, volume); train.connect(g, volume - .1); piano.connect(g, volume); speaking.connect(g, volume); drones.open(drone_wav); train.open(train_wav); piano.open(playing); speaking.open(talking); // C, bE, E, G, bB, D, F, F#, A [ 0, 3, 4, 7, 12, 14, 17, 18, 21 ] @=> int hi[]; <<< "here we are" >>>; // this number is significant -- it defines the metrical relationship of the // multi-drone period 15 => int metrical_slice; 4 => int min_metrical_slice; // launch the drones and let them go for a bit at the metrical_slice meter drones.Drone(1::second, min_metrical_slice); 5::second => now; false => int talking_done; false => int playing_done; false => int singing_done; false => int changing_pitch; 12::second => dur time_remaining; // our main loop; time_remaining won't start decreasing until we randomly hit all // singing, playing, and talking events below while (time_remaining > 0::second) { // change the pitch at random of the second drone if (!changing_pitch) { if (Std.rand2(0,2) == 0) { drones.UpdatePitch(1, hi[Std.rand2(0,hi.cap()-1)]); true => changing_pitch; } // reset pitch of second drone to no-pitch } else { -1 => int no_pitch; drones.UpdatePitch(1, no_pitch); false => changing_pitch; } 2::second => now; if (talking_done) { time_remaining - 2::second => time_remaining; } // if we're not changing the pitch, 33% of time update meter of drones if (!changing_pitch && Std.rand2(0,3) == 1) { train.UpdateGain(Std.rand2f(min_volume,max_volume)); train.play(); Std.rand2(min_metrical_slice, metrical_slice) => int slice; <<< "update drone period ", slice >>>; drones.UpdateMeter(1::second, slice); } // see if we have a singing, playing, or talking event to launch if (Std.rand2(0,6) == 2) { <<< "singing" >>>; if (Std.rand2(0, 1) == 1) { spork ~players.AddPlayer(singing1, g, volume); } else { spork ~players.AddPlayer(singing2, g, volume); } true => singing_done; } else if (!talking_done && Std.rand2(0, 7) == 3) { <<< "playing" >>>; piano.play(); singing_done => playing_done; } else if (!talking_done && !changing_pitch && playing_done && Std.rand2(0, 6) == 5) { <<< "talking" >>>; speaking.play(); 3::second => now; piano.play(); true => talking_done; } Std.rand2(100,900)::ms => now; } if (changing_pitch) { drones.UpdatePitch(1, -1); } spork ~players.AddPlayer(singing2, g, volume); while (volume >= min_volume) { volume - min_volume => volume; drones.UpdateGain(volume); 2::second => now; } // "hw4x.wav" => w.closeFile;