In order to read MIDI data, you need to first call snd_rawmidi_open() once, passing it the hardware name of the desired card/device/sub-device. snd_rawmidi_open will return a handle you can use to call snd_rawmidi_read() which returns any MIDI data which has already been received by that device (and stored in the driver's internal input buffer).
After you're done inputting from the device (and have no further use for it), you must close that device with snd_rawmidi_close().
Think of a MIDI device like a file. You open it, you read from it, and then you close it.
List rawmidi inputs
One of the first things you'll want to do is get a listing of rawmidi inputs. ALSA has some functions to do that. In fact, the way to enumerate MIDI intputs is almost the same as enumerating MIDI outputs, except that instead of passing SND_RAWMIDI_STREAM_OUTPUT to snd_rawmidi_info_set_stream, we pass SND_RAWMIDI_STREAM_INPUT.
Open an input
Once you've discovered the hardware name of some MIDI input, you can pass this name to snd_rawmidi_open() (as the third arg) to open this MIDI input. You must also pass a handle to where you want ALSA to return the device handle. You pass this as the first arg (ie, not the second arg, as you would do to open a MIDI output).
For example, assuming that "hw:0,0,0" is the hardware name of a MIDI input, here's how we open it and get its handle in our variable midiInHandle:
snd_rawmidi_t *midiInHandle;
register int err;
// Open input MIDI device hw:0,0,0
if ((err = snd_rawmidi_open(&midiInHandle, 0, "hw:0,0,0", 0)) < 0)
{
printf("Can't open MIDI input: %s\n", snd_strerror(err));
}
Note: If a particular hardware name is both a MIDI input, as well as a MIDI output, you can simultaneously open both the input and output with a single call to snd_rawmidi_open. You must pass two handles; one for the input and one for the output. For example, here we open hw:0,0,0 for both MIDI input and output:
snd_rawmidi_t *midiInHandle; snd_rawmidi_t *midiOutHandle; snd_rawmidi_open(&midiInHandle, &midiOutHandle, "hw:0,0,0", 0);
Later, you must close each handle separately via snd_rawmidi_close. (ie, You'll call snd_rawmidi_close twice, once passing midiInHandle, and the second time passing midiOutHandle).
Input midi bytes
After opening a MIDI output, you can send bytes to it by calling snd_rawmidi_read(). You pass the handle you got from snd_rawmidi_open, the address of a buffer where you want the received MIDI bytes stored, and a count of how many bytes maximum you want placed into the buffer. snd_rawmidi_read will return a count of how many bytes were actually read, or a negative number if an error. For example, here we ask for the next (ie, one) received MIDI byte:
unsigned char buffer;
register int err;
if ((err = snd_rawmidi_read(midiInHandle, &buffer, 1)) < 0)
{
printf("Can't read MIDI input: %s\n", snd_strerror(err));
}
Note: snd_rawmidi_read does not resolve running status, so you should be prepared to deal with running status. (ie, Not every MIDI message may have a status byte).
The driver stores incoming MIDI bytes in its own internal driver, waiting for you to call snd_midi_read to retrieve them. If you don't retrieve bytes fast enough such that the driver's internal buffer fills up even as more MIDI bytes are arriving at MIDI In, then the driver will get an overflow condition. In that case, the driver may discard incoming MIDI bytes. If you encounter such a situation, you may want to redesign your software so that it retrieves bytes more quickly. Or, you can resize the driver's input buffer to make it large enough to avoid overflowing. You do the latter with a call to snd_rawmidi_params_set_buffer_size() (as shown in resizing the buffer, except of course you pass the handle to your MIDI input device).
To determine if an overflow condition has happened, you can call snd_rawmidi_status_get_xruns. This will return 0 if there are no overflows.
snd_rawmidi_status_t *ptr;
register int err;
// We need to get a snd_rawmidi_status_t struct
if ((err = snd_rawmidi_status_malloc(&ptr)) < 0)
printf("Can't get snd_rawmidi_status_t: %s\n", snd_strerror(err));
else
{
// Tell ALSA to fill in our snd_rawmidi_status_t struct with this device's status
if ((err = snd_rawmidi_status(midiInHandle, ptr)) < 0)
printf("Can't get status: %s\n", snd_strerror(err));
else
{
err = snd_rawmidi_status_get_xruns(ptr);
printf("There are %i errors\n", err);
}
// Free the snd_rawmidi_status_t struct when done
snd_rawmidi_status_free(ptr);
}
Close an input
When you're finally done with an input (ie, no longer need to input any MIDI bytes from it), you pass the handle (you got from snd_rawmidi_open) to snd_rawmidi_close(). You need do this only once.
snd_rawmidi_close(midiInHandle);
Blocking and non-blocking modes
Normally, a MIDI input is opened in blocking mode. What this means is that, when you call snd_rawmidi_read(), then snd_rawmidi_read will not return until all of the bytes you requested are available. So, if you tell snd_rawmidi_read to return 10 bytes, then snd_rawmidi_read will not return until 10 MIDI bytes have arrived at MIDI In, and are copied to your buffer:
unsigned char buffer[10]; register int err; err = snd_rawmidi_read(midiInHandle, &buffer[0], 10); // In blocking mode, we don't get here until buffer[] contains 10 MIDI bytes
But given that MIDI is a slow transmission protocol, it can take a long time to input some MIDI bytes. You can instead use non-blocking mode which means that snd_rawmidi_read quickly copies whatever bytes are in the driver's internal buffer (upto how many you requested), and then returns immediately. So in the above example, this means that snd_rawmidi_read may return a count less than 10 (or even 0 if there are no more MIDI bytes waiting to be read, or a negative number if there's an error).
To enable non-blocking mode, you can call snd_rawmidi_nonblock after you open the MIDI input. You pass the handle (you got from snd_rawmidi_open), and a 1 to enable non-blocking (or a 0 to disable it).
// Enable non-blocking mode snd_rawmidi_nonblock(midiInHandle, 1);
Alternately, you can enable non-blocking right when you open the MIDI input, by passing SND_RAWMIDI_NONBLOCK as the fourth arg to snd_rawmidi_open().
Note: Normally, if some other app is using the particular MIDI input you want, then snd_rawmidi_open will not return until the other app closes that MIDI input. So, your app may be "stuck" in that call to snd_rawmidi_open for a long time. But when you specify SND_RAWMIDI_NONBLOCK, then snd_rawmidi_open will return immediately with an error code of -EBUSY if some other app is using the MIDI input. So, SND_RAWMIDI_NONBLOCK can be useful if you don't want to be stuck in snd_rawmidi_open when the MIDI input is not available for your use. You can always call snd_rawmidi_nonblock to turn off non-blocking mode, after you open the output, if you don't desire blocking mode for snd_rawmidi_read.
// Open input MIDI device hw:0,0,0 in non-blocking mode
if ((err = snd_rawmidi_open(&midiInHandle, 0, "hw:0,0,0", SND_RAWMIDI_NONBLOCK)) < 0)
{
// Is someone else using this MIDI input?
if (err == -EBUSY)
{
// Here you may wish to try opening a different MIDI input
}
printf("Can't open MIDI input: %s\n", snd_strerror(err));
}
// Here you may wish to turn off non-blocking mode
else
snd_rawmidi_nonblock(midiInHandle, 0);
Since snd_rawmidi_read does not wait in non-blocking mode, but rather will return 0 if there are no MIDI bytes to return to you, you can eat up CPU cycles if you simply loop around a call to snd_rawmidi_read. At the very least, you should sleep for a millisecond before each call to snd_rawmidi_read, to give time for another byte to arrive.
Examples
The directory rawmidi contains some rawmidi examples. The subdirectory listrawmidi contains a program that lists all of the MIDI input and output devices/sub-devices on the system. It also display more detailed information about each device/sub-device.
The subdirectory rawmidiinput contains a program that displays incoming MIDI bytes. You can supply the hardware name of the MIDI input to use, or let the program use the first MIDI input it finds.