1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225
|
class:: MIDIOut
summary:: send MIDI messages
related:: Classes/MIDIClient, Classes/MIDIIn, Guides/MIDI, Guides/UsingMIDI
categories:: External Control>MIDI
description::
a MIDIOut is bound to a specific link::Classes/MIDIEndPoint:: as defined by the operating system.
Linux users, or any users who require cross-platform compatibility with Linux, should read link::#Linux specific: Connecting and disconnecting ports:: carefully.
ClassMethods::
private::connectByUID, disconnectByUID
method::new
Create a new MIDIOut instance. Note that this method is not safe for cross-platform usage with Linux, because the meaning of the teletype::port:: argument is different. See link::#Linux specific: Connecting and disconnecting ports:: for details.
argument::port
definitionList::
## macOS, Windows || The index of the MIDIEndPoint in the code::MIDIClient.destinations:: array.
## Linux || The output port number. teletype::MIDIEndPoint("SuperCollider", "out0"):: is port 0; teletype::"out1":: is port 1. In Linux, this argument has no connection at all to code::MIDIClient.destinations::.
::
argument::uid
definitionList::
## macOS / Windows || uid is optional; if specified, it should be the uid of that port ie. MIDIClient.destinations[port].uid. If you don't provide a uid, the correct uid will be filled in for you (easier).
## Linux || using the uid is optional as described below.
::
method::newByName
Searches for the MIDI output device, by a name found in the code::MIDIClient.destinations:: array. This is safer then depending on the index which will change if your studio setup changes. It is also Linux compatible.
code::
//list connected out ports with names:
MIDIClient.init;
MIDIClient.destinations;
::
method::findPort
Searches for a connected MIDIEndPoint by name.
code::
//list connected out ports with names:
MIDIClient.init;
MIDIClient.destinations;
::
method::connect, disconnect
Linux only. MacOS does not need to connect. On Linux it is an optional feature (see below).
InstanceMethods::
method::sysex
Sends a sysex command represented as an link::Classes/Int8Array:: to the device.
note::
The method call should contain a full sysex message. In other words,
it should start with 0xF0 (240 or -16) and end with 0xF7 (247 or -9).
::
argument:: packet
An Int8Array of data bytes to be sent.
code::
m = MIDIOut(0);
m.sysex(Int8Array[0xF0, 1, 2, 3, 0xF7]); // OK!
m.sysex(Int8Array[1, 2, 3]); // not OK
::
private::send, prSysex
method::latency
This sets the latency with which a midi event is sent out. Per default, this is set to 0.2, in order to be equal to the Server.latency.
note::
On Linux, there seems to be an ALSA or kernel bug if the latency is larger than 0, for some Linux kernels. If MIDIOut does not seem to work, set the latency to 0.
::
Examples::
code::
MIDIClient.init;
m = MIDIOut(0); // Linux users: MIDIOut(0, MIDIClient.destinations[0].uid)
m.noteOn(16, 60, 60);
m.noteOn(16, 61, 60);
m.noteOff(16, 61, 60);
m.allNotesOff(16);
MIDIIn.connect; // 1 port midi interface
MIDIIn.sysex = { arg uid, packet; [uid,packet].postln };
MIDIIn.sysrt = { arg src, chan, val; [src, chan, val].postln; };
MIDIIn.smpte = { arg src, chan, val; [src, chan, val].postln; };
m.sysex(Int8Array[0xF0, 0, 0, 27, 11, 0, 0xF7])
m.smpte(24, 16)
m.midiClock
m.start
m.continue
m.stop
::
subsection::Using patterns for sending MIDI events
code::
MIDIClient.init;
m = MIDIOut(0); // Linux users: MIDIOut(0, MIDIClient.destinations[0].uid)
a = Pbind(\degree, Prand([1, 2, 3, [0, 5]], inf), \bend, Pwhite(0, 76, inf));
// chain a midi event into the pattern and play it (see Pchain)
(a <> (type: \midi, midiout: m)).play;
::
See link::Tutorials/A-Practical-Guide/PG_08_Event_Types_and_Parameters#MIDI output:: for a list of midi commands supported by the 'midi' event type.
subsection::Linux specific: Connecting and disconnecting ports
In Linux, the MIDIOut architecture is different from other operating systems.
In macOS and Windows, a MIDIOut instance is bound to a specific destination MIDI device.
SuperCollider in Linux uses the ALSA MIDI layer. ALSA MIDI applications send messages out through a "virtual output port," which is one of the members of code::MIDIClient.sources::.
code::
MIDIClient.init;
::
teletype::
MIDI Sources:
MIDIEndPoint("System", "Timer")
MIDIEndPoint("System", "Announce")
MIDIEndPoint("Midi Through", "Midi Through Port-0")
MIDIEndPoint("SuperCollider", "out0")
MIDIEndPoint("SuperCollider", "out1")
::
At this point, creating code::MIDIOut(0):: tells SuperCollider that this MIDIOut object should direct messages to teletype::MIDIEndPoint("SuperCollider", "out0")::. (It seems strange, if you think of SuperCollider sending messages to this MIDI emphasis::source::. It is more accurate to think of SuperCollider sending messages emphasis::through:: this port. The port is, then, a source for the rest of the system.)
A code::MIDIOut(0):: object, then, will not reach any destinations by default. The user needs to connect destination devices to the virtual source port, using either a graphical tool such as Qjackctl, or by MIDIOut's teletype::connect:: method.
code::
m = MIDIOut(0); // use virtual source port "out0"
m.connect(1); // connect to MIDIClient.destinations[1]
::
WARNING:: The teletype::port:: argument to link::Classes/MIDIOut#*new:: has an entirely different meaning in Linux, compared to macOS and Windows. If user code calls this method emphasis::and:: cross-platform compatibility is needed, it is the user's responsibility to handle Linux separately. User code can check the platform using code::thisProcess.platform.name:: (which returns one of code::\osx::, code::\linux:: or code::\windows::). Or, for compatibility, use link::#*newByName:: instead.::
link::Classes/MIDIOut#*new:: optionally takes a second argument, for the uid of the MIDI destination device. If the uid is non-zero, then: 1/ all connections in the ALSA MIDI patchbay are ignored, and 2/ individual messages will be sent directly to the specified device.
link::Classes/MIDIOut#*newByName:: locates a device by name and populates the uid in the new MIDIOut object. After successful completion, a MIDIOut object created using this method will have a non-zero uid.
note::
A non-zero uid (whether specified in teletype::MIDIOut.new::, found by teletype::MIDIOut.newByName::, or set manually later) does not create or use any ALSA MIDI connections. It is not a "connection" at all; rather, it is a parameter that causes outgoing MIDI messages to bypass ALSA MIDI connections and go directly to one specific device. Because it is not a connection, there is nothing to show in MIDI patchbay interfaces; users should not expect to see connections for these MIDIOut objects.
Also, because a uid bypasses patchbay connections, it is meaningless to specify a uid and use teletype::.connect:: at the same time. A single MIDIOut object should not do both at once. (It does not break anything, but the patchbay connections are ignored.)
::
strong::Compatibility::
list::
## code::MIDIOut.newByName("device", "port"):: -- Should be compatible on all systems.
## code::MIDIOut(index):: -- macOS, Windows (teletype::index:: is the device's index in code::MIDIClient.destinations::). Note that this usage is emphasis::not:: compatible with Linux! macOS and Windows users should not expect Linux users to be able to run this style of connection.
## code::MIDIOut(0, MIDIClient.destinations[index].uid):: -- Linux only (teletype::index:: is the device's index in code::MIDIClient.destinations::).
::
subsection::macOS specific: Sending MIDI to other applications
Open the Audio MIDI Setup application. Double-click on IAC Driver and check "device is online".
reinitialize:
MIDIClient.init(numIns,numOuts)
The IAC Bus will now appear in MIDIClient.destinations. It will appear first, which means that any code that you have written that addresses the first physical bus as 0 will now have to be changed.
For this reason, it is always safer to find the port by name :
code::
MIDIOut.newByName("RemoteSL IN","Port 1");
::
The IAC Bus will now also appear to other applications.
MIDIMonitor (freeware) can be very useful for troubleshooting:
http://www.snoize.com/MIDIMonitor/
subsection::Sysex example
a machinedrum manual say sysex commands should be formatted like this...
code::
$f0,$00,$20,$3c,$02,$00,command,...,$f7
::
and to set the tempo the machinedrum expects this command...
code::
$61 | Set tempo ID
%0aaaaaaa | Upper bits
%0bbbbbbb | Lower bits
$f7 | SYSEX end
Note: Tempo = %aaaaaaabbbbbbb / 24, max 300 BPM, min 30 BPM
::
so to create and send a valid set tempo sysex command from SuperCollider to this machinedrum do...
code::
MIDIClient.init;
m = MIDIOut(0); // Linux users: MIDIOut(0, MIDIClient.destinations[0].uid)
m.sysex(Int8Array[0xf0, 0x00, 0x20, 0x3c, 0x02, 0x00, 0x61, 21, 54, 0xf7]);
::
This will set the tempo to 114.23 bpm. One can calculate the upper and lower 7bit values like this...
code::
(
var bpm, val, upper, lower;
bpm = 114.23;
val = (bpm*24).round.asInteger;
upper = val&2r11111110000000>>7;
lower = val&2r00000001111111;
[upper, lower].postln;
)
::
where the resulting 21 and 54 are the same as 2r0010101 and 2r0110110 in binary notation.
|