Tom Says: Safe code is boring code! Why??
Previous page:
Daily Crap 2009-03-13
Next page:
Cheating at Scramble (and Boggle, and …)
"There is plenty of good completely consonant music left to be made," we were told before starting work on our first composition assignment in the music theory course I'm (finally) taking. The professor complained that students often wanted to start their song-writing career in a flash of dissonant notes and complicated, non-repeating song structure. No, he said, just give it an introduction, a contrasting section, and an outro that mirrors the introduction. ABA. And stay within the chords.
That leads to the question, how can I find interesting chord progressions and what does it sound like to take notes from them for a melody? And as a ChucK programmer, that led me to opening a fresh .ck file and filling it with functions.
This script requires scale.ck which comes from the cool tutorial "Notes on ChucK for Music." It (I wish I could say neatly) encapsulates transposition and arpeggiation in an object that's easy to drop into things. I wish it were more like the Ruby equivalent jazztoolbox, except that the latter requires a database and is primarily meant to be accessed as a web service (wtf?).
Anyway, run it with chuck scale.ck improv.ck or in the miniAudicle by adding scale.ck first. Controls:
// improv.ck
// requires scale.ck
4. * (1::minute / 90.) => dur measure; // length of a measure
48 => int base; // note base
0 => int chord; // current chord (zero-indexed)
Scale sc;
[sc.maj, sc.min] @=> int scales[][];
0 => int scale; // index of the current scale
// patch
JCRev r => dac;
Rhodey voices[5];
.8 => r.gain;
.3 => r.mix;
// connect the Rhodeys to the reverb
for(0 => int i; i < voices.cap(); i++) {
voices[i] => r;
.8 => voices[i].gain;
}
// Rhodey voice allocation
0 => int freevoice;
fun int getvoice() {
freevoice => int voice;
(freevoice + 1) % voices.cap() => freevoice;
return voice;
}
fun void status() {
<<< "chord:", chord+1, "--", "scale:", scale >>>;
}
// play a note on a free Rhodey and return immediately
fun void note(int n) {
getvoice() => int voice;
Std.mtof(base + sc.scale(n, scales[scale])) => voices[voice].freq;
voices[voice].noteOn(1);
}
// watch for key presses
fun void key() {
Hid hi;
HidMsg msg;
if( !hi.openKeyboard( 0 ) ) me.exit();
while(true) {
hi => now;
while( hi.recv( msg ) )
{
if( msg.isButtonDown() )
{
if(msg.ascii >= 49 && msg.ascii <= 57) // 1-9
msg.ascii - 49 => chord; // change to numbered chord
else if(msg.ascii == 32) // space, cycle through scales
(scale + 1) % scales.cap() => scale;
else
<<< "down:", msg.ascii >>>;
status();
}
}
}
}
// spork to play chords on the downbeats
fun void playchord() {
while(true) {
note(chord);
note(chord+2);
note(chord+4);
measure => now;
}
}
// spork to play a melody over the current chord
fun void playmelody() {
while(true) {
note(chord + 7 + 2 * Std.rand2(0, 2));
Std.rand2(1, 4) * (measure / 8) => now;
}
}
// spork to play ticks on the beats
fun void tick() {
Impulse i => dac;
while(true) {
1 => i.next;
measure / 4 => now;
}
}
status();
spork ~ key();
spork ~ playchord();
spork ~ playmelody();
spork ~ tick();
while(1::day => now);
Posted Mar 27, 2009, in the night.