// @title hw3-exampleScore.ck // @author Chris Chafe (cc@ccrma), Hongchan Choi (hongchan@ccrma) // @desc An example score file for homework 3, Music220a-2012 // @version chuck-1.3.2.0 // @revision 2 // pick a physical model synth, send to left chan (0) Mandolin mand => JCRev r => Echo a => Echo b => Echo c => dac.left; // set the mandolin pluck position .9 => mand.pluckPos; // set the gain .95 => r.gain; // set the reverb mix .05 => r.mix; // set max delay for echo 1000::ms => a.max => b.max => c.max; // set delay for echo 750::ms => a.delay => b.delay => c.delay; // set the initial effect mix 0.25 => a.mix => b.mix => c.mix; // scale fun int scale(float in) { [ 0, 3, 5, 7, 10, 12] @=> int scale[]; int index; Math.round( in*5 ) $ int => index; if ( index < 0 ) { return -scale[-index]; } else { return scale[index]; } } // bring in a mono .wav file from which to play sample // pass through Envelope to avoid clicks from SndBuf // to right chan (1) SndBuf sampler => Envelope samplerEnv => JCRev r_s => Echo q => dac.right; .95 => r_s.gain; // set the reverb mix .25 => r_s.mix; 1000::ms => q.max; 750::ms => q.delay; 0.25 => q.mix; me.sourceDir() + "/sample.wav" => string _file; _file => sampler.read; cherr <= "[score] SndBuf: " <= _file <= IO.newline(); 10::ms => samplerEnv.duration; // write signals to file dac => WvOut2 out => blackhole; me.sourceDir() + "/exampleScore.wav" => string _capture; _capture => out.wavFilename; // ------------------------------------------------------------ // three algorithms which generate numbers in range -1.0 to 1.0 // see 1) logisticMapNotes.ck 2) logisticMapNotesOSC.ck for // more info fun float logistic(float x, float r) { return r * x * (1.0 - x); } // see 1) psuedoRandomNotes.ck 2) psuedoRandomNotesOSC.ck for // more info fun float random(float r) { if (r > 1.0) { 1.0 => r; } // get a random value return Math.random2f(-r, r); } // see 1) sinNotes.ck 2) sinNotesOSC.ck fun float sinusoid(float r) { // rate in radians at 1 sec period 2 * pi => float sinScale; // convert to desired rate in radians if (r > 0.0) r /=> sinScale; // current time in seconds now / second => float nowInSeconds; // convert to phase angle using rate in radians nowInSeconds * sinScale => float currentPhase; // or could be another trig function return Math.sin(currentPhase); } public class lorenzAttractor { [0.1, 0.0, 0.0] @=> float lorenz_state[]; [10000.0, 10000.0, 10000.0] @=> float mins[]; [-10000.0, -10000.0, -10000.0] @=> float maxes[]; 10.0 => float a; 28.0 => float b; 8.0 / 3.0 => float c; 0.01 => float t; fun float[] tick() { lorenz_state[0] => float x; lorenz_state[1] => float y; lorenz_state[2] => float z; //<<>>; x + t * a * (y - x) => float xt; y + t * (x * (b - z) - y) => float yt; z + t * (x * y - c * z) => float zt; xt => lorenz_state[0]; yt => lorenz_state[1]; zt => lorenz_state[2]; update_stats(); } fun void init() { for ( 0 => int i; i < 100 ; i++ ){ tick(); } } fun void update_stats() { float var; for ( 0 => int i; i < 3 ; i++ ){ lorenz_state[i] => var; //update maxes if (var > maxes[i]) { var => maxes[i]; } //update mins if (var < mins[i]) { var => mins[i]; } } } fun float x(){ (lorenz_state[0] - mins[0]) / (maxes[0] - mins[0]) => float x; return x; } fun float y(){ (lorenz_state[1] - mins[1]) / (maxes[1] - mins[1]) => float y; return y; } fun float z(){ (lorenz_state[2] - mins[2]) / (maxes[2] - mins[2]) => float z; return z; } } // ------------------------------------------------------------ // tick the chosen algorithm to get a new value fun float algorithm(float state, string type, float r) { if (type == "logistic") { // choose me to iterate the map and hear logistic map "tune" return logistic(state, r); } else if (type == "random") { // choose me for random "tune" return random(r); } else if (type == "sinusoid") { // choose me for sin function "tune" return sinusoid(r); } else { cherr <= "[score] Unknown algorithm type.\n"; } } // sporkable function that plays physical model notes forever fun void pLoop(string type, float r) { 0.1 => float state; lorenzAttractor lorenz; lorenz.init(); while (true) { algorithm(state, type, r) => state; // use state as a frequency scale(state) => int freq; 220.0 * Math.pow( 1.05946, (Math.random2(0,2)*12) + freq ) => mand.freq; //use lorenz_state for pluck "strength," play length?, and pluck pos lorenz.tick(); // pluck it! //<<< lorenz.x() >>>; lorenz.x() => mand.pluck; .5 => mand.pluckPos; //100 => mand.bodySize; if( Math.randomf() > 0.8 ) { 2*500::ms => now; } else if( Math.randomf() > 6 && Math.randomf() > .925 ) { 2*250::ms => now; } else if( Math.randomf() > 0.05 ) { 2*.125::second => now; } else { 1 => int i => int pick_dir; // how many times 4 * Math.random2( 1, 5 ) => int pick; 0.0 => float pluck; 0.7 / pick => float inc; // time loop for( ; i < pick; i++ ) { 75::ms => now; Math.random2f(.2,.3) + i*inc => pluck; pluck + -.2 * pick_dir => mand.pluck; // simulate pluck direction !pick_dir => pick_dir; } // let time pass for final pluck 75::ms => now; } 50::ms => now; } } // sporkable function that plays sampler notes forever fun void sLoop(string type, float r) { 0.1 => float state; while (true) { // turn off envelope in case it's already on samplerEnv.target(0.0); // a short rest 50::ms => now; algorithm(state, type, r) => state; sampler.length() => dur totalFile; // time in file to start playing sample from ((state + 1.0) * 0.5) * totalFile => dur start; // sample duration 250::ms => dur duration; // end time of sample start + duration => dur end; if (end > totalFile) { end - duration => start; } // start position in file in actual samples sampler.pos((start / samp) $ int); // ramp up the envelope samplerEnv.target(1.0); sampler.rate(.7); // OPTIONAL ---------------------------------------- // play around with // .rate - (float, READ/WRITE) set/get playback rate // relative to file's natural speed. Try the // range between [-1, 1] and experiment. duration => now; } } // ------------------------------------------------------------ // prepare the show Shred pShred; Shred sShred; // needed to stop both shreds gracefully fun void killShreds() { pShred.exit(); sShred.exit(); me.yield(); // note off //physmod.stopBlowing(1.0); // turn off envelope samplerEnv.target(0.0); 10::ms => now; } lorenzAttractor lorenz2; lorenz2.init(); //spork ~pLoop("sinusoid", 3.9) @=> pShred; //15::second => now; //killShreds(); //1::second => now; //spork ~sLoop("sinusoid", 3.6) @=> sShred; // samples pulled randomly //25::second => now; //killShreds(); //.1::second => now; fun void conduct() { now+100::second => time later; while(now < later) { lorenz2.y() => float y; lorenz2.x() => float x; string type; if (x < .3) { "logistic" => type; } else if( x < .7) { "sinusoid" => type; } else { "random" => type; } if (y < .3) { spork ~pLoop(type, 3.9) @=> pShred; } else if (y < .7) { spork ~sLoop(type, 3.9) @=> sShred; spork ~pLoop(type, 3.9) @=> pShred; } else { spork ~sLoop(type, 3.9) @=> sShred; } lorenz2.tick(); <<>>; <<>>; <<>>; lorenz2.z()::second => now; killShreds(); } } conduct(); // ------------------------------------------------------------ //start the show, this is just an example score with changing sections //spork ~pLoop("sinusoid", 0.8) @=> pShred; //spork ~sLoop("sinusoid", 0.8) @=> sShred; //12::second => now; //killShreds(); //0.05::second => now; //spork ~sLoop("logistic", 7) @=> sShred; // samples pulled randomly //2::second => now; //killShreds(); //.1::second => now; //spork ~pLoop("sinusoid", 0.8) @=> sShred; //5::second => now; //spork ~sLoop("logistic", 1.2) @=> sShred; // samples pulled randomly //3::second => now; //killShreds(); //spork ~pLoop("logistic", 3.9) @=> pShred; //15::second => now; //killShreds(); //1::second => now; // ------------------------------------------------------------ // finish the show out.closeFile(); // print message in terminal for sox command cherr <= "[score] Finished.\n"; cherr <= me.sourceDir() + "/exampleScore.wav";