//-------------------------------------------------------------------- // name: shepard.ck // desc: continuous shepard-risset tone generator; // ascending but can easily made to descend // // author: Ge Wang (https://ccrma.stanford.edu/~ge/) // date: spring 2016 //-------------------------------------------------------------------- // mean for normal intensity curve 66 => float MU; // standard deviation for normal intensity curve 42 => float SIGMA; // normalize to 1.0 at x==MU 1 / gauss(MU, MU, SIGMA) => float SCALE; // increment per unit time (use negative for descending) .004 => float INC; // unit time (change interval) 15::ms => dur T; // starting pitches (in MIDI note numbers, octaves apart) [ 12.0, 24, 36, 48, 60, 72, 84, 96, 108 ] @=> float pitches[]; // number of tones pitches.size() => int N; // overall gain 1.0/N => float initial_gain; /* fun void fade() { while (now > decres) { 1 - (end - now) / decres_dur => float progress; initial_gain * (1 - progress) => gain.gain; 50::ms => now; } }*/ // infinite time loop fun void shepard(int is_up, float pitches_iter[]) { if (is_up == 1) { .004 => INC; } else { -.004 => INC; } // bank of tones SqrOsc tones[N]; Gain gain => dac; initial_gain => gain.gain; // connect to dac for( int i; i < N; i++ ) { tones[i] => gain; } 4::second => dur duration; now + duration => time end; 1::second => dur sustain; 1.5::second => dur fade; now + fade => time cres; now + fade + sustain => time decres; while( now < end ) { for( int i; i < N; i++ ) { // wrap (for positive INC) if( pitches_iter[i] > 120 ) 108 -=> pitches_iter[i]; // wrap (for negative INC) else if( pitches_iter[i] < 12 ) 108 +=> pitches_iter[i]; // set frequency from pitch pitches_iter[i] => Std.mtof => tones[i].freq; // compute loundess for each tone gauss( pitches_iter[i], MU, SIGMA ) * SCALE => float intensity; // map intensity to amplitude intensity*96 => Math.dbtorms => tones[i].gain; // increment pitch 1 - (end - now) / duration => float pitch_progress; Math.exp(pitch_progress) - Math.exp(-1) +=> pitches[i]; INC +=> pitches[i]; if (now > decres) { 1 - (end - now) / fade => float progress; initial_gain * (1 - progress) => gain.gain; } else if (now < cres) { 1 - (cres - now) / fade => float progress; initial_gain * progress => gain.gain; } } // advance time //1 - (end - now)/ duration => float progress; //T * progress + 1::ms => now; T => now; } } // normal function for loudness curve // NOTE: chuck-1.3.5.3 and later: can use Math.gauss() instead fun float gauss( float x, float mu, float sd ) { return (1 / (sd*Math.sqrt(2*pi))) * Math.exp( -(x-mu)*(x-mu) / (2*sd*sd) ); } fun float[] increment_pitch(float pitches[], float increment) { for (int i; i< N; i++) { increment +=> pitches[i]; } return pitches; } fun void modalbar() { [(4.0/6)::second, (2.0/6)::second, (2.0/6)::second, (1.0/6)::second, (1.0/6)::second, (0.5/6)::second, (0.5/6)::second, (0.5/6)::second, (0.5/6)::second] @=> dur initial[]; ModalBar bar => dac; [48, 49, 50] @=> int bar_pitch[]; for (int i; i < 9; i++) { //initial / (2 ^ i) => dur duration; for( int j; j < 6; j++) { Std.mtof(bar_pitch[j % 3]) => bar.freq; 1.0 => bar.strike; initial[i] => now; } for (int j; j < 3; j++) { 1 +=> bar_pitch[j]; } <<>>; } } //spork ~ shepard(0, pitches); //spork ~modalbar(); for (int i; i < 5; i++) { increment_pitch(pitches, 10) @=> pitches; spork ~ shepard(0, pitches); if(i == 2) { spork ~modalbar(); } 4::second => now; } 60::second => now;