//HW5 instrument 1 - adapted from CCRMA ChucK archives //Gabriele Carotti-Sha //----------------------------------------------------------------- // @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 //----------------------------------------------------------------- adc => FFT fft =^ RMS rms => blackhole; fft =^ Centroid cent => blackhole; //FFT setup 4096 => fft.size; Windowing.hann(fft.size() / 2) => fft.window; 20 => int overlap; 0 => int ctr; second / samp => float srate; // Audio graph and parameter setting Clarinet cl1 => dac.right; 60 => Std.mtof => cl1.freq; Clarinet cl2 => NRev rev => dac.left; 60 => Std.mtof => cl2.freq; //rev.gain(0.5); // Smoother instance for smoothing tracker Smooth sma1, sma2, smf1, smf2; // set time constant: shorter time constant gives faster // response but more jittery values sma1.setTimeConstant((fft.size() / 2)::samp); sma2.setTimeConstant((fft.size() / 2)::samp); smf1.setTimeConstant((fft.size() / 5)::samp); smf2.setTimeConstant((fft.size() / 5)::samp); // setBlowingPressure() spork ~ setBlowingPressure(); fun void setBlowingPressure() { while (true) { // apply smoothed value to pressure cl1.pressure(sma1.getLast()); cl1.freq(smf1.getLast()); cl2.pressure(sma1.getLast()); cl2.gain(sma2.getLast()); cl2.freq(smf2.getLast()); 1::samp => now; } } //----------------------------------------------------------------- while (true) { // hop in time by overlap amount (fft.size() / overlap)::samp => now; if (ctr > overlap) { //compute the FFT and RMS analyses rms.upchuck(); rms.fval(0) => float a; Math.rmstodb(a) => float db; //boost the sensitivity 30 + db * 35 => db; //clip at maximum Math.min(100, db) => db; sma1.setNext(Math.dbtorms(db)); // compute spectral centroid cent.upchuck(); cent.fval(0) * srate / 2 => float c; // then convert it to MIDI pitch c => Math.ftom => c; // set upper boundary: prevents note too low Math.min(1, c) => c; sma2.setNext(c); 0 => float max; 0 => int where; // look for a frequency peak in the spectrum // half of spectrum to save work for (0 => int i; i < fft.size() / 4; ++i) { if (fft.fval(i) > max) { fft.fval(i) => max; i => where; } } // get frequency peak (where $ float) / fft.size() * srate => float f; // convert it to MIDI pitch f => Math.ftom => float p; // add a major third 12 -=> p; // set lower boundary //Math.max(20, p) => p; // new freq if not noise if (db > 10.0) { smf1.setNext(Math.mtof(p)); smf2.setNext(Math.mtof(p)); } } ctr++; }