// 220a first-order ambisonic encode for omnitone decode // chuck -s --srate:48000 quadBeeps2foa.ck "/Users/gabrielhernandez/Documents/" => string directory; "HW3ambi" => string basename; 114::second => dur fileLength; /* directory, basename and fileLength must be set before running otherwise the script exits "/Users/kermit/Desktop/" => string basename; -- must end with '/' "test-" => string basename; 10::second => dur fileLength; */ if((directory=="")||(basename=="")||(fileLength==0::samp)) { <<<"directory, basename and fileLength must be set before running, directory =",directory,"basename =",basename," fileLength =",fileLength/second>>>; me.exit(); } 4 => int nChans; // @title hw4-starter.ck // @author Chris Chafe (cc@ccrma), Hongchan Choi (hongchan@ccrma), Madeline Huberth (mhuberth@ccrma) // @desc A starter code for homework 3, Music220a-2015 // @note a demonstration/template for auditory streaming // ------------------------------------------------------------- // This code by default 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. // ------------------------------------------------------------- // how many pitches to play in a cycle 6 => int nPitches; // array to hold midi pitches (key numbers) // these will be converted into the carrier frequencies int keyn[nPitches]; [65, 60, 57, 64, 69, 72] @=> keyn; // ------------------------------------------------------------- // against a cycle of a different length which we'll use to vary // instrument parameters 4 => int nInsts; // 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 // modulation frequency [60.0, 80.0, 120.0, 200.0] @=> float mfreq[]; // modulator gain [200, 400, 100, 50] @=> int mgain[]; // modulator peak point [.1, .3, .1, .3] @=> float mPeakPoint[]; // array of carrier ADSR parameter arrays [[.3,.3,.7,.5], [.3,.3,.7,.5], [.3,.3,.7,.5], [.3,.3,.7,.5] ] @=> float cADSR[][]; // instantiate UGens FMFS fm[nInsts]; // assign them to different dac channels //for (int i; i < nInsts; ++i) { //fm[i].out => dac.chan(i%4); //chout <= i; //} // ------------------------------------------------------------- // global parameters // set a common note duration 50::ms => dur duration; // starting inter-onset interval (inverse of tempo) 600::ms => dur ioi; // accelerate to this smallest IOI (inter-onset-interval - the length of silence between notes!) 1::ms => dur minIoi; // which pitch is next 0 => int p; // which instrument is next 0 => int i; // infinite loop // ------------------------------------------------------------- // @class FMFS // fm implementation from scratch with envelopes // @author Madeline Huberth - from 220a HW3 lab, slightly modified class FMFS { SinOsc m => Envelope envm => SinOsc c => ADSR envc => Gain out => blackhole; //modulator to carrier //set to do fm synthesis 2 => c.sync; //play function for our FM instrument //this function fun void playFM( float mfreq, float cfreq, float mgain, dur length, float mPeakPoint, float cADSR[] ) { //set frequency values cfreq => c.freq; mfreq => m.freq; //open and close envelopes spork ~ playEnvm( mgain, length, mPeakPoint ); spork ~ playEnvc( length, cADSR ); length => now; } fun void playEnvm( float mgain, dur length, float mPeakPoint ) { //get value of a half length * mPeakPoint => dur mPeakPointDur; //set target value for envelope for env1 envm.target( mgain ); //set time to reach target envm.duration( mPeakPointDur ); //turn on the modulator! envm.keyOn(); mPeakPointDur => now; envm.keyOff(); length - mPeakPointDur => now; } fun void playEnvc( dur length, float cADSR[] ) { //get values for carrier ADSR envelope length * cADSR[0] => dur A; length * cADSR[1] => dur D; cADSR[2] => float S; length * cADSR[3] => dur R; //set ADSR envelope for envc envc.set( A, D, S, R ); // open envelope (start attack) envc.keyOn(); // wait through A+D+S, before R length-envc.releaseTime() => now; // close envelope (start release) envc.keyOff(); // wait for release envc.releaseTime() => now; } } // END OF CLASS: FM /////////////////////////////////////////////////// class foa{ 4 => int nFOAchans; Gain out[nFOAchans]; // w,x,y,z 4 => int nSrcs; Gain src[]; // empty until init is called float theta[]; // source angles Step srcOut[][]; // each source's FOA signals fun void init (float srcAngles[]) // initialize encoder with all source angles { srcAngles.cap() => nSrcs; srcAngles @=> theta; Gain tmp1[nSrcs] @=> src; Step tmp2[nSrcs][nFOAchans] @=> srcOut; for (0 => int i; i blackhole; // signal in for (0 => int j; j out[j]; // mix to foa out Math.sin(theta[i]) => float xcoeff; Math.cos(theta[i]) => float ycoeff; while(true) { srcOut[i][0].next( src[i].last() ); // w srcOut[i][1].next( src[i].last() * xcoeff ); // x srcOut[i][2].next( src[i].last() * ycoeff ); // y srcOut[i][3].next( 0.0 ); // z 1::samp => now; } } } /////////////////////////////////////////////////// // list all incoming source angles we want to encode 3.14159265359 / 4.0 => float piOverFour; [piOverFour, 3.0*piOverFour, 5.0*piOverFour, -piOverFour] @=> float thetas[]; foa encoder; encoder.init(thetas); SawOsc x => PRCRev rev => Pan2 p1 => encoder.src[3]; for (0 => int i; i encoder.src[i]; } WvOut w[4]; ["w","x","y","z"] @=> string ambiName[]; // omnitone script will convert to ACN for (0 => int i; i<4; i++) w[i].wavFilename(directory+basename+ambiName[i]+".wav"); for (0 => int i; i w[i] => blackhole; for (0 => int i; i<4; i++) <<<"encoding to",(directory+basename+ambiName[i]+".wav")>>>; now + fileLength => time quit; <<<"for",fileLength/second,"seconds">>>; // create an infinite pattern that rotates around the speakers while (now < quit) { <<<"now: ", now, " quit: ", quit>>>; 0 => p1.pan; rev.mix(0.30); x.freq(50); 0 => float bGain; x.gain(bGain); while (bGain < 0.6) { // print pitch index, instrument index chout <= "PITCH = " <= p <= "\t\t"; chout <= "INST. = " <= i <= "\t\t"; chout <= "bGain = " <= bGain <= IO.newline(); Std.mtof(keyn[p]) => float cfreq; // assign pitch fm[i].playFM(mfreq[i],cfreq,mgain[i],duration,mPeakPoint[i],cADSR[i]); // increment note and instrument p++; i++; // cycle pitch through full array nPitches %=> p; // cycle instrument through full array nInsts %=> i; // advance time by interval and calculate the next time interval ioi => now; // accelerate ioi * 0.98 => ioi; // can't go faster than minIoi if (ioi < minIoi) { minIoi => ioi; x.gain(bGain+.002); bGain + .002 => bGain; } if (bGain > .28) { p1.pan() + .02 => p1.pan; if (p1.pan() > 1) 1 => p1.pan; } if (bGain > .5) { p1.pan() - .02 => p1.pan; if (p1.pan() < -1) -1 => p1.pan; } } [65, 60, 56, 63, 68, 72] @=> keyn; while (bGain > 0.002) { // print pitch index, instrument index chout <= "PITCH = " <= p <= "\t\t"; chout <= "INST. = " <= i <= "\t\t"; chout <= "bGain = " <= bGain <= IO.newline(); Std.mtof(keyn[p]) => float cfreq; // assign pitch fm[i].playFM(mfreq[i],cfreq,mgain[i],duration,mPeakPoint[i],cADSR[i]); // increment note and instrument p++; i++; // cycle pitch through full array nPitches %=> p; // cycle instrument through full array nInsts %=> i; // advance time by interval and calculate the next time interval ioi => now; // deccelerate ioi * 1.02 => ioi; // can't go faster than minIoi x.gain(bGain-.0018); bGain - .0018 => bGain; if (bGain > .45) { p1.pan() - .03 => p1.pan; if (p1.pan() < -1) -1 => p1.pan; } if (bGain < .4) { p1.pan() + .01 => p1.pan; if (p1.pan() > 1) 1 => p1.pan; } } } <<<"done writing",nChans,"sources into 4 mono ambisonic component files">>>; for (0 => int i; i<4; i++) w[i].closeFile(); // last steps when this is done, // cd to // be certain that there are only the 4 recent basename(d) files when you execute // ls * // then, execute // sox -M * -c4 ambi.wav // copy ambi.wav to the directory where omnitone will serve it from // usually ~/Library/Web/220a/ // rename ambi.wav to the unique name referred to in your .html -- for example, hw2ambi.wav --