//=================================================================== // call setupOut(midi device, midi channel, true enables CC messages input) // // for all devices that receive MIDI data. Run chuck --probe to find device IDs Rocket rockets[4]; rockets[0].setupOut(3, 1, false); // Rocket #1 rockets[1].setupOut(4, 2, true); // Rocket #2 rockets[2].setupOut(5, 3, true); // Rocket #3 rockets[3].setupOut(6, 4, true); // Rocket #4 // call setupIn(midi device, midi channel) // for all device that send MIDI data. If you include a device that you also // ran setupOut for here, and it sends MIDI CC data, you must set the third // argument for it above to false. Master master1; master1.setupIn(3, 1); // Rocket #1 Master master2; master2.setupIn(2, 0); // Keyboard //=================================================================== class Rocket { MidiOut midiOut; int midiDevice; int midiChannel; int acceptMidiCC; -1 => int noteOn; MidiMsg midiMsg; fun void setupOut(int device, int channel, int acceptCC) { device => midiDevice; channel => midiChannel; acceptCC => acceptMidiCC; if (midiOut.open(device)) { <<< "Opened MIDI output ", midiOut.name() >>>; } else { <<< "Couldn't open MIDI output #", device, " :(" >>>; } } fun int sendMidi(int data1, int data2, int data3, int force) { 0 => int sendIt; // <<< "Got msg ", data1, data2, data3, " state=", noteOn, force, sendIt >>>; if (isNoteOn(data1, data3)) { if (noteOn == -1 || force) { if (noteOn > -1) { // Send note off to prevent lingering notes on the Rocket (data1 & 0xf0) + midiChannel => midiMsg.data1; 0x80 + noteOn => midiMsg.data2; 0 => midiMsg.data3; midiOut.send(midiMsg); } data2 => noteOn; 1 => sendIt; } } else if (isNoteOff(data1, data3)) { if (data2 == noteOn) { -1 => noteOn; 1 => sendIt; } } else if (acceptMidiCC) { 1 => sendIt; } if (sendIt) { (data1 & 0xf0) + midiChannel => midiMsg.data1; data2 => midiMsg.data2; data3 => midiMsg.data3; midiOut.send(midiMsg); return true; } else { return false; } } } class Master { int midiDevice; int midiChannel; 0 => int nextRocket; fun void setupIn(int device, int channel) { device => midiDevice; channel => midiChannel; spork ~ listener(); } fun int rocketIsMe(int rocket) { return rockets[rocket].midiDevice == midiDevice && rockets[rocket].midiChannel == midiChannel; } fun void findRocketAndSend(int data1, int data2, int data3) { isNoteOn(data1, data3) || isNoteOff(data1, data3) => int isNoteMessage; 0 => int midiSent; for (0 => int i; i < rockets.size(); i++) { (nextRocket + i) % rockets.size() => int rocket; if (!rocketIsMe(rocket) && rockets[rocket].sendMidi(data1, data2, data3, false)) { 1 => midiSent; if (isNoteMessage) { if (isNoteOn(data1, data2)) { (rocket + 1) % rockets.size() => nextRocket; } break; } } } if (!midiSent && isNoteOn(data1, data3)) { rockets[nextRocket].sendMidi(data1, data2, data3, true); (nextRocket + 1) % rockets.size() => nextRocket; } } fun void listener() { MidiIn midiIn; MidiMsg midiMsg; if (midiIn.open(midiDevice)) { <<< "Opened MIDI input ", midiIn.name() >>>; } else { <<< "Couldn't open MIDI input #" + midiDevice + " :(" >>>; } while (true) { midiIn => now; while (midiIn.recv(midiMsg)) { // <<< "Received midi: ", midiMsg.data1, midiMsg.data2, midiMsg.data3, (midiMsg.data1 & 0x0f) >>>; if ((midiMsg.data1 & 0x0f) == midiChannel) { findRocketAndSend(midiMsg.data1, midiMsg.data2, midiMsg.data3); } } } } } fun int isNoteOn(int noteOnOff, int velocity) { return (noteOnOff & 0xf0) == 0x90 && velocity > 0; } fun int isNoteOff(int noteOnOff, int velocity) { return (noteOnOff & 0xf0) == 0x80 || (noteOnOff & 0xf0) == 0x90 && velocity == 0; } while (true) { 1::week => now; }