1

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...
9
  • So the problem is that you cannot read the response? Is there a reason that you are keeping that code secret?
    – CL.
    Commented Mar 7, 2016 at 12:05
  • no reason for secrecy except screen space.
    – spinalwrap
    Commented Mar 7, 2016 at 12:08
  • I can read the response, if there is one, and then it works well. Not sure where it fails: on sending the request, or on reading the response. RawMIDI does not spit out an error. So I can send the request, but there may be no response. When I start the process, it sometimes just doesn't work at all, other times it works perfectly.
    – spinalwrap
    Commented Mar 7, 2016 at 12:12
  • If I send events (notes, CCs, manual sysex dumps) from the hardware synth, the process will still read them fine. So the issue is probably with sending the request..
    – spinalwrap
    Commented Mar 7, 2016 at 12:14
  • it appears that if I wait a bit after process launch before sending any requests, it's more likely that that the communication works. Not sure how long is "long enough" - documentation for the MIDI API is pretty sparse :)
    – spinalwrap
    Commented Mar 7, 2016 at 12:37

1 Answer 1

0

An ALSA raw MIDI device does not actually begin reading data from the hardware until snd_rawmidi_read() is called. This means that you must call snd_rawmidi_read() early enough, or the first byte(s) of the repsonse might get lost.

The safest way to do this is to call snd_rawmidi_read() before the request is sent. (See, e.g., amidi.)

2
  • thanks, not sure if this is the issue. if I don't send any request, and just send note-on/note-off events from the device to the process, it can also happen that the first event is ignored.
    – spinalwrap
    Commented Mar 7, 2016 at 15:24
  • i.e. I launch the process, start reading. I press a key on the synth. Sometimes, the first note-on is not received, the read function does not receive its 3 bytes. the note-off and all following commands are received. So the issue actually doesn't seem to be limited to sysex, or to the requests.
    – spinalwrap
    Commented Mar 7, 2016 at 15:29

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .