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...

Tuesday, January 25, 2005

Multiple DAC's For Multiple Voices

I've been noticing that the example files all seem to be single-voiced, so I've been wondering about doing chords...

So I looked at the definition of the dac in the programming guide and it didn't seem to prohibit there from being more than one reference.

[ugen] dac
* digital/analog converter
* abstraction for underlying audio output device


So I decided to try it out. It works! (Or seems to — running multiple versions of the multi-voice program does seem to cause some interference.)
chuck bpmulti2.ck bpmulti2.ck bpmulti2.ck rec2.ck
seems to cause some interference...

I've reworked the bluespenta.ck file to use multiple dac's (and JCRev's and Echo's to give interest to the secondary voices). You can download it here (right-click and Save, or click to view in a new window). Here's the code:




// bpmulti2.ck
// Bluesy R&R* C Eb F G Bb C -

// try out multiple dac's for multiple voices
// and for more interesting voices, multiple effects too

// scale exercise using TubeBell STK
// added scale run-up run-down to display scale

// modified from examples/wurley.ck by Gary Williams
// even more music for replicants Wurley changed to TubeBell

TubeBell voc=> JCRev r => Echo a => Echo b => Echo c => dac;

// wonder if this will work (it does)
TubeBell voc1=> JCRev r1 => Echo a1 => Echo b1 => Echo c1 => dac;
TubeBell voc2=> JCRev r2 => Echo a2 => Echo b2 => Echo c2 => dac;

// was 220.0
880.0 => voc.freq => voc1.freq => voc2.freq;
0.95 => voc.gain => voc1.gain => voc2.gain;
.8 => r.gain => r1.gain => r2.gain;
.2 => r.mix => r1.mix => r2.mix;

1000::ms => a.max => b.max => c.max;
750::ms => a.delay => b.delay => c.delay;
.50 => a.mix => b.mix => c.mix;

1000::ms => a1.max => b1.max => c1.max;
750::ms => a1.delay => b1.delay => c1.delay;
.50 => a1.mix => b1.mix => c1.mix;

1000::ms => a2.max => b2.max => c2.max;
750::ms => a2.delay => b2.delay => c2.delay;
.50 => a2.mix => b2.mix => c2.mix;

// shred to modulate the mix
fun void vecho_shred( )
{
0.0 => float decider;
0.0 => float mix;
0.0 => float old;
0.0 => float inc;
0 => int n;

// time loop
while( true )
{
std.rand2f(0.0,1.0) => decider;
if( decider < .3 ) 0.0 => mix;
else if( decider < .6 ) .08 => mix;
else if( decider < .8 ) .5 => mix;
else .15 => mix;

// find the increment
(mix-old)/1000.0 => inc;
1000 => n;
while( n-- )
{
old + inc => old;
old => a.mix => b.mix => c.mix;
old => a1.mix => b1.mix => c1.mix;
old => a2.mix => b2.mix => c2.mix;
1::ms => now;
}
mix => old;
std.rand2(2,6)::second => now;
}
}

// let echo shred go
spork ~ vecho_shred();

// function to set tone duration
function int setnotedur() {
std.rand2f( 0.6, 0.8 ) => voc.noteOn;
std.rand2f( 0.6, 0.8 ) => voc1.noteOn;
std.rand2f( 0.6, 0.8 ) => voc2.noteOn;

// duration
if( std.randf() > 0.7 )
{ 1000::ms => now; }
else if( std.randf() > .7 )
{ 500::ms => now; }
else if( std.randf() > -0.8 )
{ .250::second => now; }
else
{
0 => int i;
2 * std.rand2( 1, 3 ) => int pick;
0 => int pick_dir;
0.0 => float pluck;

for( ; i < pick; i++ )
{
std.rand2f(.4,.6) + (float)i*.035 => pluck;
pluck + 0.03 * (float)(i * pick_dir) => voc.noteOn;
std.rand2f(.4,.6) + (float)i*.035 => pluck;
pluck + 0.03 * (float)(i * pick_dir) => voc1.noteOn;
std.rand2f(.4,.6) + (float)i*.035 => pluck;
pluck + 0.03 * (float)(i * pick_dir) => voc2.noteOn;
!pick_dir => pick_dir;
250::ms => now;
}
}

// report
// machine.status();
return( 1 );
}

