// @title amplitudeTracker.ck // @author Chris Chafe (cc@ccrma), Hongchan Choi (hongchan@ccrma), Reza Payami (rpayami@ccrma) // @desc amplitude tracking using UAna ugens and FM // @version chuck-1.3.1.3 / ma-0.2.2c // @revision 1 // IMPORTANT NOTE: this patch is designed to use microphone. // If you're using speakers and your microphone at the same time, // you might experience serious feedback. Make sure to use // the headphone or earbuds to avoid it. // pipe input into analysis audio graph: adc => FFT fft =^ RMS rms => blackhole; // choose high-quality transform parameters 4096 => fft.size; Windowing.hann(fft.size()/2) => fft.window; 20 => int overlap; 0 => int ctr; // actual audio graph and parameter setting // NOTE: gain 'g' prevents direct connection bug adc => Gain g; // instantiate a smoother to smooth tracker results (see below) Smooth sma; // set time constant: shorter time constant gives faster // response but more jittery values sma.setTimeConstant((fft.size() / 2)::samp); 3 => int nInsts; // loudness (100dB = loudest) [80.0, 80.0, 80.0] @=> float loud[]; // fmIndex [333.0, 333.0, 333.0] @=> float fmIndex[]; // fmRatio [300.3, 439.3, 5023.5] @=> float fmRatio[]; // instantiate UGens FM fm[nInsts]; for (0 => int i; i < nInsts; i++) { fm[i].out => dac; fm[i].setPitch(0); // assign loudness after converting to amplitude fm[i].out.gain(Math.dbtorms(loud[i])); } // setBlowingPressure() spork ~ setBlowingPressure(); fun void setBlowingPressure() { while (true) { if (sma.getLast() > 0.1) { for (0 => int i; i < nInsts; i++) { Std.rand2f(1.0, 10.0) => float fmIndex; fm[i].setIndex(fmIndex); Std.rand2f(1.0, 10.0) => float fmRatio; fm[i].setRatio(fmRatio); Std.rand2f(1, 127) => float pitch; fm[i].setPitch(pitch); fm[i].attack(10::ms); } } else { for (0 => int i; i < 3; i++) { fm[i].release(4000::ms); } } 1::samp => now; } } // main inf-loop while(true) { // hop in time by overlap amount (fft.size() / overlap)::samp => now; // then we've gotten our first bufferful if (ctr > overlap) { // compute the RMS analysis rms.upchuck(); rms.fval(0) => float a; Math.rmstodb(a) => float db; // boost the sensitity 75 +=> db; // but clip at maximum Math.min(100, db) => db; sma.setNext(Math.dbtorms(db)); } ctr++; } // @class Smooth // @desc contral signal generator for smooth transition class Smooth { // audio graph Step in => Gain out => blackhole; Gain fb => out; out => fb; // init: smoothing coefficient, default no smoothing 0.0 => float coef; initGains(); // initGains() fun void initGains() { in.gain(1.0 - coef); fb.gain(coef); } // setNext(): set target value fun void setNext(float value) { in.next(value); } // getLast(): return current interpolated value fun float getLast() { 1::samp => now; return out.last(); } // setExpo(): set smoothing directly from exponent fun void setExpo(float value) { value => coef; initGains(); } // setTimeConstant(): set smoothing duration fun void setTimeConstant(dur duration) { Math.exp(-1.0 / (duration / samp)) => coef; initGains(); } } // END OF CLASS: Smooth /////////////////////////////////////////////// // ------------------------------------------------------------- // @class FMFS fm implementation from scratch with envelopes // @author Chris Chafe (cc@ccrma), Reza Payami (rpayami@ccrma) class FM { // modulator with index envelope and carrier with // amplitude envelope SinOsc mod => Gain ind => ADSR indEnv => SinOsc car; car => ADSR ampEnv => Gain out => blackhole; // unity constant for center pitch of event Step unity => Gain 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 (10::ms, 10::ms, 0.5, 100::ms); ampEnv.decayRate(0); indEnv.set (50::ms, 50::ms, 1, 100::ms); // 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); } // attack(): sculpt a note using envelopes fun void attack(dur attack) { // rise time of ADSR ampEnv.attackTime(attack); indEnv.attackTime(attack); // trigger ADSR ampEnv.keyOn(); indEnv.keyOn(); } // release() fun void release(dur release) { // release time of ADSR ampEnv.releaseTime(release); indEnv.releaseTime(release); // trigger ADSR release ampEnv.keyOff(); indEnv.keyOff(); } } // END OF CLASS: FM