// @title hw4.ck // @author Tim O'Brien (tsob@ccrma), // adapted from code by Chris Chafe (cc@ccrma) and Hongchan Choi (hongchan@ccrma) // @desc A binaural audio streaming study for homework 4, Music220a-2012 // @version chuck-1.3.1.3 / ma-0.2.2c // @revision 1 // @note depends on Binaural4.ck and DBAP4e.ck, previously provided by Chafe and Choi. // writing signals to files... dac.left => WvOut leftOut => blackhole; dac.right => WvOut rightOut => blackhole; me.sourceDir() + "/left_final.wav" => string _captureL; me.sourceDir() + "/right_final.wav" => string _captureR; _captureL => leftOut.wavFilename; _captureR => rightOut.wavFilename; // ------------------------------------------------------------- // play a cycle of pitches... 5 => int nPitches; // array to hold midi pitches (key numbers) int keyn[nPitches]; [79, 81, 77, 65, 72] @=> keyn; // ------------------------------------------------------------- // against a cycle of a different length which varies instrument // parameters 4 => int nInsts; // a counter variable 0 => int counter; // distance from center in virtual space 0.5 => float dist; // arrays to hold loudnesses, pitch register transpositions, // channels, fm instrument parameters // loudness contour (100dB = loudest) [80.0, 80.0, 80.0, 80.0] @=> float loud[]; // pitch register contour // e.g., 2.0 for octave higher, 4.0 for two octaves, etc. [0.5, 0.5, 0.5, 0.5] @=> float tran[]; // channel contour [1, 0, 0, 1] @=> int chan[]; // fmIndex contour [1.0, 5.0, 16.0, 16.0] @=> float fmIndex[]; // fmRatio contour [1.0, 5.0, 1.0, 1.0] @=> float fmRatio[]; // skew contour [0.8, 0.1, 0.0, 0.0] @=> float skew[]; DBAP4e myout; // instantiate UGens FMFS fm[4]; for (int i; i < 4; ++i) { // start with instruments muted 0.0 => fm[i].out.gain; fm[i].out => myout; } myout.setMode("binaural"); myout.setReverb(0.05); myout.setDelayTime([5.0, 5.0, 5.0, 5.0]); myout.setPosition(Math.cos(counter*pi/2.0+pi/4.0)*dist, Math.sin(counter*pi/2.0+pi/4.0)*dist); // ------------------------------------------------------------- // global parameters // set a common note duration 500::ms => dur duration; // accelerate to this smallest duration 100::ms => dur minDur; // decelerate to this largest duration 2::second => dur maxDur; // which pitch is next 0 => int p; // which instrument is next 0 => int i; // first part while ( duration > minDur ) { Std.mtof(keyn[p]) * tran[i] => float tmp; // assign pitch fm[i].setPitch(tmp); // assign loudness after converting to amplitude fm[i].out.gain(Math.dbtorms(loud[i])); // assign modulator index fm[i].setIndex(fmIndex[i]); // assign modulator / carrier frequency ratio fm[i].setRatio(fmRatio[i]); // assign pitch skew fm[i].setSkew(skew[i]); // start note fm[i].attack(100::ms); // wait duration => now; // stop note fm[i].release(10::ms); // increment note and instrument p++; i++; // cycle pitch through full array nPitches %=> p; // cycle instrument through full array nInsts %=> i; //ioi 10::ms => now; // accelerate duration * 0.98 => duration; dist * 1.001 => dist; // can't go faster than minDur if (duration < minDur) { minDur => duration; } counter + 1 => counter; myout.setPosition(Math.cos(counter*pi/2.0+pi/4.0)*dist, Math.sin(counter*pi/2.0+pi/4.0)*dist); //myout.setPosition(-1.0, -1.0); //myout.setPosition(2.0*cos((counter%4)*pi/2.0+pi/4.0), 2.0*sin((counter%4)*pi/2.0+pi/4.0)); } counter%4 => counter; // second part while ( counter < 80 ) { Std.mtof(keyn[p]) * tran[i] => float tmp; // assign pitch fm[i].setPitch(tmp); // assign loudness after converting to amplitude fm[i].out.gain(Math.dbtorms(loud[i])); // assign modulator index fm[i].setIndex(fmIndex[i]); // assign modulator / carrier frequency ratio fm[i].setRatio(fmRatio[i]); // assign pitch skew fm[i].setSkew(skew[i]); // start note fm[i].attack(100::ms); // wait duration => now; // stop note fm[i].release(10::ms); // increment note and instrument p++; i++; // cycle pitch through full array nPitches %=> p; // cycle instrument through full array nInsts %=> i; //ioi 10::ms => now; counter + 1 => counter; myout.setPosition(Math.cos(counter*pi/2.0+pi/4.0)*dist, Math.sin(counter*pi/2.0+pi/4.0)*dist); //myout.setPosition(-1.0, -1.0); //myout.setPosition(2.0*cos((counter%4*pi/2.0+pi/4.0), 2.0*sin((counter%4)*pi/2.0+pi/4.0)); } // Switch to 6 pitches! 6 => nPitches; [79, 81, 77, 65, 72, 77] @=> keyn; [1.0, 0.5, 1.0, 0.5] @=> tran; [2.0, 5.0, 1.0, 8.0] @=> fmIndex; [4.0, 5.0, 6.0, 1.0] @=> fmRatio; counter%4 => counter; // third part while ( counter < 80 ) { Std.mtof(keyn[p]) * tran[i] => float tmp; // assign pitch fm[i].setPitch(tmp); // assign loudness after converting to amplitude fm[i].out.gain(Math.dbtorms(loud[i])); // assign modulator index fm[i].setIndex(fmIndex[i]); // assign modulator / carrier frequency ratio fm[i].setRatio(fmRatio[i]); // assign pitch skew fm[i].setSkew(skew[i]); // start note fm[i].attack(100::ms); // wait duration => now; // stop note fm[i].release(10::ms); // increment note and instrument p++; i++; // cycle pitch through full array nPitches %=> p; // cycle instrument through full array nInsts %=> i; //ioi 10::ms => now; 0.999*dist => dist; counter + 1 => counter; myout.setPosition(Math.cos(counter*pi/2.0+pi/4.0)*dist, Math.sin(counter*pi/2.0+pi/4.0)*dist); //myout.setPosition(-1.0, -1.0); //myout.setPosition(2.0*cos((counter%4)*pi/2.0+pi/4.0), 2.0*sin((counter%4)*pi/2.0+pi/4.0)); } [79, 81, 77, 65, 72, 77] @=> keyn; [1.5025, 0.5, 1.5025, 0.5] @=> tran; [2.0, 5.0, 1.0, 8.0] @=> fmIndex; [4.0, 5.0, 6.0, 1.0] @=> fmRatio; counter%4 => counter; // fourth part while ( counter < 80 ) { Std.mtof(keyn[p]) * tran[i] => float tmp; // assign pitch fm[i].setPitch(tmp); // assign loudness after converting to amplitude fm[i].out.gain(Math.dbtorms(loud[i])); // assign modulator index fm[i].setIndex(fmIndex[i]); // assign modulator / carrier frequency ratio fm[i].setRatio(fmRatio[i]); // assign pitch skew fm[i].setSkew(skew[i]); // start note fm[i].attack(100::ms); // wait duration => now; // stop note fm[i].release(10::ms); // increment note and instrument p++; i++; // cycle pitch through full array nPitches %=> p; // cycle instrument through full array nInsts %=> i; //ioi 10::ms => now; 0.999*dist => dist; counter + 1 => counter; myout.setPosition(Math.cos(counter*pi/2.0+pi/4.0)*dist, Math.sin(counter*pi/2.0+pi/4.0)*dist); //myout.setPosition(-1.0, -1.0); //myout.setPosition(2.0*cos((counter%4)*pi/2.0+pi/4.0), 2.0*sin((counter%4)*pi/2.0+pi/4.0)); } [1.0, 0.5, 1.0, 0.5] @=> tran; // fifth part while ( duration < maxDur ) { Std.mtof(keyn[p]) * tran[i] => float tmp; // assign pitch fm[i].setPitch(tmp); // assign loudness after converting to amplitude fm[i].out.gain(Math.dbtorms(loud[i])); // assign modulator index fm[i].setIndex(fmIndex[i]); // assign modulator / carrier frequency ratio fm[i].setRatio(fmRatio[i]); // assign pitch skew fm[i].setSkew(skew[i]); // start note fm[i].attack(100::ms); // wait duration => now; // stop note fm[i].release(1::ms); // increment note and instrument p++; i++; // cycle pitch through full array nPitches %=> p; // cycle instrument through full array nInsts %=> i; //ioi 10::ms => now; // decelerate duration * 1.05 => duration; dist * 0.999 => dist; // can't go slower than maxDur if (duration > maxDur) { maxDur => duration; } counter + 1 => counter; myout.setPosition(Math.cos(counter*pi/2.0+pi/4.0)*dist, Math.sin(counter*pi/2.0+pi/4.0)*dist); //myout.setPosition(-1.0, -1.0); //myout.setPosition(2.0*cos((counter%4)*pi/2.0+pi/4.0), 2.0*sin((counter%4)*pi/2.0+pi/4.0)); } 2::second => now; // ------------------------------------------------------------- // @class FMFS fm implementation from scratch with envelopes // @author Chris Chafe (cc@ccrma) class FMFS { // modulator with index envelope and carrier with // amplitude envelope SinOsc mod => Gain ind => ADSR indEnv => SinOsc car; car => ADSR ampEnv => Gain out => blackhole; // generate detailed pitch contour with skew, periodic // vibrato and random jitter // unity constant for center pitch of event Step unity => Gain pit; // add in skew offset controlled by simple Envelope Step skew => Envelope skewEnv => pit; // add in vibrato controlled by ADSR Envelope SinOsc perVib => ADSR vibEnv => pit; // add in some low-frequency randomness Noise ranVib => ResonZ lpf => pit; // apply pitch everywhere that depends on it pit => car; pit => Gain rat => mod; pit => ind; // configure modes of above UG's // config oscillator for fm input (see SinOsc class) 2 => car.sync; // freq is controlled by input only 0.0 => car.freq; // config oscillator for fm input 2 => mod.sync; // freq is controlled by input only 0.0 => mod.freq; // config gain to multiply inputs (see UG class) 3 => ind.op; // initial values setPitch(440.0); setIndex(1.0); setRatio(1.0); // set A, D, S, and R all at once ampEnv.set (22::ms, 22::ms, 0.4, 40::ms); indEnv.set (50::ms, 50::ms, 1, 100::ms); skewEnv.duration (100::ms); vibEnv.set (50::ms, 500::ms, 0.4, 100::ms); // skew in semitones setSkew(1.0); // vibrato frequency, excursion in semitones setVib(6.5, 1.0); // randomness frequency, excursion in semitones setJit(6.5, 1.0); // setPitch() fun void setPitch(float pitch) { pit.gain(pitch); } // setIndex() fun void setIndex(float index) { mod.gain(index); } // setRatio() fun void setRatio(float ratio) { rat.gain(ratio); } // setSkew() fun void setSkew(float semitones) { // units are equal-tempered semitones (Math.pow(2.0, semitones/12.0) - 1.0) => float skewAmt; skew.next( skewAmt ); } // setVib() fun void setVib(float f, float semitones) { perVib.freq(f); // scale it to equal-tempered quartertones // in each direction Math.pow(2.0, semitones/24.0) - 1.0 => float jitAmt; perVib.gain( jitAmt ); } // setJit() fun void setJit(float f, float semitones) { lpf.freq(f); // bandwidth of low-frequency filter resonance, // ok as a constant lpf.Q(1.0); // scale it to equal-tempered quartertones // in each direction Math.pow(2.0, semitones/24.0) - 1.0 => float jitAmt; // empirically scaled up to where it's noticeable 12.0 *=> jitAmt; ranVib.gain(jitAmt); } // attack(): sculpt a note using envelopes fun void attack(dur attack) { // rise time of ADSR ampEnv.attackTime(attack); indEnv.attackTime(attack); vibEnv.attackTime(attack); // duration of simple Envelope skewEnv.duration(attack); // trigger ADSR ampEnv.keyOn(); indEnv.keyOn(); vibEnv.keyOn(); // kind of counterintuitive, but skew from skewAmt // to 0 for attack skewEnv.keyOff(); } // release() fun void release(dur release) { // release time of ADSR ampEnv.releaseTime(release); indEnv.releaseTime(release); vibEnv.releaseTime(release); // duration of simple Envelope skewEnv.duration(release); // trigger ADSR release ampEnv.keyOff(); indEnv.keyOff(); vibEnv.keyOff(); // also counterintuitive, but skew from 0 // to skewAmt during note off skewEnv.keyOn(); } } // END OF CLASS: FMFS // ------------------------------------------------------------ // finish the show leftOut.closeFile(); rightOut.closeFile(); // print message in terminal for sox command cherr <= "\n[score] Finished.\nMerge two products with the command below.\n\n"; cherr <= "sox -M " <= _captureL <= " " <= _captureR <= " "; cherr <= me.sourceDir() + "/Final.wav\n\n";