Skip to content

Instantly share code, notes, and snippets.

@blinks
Created October 28, 2011 18:27
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save blinks/1322987 to your computer and use it in GitHub Desktop.
Save blinks/1322987 to your computer and use it in GitHub Desktop.
ChucK script to simulate Otomata
/**
* MIDI Helper Methods
*/
class MIDIConnection {
MidiOut mout;
fun void connect(int port) {
if (!mout.open(0)) { me.exit(); }
}
fun void send(int event, int firstArg, int secondArg) {
MidiMsg _msg;
event => _msg.data1;
firstArg => _msg.data2;
secondArg => _msg.data3;
mout.send(_msg);
}
fun void noteOff(int chan, int note) {
// chan from 0 to 15
send(0x80 + chan, note, 0);
}
fun void noteOn(int chan, int note, int velocity) {
// chan from 0 to 15
send(0x90 + chan, note, velocity);
}
fun void noteAftertouch(int chan, int note, int pressure) {
// chan from 0 to 15
send(0xa0 + chan, note, pressure);
}
fun void aftertouch(int chan, int pressure) {
// chan from 0 to 15
send(0xd0 + chan, pressure, 0);
}
fun void pitchWheel(int chan, int lsb, int msb) {
// chan from 0 to 15
send(0xe0 + chan, lsb, msb);
}
fun void program(int chan, int prog) {
// chan from 0 to 15
send(0xe0 + chan, prog, 0);
}
// 0xb?: chan ? control mode change
// (See http://www.onicos.com/staff/iz/formats/midi-cntl.html)
// System Common:
// 0xf2: song position ptr
// 0xf3: song select [song #, none]
// 0xf6: tune request
// 0xf7: end of sysex
// Real Time:
// 0xf8: timing clock
// 0xfa: start
// 0xfb: continue
// 0xfc: stop
// 0xfe: active sensing
// 0xff: sys reset
}
/** Play a single note. */
fun void note(MIDIConnection conn, int index, dur length) {
conn.noteOn(0, index, 100);
length => now;
conn.noteOff(0, index);
}
/**
* A cellular automaton, inspired by Otomata.
*
* @see http://www.earslap.com/projectslab/otomata
*/
class CellularAutomaton {
complex STATE[16];
#(-1, 0) => STATE[1];
#(0, 1) => STATE[2];
#(1, 0) => STATE[4];
#(0, -1) => STATE[8];
int _scale[];
int _grid[2][24][24];
int _t;
float bpm;
dur quarter;
dur eighth;
fun void init(float bpm, int scale[]) {
// Define speed and note lengths.
(60.0 / bpm)::second => quarter;
0.5::quarter => eighth;
// Define the scale to be used.
scale @=> _scale;
if (_scale.cap() > 24) { <<< "scale too long" >>>; }
0 => _t;
for (0 => int i; i < _scale.cap(); i++) {
for (0 => int j; j < _scale.cap(); j++) {
0 => _grid[0][i][j];
0 => _grid[1][i][j];
}
}
// Initialize randomly.
Std.rand2(4, 8) => int weight;
for (0 => int k; k < weight; k++) {
Std.rand() % _scale.cap() => int i;
Std.rand() % _scale.cap() => int j;
Std.rand2(1, 15) => _grid[_t][i][j];
<<< _grid[_t][i][j], "=>", i, j >>>;
}
}
fun void step(MIDIConnection conn) {
(_t + 1) % 2 => int t1;
// Clear the grid at t1.
for (0 => int i; i < _scale.cap(); i++) {
for (0 => int j; j < _scale.cap(); j++) {
0 => _grid[t1][i][j];
}
}
// Compute the next state of the grid.
for (0 => int i; i < _scale.cap(); i++) {
for (0 => int j; j < _scale.cap(); j++) {
_grid[_t][i][j] => int state; // current state.
if (state == 0) {
// No alive cells.
continue;
} else if (state & (state - 1) != 0) {
// Not a power of 2: Rotate all cells in this location.
(state << 1) % 15 => state;
}
for (1 => int k; k < 16; k << 1 => k) {
if ((state & k) == 0) { continue; }
// Dealing purely with the `k` component of this cell.
STATE[k] => complex dir;
i + (dir.re $ int) => int i1;
j + (dir.im $ int) => int j1;
if (i1 < 0 || i1 >= _scale.cap()) {
// Use j1 to determine the note.
spork ~ note(conn, _scale[j1], eighth);
(k << 2) % 15 |=> _grid[t1][i][j]; // rotate 180
} else if (j1 < 0 || j1 >= _scale.cap()) {
// Use i1 to determine the note.
spork ~ note(conn, _scale[i1], eighth);
(k << 2) % 15 |=> _grid[t1][i][j]; // rotate 180
} else {
k |=> _grid[t1][i1][j1];
}
}
}
}
t1 => _t;
eighth => now;
}
fun void loopForever(MIDIConnection conn) {
while (true) { step(conn); }
}
}
// Connect to the first MIDI Out.
MIDIConnection conn;
0 @=> int index;
conn.connect(0);
// Start up the automaton and loop forever.
CellularAutomaton ca;
ca.init(140.0, // bpm
// Otomata Scale: D A Bb C D E F A C
[62, 69, 70, 72, 74, 76, 77, 81, 84]);
ca.loopForever(conn);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment