Skip to content
Permalink
Browse files

Support multiple ports per MIDI device

This has issues:
- MIDI device selection list not growing with ports
- Unplugging devices doesn't remove them correctly
- Includes are dirtier
- Device list code is generally dirtier
- Probably needs more error logging
- I'm a C noob so I may have added memory leaks
- I'm a C noob so the code style is probably off
  • Loading branch information
ryanlaws committed Oct 8, 2019
1 parent fe00cd4 commit 5cfbc5a638f9166aa3f2a43cf4c845bfc3865972
@@ -12,7 +12,12 @@
// start the rx thread for a device
static int dev_start(union dev *d);

union dev *dev_new(device_t type, const char *path, const char *name) {
union dev *dev_new(
device_t type,
const char *path,
const char *name,
unsigned int midi_port_index
) {
union dev *d = calloc(1, sizeof(union dev));

if (d == NULL) {
@@ -38,7 +43,7 @@ union dev *dev_new(device_t type, const char *path, const char *name) {
}
break;
case DEV_TYPE_MIDI:
if (dev_midi_init(d) < 0) {
if (dev_midi_init(d, midi_port_index) < 0) {
goto err_init;
}
break;
@@ -21,7 +21,12 @@ union dev {
// initialize device registry
extern void devices_init(void);
// create a device from a file path
extern union dev *dev_new(device_t type, const char *path, const char *name);
extern union dev *dev_new(
device_t type,
const char *path,
const char *name,
unsigned int midi_port_index
);
// destroy given device
extern void dev_delete(union dev *d);

@@ -1,5 +1,7 @@
#pragma once

#include <stdint.h>

typedef enum {
// libmonome devices
DEV_TYPE_MONOME = 0,
@@ -3,6 +3,7 @@
#include <stdlib.h>
#include <string.h>

#include "device_midi.h"
#include "events.h"
#include "device.h"
#include "device_list.h"
@@ -42,23 +43,18 @@ void dev_list_init(void) {
dq.tail = NULL;
}

void dev_list_add(device_t type, const char *path, const char *name) {
if (type < 0) {
return;
union event_data *post_add_event(union dev *d, event_t event_type) {
if (d == NULL) {
fprintf(stderr, "dev_list_add: error allocating device data\n");
return NULL;
}

struct dev_node *dn = calloc(1, sizeof(struct dev_node));

if (dn == NULL) {
fprintf(stderr, "dev_list_add: error allocating device queue node\n");
return;
}

union dev *d = dev_new(type, path, name);

if (d == NULL) {
fprintf(stderr, "dev_list_add: error allocating device data\n");
return;
free(d);
return NULL;
}

d->base.id = id++;
@@ -72,28 +68,59 @@ void dev_list_add(device_t type, const char *path, const char *name) {
dq.size++;

union event_data *ev;
ev = event_data_new(event_type);
return ev;
}

void dev_list_add(device_t type, const char *path, const char *name) {
if (type < 0) {
return;
}

union event_data *ev;
union dev *d;
unsigned int midi_port_count = 0;

switch (type) {
case DEV_TYPE_MONOME:
ev = event_data_new(EVENT_MONOME_ADD);
ev->monome_add.dev = d;
d = dev_new(type, path, name, 0);
ev = post_add_event(d, EVENT_MONOME_ADD);
if (ev != NULL) {
ev->monome_add.dev = d;
event_post(ev);
}
break;
case DEV_TYPE_HID:
ev = event_data_new(EVENT_HID_ADD);
ev->hid_add.dev = d;
d = dev_new(type, path, name, 0);
ev = post_add_event(d, EVENT_HID_ADD);
if (ev != NULL) {
ev->hid_add.dev = d;
event_post(ev);
}
break;
case DEV_TYPE_MIDI:
ev = event_data_new(EVENT_MIDI_ADD);
ev->midi_add.dev = d;
midi_port_count = dev_port_count(path);
for (unsigned int pidx = 0; pidx < midi_port_count; pidx++) {
d = dev_new(type, path, name, pidx);
ev = post_add_event(d, EVENT_MIDI_ADD);
if (ev != NULL) {
ev->midi_add.dev = d;
event_post(ev);
}
}
break;
case DEV_TYPE_CROW:
ev = event_data_new(EVENT_CROW_ADD);
ev->crow_add.dev = d;
d = dev_new(type, path, name, 0);
ev = post_add_event(d, EVENT_CROW_ADD);
if (ev != NULL) {
ev->crow_add.dev = d;
event_post(ev);
}
break;
default:
fprintf(stderr, "dev_list_add(): error posting event (unknown type)\n");
return;
}
event_post(ev);
}

void dev_list_remove(device_t type, const char *node) {
@@ -8,7 +8,49 @@
#include "device_midi.h"
#include "../clocks/clock_midi.h"

int dev_midi_init(void *self) {
unsigned int dev_port_count(const char *path) {
int card;
int alsa_dev;

if (sscanf(path, "/dev/snd/midiC%dD%d", &card, &alsa_dev) < 0) {
// TODO: Insert error message here
return 0;
}

// mostly from amidi.c
snd_ctl_t *ctl;
char name[32];

snd_rawmidi_info_t *info;
int subs = 0;
int subs_in = 0;
int subs_out = 0;

sprintf(name, "hw:%d", card);
if (snd_ctl_open(&ctl, name, 0) < 0) {
// TODO: Insert error message here
return 0;
}

snd_rawmidi_info_alloca(&info);
snd_rawmidi_info_set_device(info, alsa_dev);

snd_rawmidi_info_set_stream(info, SND_RAWMIDI_STREAM_INPUT);
if (snd_ctl_rawmidi_info(ctl, info) >= 0) {
subs_in = snd_rawmidi_info_get_subdevices_count(info);
}

snd_rawmidi_info_set_stream(info, SND_RAWMIDI_STREAM_OUTPUT);
if (snd_ctl_rawmidi_info(ctl, info) >= 0) {
subs_out = snd_rawmidi_info_get_subdevices_count(info);
}
snd_ctl_close(ctl);

subs = subs_in > subs_out ? subs_in : subs_out;
return subs;
}

int dev_midi_init(void *self, unsigned int port_index) {
struct dev_midi *midi = (struct dev_midi *) self;
struct dev_common *base = (struct dev_common *) self;

@@ -18,7 +60,7 @@ int dev_midi_init(void *self) {

sscanf(base->path, "/dev/snd/midiC%uD%u", &alsa_card, &alsa_dev);

if (asprintf(&alsa_name, "hw:%u,%u", alsa_card, alsa_dev) < 0) {
if (asprintf(&alsa_name, "hw:%u,%u,%u", alsa_card, alsa_dev, port_index) < 0) {
fprintf(stderr, "failed to create alsa device name for card %d,%d\n", alsa_card, alsa_dev);
return -1;
}
@@ -28,6 +70,19 @@ int dev_midi_init(void *self) {
return -1;
}

char *name_with_port_index;
if (asprintf(&name_with_port_index, "%s %u", base->name, port_index + 1) < 0) {
fprintf(
stderr,
"failed to create human-readable device name for card %d,%d,%d\n",
alsa_card,
alsa_dev,
port_index
);
return -1;
}

base->name = name_with_port_index;
base->start = &dev_midi_start;
base->deinit = &dev_midi_deinit;

@@ -40,7 +95,7 @@ void dev_midi_deinit(void *self) {
snd_rawmidi_close(midi->handle_out);
}

void* dev_midi_start(void *self) {
void *dev_midi_start(void *self) {
struct dev_midi *midi = (struct dev_midi *) self;
union event_data *ev;

@@ -10,7 +10,8 @@ struct dev_midi {
snd_rawmidi_t *handle_out;
};

extern int dev_midi_init(void *self);
extern unsigned int dev_port_count(const char *path);
extern int dev_midi_init(void *self, unsigned int port_index);
extern void dev_midi_deinit(void *self);
extern void* dev_midi_start(void *self);
extern ssize_t dev_midi_send(void *self, uint8_t *data, size_t n);

0 comments on commit 5cfbc5a

Please sign in to comment.