ChucK is a music programming language (and compiler/synthesizer) written by a professor (Perry Cook) and his students at Princeton University. I'm learning how to use it; I thought that writing about how to use it would help...

Friday, January 21, 2005

scalefunction.ck — The float note_freq( int note, int octave ) Chromatic Function

Although Mikael Johansson's scale.ck example on the ChucK Wiki is a clear example of variable declarations for 6 octaves of chromatic tempered scale notes, Mikael wrote us yesterday about the float mtof( float ); function for converting MIDI note values to frequency values. Unfortunately, I don't yet understand MIDI (except in physical use via various synthesizers -- I've never read the spec).

Anyway, I decided it might be useful to convert Mikael's variable declaration code to a function returning frequencies, based on my own (made up) note codes:


// A Bb B C Db D Eb E F Gb G Ab
// 0 1 2 3 4 5 6 7 8 9 10 11


It's pretty arbitrary, but it should work. I've coded up the file and a test script, which you can download here: scalefunction.ck and test_note_freq.ck (right-click and save).

Here's the code in scalefunction.ck:


// scalefunction.ck scale function using full 5-octave chromatic values
// code for use in other programs
// author: Gary Williams (gwms[AT]corninglink[DOT]com)

// derived from
// scales.ck
// author: Mikael Johansson (mikael/at/johansson.org)
// (we need arrays now!)

//--------------------------------------------------------------
// float note_freq( int note, int octave )
//
// converts note (integer 0 to 12), octave (integer 0 to 5)
// to return float frequency of corresponding note
//
// A Bb B C Db D Eb E F Gb G Ab
// 0 1 2 3 4 5 6 7 8 9 10 11
//--------------------------------------------------------------
function float note_freq( int note, int octave )
{
if( note == 0 ) {
if (octave == 0) return(55.000); // => float A0;
if (octave == 1) return(110.000); // => float A1;
if (octave == 2) return(220.000); // => float A2;
if (octave == 3) return(440.000); // => float A3;
if (octave == 4) return(880.000); // => float A4;
if (octave == 5) return(1760.000); // => float A5;
}
else if( note == 1 ) {
if (octave == 0) return(58.270); // => float Bb0;
if (octave == 1) return(116.541); // => float Bb1;
if (octave == 2) return(233.082); // => float Bb2;
if (octave == 3) return(466.164); // => float Bb3;
if (octave == 4) return(932.328); // => float Bb4;
if (octave == 5) return(1864.655); // => float Bb5;
}
else if( note == 2 ) {
if (octave == 0) return(61.735); // => float B0;
if (octave == 1) return(123.471); // => float B1;
if (octave == 2) return(246.942); // => float B2;
if (octave == 3) return(493.883); // => float B3;
if (octave == 4) return(987.767); // => float B4;
if (octave == 5) return(1975.533); // => float B5;
}
else if( note == 3 ) {
if (octave == 0) return(65.406); // => float C0;
if (octave == 1) return(130.813); // => float C1;
if (octave == 2) return(261.626); // => float C2;
if (octave == 3) return(523.251); // => float C3;
if (octave == 4) return(1046.502); // => float C4;
if (octave == 5) return(2093.005); // => float C5;
}
else if( note == 4 ) {
if (octave == 0) return(69.296); // => float Db0;
if (octave == 1) return(138.591); // => float Db1;
if (octave == 2) return(277.183); // => float Db2;
if (octave == 3) return(554.365); // => float Db3;
if (octave == 4) return(1108.731); // => float Db4;
if (octave == 5) return(2217.461); // => float Db5;
}
else if( note == 5 ) {
if (octave == 0) return(73.416); // => float D0;
if (octave == 1) return(146.832); // => float D1;
if (octave == 2) return(293.665); // => float D2;
if (octave == 3) return(587.330); // => float D3;
if (octave == 4) return(1174.659); // => float D4;
if (octave == 5) return(2349.318); // => float D5;
}
else if( note == 6 ) {
if (octave == 0) return(77.782); // => float Eb0;
if (octave == 1) return(155.563); // => float Eb1;
if (octave == 2) return(311.127); // => float Eb2;
if (octave == 3) return(622.254); // => float Eb3;
if (octave == 4) return(1244.508); // => float Eb4;
if (octave == 5) return(2489.016); // => float Eb5;
}
else if( note == 7 ) {
if (octave == 0) return(82.407); // => float E0;
if (octave == 1) return(164.814); // => float E1;
if (octave == 2) return(329.628); // => float E2;
if (octave == 3) return(659.255); // => float E3;
if (octave == 4) return(1318.510); // => float E4;
if (octave == 5) return(2637.020); // => float E5;
}
else if( note == 8 ) {
if (octave == 0) return(87.307); // => float F0;
if (octave == 1) return(174.614); // => float F1;
if (octave == 2) return(349.228); // => float F2;
if (octave == 3) return(698.456); // => float F3;
if (octave == 4) return(1396.913); // => float F4;
if (octave == 5) return(2793.826); // => float F5;
}
else if( note == 9 ) {
if (octave == 0) return(92.499); // => float Gb0;
if (octave == 1) return(184.997); // => float Gb1;
if (octave == 2) return(369.994); // => float Gb2;
if (octave == 3) return(739.989); // => float Gb3;
if (octave == 4) return(1479.978); // => float Gb4;
if (octave == 5) return(2959.955); // => float Gb5;
}
else if( note == 10 ) {
if (octave == 0) return(97.999); // => float G0;
if (octave == 1) return(195.998); // => float G1;
if (octave == 2) return(391.995); // => float G2;
if (octave == 3) return(783.991); // => float G3;
if (octave == 4) return(1567.982); // => float G4;
if (octave == 5) return(3135.963); // => float G5;
}
else if( note == 11 ) {
if (octave == 0) return(103.826); // => float Ab0;
if (octave == 1) return(207.652); // => float Ab1;
if (octave == 2) return(415.305); // => float Ab2;
if (octave == 3) return(830.609); // => float Ab3;
if (octave == 4) return(1661.219); // => float Ab4;
if (octave == 5) return(3322.438); // => float Ab5;
}

chout => "Error: note_freq: illegal note or octave value " => note => ", " => octave => endl;

// exit(-1); NOTE: ask for EXIT operator???
} // end float note_freq()


//--------------------------------------------------------------
//
// heree's Mikael's variable defs if you want to use those
//
//--------------------------------------------------------------

// declare the note vars
55.000 => float A0;
110.000 => float A1;
220.000 => float A2;
440.000 => float A3;
880.000 => float A4;
1760.000 => float A5;

58.270 => float Bb0;
116.541 => float Bb1;
233.082 => float Bb2;
466.164 => float Bb3;
932.328 => float Bb4;
1864.655 => float Bb5;

61.735 => float B0;
123.471 => float B1;
246.942 => float B2;
493.883 => float B3;
987.767 => float B4;
1975.533 => float B5;

65.406 => float C0;
130.813 => float C1;
261.626 => float C2;
523.251 => float C3;
1046.502 => float C4;
2093.005 => float C5;

69.296 => float Db0;
138.591 => float Db1;
277.183 => float Db2;
554.365 => float Db3;
1108.731 => float Db4;
2217.461 => float Db5;

73.416 => float D0;
146.832 => float D1;
293.665 => float D2;
587.330 => float D3;
1174.659 => float D4;
2349.318 => float D5;

77.782 => float Eb0;
155.563 => float Eb1;
311.127 => float Eb2;
622.254 => float Eb3;
1244.508 => float Eb4;
2489.016 => float Eb5;

82.407 => float E0;
164.814 => float E1;
329.628 => float E2;
659.255 => float E3;
1318.510 => float E4;
2637.020 => float E5;

87.307 => float F0;
174.614 => float F1;
349.228 => float F2;
698.456 => float F3;
1396.913 => float F4;
2793.826 => float F5;

92.499 => float Gb0;
184.997 => float Gb1;
369.994 => float Gb2;
739.989 => float Gb3;
1479.978 => float Gb4;
2959.955 => float Gb5;

97.999 => float G0;
195.998 => float G1;
391.995 => float G2;
783.991 => float G3;
1567.982 => float G4;
3135.963 => float G5;

103.826 => float Ab0;
207.652 => float Ab1;
415.305 => float Ab2;
830.609 => float Ab3;
1661.219 => float Ab4;
3322.438 => float Ab5;



// Scale references
// 1 2 3 4 5 6 7 8 9 10 11 12 13
// Chromatic C Db D Eb E F Gb G Ab A Bb B C

// Spanish 8 Tone C Db Eb E F Gb Ab Bb C
// Flamenco C Db Eb E F G Ab Bb C
// Symmetrical C Db Eb E Gb G A Bb C
// Diminished C D Eb F Gb Ab A B C
// Whole Tone C D E Gb Ab Bb C
// Augmented C Eb E G Ab B C
// 3 semitone C Eb Gb A C
// 4 semitone C E Ab C
//
// Ultra Locrian C Db Eb E Gb Ab A C
// Super Locrian C Db Eb E Gb Ab Bb C (Ravel)
// Indian-ish* C Db Eb E G Ab Bb C
// Locrian C Db Eb F Gb Ab Bb C
// Phrygian C Db Eb F G Ab Bb C
// Neapolitan Min C Db Eb F G Ab B C
// Javanese C Db Eb F G A Bb C
// Neapolitan Maj C Db Eb F G A B C
// Todi (Indian) C Db Eb Gb G Ab B C
// Persian C Db E F Gb Ab B C
// Oriental C Db E F Gb A Bb C
// Maj.Phryg. (Dom)C Db E F G Ab Bb C Spanish/ Jewish
// Double Harmonic C Db E F G Ab B C Gypsy/ Byzantine/ Ch$
// Marva (Indian) C Db E Gb G A B C
// Enigmatic C Db E Gb Ab Bb B C
//
// Locrian Natural 2nd C D Eb F Gb Ab Bb C
// Minor (natural) C D Eb F G Ab Bb C Aeolian/ Algerian (o$
// Harmonic Minor C D Eb F G Ab B C Mohammedan
// Dorian C D Eb F G A Bb C
// Melodic Minor (Asc) C D Eb F G A B C Hawaiian
// Hungarian Gypsy C D Eb Gb G Ab Bb C
// Hungarian Minor C D Eb Gb G Ab B C Algerian (oct1)
// Romanian C D Eb Gb G A Bb C
//
// Maj. Locrian C D E F Gb Ab Bb C Arabian
// Hindu C D E F G Ab Bb C -
// Ethiopian C D E F G Ab B C
// Mixolydian C D E F G A Bb C -
// Major C D E F G A B C Ionian
// Mixolydian Aug. C D E F Ab A Bb C -
// Harmonic Major C D E F Ab A B C -
// Lydian Min. C D E Gb G Ab Bb C -
// Lydian Dominant C D E Gb G A Bb C Overtone
// Lydian C D E Gb G A B C -
// Lydian Aug. C D E Gb Ab A Bb C -
// Leading Whole Tone C D E Gb Ab Bb B C -
// Bluesy R&R* C Eb E F G A Bb C -
// Hungarian Major C Eb E Gb G A Bb C Lydian sharp2nd
//
// "pB" C Db Eb Gb Ab C -
// Balinechuck@lists.cs.princeton.eduse 1 C Db Eb G Ab C "pE"
// Pelog (Balinese) C Db Eb G Bb C -
// Iwato (Japanese) C Db F Gb Bb C -
// Japanese 1 C Db F G Ab C Kumoi
// Hirajoshi (Japanese) C D Eb G Ab C "pA"
// "pD" C D Eb G A C -
// Pentatonic Major C D E G A C Chinese 1/ Mongolian/ "pFCG"
// Egyptian C D F G Bb C -
// Pentatonic Minor C Eb F G Bb C -
// Chinese 2 C E Gb G B C -




Naturally, this code needs some test code to drive it, so here's the test driver (with the note_freq() code copied into the file...




// make our patch
sinosc s => dac;

// now do random notes at 100 ms intervals
while( true ) {
100::ms => now;
std.rand2(0, 11) => int note;
std.rand2(0,5) => int octave;

// set frequency
note_freq( note, octave ) => float freq;
freq => s.freq;

// debug values
chout => "note, octave, freq: " => note => ", " => octave => ", " => freq => endl;
}




You'll note that in scalefunction.ck I've included all of Mikael's variable definitions, so if you want to do your own version without all the function values (maybe you just want to cherry pick one scale...) you can do that.

I've also added a request to the Wiki asking for include files so we don't have to copy these kind of function files into our scripte...