// hw3.ck // Jonathan Lin // ------------------------------------------------------------- // 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 [77, 72, 74, 72, 77, 72, 74, 72, 77, 72, 74, 72, 69, 70, 72, 72] @=> int key1a[]; [72, 69, 70, 69, 72, 69, 70, 69, 72, 69, 70, 69, 65, 67, 69, 69] @=> int key1b[]; [69, 65, 65, 65, 69, 65, 65, 65, 69, 65, 65, 65, 60, 64, 65, 65] @=> int key1c[]; [65, 64, 65, 67, 69, 67, 69, 70, 72, 74, 72, 70, 69, 67, 69, 70, 69, 70, 72, 70, 69, 67, 65, 64, 65, 64, 62, 64] @=> int key2[]; dac => WvOut2 w => blackhole; w.wavFilename(me.dir() + "hw3.wav"); now + 20::second => time later1; later1 + 25450::ms => time later2; // 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!) 150::ms => dur minIoi; playThreeArray(key1a, key1b, key1c, later1); playOneArray(key2, later2); <<<"Program over.">>>; fun void playThreeArray(int keyn1[], int keyn2[], int keyn3[], time total){ // which pitch is next 0 => int p; // which instrument is next 0 => int i; // how many pitches are in the array keyn1.cap() => int nP; //Ints for the start and end gains 0.1 => float startGain; 0.9 => float endGain; // ------------------------------------------------------------- // 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 fm1[nI]; float cAmp1[nI]; float cADSR1[nI][0]; float mRatio1[nI]; float mGain1[nI]; float mADSR1[nI][0]; for (int i; i < nI; ++i){ [.01,.4,.5,.1] @=> cADSR1[i]; 2 => mRatio1[i]; mRatio1[i] + 2.0*i => mRatio1[i]; 1.0 => mGain1[i]; mGain1[i] + 5.0*i => mGain1[i]; <<>>; [.01,.4,1.0,.1] @=> mADSR1[i]; fm1[i].out => dac.chan(i%2); } FMFS fm2[nI]; float cAmp2[nI]; float cADSR2[nI][0]; float mRatio2[nI]; float mGain2[nI]; float mADSR2[nI][0]; for (int i; i < nI; ++i){ [.01,.4,.5,.1] @=> cADSR2[i]; 2 => mRatio2[i]; mRatio2[i] + 2.0*i => mRatio2[i]; 1.0 => mGain2[i]; mGain2[i] + 5.0*i => mGain2[i]; <<>>; [.01,.4,1.0,.1] @=> mADSR2[i]; fm2[i].out => dac.chan(i%2); } FMFS fm3[nI]; float cAmp3[nI]; float cADSR3[nI][0]; float mRatio3[nI]; float mGain3[nI]; float mADSR3[nI][0]; for (int i; i < nI; ++i){ [.01,.4,.5,.1] @=> cADSR3[i]; 2 => mRatio3[i]; mRatio3[i] + 2.0*i => mRatio3[i]; 1.0 => mGain3[i]; mGain3[i] + 5.0*i => mGain3[i]; <<>>; [.01,.4,1.0,.1] @=> mADSR3[i]; fm3[i].out => dac.chan(i%2); } while (now < total) { // print pitch index, instrument index <<< "P =", p, "\tI =", i >>>; Std.mtof(keyn1[p]) => float cFreq1; Std.mtof(keyn2[p]) => float cFreq2; Std.mtof(keyn3[p]) => float cFreq3; // assign pitch spork ~fm1[i].playFM(duration, cFreq1, cADSR1[i], mRatio1[i], mGain1[i], mADSR1[i]); spork ~fm2[i].playFM(duration, cFreq2, cADSR2[i], mRatio2[i], mGain2[i], mADSR2[i]); spork ~fm3[i].playFM(duration, cFreq3, cADSR3[i], mRatio3[i], mGain3[i], mADSR3[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; //Increase gain over time if(startGain < endGain){ startGain * 1.1 => startGain; mGain1[i] * 1.1 => mGain1[i]; mGain2[i] * 1.1 => mGain2[i]; mGain3[i] * 1.1 => mGain3[i]; }else{ } } } fun void playOneArray(int keyn[], time total){ // which pitch is next 0 => int p; // which instrument is next 0 => int i; // 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%2); } while (now < total) { // 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 w.closeFile();