Rasmus.krats.se

svenska

Reminiscing this and that, on the web since 1995

12 bars of ChucK

Published tagged , .

ChucK is a system for live music programming. Way cool!

Here's a walkthrough of a simple 12-bar blues comp to play with.

Timing is everything, here's some definitions to begin with:

60::second / 125.0 => dur t4;
t4 / 2.0 => dur t8;
t4 / 4.0 => dur t16;
t4 / 8.0 => dur t32;
t4 * 2.0 => dur t2;
t4 * 4.0 => dur t1;

fun void taktpos(dur module, dur offset) {
  module - ((now - offset) % module) => now;
}

Since ChucK doesn't (yet) support including files or loading modules or something like that, I simply use this as a template and start each file as a copy of it. The magic number 125.0 in the first line is the beats per minute. Change it to vary the tempo, but be sure to change it throughout the project ...

Get rythm

Drums are notoriously hard to model, so we take the easy way out and use some samples. There is some sample data in examples/data in the ChucK distribution. Let's start with a hihat; create a pair of sndbufs and chuck them to the output, and read some data into them:

sndbuf open => dac;
sndbuf closed => dac;

"data/hihat-open.wav" => open.read;
(int)(open.length/1::samp) => open.pos;
"data/hihat.wav" => closed.read;
(int)(closed.length/1::samp) => closed.pos;

The thing after each read is that I set the playback position of each sndbuf to end of file, to avoid playing each sound emediatley when I load them.

Here's a basic swing pattern on the hihat. It's halv a bar long, and beats the (open or closed) on each 8:th (or slightly before or after, to get a little bit of swing feeling).

while(true) {
  taktpos(t2, 0::samp);
  0 => closed.pos;
  t8 - t64 => now;
  0 => open.pos;
  t8 + t64 => now;
  0 => open.pos;
  t8 - t64 => now;
  0 => closed.pos;
}

And a kick and snare pattern (in separate files, remember the template):

sndbuf kick => dac;
"data/kick.wav" => kick.read;
(int)(kick.length/1::samp) => kick.pos;

0.5 => kick.gain;

while(true) {
  taktpos(t1, 0::samp);
  0 => kick.pos;
  t2 => now;
  0 => kick.pos;
  t8 => now;
  0 => kick.pos;
}
sndbuf snare => dac;
"data/snare.wav" => snare.read;
(int)(snare.length/1::samp) => snare.pos;

while(true) {
  taktpos(t1, t1 - t8);
  0 => snare.pos;
  t16 => now;
  0 => snare.pos;

  t16 + t4 => now;
  0 => snare.pos;
  t2 => now;
  0 => snare.pos;
}

Bass loop

Put the fundamental tone in a global variable, and write a function that keeps repeating a chord:

0 => int fundamental;

fun void bassloop() {
  Rhodey instr => dac;
  0.7 => instr.gain;
  while(true) {
    taktpos(t1, 0::samp);
    std.mtof((float)(2*12 + fundamental + 0)) => instr.freq;
    0.8 => instr.noteOn;

    t4 + t8 => now;
    std.mtof((float)(2*12 + fundamental + 4)) => instr.freq;
    0.6 => instr.noteOn;

    t8 => now;
    std.mtof((float)(2*12 + fundamental + 7)) => instr.freq;
    0.7 => instr.noteOn;

    t4 => now;
    std.mtof((float)(2*12 + fundamental + 4)) => instr.freq;
    0.6 => instr.noteOn;
  }
}

Then (in the same file) spork of the function that plays the bass loop, and then keep chaning the fundamental to get a standard 12-bar blues.

taktpos(t1, t1 - 2::samp);
spork ~ bassloop();

while(true) {
  7 + 0 => fundamental;
  t1 * 4.0 => now;

  7 + 5 => fundamental;
  t1 * 2.0 => now;

  7 + 0 => fundamental;
  t1 * 2.0 => now;

  7 + 7 => fundamental;
  t1 => now;

  7 + 5 => fundamental;
  t1 => now;

  7 + 0 => fundamental;
  t1 * 2.0 => now;
}

The 2::samp offset is to make sure the change of fundamental happens before each chord is started.

Write a comment

Your name (or pseudonym).

Not published, except as gravatar.

Your homepage / presentation.

No formatting, except that an empty line is interpreted as a paragraph break.