//----------------------------------------------// // FRUIT - STRUMEN - TATION // Michael Hammond, 2009 // COS 325 //----------------------------------------------// FFT fft => blackhole; FFT fft2 => blackhole; IFFT ifft => OneZero oz => Gain g => JCRev r => dac; Event noteOff[100]; r.mix(.02); OscRecv recv; 12000 => recv.port; recv.listen(); recv.event( "/addOption, i, i" ) @=> OscEvent addOption; recv.event( "/removeOption, i, i" ) @=> OscEvent removeOption; recv.event( "/synthesisStyle, i" ) @=> OscEvent synthesisStyle; SndBuf resid[5][4][3]; SndBuf keys[100]; int fruit[0]; int attack[0]; int instrument[0]; //currently playing thangs: int in[0]; int at[0]; int fr[0]; int cl[0]; fruit << 0; attack << 0; instrument << 0; int synthesis; // synthesis style // 0 for cross-synthesis // 1 for crappy convolution spork ~ oscRecv1(); spork ~ oscRecv2(); spork ~ oscRecv3(); /* ********************* */ /* **** OSC from P5 **** */ /* ********************* */ fun void oscRecv1() { int the_option; int the_thing; while (true) { addOption => now; while ( addOption.nextMsg() != 0) { addOption.getInt() => the_option; addOption.getInt() => the_thing; if (the_option == 0) add_fruit(the_thing); else if (the_option == 1) add_instrument(the_thing); else if (the_option == 2) add_attack(the_thing); <<< "option added ", the_option, the_thing >>>; } } } fun void oscRecv2() { int the_option; int the_thing; while (true) { removeOption => now; while ( removeOption.nextMsg() != 0) { removeOption.getInt() => the_option; removeOption.getInt() => the_thing; if (the_option == 0) remove_fruit(the_thing); else if (the_option == 1) remove_instrument(the_thing); else if (the_option == 2) remove_attack(the_thing); <<< "option removed ", the_option, the_thing >>>; } } } fun void oscRecv3() { while (true) { synthesisStyle => now; while (synthesisStyle.nextMsg() != 0) { synthesisStyle.getInt() => synthesis; } } } /* ********************* */ /* **** THE FRUITS ***** */ /* ********************* */ // ------------- WATERMELON ------------- // resid[0][0][0].read("fruits/wat_pend1.wav"); resid[0][0][1].read("fruits/wat_pend2.wav"); resid[0][0][2].read("fruits/wat_pend3.wav"); resid[0][1][0].read("fruits/wat_stab1.wav"); resid[0][1][1].read("fruits/wat_stab2.wav"); resid[0][1][2].read("fruits/wat_stab3.wav"); resid[0][2][0].read("fruits/wat_fiber1.wav"); resid[0][2][1].read("fruits/wat_fiber2.wav"); resid[0][3][0].read("fruits/wat_flick1.wav"); resid[0][3][1].read("fruits/wat_flick2.wav"); resid[0][3][2].read("fruits/wat_flick3.wav"); // ------------- MANGO ------------- // resid[1][0][0].read("fruits/mang_pend1.wav"); resid[1][0][1].read("fruits/mang_pend2.wav"); resid[1][0][2].read("fruits/mang_pend3.wav"); resid[1][1][0].read("fruits/mang_stab1.wav"); resid[1][1][1].read("fruits/mang_stab2.wav"); resid[1][1][2].read("fruits/mang_stab3.wav"); resid[1][2][0].read("fruits/mang_fiber1.wav"); resid[1][2][1].read("fruits/mang_fiber2.wav"); resid[1][3][0].read("fruits/mang_flick1.wav"); resid[1][3][1].read("fruits/mang_flick2.wav"); resid[1][3][2].read("fruits/mang_flick3.wav"); // ------------- PAPAYA ------------- // resid[2][0][0].read("fruits/pap_pend1.wav"); resid[2][0][1].read("fruits/pap_pend2.wav"); resid[2][0][2].read("fruits/pap_pend3.wav"); resid[2][1][0].read("fruits/pap_stab1.wav"); resid[2][1][1].read("fruits/pap_stab2.wav"); resid[2][1][2].read("fruits/pap_stab3.wav"); resid[2][2][0].read("fruits/pap_fiber1.wav"); resid[2][2][1].read("fruits/pap_fiber2.wav"); resid[2][3][0].read("fruits/pap_flick1.wav"); resid[2][3][1].read("fruits/pap_flick2.wav"); resid[2][3][2].read("fruits/pap_flick3.wav"); // ------------- SQUASH ------------- // resid[3][0][0].read("fruits/squ_pend1.wav"); resid[3][0][1].read("fruits/squ_pend2.wav"); resid[3][0][2].read("fruits/squ_pend3.wav"); resid[3][1][0].read("fruits/squ_stab1.wav"); resid[3][1][1].read("fruits/squ_stab2.wav"); resid[3][1][2].read("fruits/squ_stab3.wav"); resid[3][2][0].read("fruits/squ_fiber1.wav"); resid[3][2][1].read("fruits/squ_fiber2.wav"); resid[3][3][0].read("fruits/squ_flick1.wav"); resid[3][3][1].read("fruits/squ_flick2.wav"); resid[3][3][2].read("fruits/squ_flick3.wav"); // ------------- CANTALOUPE ------------- // resid[4][0][0].read("fruits/cntloup_pend1.wav"); resid[4][0][1].read("fruits/cntloup_pend2.wav"); resid[4][0][2].read("fruits/cntloup_pend3.wav"); resid[4][1][0].read("fruits/cntloup_stab1.wav"); resid[4][1][1].read("fruits/cntloup_stab2.wav"); resid[4][1][2].read("fruits/cntloup_stab3.wav"); resid[4][2][0].read("fruits/cntloup_fiber1.wav"); resid[4][2][1].read("fruits/cntloup_fiber2.wav"); resid[4][3][0].read("fruits/cntloup_flick1.wav"); resid[4][3][1].read("fruits/cntloup_flick2.wav"); resid[4][3][2].read("fruits/cntloup_flick3.wav"); /* ********************* */ /* **** THE GUITAR ***** */ /* ********************* */ // 8ve one "data/d1.wav" => keys[49].read; "data/a1.wav" => keys[81].read; "data/d2.wav" => keys[65].read; "data/g1.wav" => keys[90].read; "data/a1.wav" => keys[50].read; "data/c#2.wav" => keys[87].read; "data/d2.wav" => keys[83].read; "data/a2.wav" => keys[88].read; "data/g1.wav" => keys[51].read; "data/d2.wav" => keys[69].read; "data/a2.wav" => keys[68].read; "data/b2.wav" => keys[67].read; "data/f1.wav" => keys[52].read; "data/c2.wav" => keys[82].read; "data/d2.wav" => keys[70].read; "data/a2.wav" => keys[86].read; "data/c1.wav" => keys[53].read; "data/g1.wav" => keys[84].read; "data/e2.wav" => keys[71].read; "data/f2.wav" => keys[66].read; "data/a#1.wav" => keys[54].read; "data/f2.wav" => keys[89].read; "data/d2.wav" => keys[72].read; "data/d#2.wav" => keys[78].read; "data/f#1.wav" => keys[55].read; "data/c#2.wav" => keys[85].read; "data/f2.wav" => keys[74].read; "data/g#2.wav" => keys[77].read; fun void add_fruit(int the_fruit) { fruit << the_fruit; } fun void add_attack(int the_attack) { attack << the_attack; } fun void add_instrument(int the_instrument) { instrument << the_instrument; } fun void remove_fruit(int the_fruit) { int i; for (0 => i; i < fruit.cap(); i++) if (fruit[i] == the_fruit) break; for (i => int j; j < fruit.cap()-1; j++) fruit[j+1] => fruit[j]; fruit.cap()-1 => fruit.size; } fun void remove_attack(int the_attack) { int i; for (0 => i; i < attack.cap(); i++) if (attack[i] == the_attack) break; for (i => int j; j < attack.cap()-1; j++) attack[j+1] => attack[j]; attack.cap()-1 => attack.size; } fun void remove_instrument(int the_instrument) { int i; for (0 => i; i < instrument.cap(); i++) if (instrument[i] == the_instrument) break; for (i => int j; j < instrument.cap()-1; j++) instrument[j+1] => instrument[j]; instrument.cap()-1 => instrument.size; } fun int random_fruit() { if (fruit.cap() == 0) return -1; else return fruit[Std.rand2(0, fruit.cap()-1)]; } fun int random_attack() { if (attack.cap() == 0) return -1; else return attack[Std.rand2(0, attack.cap()-1)]; } fun int random_clip(int the_attack) { if (fruit.cap() == 0) return -1; if (the_attack == 2) return Std.rand2(0, 1); else return Std.rand2(0, 2); } fun int random_instrument() { if (instrument.cap() == 0) return -1; return instrument[Std.rand2(0, instrument.cap()-1)]; } float accel_x; 1 => float resid_rate; float accel_y; 200 => g.gain; //resid.read("fruits/squ_flick3.wav"); //resid.pos(0); //1 => resid.gain; //.2 => resid.rate; // set srate second / samp => float srate; // set parameters 512 => int FFT_SIZE; FFT_SIZE => fft.size; FFT_SIZE => fft2.size; (FFT_SIZE/2)::samp => dur windowSize; windowSize/2 => dur hopSize; 44100 => int sampleRate; //(sampleRate/FFT_SIZE)*16 //1396. => oz.zero; Windowing.hann(FFT_SIZE/2) => fft.window; Windowing.hann(FFT_SIZE/2) => fft2.window; Windowing.hann(FFT_SIZE/2) => ifft.window; // use this to hold contents complex s[fft.size()/2]; complex s1[fft.size()/2]; // keyboard mappingsss [ [30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 45, 46, 42], //1234... row [20, 26, 8, 21, 23, 28, 24, 12, 18, 19, 47, 48, 49], //qwer... row [4, 22, 7, 9, 10, 11, 13, 14, 15, 51, 52], //asdf... row [29, 27, 6, 25, 5, 17, 16, 54, 55, 56] //zxcv... row ] @=> int row[][]; int keyToPitch_table[256]; //this function takes each row and tunes it in half steps, based //on whatever fundamental pitch note specified fun void tuneString(int whichString, int basepitch) { for (0 => int i; i < row[whichString].cap(); i++) { basepitch + i => keyToPitch_table[row[whichString][i]]; } } //tune them strings, in fifths here, like a viola tuneString(3, 21); tuneString(2, 14); tuneString(1, 7); tuneString(0, 0); 3 => int register; int base; spork ~ getKb(); spork ~ wobble(); fun void getKb() { Hid kb; HidMsg msg; 0 => int counter; 0 => int deviceNum; kb.openKeyboard( deviceNum ) => int deviceAvailable; if ( deviceAvailable == 0 ) me.exit(); <<< "keyboard '", kb.name(), "' ready" >>>; while( true ) { kb => now; while( kb.recv( msg ) ) { if (msg.isButtonDown() ) { if (msg.which == 79) { register++; <<< "register ==", register >>>; } else if (msg.which == 80) { register--; <<< "register ==", register >>>; } else { //<<< "down:", msg.which >>>; spork ~ play( msg.ascii, msg.which );//base + register * 12 + keyToPitch_table[msg.which] ); counter++; } } else if (msg.isButtonUp() ) { noteOff[msg.ascii].signal(); } } } } //spork ~ ozf(); fun void ozf() { 900 => int ooo; while (true) { ooo++; (ooo$float)*2*Math.PI => oz.zero; 200::ms => now; <<< ooo >>>; } } function void wobble() { Hid hi; int accel_result[]; while( true ) { // poll the tilt sensor, expect to get back 3 element array of ints hi.readTiltSensor() @=> accel_result; accel_result[0] / 100.0 => accel_x; accel_result[1] / 160.0 => accel_y; if( accel_y > 1.0 ) 1.0 => accel_y; else if( accel_y < -1.0 ) 1.0 => accel_y; Math.fabs(accel_y) => accel_y; if (accel_x > 0) accel_x*.8 => accel_x; else accel_x*(12./3.) => accel_x; -accel_x => accel_x; accel_x + 1 => accel_x; //else accel_x*.5 => accel_x; if( accel_x > 9 ) 9 => accel_x; else if( accel_x < 0.1 ) 0.1 => accel_x; accel_x => resid_rate; for ( 0 => int i; i < fr.cap(); i++) resid_rate => resid[fr[i]][at[i]][cl[i]].rate; <<< accel_x, " ", accel_y >>>; //-accel_x => accel_x; //accel_y *.3 => accel_y; //<<< accel_x, " ", accel_y >>>; 50::ms => now; } } fun void ramp_gain_down(int f, int a, int c, dur ramp_time) { 5::ms => dur interval; for (0 => int i; i < ramp_time/interval; i++) { resid[f][a][c].gain() - (interval/ramp_time) => resid[f][a][c].gain; interval => now; } 0 => resid[f][a][c].gain; } fun void play(int theAscii, int theWhich) { base + register * 12 + keyToPitch_table[theWhich] => int theNote; int ins; int att; int fru; int clp; random_instrument() => ins; random_fruit() => fru; random_attack() => att; random_clip(att) => clp; //in << ins; <<< at.cap() >>>; //e.duration(3::ms); //e.keyOn(); //<<< fru, att, clp >>>; //<<< Math.rand2(0, 6) >>>; if (fru >= 0 && att >= 0 && clp >= 0) { <<< "playing fruit" >>>; resid[fru][att][clp] => fft; 1 => resid[fru][att][clp].gain; if (att == 2) resid[fru][att][clp].loop(1); else resid[fru][att][clp].loop(0); //resid[fru][att][clp].rate(resid_rate); resid[fru][att][clp].pos(0); at << att; fr << fru; cl << clp; } 20::ms => now; if (ins == -1) { <<< "playing no instrument" >>>; noteOff[theAscii] => now; } if (ins == 0) { <<< "playing string" >>>; StifKarp m => fft2; .3 => m.gain; Std.mtof(theNote) => m.freq; m.pluck(.5); noteOff[theAscii] => now; m =< fft2; } else if (ins == 1) { <<< "playing bar" >>>; ModalBar m => fft2; .7 => m.gain; Std.mtof(theNote) => m.freq; m.noteOn(1.); noteOff[theAscii] => now; m =< fft2; } else if (ins == 2) { <<< "playing guitar" >>>; keys[theAscii] => fft2; 0 => keys[theAscii].pos; spork ~ play_sustain(theAscii); noteOff[theAscii] => now; //keys[theAscii] =< fft2; } //e.duration(100::ms); //e.keyOff(); //ramp_gain_down(fru, att, clp, 200::ms); if (fru >= 0 && att >= 0 && clp >= 0) { resid[fru][att][clp] =< fft; for (0 => int i; i < fr.cap(); i++) { if ( fru == fr[i] ) { for (i => int j; j < fr.cap()-1; j++) fr[j+1] => fr[j]; break; } } for (0 => int i; i < at.cap(); i++) { if ( att == at[i] ) { for (i => int j; j < at.cap()-1; j++) at[j+1] => at[j]; break; } } for (0 => int i; i < cl.cap(); i++) { if ( clp == cl[i] ) { for (i => int j; j < cl.cap()-1; j++) cl[j+1] => cl[j]; break; } } cl.cap()-1 => cl.size; at.cap()-1 => at.size; fr.cap()-1 => fr.size; } } fun void play_sustain(int theAscii) { keys[theAscii].length() => now; keys[theAscii] =< fft2; } // control loop while( true ) { // take fft fft.upchuck(); fft2.upchuck(); fft.spectrum(s1); // cross synthesize!! for (0 => int i; i < fft.size()/2; i++) { if (instrument.cap() == 0) { .01*fft.cval(i) => s[i]; // natural fruit impulse response } else if (fruit.cap() == 0) { .01*fft2.cval(i) => s[i]; // clean, "disembodied" instrument model } else if (synthesis == 0) // cross- Math.sqrt((fft.cval(i)$polar).mag) * fft2.cval(i) => s[i]; else if (synthesis == 1) 10*fft.cval(i) * fft2.cval(i) => s[i]; } // Math.sqrt((fft.cval(i)$polar).mag) * fft2.cval(i) => s[i]; // fft.cval(i) * fft2.cval(i) => s[i]; // send spectrum to ifft ifft.transform(s); // advance time hopSize => now; }