// @title hw3-starter.ck // @author Chris Chafe (cc@ccrma), Hongchan Choi (hongchan@ccrma), Madeline Huberth (mhuberth@ccrma) // @desc A starter code for homework 3, Music220a-2016 // @note a demonstration/template for auditory streaming // ------------------------------------------------------------- // This code creates three FM 'instruments' that, when played // at a slow rate, create one 'auditory stream'. When the tempo is sped up, however, // different rhythms and melodic groupings pop out of the texture, due to your mind's // grouping of the sounds. // ------------------------------------------------------------- // array to hold midi pitches (key numbers) // these will be converted into the carrier frequencies [60, 62, 64, 65] @=> int keyn[]; // how many pitches are in the array keyn.cap() => int nP; // ------------------------------------------------------------- // against a cycle of a different length which we'll use to vary // instrument parameters nP - 1 => int nI; // arrays to hold modulation frequency, timing of modulation envelope, // and timing/gain of carrier ADSRs // really, anything that we want to use to break the repeating pitches // into multiple streams // instantiate UGens, make arrays for carrier amplitude & amplitude envelope, modulation frequency & gain FMFS fm[nI]; float cAmp[nI]; float cADSR[nI][0]; float mRatio[nI]; float mGain[nI]; float mADSR[nI][0]; for (int i; i < nI; ++i) { [.01,.4,.5,.1] @=> cADSR[i]; 2 => mRatio[i]; mRatio[i] + 2.0*i => mRatio[i]; 1.0 => mGain[i]; mGain[i] + 5.0*i => mGain[i]; <<>>; [.01,.4,1.0,.1] @=> mADSR[i]; fm[i].out => dac.chan(i%1); } // ------------------------------------------------------------- // global parameters // set a common note duration 100::ms => dur duration; // starting inter-onset interval (inverse of tempo) 800::ms => dur ioi; // accelerate to this smallest IOI (inter-onset-interval - the length of silence between notes!) 180::ms => dur minIoi; // which pitch is next 0 => int p; // which instrument is next 0 => int i; // infinite loop while (true) { // print pitch index, instrument index <<< "P =", p, "\tI =", i >>>; Std.mtof(keyn[p]) => float cFreq; // assign pitch spork ~fm[i].playFM(duration, cFreq, cADSR[i], mRatio[i], mGain[i], mADSR[i]); // increment note and instrument p++; i++; // cycle pitch through full array nP %=> p; // cycle instrument through full array nI %=> i; // advance time by interval and calculate the next time interval ioi => now; // accelerate if (ioi > minIoi) ioi * 0.95 => ioi; else // can't go faster than minIoi minIoi => ioi; } // ------------------------------------------------------------- // @class FMFS // fm implementation from scratch with envelopes // @author 2015 Madeline Huberth, 2016 version by CC class FMFS { // two uses of ADSR envelope... Step unity => ADSR envM => blackhole; //...as a separate signal SinOsc mod => blackhole; SinOsc car => ADSR envC => Gain out; //..as a modifier of a signal car.gain(0.2); float freq, index, ratio; fun void fm() { while (true) { envM.last() * index => float indexEnv; mod.gain( freq * indexEnv ); mod.freq( freq * ratio ); car.freq( freq + mod.last() ); 1::samp => now; } } spork ~fm(); //function to play a note on our FM instrument //this function fun void playFM( dur length, float cFreq, float cADSR[], float mRatio, float mGain, float mADSR[] ) { //set frequency values cFreq => freq; mRatio => ratio; mGain => index; //open and close envelopes spork ~ playEnv( envC, length, cADSR ); spork ~ playEnv( envM, length, mADSR ); length => now; } fun void playEnv( ADSR env, dur length, float adsr[] ) { //get values for carrier ADSR envelope length * adsr[0] => dur A; length * adsr[1] => dur D; adsr[2] => float S; length * adsr[3] => dur R; //set ADSR envelope for envc env.set( A, D, S, R ); // open envelope (start attack) env.keyOn(); // wait through A+D+S, before R length-env.releaseTime() => now; // close envelope (start release) env.keyOff(); // wait for release env.releaseTime() => now; } } // END OF CLASS: FM