I couldn't wait until Rebecca's ISMIR tutorial on ChucK - so I downloaded the latest ChucK distribution, fired it up and worked through the reference guide. It's a lot of fun. ChucK is a strongly-timed, concurrent, and on-the-fly audio programming language that presents a time-based, concurrent programming model that's highly precise and expressive. In some ways, ChucK reminds me of CSound - in that you have lots of control over how to make sounds. The more you know about signal processing and filters and the theory of synthesis the more you can do with ChucK. But unlike CSound, ChucK is a modern program language with classes, objects - all the things that make a Java programmer comfortable, whereas CSound is like coding in assembler.

ChucK is pretty powerful too. Here's a bit of ChucK code by Perry Cook that does a reasonable job imitating the THX deep note - compare this 80 lines of code to the 20,000 lines of code required to make the original.

// THX emulator
//   author: Perry R. Cook (Jan 8, 2007)
// modified: Ge Wang (added parameters up top)

// F-1, B1b,  F1,    B2b,   F2,    B3b,   F3,    A5,    F4,   A6
[ 29.0, 87.5, 116.0, 175.0, 233.0, 350.0, 524.0, 880.0, 1048, 1760,
  29.0, 87.5, 116.0, 175.0, 233.0, 350.0, 524.0, 880.0, 1048, 1760,
  29.0, 87.5, 116.0, 175.0, 233.0, 350.0, 524.0, 880.0, 1048, 1760
] @=> float targets[];

// storage
float initials[30];
float deltas[30];

// parameters (play with these to control timing)
10000 => int steady_samps;
20000 => int sweep_steps;
15000 => int hold_steps;
8000 => int decay_steps;

// UGens
SawOsc s[30];
Gain gl[30];
Gain gr[30];
JCRev rl => dac.left;
JCRev rr => dac.right;

// reverb settings
0.025 => rl.mix => rr.mix;

// variables
0 => int i => int j;

// compute stuff
for( 0 => i; i < 30; i++ )
{
    // random freqs
    Std.rand2f( 200.0, 800.0 ) => initials[i] => s[i].freq;
    // 10 sample updates
    ( targets[i] - initials[i] ) / sweep_steps => deltas[i];
    // initial gain
    0.1 => s[i].gain;
    // random
    Std.rand2f( 0.0, 1.0 ) => gl[i].gain;
    // panning
    1.0 - gl[i].gain() => gr[i].gain;
    // hook up
    s[i] => gl[i] => rl;
    // all the oscs
    s[i] => gr[i] => rr;
}

steady_samps :: samp => now;                            // steady cluster

while( j < sweep_steps ) {
    for( 0 => i; i < 30; i++ ) {
        initials[i] + (deltas[i]*j) => s[i].freq;       // sweep freqs.
    }
    j + 1 => j;
    10 :: samp => now;
}

0 => j;
while( j < hold_steps ) {                               // hold chord
    10 :: samp => now;
    j + 1 => j;
}

0 => j;
while( j < decay_steps ) {
    for( 0 => i; i < 30;  i++) {
        0.1 * (decay_steps-j) / decay_steps => s[i].gain;       // decay gains
    }
    10 :: samp => now;
    j + 1 => j;
}

60000 :: samp => now;                                   // reverb tail

Comments:

Post a Comment:
Comments are closed for this entry.

This blog copyright 2010 by plamere