//welcome to the pachinkinator, the squeakuencer. the spherecuencer, the half-pyramidequencer. //------------------CHUNITY vars------------------- //-----GLOBAL (incoming from Unity)------- global Event squareClicked; global Event BPMChanged; 200 => global int BEAT_int; //[] @=> global int bufferInstruments[]; //i think all these arrays are the only way to get information from Unity => Chuck... :'( //Dear Cody: please do not edit here and expect sound to happen. Best, -Cody. [0] @=> global int boxes1[]; [0,0] @=> global int boxes2[]; [0,0,0] @=> global int boxes3[]; [0,0,0,0] @=> global int boxes4[]; [0,0,0,0,0] @=> global int boxes5[]; [0,0,0,0,0,0] @=> global int boxes6[]; [0,0,0,0,0,0,0] @=> global int boxes7[]; [0,0,0,0,0,0,0,0] @=> global int boxes8[]; [0,0,0,0,0,0,0,0,0] @=> global int boxes9[]; [0,0,0,0,0,0,0,0,0,0] @=> global int boxes10[]; [0,0,0,0,0,0,0,0,0,0,0] @=> global int boxes11[]; [0,0,0,0,0,0,0,0,0,0,0,0] @=> global int boxes12[]; [0,0,0,0,0,0,0,0,0,0,0,0,0] @=> global int boxes13[]; [0,0,0,0,0,0,0,0,0,0,0,0,0,0] @=> global int boxes14[]; [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0] @=> global int boxes15[]; [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0] @=> global int boxes16[]; //spork event shreds deteecting input from unity spork ~ OnSquareClick(squareClicked); spork ~ OnBPMChange(BPMChanged); //-----GLOBAL (outgoing to Unity)------- global Event beatHappened; global Event halfBeatHappened; //beats will drive the main timing 0 => global int beats; //-----INTERNAL CODE----- //what are the names of sound files we should expect to find? [ "02_kick.wav", "hihat.wav", "clap1.wav", "spunk_drum.wav", "ahh.wav", "low_kick.wav", "low_kick2.wav", "tit.wav", "", "", ""] @=> string samplefiles[]; [8,9,10] @=> int synthnums[]; //see play() func //keep track of the total number of samples to cycle thru samplefiles.cap() => global int NUMSOUNDS; /* it's the McFreakin' Jagged Array. each row is a different perspective on time. Each row has increasing beats per measure, and are simultaneously re-interpreting the current beat. Key: each row is the length of its index. eg row 0 is empty. Legend: [0]: Nothing, [1-X]: Play Sound 1-X; */ [[0], //ignore! [0],// 1/4 [0,0],// 2/4 [0,0,0],// 3/4 [0,0,0,0],// 4/4 [0,0,0,0,0],// 5/4 [0,0,0,0,0,0],// 6/4 [0,0,0,0,0,0,0],// 7/4 [0,0,0,0,0,0,0,0],// 8/4 [0,0,0,0,0,0,0,0,0],// 9/4 [0,0,0,0,0,0,0,0,0,0],// 10/4 [0,0,0,0,0,0,0,0,0,0,0],// 11/4 [0,0,0,0,0,0,0,0,0,0,0,0],// 12/4 [0,0,0,0,0,0,0,0,0,0,0,0,0],// 13/4 [0,0,0,0,0,0,0,0,0,0,0,0,0,0],// 14/4 [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],// 15/4 [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]// 16/4 ] @=> int BOXES[][]; //building the signal chain // beat (smallest division; dur between chickens) 200::ms => dur BEAT; SndBuf my_buffers[NUMSOUNDS]; // reverb NRev reverb => dac; .1 => reverb.mix; for(0 => int i; i < NUMSOUNDS; i++) { 0 => my_buffers[i].gain; //silence me.dir() + samplefiles[i] => my_buffers[i].read; //plug in file my_buffers[i] => reverb; //connect to dac } SqrOsc osc1 => ADSR adsr1 => reverb; BlitSquare osc2 => ADSR adsr2 => reverb; SawOsc osc3 => ADSR adsr3 => reverb; .35 => osc1.gain => osc2.gain => osc3.gain; adsr1.set( 10::ms, 8::ms, .5, 15::ms ); adsr2.set( 10::ms, 50::ms, .7, 300::ms ); adsr3.set( 70::ms, 58::ms, .2, 35::ms ); Std.mtof(57) => osc1.freq => osc2.freq => osc3.freq; // simple sequencer loop while( true ) { // advance time by duration of one beat //we have multiple beat events for max resolution BEAT/2 => now; halfBeatHappened.broadcast(); BEAT/2 => now; halfBeatHappened.broadcast(); beatHappened.broadcast(); //return which instrument should play this beat checkboxes(beats) => int shouldplay; //beat is over, iterate timing 1 +=> beats; } //t is the timing-to-wrap fun int checkboxes(int t) { 0 => int shouldplay; //for each box: check if the box has '1' at the correct wrapped time //each box wraps time to its number //multiple boxes' rules can converge on one beat; only one note will be played regardless. for(1=> int i; i < BOXES.cap(); i++) { //for each time unit, iterate through each index of each array for (0 => int q; q < i; q++) { //check if this is the right time (e.g. 'if(9%3==0)', i.e. if our time is 9, our 3-array says to play at beat 0, and our 3-wrapped-beat is 0) if (t % i == q) { BOXES[i][q] => int shouldplay; if(shouldplay != 0) { spork ~ play(shouldplay, i, q); } } } } return shouldplay; } //plays the correct buffer or synth //passes in square as a parameter to add variation to synths fun void play(int shouldplay, int i, int q) { 1 -=> shouldplay; //hard-coded synth modes if(shouldplay == synthnums[0]) { Std.mtof(50)=> osc1.freq; if(beats%8==0){osc1.freq(osc1.freq()-3);} if(beats%16==0){osc1.freq(osc1.freq()-3);} adsr1.keyOn(); BEAT/2 => now; adsr1.keyOff(); BEAT/2 => now; } else if(shouldplay == synthnums[1]) { Std.mtof(47)=> osc2.freq; if(beats%5==0){osc2.freq(osc2.freq()-3);} adsr2.keyOn(); BEAT/2 => now; adsr2.keyOff(); BEAT/2 => now; } else if(shouldplay == synthnums[2]) { 220=> osc3.freq; if(beats%7==4 || beats % 13==12){osc3.freq(osc3.freq()+12);} adsr3.keyOn(); BEAT/2 => now; adsr3.keyOff(); BEAT/2 => now; } else { //sndbuf mode 1 => my_buffers[shouldplay].gain; 0 => my_buffers[shouldplay].pos; } } //takes input from unity and saves it for our audio generation //by the time this event is broadcast, all the boxes arrays should be updated. we will use them to update our jagged array. fun void OnSquareClick(Event e) { while(true) { e => now; boxes1 @=> BOXES[1]; boxes2 @=> BOXES[2]; boxes3 @=> BOXES[3]; boxes4 @=> BOXES[4]; boxes5 @=> BOXES[5]; boxes6 @=> BOXES[6]; boxes7 @=> BOXES[7]; boxes8 @=> BOXES[8]; boxes9 @=> BOXES[9]; boxes10 @=> BOXES[10]; boxes11 @=> BOXES[11]; boxes12 @=> BOXES[12]; boxes13 @=> BOXES[13]; boxes14 @=> BOXES[14]; boxes15 @=> BOXES[15]; boxes16 @=> BOXES[16]; } } //update new beat duration fun void OnBPMChange(Event e) { while(true) { e => now; //convert int from unity into a dur var BEAT_int::ms => BEAT; } }