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? Commented Mar 7, 2016 at 12:05
  • no reason for secrecy except screen space. 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. 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.. 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 :) 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. 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. Commented Mar 7, 2016 at 15:29

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.