// Bluesy R&R* C Eb E F G A Bb C
// Blues Pentatonic C Eb F G Bb C
// octave
// MIDI 60 63 64 65 67 69 70 72 0
// 72 75 76 77 79 81 82 84 1
// 84 87 88 89 91 93 94 96 2
// 96 99 100 101 103 105 106 107 3
// NOTE # 0 1 2 3 4 5 6 7

// map notes 2 and 5 to 0 and 7
fun float note_to_midi( int note, int octave ) {
if( note == 0 || note == 2 ){
if( octave == 0 ) { return(60.0); }
else if( octave == 1 ) { return( 72.0 ); }
else if( octave == 2 ) { return( 84.0 ); }
else return( 96.0 );
}
else if( note == 1 ){
if( octave == 0 ) { return(63.0); }
else if( octave == 1 ) { return( 75.0 ); }
else if( octave == 2 ) { return( 87.0 ); }
else return( 99.0 );
}
////////// else if( note == 2 ){
////////// if( octave == 0 ) { return(64.0); }
////////// else if( octave == 1 ) { return( 75.0 ); }
////////// else if( octave == 2 ) { return( 88.0 ); }
////////// else return( 100.0 );
////////// }
else if( note == 3 ){
if( octave == 0 ) { return(65.0); }
else if( octave == 1 ) { return( 77.0 ); }
else if( octave == 2 ) { return( 89.0 ); }
else return( 101.0 );
}
else if( note == 4 ){
if( octave == 0 ) { return(67.0); }
else if( octave == 1 ) { return( 79.0 ); }
else if( octave == 2 ) { return( 91.0 ); }
else return( 103.0 );
}
////////// else if( note == 5 ){
////////// if( octave == 0 ) { return(69.0); }
////////// else if( octave == 1 ) { return( 81.0 ); }
////////// else if( octave == 2 ) { return( 93.0 ); }
////////// else return( 105.0 );
////////// }
else if( note == 6 ){
if( octave == 0 ) { return(70.0); }
else if( octave == 1 ) { return( 82.0 ); }
else if( octave == 2 ) { return( 93.0 ); }
else return( 106.0 );
}
else if( note == 7 || note == 5 ){
if( octave == 0 ) { return(72.0); }
else if( octave == 1 ) { return( 84.0 ); }
else if( octave == 2 ) { return( 96.0 ); }
else return( 107.0 );
}
return( 120.0 ); // error code
}

// vars
0 => int i;
0 => int oct;
0.0 => float freq;
0.0 => float f;

// run up and down scale first, to display scale being featured
"Blues Pentatonic Scale: up" => stdout;
for( 0 => oct; oct < 4; ++oct ) {
for( 0 => i; i < 8; ++i ) {
std.mtof( note_to_midi( i++, oct) ) => f;
f => voc.freq;
chout => "i, freq: " => i => ", " => f => endl;

std.mtof( note_to_midi( i++, oct) ) => f;
f => voc1.freq;
chout => "i, freq: " => i => ", " => f => endl;

std.mtof( note_to_midi( i++, oct) ) => f;
f => voc2.freq;

chout => "i, freq: " => i => ", " => f => endl;
setnotedur();
}
}

"Blues Pentatonic Scale: down" => stdout;
for( 4 => oct; oct > -1; --oct ) {
for( 7 => i; i > -1; --i ) {
std.mtof( note_to_midi( i--, oct) ) => f;
f => voc.freq;
chout => "i, freq: " => i => ", " => f => endl;

std.mtof( note_to_midi( i--, oct) ) => f;
f => voc1.freq;
chout => "i, freq: " => i => ", " => f => endl;

std.mtof( note_to_midi( i--, oct) ) => f;
f => voc2.freq;
chout => "i, freq: " => i => ", " => f => endl;
setnotedur();
}
}

"Change To Random Notes" => stdout;

// our main loop
while( true )
{
std.rand2(0, 7) => i;
std.rand2(0, 3) => int octave;
std.mtof( note_to_midi( i, octave) ) => float freq;
freq => voc.freq;
chout => "i,i1,i2: " => i; // => ", " => freq; // => endl;

std.rand2(0, 7) => i;
std.rand2(0, 3) => octave;
std.mtof( note_to_midi( i, octave) ) => freq;
freq => voc1.freq;
chout => " " => i; // => ", " => freq; // => endl;

std.rand2(0, 7) => i;
std.rand2(0, 3) => octave;
std.mtof( note_to_midi( i, octave) ) => freq;
freq => voc2.freq;
chout => " " => i => endl; // => ", " => freq => endl;

setnotedur();
}




I did some MP3's, they sound really pleasant, but my server is full right now so I can't upload it right now. (I'm pursuing some more space, but I'm waiting for the admins to tell me they've upped by disk space...) Look for bpmulti...mp3 in the recording links for the mp3.

Sunday, January 23, 2005

Recording ChucK Sounds

There's a detailed lesson on the ChucK Wiki http://wiki.cs.princeton.edu/index.php/ChucK_Record. Basically, the key is to add rec.ck or rec2.ck to the files being chucked. (That's http://chuck.cs.princeton.edu/doc/examples/rec.ck and http://chuck.cs.princeton.edu/doc/examples/rec2.ck).

rec.ck writes a file called foo.wav in the current directory.

D:\CHUCK>chuck hihat.ck snare.ck kick.ck baseloop.ck bluespenta.ck rec.ck


rec2.ck writes a file that's tagged with the current date and time: chuck-session(Sun Jan 23 17h30m58 2005).wav when you run it tagged on the end of the chuck list:

D:\CHUCK>chuck SuperLocrianBells.ck rec2.ck


You can either modify rec.ck/rec2.ck to terminate, or you can just kill all the shreds with CTL-C (the close-out routine ChucK uses closes all open files).

.WAV files are somewhat large, so it's probably worthwhile to go to SourceForge and download the current version of Audacity (http://audacity.sourceforge.net/). (You'll need to also find and download the LAME mp3 recording library via the SourceForge site to export MP3 files from Audacity.)

Or you can use Audacity to export Ogg/Vorbis files...

You'll notice I've added links to a variety of MP3 files I've recorded from the .CK files we've shown here...as well as Mikael's djembe.mp3, which he filed a link to on the ChucK Wiki (I've added links to my files there too...)

Saturday, January 22, 2005

Using A Scale Function -- SuperLocrianBells.ck

Michael Johansson's scales.ck includes an extensive list of comments on various types of scales and modes. I thought it would be useful to illustrate the creation of a scale selecting function that picks out the notes of a specific scale. I picked C Super Locrian (which Michael subtitles Ravel). So I wrote a float note_to_midi( int note, int octave ) function. I used the code from my modification of the wurley.ck example program, which uses the TubeBell STK FM synthesis module.

The full program is here: SuperLocrianBells.ck (as usual, click to see full code in a new window, or right-click/SAVE to download).

