// MIDI Event IDs int codes[0]; 144 => codes["NoteOn"]; 128 => codes["NoteOff"]; 176 => codes["ControlChange"]; class MidiMessage { int id; fun int[] data() { return [ 0, 0 ]; } } class NoteMessage extends MidiMessage { int pitch; int velocity; fun int[] data() { return [ pitch, velocity ]; } } class NoteOnMessage extends NoteMessage { codes["NoteOn"] => id; 100 => velocity; } class NoteOffMessage extends NoteMessage { codes["NoteOff"] => id; 0 => velocity; } class ControlChangeMessage extends MidiMessage { codes["ControlChange"] => id; 8 => int control; 127 => int value; fun int [] data() { return [ control, value ]; } } class MidiHandler { // Members MidiIn input; MidiOut output; 0 => int inputDevice; 0 => int outputDevice; // Constructor if(!input.open(inputDevice)) { <<< "Could not open MIDI input device." >>>; me.exit(); } if(!output.open(outputDevice)) { <<< "Could not open MIDI output device." >>>; me.exit(); } fun void send(MidiMessage message) { message.data() @=> int data[]; if(data.cap() == 2) { MidiMsg out; message.id => out.data1; data[0] => out.data2; data[1] => out.data3; output.send(out); } else { <<< "Invalid data() for MidiMessage." >>>; } } fun void run() { // Now handle incoming events. MidiMsg message; while(true) { input => now; while(input.recv(message)) { message.data1 => int code; if(code == codes["NoteOn"]) { spork ~ noteOn(message.data2, message.data3); } else if(code == codes["NoteOff"]) { spork ~ noteOff(message.data2, message.data3); } else if(code == codes["ControlChange"]) { spork ~ controlChange(message.data2, message.data3); } else { <<< "Unhandled MIDI Message: ", message.data1, message.data2, message.data3 >>>; } } } } fun void noteOn(int pitch, int velocity) { <<< "Note On: ", pitch, velocity >>>; } fun void noteOff(int pitch, int velocity) { <<< "Note Off: ", pitch, velocity >>>; } fun void controlChange(int control, int value) { <<< "Control Change: ", control, value >>>; } } class Control { -1 => int cc; ControlDispatcher.register(this); fun void set(int value) { <<< "Control Changed: ", cc, ", ", value >>>; } } class ControlEvent extends Event { int control; int value; } class EventControl extends Control { ControlEvent changed; fun void set(int value) { cc => changed.control; value => changed.value; changed.broadcast(); } } class ControlNode { ControlNode @ next; Control @ item; } class ControlList { static ControlNode @ first; static ControlNode @ last; fun void append(Control control) { if(first == null) { new ControlNode @=> first; first @=> last; control @=> first.item; } else { new ControlNode @=> last.next; last.next @=> last; control @=> last.item; } } } class ControlDispatcher extends MidiHandler { static ControlList @ controls; fun void controlChange(int control, int value) { if(controls == null) { return; } controls.first @=> ControlNode @ node; while(node != null) { if(node.item.cc == control) { node.item.set(value); } node.next @=> node; } } fun static void register(Control control) { if(controls == null) { new ControlList @=> controls; } controls.append(control); } } ControlDispatcher controller; // Two demos here: one with subclassing, one with events: class FooControl extends Control { 1 => cc; fun void set(int value) { <<< "Foo: ", value >>>; } } FooControl foo; // And now with events. EventControl bar; 2 => bar.cc; fun void listener() { while(true) { bar.changed => now; <<< "Bar: ", bar.changed.value >>>; } } spork ~ listener(); // And now let's create some fake hardware controls to test things. fun void fakeKnob() { ControlChangeMessage message; 1 => message.control; for(0 => int i; i < 10; i++) { i => message.value; controller.send(message); 10::ms => now; } } fun void fakeButton() { ControlChangeMessage message; 2 => message.control; 127 => message.value; controller.send(message); } spork ~ fakeKnob(); spork ~ fakeButton(); controller.run();