Browse Source

Select MIDI backend at runtime

Use function pointers in the midi_io_t structure to call static
backend-dependent functions when the public, backend-independent
functions are called.

The actual choice of backend is done upon opening the MIDI port:
the open function of each backend is called, the first one that
succeeds at opening the port will set the backend for all the
following operations on that port.
develop
Damien Goutte-Gattat 8 years ago
parent
commit
18f8e994a4
  1. 430
      src/midi.c

430
src/midi.c

@ -22,45 +22,124 @@
#include <midi.h>
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <xmem.h>
#if USE_ALSA_MIDI_API
/* Linux ALSA API */
#ifdef HAVE_ALSA
#include <alsa/asoundlib.h>
#endif
#ifdef HAVE_OSS
#include <fcntl.h>
#include <string.h>
#include <dirent.h>
#endif
typedef int (*m_close) (midi_io_t *);
typedef ssize_t (*m_write) (midi_io_t *, unsigned char *, size_t);
typedef ssize_t (*m_read) (midi_io_t *, unsigned char *, size_t);
typedef const char * (*m_error) (midi_io_t *);
#define MIDI_IO_BUFFER_SIZE 256
struct midi_io
{
snd_rawmidi_t *in;
snd_rawmidi_t *out;
/* Backend-dependent functions. */
struct {
m_close close;
m_write write;
m_read read;
m_error error;
} backend;
/* Common data. */
int error;
unsigned char buffer[MIDI_IO_BUFFER_SIZE];
size_t pos;
size_t len;
unsigned char status;
/* Backend-dependent data. */
union {
#ifdef HAVE_ALSA
struct {
snd_rawmidi_t *in;
snd_rawmidi_t *out;
} alsa;
#endif
#ifdef HAVE_OSS
struct {
int fd;
} oss;
#endif
};
};
midi_io_t *
midi_open(const char *name)
/* Backend-specific functions. */
#ifdef HAVE_ALSA
static int
alsa_midi_close(midi_io_t *midi)
{
midi_io_t *midi;
snd_rawmidi_close(midi->alsa.in);
snd_rawmidi_close(midi->alsa.out);
assert(name != NULL);
free(midi);
return 0;
}
static ssize_t
alsa_midi_write(midi_io_t *midi, unsigned char *buffer, size_t len)
{
/* Flush reading buffer. */
midi->pos = midi->len = 0;
midi->error = snd_rawmidi_write(midi->alsa.out, buffer, len);
return midi->error;
}
static ssize_t
alsa_midi_read(midi_io_t *midi, unsigned char *buffer, size_t len)
{
midi->error = snd_rawmidi_read(midi->alsa.in, buffer, len);
return midi->error;
}
static const char *
alsa_midi_error(midi_io_t *midi)
{
if ( midi->error > 0 )
return NULL;
else
return snd_strerror(midi->error);
}
static midi_io_t *
alsa_midi_open(const char *name)
{
midi_io_t *midi;
midi = xmalloc(sizeof(*midi));
midi->in = midi->out = NULL;
midi->alsa.in = midi->alsa.out = NULL;
midi->error = midi->pos = midi->len = midi->status = 0;
if ( snd_rawmidi_open(&(midi->in), &(midi->out), name,
midi->backend.close = alsa_midi_close;
midi->backend.write = alsa_midi_write;
midi->backend.read = alsa_midi_read;
midi->backend.error = alsa_midi_error;
if ( snd_rawmidi_open(&(midi->alsa.in), &(midi->alsa.out), name,
SND_RAWMIDI_NONBLOCK) < 0 ) {
free(midi);
midi = NULL;
}
else if ( snd_rawmidi_nonblock(midi->out, 0) < 0 ) {
else if ( snd_rawmidi_nonblock(midi->alsa.out, 0) < 0 ) {
midi_close(midi);
midi = NULL;
}
@ -68,161 +147,106 @@ midi_open(const char *name)
return midi;
}
int
midi_close(midi_io_t *midi)
{
assert(midi != NULL);
#endif /* HAVE_ALSA */
snd_rawmidi_close(midi->in);
snd_rawmidi_close(midi->out);
#ifdef HAVE_OSS
static int
oss_midi_close(midi_io_t *midi)
{
close(midi->oss.fd);
free(midi);
return 0;
}
ssize_t
midi_write(midi_io_t *midi, unsigned char *buffer, size_t len)
static ssize_t
oss_midi_write(midi_io_t *midi, unsigned char *buffer, size_t len)
{
assert(midi != NULL);
assert(buffer != NULL);
ssize_t n;
/* Flush reading buffer. */
midi->pos = midi->len = 0;
midi->error = snd_rawmidi_write(midi->out, buffer, len);
return midi->error;
}
ssize_t
midi_read(midi_io_t *midi, unsigned char *buffer, size_t len)
{
assert(midi != NULL);
assert(buffer != NULL);
if ( (n = write(midi->oss.fd, buffer, len)) == -1 )
midi->error = errno;
else
midi->error = 0;
midi->error = snd_rawmidi_read(midi->in, buffer, len);
return midi->error;
return n;
}
int
midi_next(midi_io_t *midi)
static ssize_t
oss_midi_read(midi_io_t *midi, unsigned char *buffer, size_t len)
{
unsigned char b;
assert(midi != NULL);
if ( midi->pos >= midi->len ) {
ssize_t n;
n = midi_read(midi, midi->buffer, MIDI_IO_BUFFER_SIZE);
if ( n < 0 )
return n;
midi->len = n;
midi->pos = 0;
}
b = (unsigned char) midi->buffer[midi->pos++];
if ( b >= 0x80 && b < 0xF8 ) /* Status byte? */
midi->status = b;
ssize_t n;
return b;
}
if ( (n = read(midi->oss.fd, buffer, len)) == -1 )
midi->error = errno;
else
midi->error = 0;
inline unsigned char
midi_status(midi_io_t *midi)
{
return midi->status;
return n;
}
const char *
midi_error(midi_io_t *midi)
static const char *
oss_midi_error(midi_io_t *midi)
{
assert(midi != NULL);
if ( midi->error > 0 )
if ( midi->error == 0 )
return NULL;
else
return snd_strerror(midi->error);
return strerror(midi->error);
}
char **
midi_get_ports(void)
static midi_io_t *
oss_midi_open(const char *name)
{
char **ports;
size_t n, max;
int card;
ports = NULL;
n = max = 0;
card = -1;
while ( snd_card_next(&card) >= 0 && card != -1 ) {
snd_ctl_t *ctl;
char name[32];
sprintf(name, "hw:%d", card);
if ( snd_ctl_open(&ctl, name, 0) >= 0 ) {
int device;
midi_io_t *midi;
device = -1;
while ( snd_ctl_rawmidi_next_device(ctl, &device) >= 0 && device >= 0 ) {
sprintf(name, "hw:%d,%d", card, device);
assert(name != NULL);
if ( n >= max ) {
max += 10;
ports = xrealloc(ports, max);
}
ports[n++] = xstrdup(name);
}
midi = xmalloc(sizeof(*midi));
midi->oss.fd = -1;
midi->error = midi->pos = midi->len = midi->status = 0;
snd_ctl_close(ctl);
}
}
midi->backend.close = oss_midi_close;
midi->backend.write = oss_midi_write;
midi->backend.read = oss_midi_read;
midi->backend.error = oss_midi_error;
if ( n >= max ) {
max += 1;
ports = xrealloc(ports, max);
if ( (midi->oss.fd = open(name, O_RDWR, 0)) == -1 ) {
free(midi);
midi = NULL;
}
ports[n++] = NULL;
return ports;
return midi;
}
#elif USE_OSS_MIDI_API
/* Open Sound System MIDI API */
#include <fcntl.h>
#include <string.h>
#include <dirent.h>
static int
oss_midi_device(const struct dirent *entry)
{
return strncmp(entry->d_name, "midi", 4) == 0;
}
#define MIDI_IO_BUFFER_SIZE 256
#endif /* HAVE_OSS */
struct midi_io
{
int fd;
int error;
unsigned char buffer[MIDI_IO_BUFFER_SIZE];
size_t pos;
size_t len;
unsigned char status;
};
/* Backend-independent functions. */
midi_io_t *
midi_open(const char *name)
{
midi_io_t *midi;
assert(name != NULL);
midi = xmalloc(sizeof(*midi));
midi->fd = -1;
midi->error = midi->pos = midi->len = midi->status = 0;
if ( (midi->fd = open(name, O_RDWR, 0)) == -1 ) {
free(midi);
midi = NULL;
}
midi = NULL;
#ifdef HAVE_ALSA
if ( midi == NULL )
midi = alsa_midi_open(name);
#endif
#ifdef HAVE_OSS
if ( midi == NULL )
midi = oss_midi_open(name);
#endif
return midi;
}
@ -232,45 +256,100 @@ midi_close(midi_io_t *midi)
{
assert(midi != NULL);
close(midi->fd);
free(midi);
return 0;
return midi->backend.close(midi);
}
ssize_t
midi_write(midi_io_t *midi, unsigned char *buffer, size_t len)
{
ssize_t n;
assert(midi != NULL);
assert(buffer != NULL);
/* Flush reading buffer. */
midi->pos = midi->len = 0;
if ( (n = write(midi->fd, buffer, len)) == -1 )
midi->error = errno;
else
midi->error = 0;
return n;
return midi->backend.write(midi, buffer, len);
}
ssize_t
midi_read(midi_io_t *midi, unsigned char *buffer, size_t len)
{
ssize_t n;
assert(midi != NULL);
assert(buffer != NULL);
if ( (n = read(midi->fd, buffer, len)) == -1 )
midi->error = errno;
else
midi->error = 0;
return midi->backend.read(midi, buffer, len);
}
return n;
const char *
midi_error(midi_io_t *midi)
{
assert(midi != NULL);
return midi->backend.error(midi);
}
char **
midi_get_ports(void)
{
char **ports, name[32];
size_t n, max;
ports = NULL;
n = max = 0;
#ifdef HAVE_ALSA
{
int card = -1;
while ( snd_card_next(&card) >= 0 && card != -1 ) {
snd_ctl_t *ctl;
snprintf(name, sizeof(name), "hw:%d", card);
if ( snd_ctl_open(&ctl, name, 0) >= 0 ) {
int device;
device = -1;
while ( snd_ctl_rawmidi_next_device(ctl, &device) >= 0 && device >= 0 ) {
snprintf(name, sizeof(name), "hw:%d,%d", card, device);
if ( n >= max ) {
max += 10;
ports = xrealloc(ports, max);
}
ports[n++] = xstrdup(name);
}
snd_ctl_close(ctl);
}
}
}
#endif
#ifdef HAVE_OSS
{
size_t k;
struct dirent **namelist;
if ( (k = scandir("/dev", &namelist, oss_midi_device, alphasort)) != -1 ) {
while ( k-- ) {
snprintf(name, sizeof(name), "/dev/%s", namelist[k]->d_name);
free(namelist[k]);
if ( n >= max ) {
max += 10;
ports = xrealloc(ports, max);
}
ports[n++] = xstrdup(name);
}
free(namelist);
}
}
#endif
if ( n >= max ) {
max += 1;
ports = xrealloc(ports, max);
}
ports[n++] = NULL;
return ports;
}
int
@ -305,65 +384,6 @@ midi_status(midi_io_t *midi)
return midi->status;
}
const char *
midi_error(midi_io_t *midi)
{
assert(midi != NULL);
if ( midi->error == 0 )
return NULL;
else
return strerror(midi->error);
}
static int
is_midi_device(const struct dirent *entry)
{
if ( strncmp(entry->d_name, "midi", 4) == 0 )
return 1;
return 0;
}
char **
midi_get_ports(void)
{
char **ports;
size_t max, n, k;
struct dirent **namelist;
char path[32];
ports = NULL;
max = n = 0;
if ( (k = scandir("/dev", &namelist, is_midi_device, alphasort)) != -1 ) {
while ( k-- ) {
snprintf(path, sizeof(path), "/dev/%s", namelist[k]->d_name);
free(namelist[k]);
if ( n >= max ) {
max += 10;
ports = xrealloc(ports, max);
}
ports[n++] = xstrdup(path);
}
free(namelist);
}
if ( n >= max ) {
max += 1;
ports = xrealloc(ports, max);
}
ports[n++] = NULL;
return ports;
}
#else
#error "No MIDI API available"
/* TODO: Support for others MIDI APIs (JackMIDI?, WinAPI?, ...) */
#endif
int
midi_change_program(midi_io_t *midi,
unsigned char channel,

Loading…
Cancel
Save