I've clipped out [SNIP] the "standard" stuff from the example and just kept the new function and main driver (and scale comments from scales.ck). You'll notice that note_to_midi() contains four complete octaves, duplicating each octave note (C and C'). I did it this way because by having complete scales for each octave stresses the root note for the scale. Be picking 0 - 7, you have two chances in each octave to get a root note, focusing the piece on the "key". This may be mathematically illegitimate, but it's my choice because I have the illogical feeling that it makes it more "musical". (Heh — or jejeje as one of my Spanish friends says).

Anyway, here's the code:



// SuperLocrianBells.ck
// scale exercise using TubeBell STK

// modified from examples/wurley.ck by Gary Williams
// even more music for replicants Wurley changed to TubeBell

[SNIP...]

0 => int i;
0.0 => float freq;

// Super Locrian C Db Eb E Gb Ab Bb C octave (Ravel)
// MIDI 60 61 63 64 65 68 70 72 0
// 72 73 75 76 77 80 82 83 1
// 84 85 87 88 89 92 93 96 2
// 96 97 99 100 101 104 106 108 3
// NOTE # 0 1 2 3 4 5 6 7
fun float note_to_midi( int note, int octave ) {
if( note == 0 ){
if( octave == 0 ) { return(60.0); }
else if( octave == 1 ) { return( 72.0 ); }
else if( octave == 2 ) { return( 84.0 ); }
else return( 96.0 );
}
else if( note == 1 ){
if( octave == 0 ) { return(61.0); }
else if( octave == 1 ) { return( 73.0 ); }
else if( octave == 2 ) { return( 85.0 ); }
else return( 97.0 );
}
else if( note == 2 ){
if( octave == 0 ) { return(63.0); }
else if( octave == 1 ) { return( 75.0 ); }
else if( octave == 2 ) { return( 87.0 ); }
else return( 99.0 );
}
else if( note == 3 ){
if( octave == 0 ) { return(64.0); }
else if( octave == 1 ) { return( 76.0 ); }
else if( octave == 2 ) { return( 88.0 ); }
else return( 100.0 );
}
else if( note == 4 ){
if( octave == 0 ) { return(65.0); }
else if( octave == 1 ) { return( 77.0 ); }
else if( octave == 2 ) { return( 89.0 ); }
else return( 101.0 );
}
else if( note == 5 ){
if( octave == 0 ) { return(68.0); }
else if( octave == 1 ) { return( 80.0 ); }
else if( octave == 2 ) { return( 92.0 ); }
else return( 104.0 );
}
else if( note == 6 ){
if( octave == 0 ) { return(70.0); }
else if( octave == 1 ) { return( 82.0 ); }
else if( octave == 2 ) { return( 93.0 ); }
else return( 106.0 );
}
else if( note == 7 ){
if( octave == 0 ) { return(72.0); }
else if( octave == 1 ) { return( 83.0 ); }
else if( octave == 2 ) { return( 96.0 ); }
else return( 108.0 );
}
return( 120.0 ); // error code
}


// our main loop
while( true )
{
std.rand2(0, 7) => i;
std.rand2(0, 3) => int octave;
std.mtof( note_to_midi( i, octave) ) => float freq;
freq => voc.freq;

chout => "i, freq: " => i => ", " => freq => endl;
setnotedur();
}

// 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 -




Oh, and if you'd like to hear it without running it yourself, here's SuperLocrianBells.mp3.

The Synthesis ToolKit in C (STK)

via The Synthesis ToolKit in C (STK)

The Synthesis ToolKit in C (STK)

4.2.0
Perry R. Cook & Gary P. Scavone

The Synthesis ToolKit in C (STK) is a set of open source audio signal processing and algorithmic synthesis classes written in the C programming language. STK was designed to facilitate rapid development of music synthesis and audio processing software, with an emphasis on cross-platform functionality, realtime control, ease of use, and educational example code. The Synthesis ToolKit is extremely portable (it's mostly platform-independent C and C code), and it's completely user-extensible (all source included, no unusual libraries, and no hidden drivers). We like to think that this increases the chances that our programs will still work in another 5-10 years. In fact, the ToolKit has been working continuously for nearly 10 years now. STK currently runs with 'realtime' support (audio and MIDI) on SGI (Irix), Linux, Macintosh OS X, and Windows computer platforms. Generic, non-realtime support has been tested under NeXTStep, Sun, and other platforms and should work with any standard C compiler.
[more]

Friday, January 21, 2005

A Chromatic Exercise Using std.mtof()

Now that Mikael Johansson pointed the way to the MIDI table, I thought it would be worthwhile to do a simple exercise that runs the full range of MIDI note values.

So here it is. chromatic.ck Just using a simple sine oscillator isn't very attractive, so I also did a version using the TubeBell STK function, modeled on the Replicant examples. tubebellChromatic.ck Permanent links are over there on the right, the new section of .CK Example links.

Here's the code for chromatic.ck:



// chromatic.ck -- run the full range of MIDI values


// author: Gary Williams (gwms[AT]corninglink{DOT}com)

0 => int i;
0.0 => float freq;

// make our patch
sinosc s => dac;

// do a random delay
fun int setdur() {
if( std.randf() > 0.7 )
{ 1000::ms => now; }
else if( std.randf() > .7 )
{ 500::ms => now; }
else if( std.randf() > -0.8 )
{ .250::second => now; }
else {
100::ms => now;
}
return(1);
}

// loop through chromatic range 0 - 127 and back down 127 - 0
for(; i < 128; i++) {
std.mtof( (float)i) => float freq;
freq => s.freq;
setdur();
chout => "i, freq: " => i => ", " => freq => endl;
}

// loop back to 0
for(; i > -1; i--) {
std.mtof( (float)i) => float freq;
freq => s.freq;
setdur();
chout => "i, freq: " => i => ", " => freq => endl;
}

// note change to random
"Change To Random" => stdout;

// our main loop
while( true )
{
std.rand2(0, 127) => i;
std.mtof( (float)i) => float freq;
freq => s.freq;
setdur();
chout => "i, freq: " => i => ", " => freq => endl;
}



On my system (a Sony VAIO laptop running Windows XP), the tones below 58 are inaudible, and the tones above around 120 are too. So for real use, the MIDI note range should probably be limited, based on the capability of the output devices.

Another thing I noticed while doing debug last night, the stdout print device doesn't seem to allow multiple value displays, but the chout.ck example shows the use of

chout => "string label" => v1 => "string separator" => v2 => endl;

which is really useful for debugging. I've always found that watching values on the fly is more valuable for fixing a new program than any static view, so having on-the-fly display verbs is helpful.

In the function programming I was doing yesterday, I did notice something that I hope gets added to the language: we need an exit() verb. At the end of the note_freq() function, I needed to handle program errors (where the function gets called with illegal arguments). It's the kind of thing that's pretty common when you start a new program, but it's worth being able to do a quick exit with a message when the parameter tests fail.

Mikael Johansson Answers My MIDI Question

Mikael Johansson answers my question about MIDI numbers (via the ChucK mailing list while I slept last night):


http://www.harmony-central.com/MIDI/Doc/table2.html

Basically, number 60 is middle C, denoted as C4. The number steps one step
for each halftone. So you get which of the tones in the scale by
calculating notenumber%12, and which octave by integer division by 12.


I've copied the table from the MIDI page (and I've added the page link to the links list on the right over there):


Octave #Note Numbers
CC#D D#EFF#GG#AA#B
-1 01 2 34567891011
01213141516171819202122 23
1242526272829303132333435
2363738394041424344454647
3484950515253545556575859
4606162636465666768697071
5727374757677787980818283
6848586878889909192939495
796979899100101102103104105106107
8108109110111112113114115116117118119
9120121122123124125126127


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...

Thursday, January 20, 2005

Mikael's Scales.ck Program

I was just looking over new material on the ChucK Wiki and found that Mikael Johansson has uploaded a program more sophisticated than my Ascale.ck: http://wiki.cs.princeton.edu/index.php/ChucK_Program_Scales.

As he correctly notes, it does show the real need for arrays in ChucK!

I guess my project for the day is to write a function version that can produce the frequency easily on-demand (or learn how to use the MIDI converter Mikael pointed out yesterday float std.mtof(float);).

Some Page Format Changes

Yesterday, when I set up Beginning ChucK, I just took one of Blogger's templates and pushed to get a couple of articles posted...

After the A Scale post, Mikael Johansson was nice enough to send me a note saying that he tried to add a comment suggesting using the float std.mtof(float); function which (undocumented) translates a MIDI number to frequency, thus avoiding the preamble tables I used in Ascale.ck. Since then I've fiddled with the settings here and (I hope) enabled anonymous comments (so you won't need a blogger ID to post a comment...). Of course, this may cause some comment spamming, which I'll deal with if it comes up.

