Cellular Automata and Audio
This week in Ge’s class we’re asked to create three audio files, one based on cellular automata and two “ChuckKus”.
For the cellular automata part I created an instrument which is a linear bank of sin oscillators with gains controlled by the state bits in a cyclic linear cellular automata (CA). The state update for the CA is done every 100msec or so, which causes a timbral shift for the instrument. Since some CAs get stuck in repetetive cycles, this can result in a vibrato type effect, or even notes simply extinguishing early.
In the piece, I play first a few chords and then have a random melody played over a static bass line. There are a couple lines of code to make sure that the two pieces end together.
Some interesting points:
- A parameter controls the randomness of the oscillators from their ideal frequency, which creates a little more richness and ambiguity in the tone.
- The initial value of the CAM is seeded randomly.
- Although the rule 110 CA is universal, there are many others with different timbral possibilities to explore. The note generator choses a different rule for each note, so they all sound a bit different.
Inevitable
The first chuck-ku plays with frequency modulation of triangleOsc. In the first case, we generate a low rumbling tone by chosing a random frequency in the 0-60Hz range. The melodic element is caused by moving the second oscillator on a random walk with a slight upward bias. The result is a feeling of increasing unresolved tension as the pitch slowly climbs. Note the perceived shift in volume as this oscillator slews.
Gain g=>JCRev r=>WvOut w=>dac; 0.1=>g.gain; TriOsc t=>g; TriOsc t2=>g;
while (10::ms => now) { Std.rand2f(0,60) => t.freq;
t2.freq() * Std.rand2f(0.998,1.00205) => t2.freq; }
Listen to
Inevitable.wav
Chase
A second example uses the same techniques, but this time one oscillator does the random walk while the other pursues “in a statistical sense”.
Gain g=>JCRev r=>WvOut w=>dac; 0.1=>g.gain; TriOsc t=>g; TriOsc t2=>g;
while (t2.freq()>30) {Math.max(30,t2.freq()*Std.rand2f(0.9,1.1)) => t2.freq;
t.freq()+Std.rand2f(-0.01,0.02)*(t2.freq()-t.freq()) => t.freq; 20::ms=>now;
<<< t.freq() >>>;
}