164 lines · 5.1 KB · Text
RawDownload
//--------------------------------------------------------------------// name: chant.ck// desc: chant synthesizer; demonstrates multi-shredded concurrency,//       variable rates, source-filter model, and interpolation.//// This is a classic source-filter model for rudimentary singing// synthesis: an impulse train (the "source", crudely modeling// opening/closing of the glottis in the vocal tract) going through// a bank of three formant filters (roughly modeling the filtering// by the vocal cavity to induce the perception of different vowels).//// This example demonstrates an elegant way to implement the above// in ChucK, by breaking up the tasks into three concurrent shreds://   1. a main shred selects the next target pitch and formants//   2. doImpulse() generates the impulse train; using ChucK's//      strongly-timed mechanisms to modulate the impulse train//      period to create vibrato//   3. doInterpolation() interpolates the period and formants,//      to smoothly glide from note to note, vowel to vowel//// author: Perry R. Cook (2006)//         modified by Rebecca Fiebrink and Ge Wang (2007, 2021)//         published in ChucK examples 2021//-------------------------------------------------------------------- // synthesis patchImpulse i => TwoZero t => TwoZero t2 => OnePole p;// formant filtersp => TwoPole f1 => Gain g;p => TwoPole f2 => g;p => TwoPole f3 => g;// reverbsg => JCRev r => dac;g => JCRev rL => dac.left;g => JCRev rR => dac.right;// delaysg => Delay d1 => Gain g1 => r;g => Delay d2 => Gain g2 => rL;g => Delay d3 => Gain g3 => rR;// connect gains to delaysg1 => d1; g2 => d2; g3 => d3; // source gain (amplitude of the impulse train)0.25 => float sourceGain; // set filter coefficients1.0 => t.b0;  0.0 => t.b1; -1.0 => t.b2;1.0 => t2.b0; 0.0 => t2.b1; 1.0 => t2.b2;// set gains0.1 => g1.gain;	0.1 => g2.gain;	0.1 => g3.gain;// set reverb mix0.025 => r.mix;// set delay max and length1.5 :: second => d1.max;2.0 :: second => d2.max;2.8 :: second => d3.max;1.41459 :: second => d1.delay;1.97511 :: second => d2.delay;2.71793 :: second => d3.delay; // set two pole filter radii and gain0.997 => f1.radius; 0.997 => f2.radius; 0.997 => f3.radius;1.0 => f1.gain; 0.8 => f2.gain; 0.6 => f3.gain;// randomize initial formant frequenciesMath.random2f( 230.0, 660.0 ) => f1.freq;Math.random2f( 800.0, 2300.0 ) => f2.freq;Math.random2f( 1700.0, 3000.0 ) => f3.freq; // variables for interpolating current and target formant frequencies400.0 => float f1freq;1000.0 => float f2freq;2800.0 => float f3freq;400.0 => float target_f1freq;1000.0 => float target_f2freq;2800.0 => float target_f3freq; // leaky integrator0.99 => p.pole;1.0 => p.gain; // variables that control impulse train source0.013 => float period;0.013 => float targetPeriod;0.0 => float modphase;0.0001 => float vibratoDepth; // scale[ 0, 1, 5, 7,  8, 11, 8, 7,  11, 12, 14, 15,  19, 17, 20, 24 ] @=> int scale[];// names (for printing)[ "ut0", "ra0", "fa0", "ut0",  "ra0", "mi0", "ra1", "ut1",   "mi0", "ut1", "re1", "mi1",   "ut1", "fa1", "re1", "ut2" ] @=> string names[];// current location in scale9 => int scalepoint;// frequencyfloat theFreq; // spork two concurrent child shreds...spork ~ doImpulse(); // generate voice sourcespork ~ doInterpolation( 10::ms ); // interpolate pitch and formants // main shred loopwhile( true ){    // determine new formant targets    Math.random2f( 230.0, 660.0 ) => target_f1freq;    Math.random2f( 800.0, 2300.0 ) => target_f2freq;    Math.random2f( 1700.0, 3000.0 ) => target_f3freq;     // next pitch (random walk the scale)    Math.random2(-1,1) + scalepoint => scalepoint;    if( scalepoint < 0 ) 0 => scalepoint;    if( scalepoint > 15 ) 15 => scalepoint;    // compute the frequency    32 + scale[scalepoint] => Std.mtof => theFreq;    // print things for fun    <<< names[scalepoint], theFreq >>>;    // calculate corresponding target period    1.0 / theFreq  => targetPeriod;     // wait until next note    Math.random2f( 0.2, 0.9 )::second => now;} // entry point for shred: generate source impulse trainfun void doImpulse(){    // infinite time-loop    while( true )    {        // fire impulse        sourceGain => i.next;        // phase variable        modphase + period => modphase;        // vibrato depth        .0001 => vibratoDepth;        // modulate wait time until next impulse: vibrato        (period + vibratoDepth*Math.sin(2*pi*modphase*6.0))::second => now;    }} // entry point for shred: interpolate period and formant frequenciesfun void doInterpolation( dur T ){    // percentage progress per time slice    0.10 => float slew;    // infinite time-loop    while( true )    {        // go towards target period (pitch)        (targetPeriod - period) * slew + period => period;        // go towards targat formant frequencies        (target_f1freq - f1freq) * slew + f1freq => f1freq => f1.freq;        (target_f2freq - f2freq) * slew + f2freq => f2freq => f2.freq;        (target_f3freq - f3freq) * slew + f3freq => f3freq => f3.freq;         // interpolation rate        T => now;    }}

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t