// @title hw3.ck // @author Tim O'Brien (tsob@ccrma), // following example code by Chris Chafe (cc@ccrma), Hongchan Choi (hongchan@ccrma) // @desc Score file for homework 3, Music220a-2012 // @version chuck-1.3.1.3 / ma-0.2.2c // @milestone 1 Mandolin physmod => NRev rev => dac.left; 0.025 => physmod.stringDetune; 0.42 => physmod.bodySize; 0.03 => rev.mix; SndBuf sampler => Envelope samplerEnv => dac.right; me.sourceDir() + "/sample.wav" => string _file; _file => sampler.read; cherr <= "[score] SndBuf: " <= _file <= IO.newline(); 10::ms => samplerEnv.duration; dac.left => WvOut physmodOut => blackhole; dac.right => WvOut samplerOut => blackhole; me.sourceDir() + "/phys.wav" => string _captureP; me.sourceDir() + "/samp.wav" => string _captureS; _captureP => physmodOut.wavFilename; _captureS => samplerOut.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); } fun float random(float r) { if (r > 1.0) { 1.0 => r; } // get a random value return Math.random2f(-r, r); } fun float sinusoid(float r) { 2 * pi => float sinScale; if (r > 0.0) r /=> sinScale; now / second => float nowInSeconds; nowInSeconds * sinScale => float currentPhase; return Math.sin(currentPhase); } // ------------------------------------------------------------ // Scale function: // take a variable between -1 and 1 and map to scale degrees // in relative midi notes [0, 3, 5, 7, 10, 12] @=> int penta[]; [0, 2, 3, 5, 7, 8, 10, 12] @=> int minor[]; [0, 2, 3, 5, 7, 8, 11, 12] @=> int harmMinor[]; fun int scale(string type, float in) { int index; if (type == "pentatonic") { Math.round( in*5 ) $ int => index; if ( index < 0 ) { return -penta[-index]; } else { return penta[index]; } } else if (type == "minor") { Math.round( in*7 ) $ int => index; if ( index < 0 ) { return -minor[-index]; } else { return minor[index]; } } else if (type == "harmMinor") { Math.round( in*7 ) $ int => index; if ( index < 0 ) { return -harmMinor[-index]; } else { return harmMinor[index]; } } else if (type == "chromatic") { Math.round( in*12 ) $ int => index; return index; } else { cherr <= "[scale] Unknown scale type.\n"; } } // tick the chosen algorithm to get a new value fun float algorithm(float state, string type, float r) { if (type == "logistic") { return logistic(state, r); } else if (type == "random") { return random(r); } else if (type == "sinusoid") { return sinusoid(r); } else if (type == "constant") { return r; } else { cherr <= "[score] Unknown algorithm type.\n"; } } // sporkable function that plays physical model notes forever // Can control frequency, pluck position, and rhythm via arguments. fun void pLoop(string ftype, float fr, string ptype, float pr, string rtype, float rr, int rootnote, string scalename) { 0.1 => float fstate; 0.1 => float pstate; 0.1 => float rstate; while (true) { // a short rest 50::ms => now; algorithm(fstate,ftype,fr) => fstate; algorithm(pstate,ptype,pr) => pstate; algorithm(rstate,rtype,rr) => rstate; // use pstate for pluck position (pstate*0.2 + 0.8) => pstate; physmod.pluckPos(pstate); // use fstate for frequency Std.mtof( rootnote + scale(scalename, fstate) ) => float f; physmod.freq(f); //use rstate for rhythm timing and to modulate pluck velocity and string damping. physmod.stringDamping(rstate*0.3+0.7); physmod.pluck(rstate*0.35+0.65); physmod.pluck(0.8); (rstate*150 + 190)::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 100::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); duration => now; } } // Another sporkable function that plays sampler notes forever fun void sLoop2(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 //100::ms => dur duration; (Math.random2f(1, 4) * 50)::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); samplerEnv.target(1.0); duration => now; } } // One final sporkable function that plays sampler notes forever fun void sLoop3(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 //100::ms => dur duration; //(Math.random2f(1, 4) * 50)::ms => dur duration; (Math.random2f(60, 200))::ms => dur duration; // end time of sample start + duration => dur end; if (end > totalFile) { end - duration => start; } sampler.pos((start / samp) $ int); // ramp up the envelope samplerEnv.target(1.0); duration => now; } } // ------------------------------------------------------------ // prepare the show Shred pShred; Shred sShred; // needed to stop both shreds gracefully fun void killShreds() { pShred.exit(); sShred.exit(); me.yield(); // turn off envelope samplerEnv.target(0.0); 10::ms => now; } // ------------------------------------------------------------ // start the show //Borrow the musical form from Ferretti's piece: // A: solo intro spork ~pLoop("logistic", 3.9, "sinusoid", 0.2, "sinusoid", 3, 57, "pentatonic") @=> pShred; 5::second => now; killShreds(); 1000::ms => now; spork ~pLoop("logistic", 3.825, "sinusoid", 2.0, "logistic", 3.2, 69, "minor") @=> pShred; 5::second => now; killShreds(); 1000::ms => now; // B: second instrument entrance and solo spork ~sLoop("logistic", 3.9) @=> sShred; 5::second => now; killShreds(); spork ~sLoop("logistic", 3.97) @=> sShred; 5::second => now; killShreds(); // C: duo to end containing moments of sync interspersed between lots of independent playing. spork ~sLoop3("logistic", 3.9) @=> sShred; spork ~pLoop("sinusoid", 2.0, "sinusoid", 1.0, "sinusoid", 11.0, 57, "harmMinor") @=> pShred; 10::second => now; killShreds(); 100::ms => now; // Come together with same constant rhythm here... spork ~sLoop("logistic", 3.79) @=> sShred; spork ~pLoop("logistic", 3.72, "sinusoid", 5.0, "constant", -0.6, 57, "minor") @=> pShred; 10::second => now; killShreds(); 100::ms => now; spork ~pLoop("random", 3.9, "sinusoid", 0.22, "sinusoid", 2.8, 57, "chromatic") @=> pShred; spork ~sLoop("random", 0.8) @=> sShred; 7::second => now; killShreds(); spork ~sLoop3("logistic", 3.9) @=> sShred; spork ~pLoop("sinusoid", 0.5, "sinusoid", 0.5, "constant", -1.2, 69, "harmMinor") @=> pShred; 5::second => now; killShreds(); spork ~sLoop3("logistic", 3.9) @=> sShred; spork ~pLoop("sinusoid", 2.0, "sinusoid", 1.0, "sinusoid", 12.0, 57, "harmMinor") @=> pShred; 10::second => now; killShreds(); spork ~sLoop("logistic", 3.79) @=> sShred; spork ~pLoop("logistic", 3.72, "sinusoid", 5.0, "constant", -0.6, 57, "pentatonic") @=> pShred; 10::second => now; killShreds(); 100::ms => now; // just mower... spork ~sLoop("sinusoid", 1) @=> sShred; 4::second => now; killShreds(); // together again... spork ~sLoop("logistic", 3.95) @=> sShred; spork ~pLoop("logistic", 3.95, "sinusoid", 5.0, "constant", -0.6, 57, "pentatonic") @=> pShred; 10::second => now; killShreds(); spork ~pLoop("logistic", 3.9, "sinusoid", 0.2, "sinusoid", 3, 57, "pentatonic") @=> pShred; spork ~sLoop("logistic", 3.82) @=> sShred; 5::second => now; killShreds(); 100::ms => now; spork ~pLoop("sinusoid", 2.0, "sinusoid", 1.0, "sinusoid", 12.0, 57, "harmMinor") @=> pShred; 2::second => now; killShreds(); 100::ms => now; // revisit melody from beginning spork ~pLoop("logistic", 3.9, "sinusoid", 0.2, "sinusoid", 3, 57, "pentatonic") @=> pShred; spork ~sLoop("sinusoid", 1) @=> sShred; 5::second => now; killShreds(); // pad with a bit of silence at the end. 2::second => now; // ------------------------------------------------------------ // finish the show physmodOut.closeFile(); samplerOut.closeFile(); // print message in terminal for sox command cherr <= "[score] Finished. Merge two products with the command below.\n"; cherr <= "sox -M " <= _captureP <= " " <= _captureS <= " "; cherr <= me.sourceDir() + "/Milestone.wav";