I'm trying to write an application using the ALSA RawMIDI interface to exchange sysex data with a hardware synthesizer via USB on a Raspberry PI. the RawMIDI library comes with libasound2-dev.
In my test app, I'm sending a sysex request to the device like so:
uint8_t req[] = {0xF0, 0x00, 0x20, 0x3C, 0x07, 0x00, type, 0x01, 0x01, position, 0x00, 0x00, 0x00, 0x05, 0xF7};
if ((status = snd_rawmidi_write(midiout, req, 15)) < 0)
{
errormessage("Problem sending request: %s", snd_strerror(status));
exit(1);
}
snd_rawmidi_drain(midiout);
the device then responds with a sysex data structure.
It works, but there sometimes seems to be an initialization issue of the MIDI driver. One out of maybe ten times when I launch the app, the request/response does not succeed at all. I then relaunch the app, and it will sometimes work, and sometimes won't work.
If it works, it works well. If the first request succeeds, I can send thousands more requests and the communication with the synth is reliable.
So I think this has to do with initialization/teardown of the MIDI library. The library does not report any errors during initialization or sending of data.
Probably I'm missing something during initialization or teardown? Is there a way to reset the MIDI driver when I start the app?
here's my init code:
if ((status = snd_rawmidi_open(&midiin, &midiout, portname, mode)) < 0)
{
errormessage("Problem opening MIDI connection: %s", snd_strerror(status));
exit(1);
}
and here's my teardown code:
snd_rawmidi_close(midiin);
snd_rawmidi_close(midiout);
midiin = NULL;
midiout = NULL;
looks easy enough, right?
edit: here's my main.cpp
#include <signal.h>
#include <thread>
#include "MIDI.hpp"
using namespace std;
static MIDI midi;
void sighandler(int dum)
{
midi.quit();
exit(0);
}
void midiRead()
{
while(1)
midi.read();
}
int main()
{
signal(SIGINT,sighandler);
thread midiReadThread(midiRead);
midiReadThread.join();
return 0;
}
and here's the midiRead method:
void MIDI::read()
{
uint8_t readByte;
if ((status = snd_rawmidi_read(midiin, &readByte, 1)) < 0) {
errormessage("Problem reading MIDI input: %s", snd_strerror(status));
}
// if status byte other than sysex end, reset read buffer:
if(readByte & 0x80 && readByte != 0xF7)
{
if(readByte == 0xF0)
printf("syx");
argsLeft = getArgsExpected(readByte);
bufIdx = 0;
currentCommand = readByte;
inBuffer[bufIdx++] = readByte;
}
// if it's a data byte or sysex end:
else if(argsLeft || currentCommand == 0xF0)
{
inBuffer[bufIdx++] = readByte;
argsLeft--;
// handle the sysex message:
if(readByte == 0xF7)
{
printf(" done\n");
handleSysex(inBuffer, bufIdx);
}
}
// if we don't expect any more data bytes from non-sysex:
if(!argsLeft && currentCommand != 0xF0)
{
switch (currentCommand & 0xF0) {
case 0x90:
{
handleNoteOn(inBuffer[0] & 0x0F, inBuffer[1] & 0x7F, inBuffer[2] & 0x7F);
break;
}
case 0x80:
{
handleNoteOff(inBuffer[0] & 0x0F, inBuffer[1] & 0x7F, inBuffer[2] & 0x7F);
break;
}
case 0xA0:
{
handleAftertouch(inBuffer[0] & 0x0F, inBuffer[1] & 0x7F, inBuffer[2] & 0x7F);
break;
}
case 0xB0:
{
handleController(inBuffer[0] & 0x0F, inBuffer[1] & 0x7F, inBuffer[2] & 0x7F);
break;
}
default:
break;
}
}
}
to clarify: if I just launch the process, I can send sysex or other MIDI data to the PI manually from the machine, this usually works.
if, directly after process launch, I send a request to the machine, it sometimes responds, sometimes doesn't. If I wait a bit, it's more likely that the request/response mechanism works.
Currently I think the reason for the failure is that I have to wait for the MIDI initialization to finish. It seems that when snd_rawmidi_open returns, it's not actually fully initialized. I don't know how long I should wait though?
more edit:
it seems the issue is not limited to sysex. if I launch the process and start reading, and then send MIDI note events from the synth to the process, sometimes the very first note-on is not read. the following note-off and all following events are read correctly.
for example, if I put printf("reading...\n"); at the top of the read function, and launch the process, log output looks like this:
Init MIDI...
reading...
Normally, if I then send a note-on followed by a note-off from the synth, it looks like this:
Init MIDI...
reading...
reading...
reading...
note on chn: 0 note: 36 vel: 100
reading...
reading...
reading...
note off chn: 0 note: 36 vel: 0
reading...
but sometimes, the first note-on is not received:
Init MIDI...
reading...
reading...
reading...
note off chn: 0 note: 36 vel: 0
reading...