Also, I've added some needed links to the Princeton ChucK homepage and other necessary and useful sites over there on the right...

Wednesday, January 19, 2005

The Simple A Scale Exercise

I know the tutorial material at Princeton is mostly aimed at using ChucK as a programming language (so far), but I wanted to start by doing some note (scale note) programming, so to start I found the article I noted in the early post: http://tyala.freeyellow.com/t4scales.htm, which gives you a discussion of musical scales, their principles and so on — and gives you a table of frequencies. (I'll probably rework that and put it up here, since it would be useful.)

But to start I did a simple exercise in writing ChucK. I set some variables to the frequencies of the A scale and wrote two loops, one to run a simple oscilator up and down the scale four times, and the second loop to run endlessly playing random notes of the A scale.

If you'd like a copy of the program Ascale.ck — right click and save here.

Here's the code:


// A scale -- simple use of set scale frequencies
// set scale vars
440.0 => float A;
493.883 => float B;
554.365 => float Csharp;
587.330 => float D;
659.255 => float E;
739.989 => float Fsharp;
830.609 => float Gsharp;
880.0 => float Aoct;

// make our patch
sinosc s => dac;

// run the scale up, then down, at 100 ms notes 4 times
5 => int i;
while( i-- ) {
A => s.freq;
100::ms => now;
B => s.freq;
100::ms => now;
Csharp => s.freq;
100::ms => now;
D => s.freq;
100::ms => now;
E => s.freq;
100::ms => now;
Fsharp => s.freq;
100::ms => now;
Gsharp => s.freq;
100::ms => now;
Aoct => s.freq;
100::ms => now;
now => stdout;

Aoct => s.freq;
100::ms => now;
Gsharp => s.freq;
100::ms => now;
Fsharp => s.freq;
100::ms => now;
E => s.freq;
100::ms => now;
D => s.freq;
100::ms => now;
Csharp => s.freq;
100::ms => now;
B => s.freq;
100::ms => now;
A => s.freq;
100::ms => now;
now => stdout;
}
// now do random notes in the A scale at 100 ms intervals
while( true ) {
100::ms => now;
std.rand2(0, 7) => i;
i => stdout;
if( i == 0) A => s.freq;
else if( i == 1) B => s.freq;
else if( i == 2) Csharp => s.freq;
else if( i == 3) D => s.freq;
else if( i == 4) E => s.freq;
else if( i == 5) Fsharp => s.freq;
else if( i == 6) Gsharp => s.freq;
else if( i == 7) Aoct => s.freq;

}




ChucK : Concurrent, On-the-fly Audio Programming Language

via ChucK : Concurrent, On-the-fly Audio Programming Language

what is it? : ChucK is a new audio programming language for real-time synthesis, composition, and performance, which runs on commodity operating systems. ChucK presents a new time-based concurrent programming model, which supports a more precise and fundamental level of expressiveness, as well as multiple, simultaneous, dynamic control rates, and the ability to add, remove, and modify code, on-the-fly, while the program is running, without stopping or restarting. It offers composers, researchers, and performers a powerful and flexible programming tool for building and experimenting with complex audio synthesis programs, and real-time interactive control.

Learning ChucK

I'm learning ChucK. I thought that writing about it might help me learn it.

Here's a couple of links that are useful:

================== ChucK PDF ====================================
http://soundlab.cs.princeton.edu/publications/chuck_icmc2003.pdf

----- scales and frequency -------------
http://tyala.freeyellow.com/t4scales.htm