In this second look at ChucK I focus on the language features that makes this system unique and outrageously cool for realtime music production. I also run through the major limitations.
Working with the ChucK language might take some adjustments, depending on what you are used to. It has control structures, strict typing, associative and multi-dimensional arrays, most of which will look familiar enough to any programmer. But a couple of things are strange. To start off with, operators are left-associative. Second, a lot is done with the ChucK operator, including connecting audio components (called Unit Generators or ugens). In this case the operator acts like a patch cable. For example:
Impulse imp => JCRev rev => dac;
This connects an impulse generator to a reverberation unit and then out to the DAC, which abstracts a sound card output. Neat, huh?
.95 => rev.gain;
.2 => rev.mix;
This gives us a 20% reverb mix and reduces the gain to 95%.
This operator can also be used to call functions. One might think to use an assignment to capture the result of a function, and this in fact works:
addthese(3, 6) => int result;
But so does the more peculiar:
(3, 6) => addthese => int result;
(And oh yes, semi-colons go at the end of every line and braces must be used to match up indent levels. I hate this after living in the simpler, cleaner Python world. Oh well.)
The coolest thing about ChucK is how it manages concurrency. Each patch (or function) may be run in its own process (called a "shred"). The process of starting one of these is called "sporking". (Did these guys watch too much Mork and Mindy as kids?) Each process refers to local time to make events happen. Then -- and this is the miracle -- all running processes are automatically synchronised at the sample level!
The canonical example is the command chuck foo.ck bar.ck which runs the two shreds "foo" and "bar" concurrently. Need to record the output from these? Simply add in the shred "rec" like so: chuck foo.ck bar.ck rec.ck. Remarkably, the file "rec" is about 10 lines of code that does not refer to the other shreds at all.
The other major innovation is that ChucK is a strongly-timed language. Two native types, "time" and "dur" -- duration are available in convenient units and may be operated upon in a natural fashion. So for example, n should be 50 after this statement:
1::second / 20::ms => float n;
The real magic is due to the explicit handling of the current time using the "now" keyword. The following snippet advances time by one second. The current shred is suspended, audio is generated, and any other shreds keep computing.
1::second => now;
ChucK supports events; keyboard, mouse and joystick support; plus MIDI and OSC communications. It comes with some useful built-in libraries and ugens such as Impulse, Step, Noise, filters (one-pole, two-pole, low-pass, high-pass, band-pass, band-reject, resonance and custom), oscillators (sine, ramp, pulse, square, triangle, saw), panner, mixdown, envelopes, effects (delay, three implementations of rev, chorus, modulation) and loads of instrument simulations (everything from sitar to percussion). It can read and write audio files, and a sparsely documented new LiSa feature supports live sample manipulation for granular synthesis and looping effects.
This leaves very little outside Chuck's scope, though there are definitive limitations in the current implementation.
1. There is no serial I/O functionality, meaning that ChucK cannot be used to talk to boards such as the Arduino.
2. There is no file I/O for reading/writing settings and other data. This also means that custom log files and the like are not possible.
3. Namespace support is limited, so it is not easy to organise code into libraries for reuse. Each file can have only one public class. Files do not have explicit namespaces, though one is created if a file is sporked as a shred. But this is not addressable, it merely keeps private data in each process from colliding.
5. Sound file writing is limited to mono files, so in order to get stereo two files have to be written and later combined outside ChucK. See this example.
6. The language contains (almost) no string manipulation functions. In fact strings are barely mentioned in the manual.
7. Garbage collection is not implemented. This means that sporking files eats memory. The workaround is to re-use esisting processes, but this is obviously more work than would be nice.
8. The built-in ugen class cannot be subclassed, so there is no way to inherit behaviour for one's own generators. It is possible to rewrite some of this is a class of one's own devising, but that's redundant. I posted an example of this here.
9. The architecture is not as powerful as the client-server paradigm used in SuperCollider.
I look forward to future versions which remove these limitations, starting with number 1. In the meantime ChucK is fun to fool around with.
I thank the Arts Council for their support in this research.