Created
January 21, 2023 15:33
-
-
Save reductionistearthcatalog/79689486712dbc74891a210b02090792 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//block 1: start an instance of ttymidi for every connected GRAINS and the Arduino gate-to-midi | |
( | |
Pipe.new("sudo systemctl start jack", "r"); | |
Pipe.new("ttymidi -s /dev/ttyUSB0 -n ttyUSB0 &", "r"); | |
Pipe.new("ttymidi -s /dev/ttyUSB1 -n ttyUSB1 &", "r"); | |
Pipe.new("ttymidi -s /dev/ttyUSB2 -n ttyUSB2 &", "r"); | |
Pipe.new("ttymidi -s /dev/ttyUSB3 -n ttyUSB3 &", "r"); | |
) | |
s.boot; | |
//block 2: | |
( | |
MIDIClient.init; | |
) | |
//block 3: set up midiouts, including one for the pisound MIDI interface | |
( | |
~midiouts = Array.newClear(4); | |
MIDIClient.destinations.do({ |item, i| | |
if (item.name == "MIDI in", { | |
if (item.device == "ttyUSB0", { | |
~midiouts[0] = MIDIOut(0, MIDIClient.destinations[i].uid); | |
~midiouts[0].latency = 0; | |
}); | |
if (item.device == "ttyUSB1", { | |
~midiouts[1] = MIDIOut(1, MIDIClient.destinations[i].uid); | |
~midiouts[1].latency = 0; | |
}); | |
if (item.device == "ttyUSB2", { | |
~midiouts[2] = MIDIOut(2, MIDIClient.destinations[i].uid); | |
~midiouts[2].latency = 0; | |
}); | |
if (item.device == "ttyUSB3", { | |
~midiouts[3] = MIDIOut(3, MIDIClient.destinations[i].uid); | |
~midiouts[3].latency = 0; | |
}); | |
}); | |
if (item.device == "pisound", { | |
~m = MIDIOut(0, MIDIClient.destinations[i].uid); | |
~m.latency = 0; | |
}); | |
}); | |
) | |
//block 4: connect all midiins, register a cc responder for the MIDI handshake to distinguish | |
// different connected grains/arduinos | |
( | |
MIDIIn.connectAll; | |
~respVal = false; | |
MIDIdef.cc(\ccResp, { | |
arg ...args; | |
if (args[1] == 127, { | |
~respVal = args[0]; | |
}); | |
}); | |
) | |
//block 5: perform MIDI handshake to give descriptive names to the ttymidi MIDIOuts | |
// (~fm, ~wg, ~ts, ~gates) | |
( | |
{ | |
~respVal = false; | |
0.1.wait; | |
4.do({ |chan| | |
~midiouts.do({ | |
|item, i| | |
item.control(chan, 3, 60); | |
//("Channel sent: "++chan.asString).postln; | |
//("MIDIout sent: "++i.asString).postln; | |
0.05.wait; | |
if (~respVal == 4, { | |
~fm = item; | |
~respVal = false; | |
}); | |
if (~respVal == 3, { | |
~wg = item; | |
~respVal = false; | |
}); | |
if (~respVal == 2, { | |
~ts = item; | |
~respVal = false; | |
}); | |
if (~respVal == 1, { | |
~gates = item; | |
~respVal = false; | |
}); | |
0.2.wait; | |
}); | |
}); | |
}.fork; | |
) | |
// set up a MIDI note buffer for the MIDIouts being used, so that the noteOff number which needs | |
// to be sent can be stored | |
( | |
~wg1NoteBuf = 60; | |
~ts1NoteBuf = 60; | |
) | |
// set up MIDI patterns that can be triggered with the gates-to-MIDI module. Here, the notes are | |
// described based on scale degrees. Normally, this would be changed to MIDI note values | |
// behind the scenes, but something about retrieving the next event based on an external trigger | |
// requires me to manually calculate the midinote; see \midinote key for the Pfunc. the note | |
// buffers are set to be the MIDI note playing so that noteOffs can be sent when the ends of | |
// gates are detected at the gate-to-MIDI module | |
( | |
~wgP1 = Pbind( | |
\type, \midi, | |
\midicmd, \noteOn, | |
\midiout, ~wg, | |
\chan, 2, | |
\scale, Scale.hexDorian, | |
\degree, Pxrand([0,3,4,8],inf), | |
\octave, 3, | |
\mtranspose, 0, | |
\gtranspose, 0, | |
\stepsPerOctave, 12.0, | |
\root, 0, | |
\midinote, Pfunc({ |ev| ~wg1NoteBuf = ((((ev.degree + ev.mtranspose).degreeToKey(ev.scale, ev.stepsPerOctave)) + ev.gtranspose+ev.root) / ev.stepsPerOctave + ev.octave) * 12.0; }), | |
).asStream; | |
~tsP1 = Pbind( | |
\type, \midi, | |
\midicmd, \noteOn, | |
\midiout, ~ts, | |
\chan, 1, | |
\scale, Scale.hexDorian, | |
\degree, Pseq([Pseq([0,3,5,6,8],1), Pseq([7,5,4,3,0],1)],inf), | |
\octave, 5, | |
\mtranspose, 0, | |
\gtranspose, 0, | |
\stepsPerOctave, 12.0, | |
\root, 0, | |
\midinote, Pfunc({ |ev| ~ts1NoteBuf = ((((ev.degree + ev.mtranspose).degreeToKey(ev.scale, ev.stepsPerOctave)) + ev.gtranspose+ev.root) / ev.stepsPerOctave + ev.octave) * 12.0; }), | |
).asStream; | |
) | |
// set up a MIDI panic code block to send noteOffs to all hanging notes, just in case | |
( | |
//panic | |
127.do({ | |
|item| | |
~wg.noteOff(3, item, 0); | |
~ts.noteOff(3, item, 0); | |
}); | |
) | |
// set up MIDI noteOn responders. In this example, noteOns 7 and 6 from the gates-to-MIDI module | |
// will retrieve the next events from the ~wgP1 and ~tsP1 patterns, respectively, and play them. | |
// Random portamento times are also calculated for each noteOn. If noteOn 1 is received, the | |
// patterns will be reset. | |
( | |
MIDIdef.noteOn(\noteResp, { | |
arg ...args; | |
if (args[1] == 7, { | |
~wg1ev = ~wgP1.next(()); | |
~wg.control(2,5,10.rand); | |
~wg1ev.play; | |
}); | |
if (args[1] == 6, { | |
~ts1ev = ~tsP1.next(()); | |
~ts.control(2,5,10.rand); | |
~ts1ev.play; | |
}); | |
if (args[1] == 1, { | |
~wgP1.reset; | |
~tsP1.reset; | |
}); | |
}); | |
) | |
// the noteOff responder sends noteOffs to the proper MIDIouts | |
( | |
MIDIdef.noteOff(\noteOffResp, { | |
arg ...args; | |
if (args[1] == 7, { | |
~wg.noteOff(2, ~wg1NoteBuf, 0); | |
}); | |
if (args[1] == 6, { | |
~ts.noteOff(1, ~ts1NoteBuf, 0); | |
}); | |
}); | |
) | |
//to shut it all down: | |
Pipe.new("sudo killall ttymidi", "r"); | |
MIDIClient.disposeClient; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment