That probably works OK in many cases when you only have one MIDI port and for 99% of Sysex users (i.e.: 99.99% of total users would already be happy with this), so let's work out how to make it work for the remaining 1% / 0.01%.
However,
while(gSysexPipe.readRt(byte) > 0) {
libpd_sysex(0, byte);
}
here you are hardcoding 0
as the port number, but what if one has more than one Midi port (i.e.: if midi.size() > 1
) ? That first argument becomes the output of the second outlet of [sysexin]
and with the code as it is, that will always be 0
, regardless of how many MIDI devices are connected and regardless of which receives the data. So there is need to pass the information about the device on which this was received to the libpd_sysex()
call.
Now, there are two ways of doing this. One is to use a separate Pipe
object per each MIDI device that is created, the other is to send not just the byte but also the MIDI device index down the pipe. The former option requires sending less data overall, but the audio thread would have to read not from one but from several pipes for every callback. The latter option would require sending more data down a single pipe (e.g.: one-byte idx, one-byte data). I suspect the latter option is more efficient (as sysex data is normally pretty infrequent) and it is surely more fun to implement, so let's see how to do this.
To send structured data down a pipe, you will need - well - a struct
:
struct SysexMsg {
unsigned char idx;
midi_byte_t data;
}
Then the callback would send both midi port idx and data:
void sysexCallback(midi_byte_t byte, void*arg) {
SysexMsg msg;
msg.idx = midiPortIdx;
msg.data = byte
gSysexPipe.writeNonRt(msg);
}
and the audio thread can retrieve the full msg and use both of its elements:
SysexMsg msg;
while(gSysexPipe.readRt(msg) > 0) {
libpd_sysex(msg.idx, msg.data);
}
Nice. However, this won't compile because midiPortIdx
in the callback is not defined. How to retrieve - from within the callback - what's the index of the Midi
object that triggered the call? One could look at this
and compare it with the addresses of the elements of midi
, but that wouldn't look particularly good. The canonic solution is to leverage the callback's argument. Remember where in setup()
you call
newMidi->getParser()->setSysexCallback(sysexCallback, NULL);
? Well, the second argument to setSysexCallback
is passed as an argument to sysexCallback
when this gets called (that void* arg
that is currently unused). So we could pass the index in there:
newMidi->getParser()->setSysexCallback(sysexCallback, midi.size());
And then in the callback:
void sysexCallback(midi_byte_t byte, void*arg) {
unsigned char midiPortIdx = arg;
SysexMsg msg;
msg.idx = midiPortIdx;
...
}
If your compiler is lenient enough, this may even compile, but for good measure one should be explicitly casting data to and from the void*
on both sides:
// in setup()
newMidi->getParser()->setSysexCallback(sysexCallback, (void*)midi.size());
and
// in the callback
unsigned char midiPortIdx = (unsigned char)arg;
Note: if you had to pass more data to the callback, you wouldn't pass it by value, but pass a pointer to a struct
of arbitrary size and sufficient lifetime.
Right, I suspect the above will work.
Finally,
gSysexPipe.setup("sysexPipe", 16);
you probably want a much larger size for the pipe (especially if using the 2-byte per data byte approach and using a single Pipe
). Memory is basically free, make it at least 16384!