aboutsummaryrefslogtreecommitdiffhomepage
path: root/backends/midi.c
diff options
context:
space:
mode:
Diffstat (limited to 'backends/midi.c')
-rw-r--r--backends/midi.c190
1 files changed, 144 insertions, 46 deletions
diff --git a/backends/midi.c b/backends/midi.c
index d856ced..92776ca 100644
--- a/backends/midi.c
+++ b/backends/midi.c
@@ -3,32 +3,27 @@
#include "midi.h"
#define BACKEND_NAME "midi"
+static char* sequencer_name = NULL;
static snd_seq_t* sequencer = NULL;
-typedef union {
- struct {
- uint8_t pad[5];
- uint8_t type;
- uint8_t channel;
- uint8_t control;
- } fields;
- uint64_t label;
-} midi_channel_ident;
-
-/*
- * TODO
- * Optionally send note-off messages
- * Optionally send updates as after-touch
- */
enum /*_midi_channel_type*/ {
none = 0,
note,
cc,
+ pressure,
+ aftertouch,
+ pitchbend,
nrpn,
sysmsg
};
-int init(){
+static struct {
+ uint8_t detect;
+} midi_config = {
+ .detect = 0
+};
+
+MM_PLUGIN_API int init(){
backend midi = {
.name = BACKEND_NAME,
.conf = midi_configure,
@@ -41,8 +36,8 @@ int init(){
.shutdown = midi_shutdown
};
- if(snd_seq_open(&sequencer, "default", SND_SEQ_OPEN_DUPLEX, 0) < 0){
- fprintf(stderr, "Failed to open ALSA sequencer\n");
+ if(sizeof(midi_channel_ident) != sizeof(uint64_t)){
+ fprintf(stderr, "MIDI channel identification union out of bounds\n");
return 1;
}
@@ -52,17 +47,19 @@ int init(){
return 1;
}
- snd_seq_nonblock(sequencer, 1);
-
- fprintf(stderr, "MIDI client ID is %d\n", snd_seq_client_id(sequencer));
return 0;
}
static int midi_configure(char* option, char* value){
if(!strcmp(option, "name")){
- if(snd_seq_set_client_name(sequencer, value) < 0){
- fprintf(stderr, "Failed to set MIDI client name to %s\n", value);
- return 1;
+ free(sequencer_name);
+ sequencer_name = strdup(value);
+ return 0;
+ }
+ else if(!strcmp(option, "detect")){
+ midi_config.detect = 1;
+ if(!strcmp(value, "off")){
+ midi_config.detect = 0;
}
return 0;
}
@@ -86,14 +83,14 @@ static instance* midi_instance(){
return inst;
}
-static int midi_configure_instance(instance* instance, char* option, char* value){
- midi_instance_data* data = (midi_instance_data*) instance->impl;
+static int midi_configure_instance(instance* inst, char* option, char* value){
+ midi_instance_data* data = (midi_instance_data*) inst->impl;
//FIXME maybe allow connecting more than one device
if(!strcmp(option, "read")){
//connect input device
if(data->read){
- fprintf(stderr, "MIDI port already connected to an input device\n");
+ fprintf(stderr, "MIDI instance %s was already connected to an input device\n", inst->name);
return 1;
}
data->read = strdup(value);
@@ -102,7 +99,7 @@ static int midi_configure_instance(instance* instance, char* option, char* value
else if(!strcmp(option, "write")){
//connect output device
if(data->write){
- fprintf(stderr, "MIDI port already connected to an output device\n");
+ fprintf(stderr, "MIDI instance %s was already connected to an output device\n", inst->name);
return 1;
}
data->write = strdup(value);
@@ -113,48 +110,87 @@ static int midi_configure_instance(instance* instance, char* option, char* value
return 1;
}
-static channel* midi_channel(instance* instance, char* spec){
+static channel* midi_channel(instance* inst, char* spec, uint8_t flags){
midi_channel_ident ident = {
.label = 0
};
+ //support deprecated syntax for a transition period...
+ uint8_t old_syntax = 0;
char* channel;
- if(!strncmp(spec, "cc", 2)){
+ if(!strncmp(spec, "ch", 2)){
+ channel = spec + 2;
+ if(!strncmp(spec, "channel", 7)){
+ channel = spec + 7;
+ }
+ }
+ else if(!strncmp(spec, "cc", 2)){
ident.fields.type = cc;
channel = spec + 2;
+ old_syntax = 1;
}
else if(!strncmp(spec, "note", 4)){
ident.fields.type = note;
channel = spec + 4;
+ old_syntax = 1;
}
else if(!strncmp(spec, "nrpn", 4)){
ident.fields.type = nrpn;
channel = spec + 4;
+ old_syntax = 1;
}
else{
- fprintf(stderr, "Unknown MIDI channel specification %s\n", spec);
+ fprintf(stderr, "Unknown MIDI channel control type in %s\n", spec);
return NULL;
}
ident.fields.channel = strtoul(channel, &channel, 10);
-
- //FIXME test this
- if(ident.fields.channel > 16){
- fprintf(stderr, "MIDI channel out of range in channel spec %s\n", spec);
+ if(ident.fields.channel > 15){
+ fprintf(stderr, "MIDI channel out of range in midi channel spec %s\n", spec);
return NULL;
}
if(*channel != '.'){
- fprintf(stderr, "Need MIDI channel specification of form channel.control, had %s\n", spec);
+ fprintf(stderr, "Need MIDI channel specification of form channel<X>.<control><Y>, had %s\n", spec);
return NULL;
}
+ //skip the period
channel++;
+ if(!old_syntax){
+ if(!strncmp(channel, "cc", 2)){
+ ident.fields.type = cc;
+ channel += 2;
+ }
+ else if(!strncmp(channel, "note", 4)){
+ ident.fields.type = note;
+ channel += 4;
+ }
+ else if(!strncmp(channel, "nrpn", 4)){
+ ident.fields.type = nrpn;
+ channel += 4;
+ }
+ else if(!strncmp(channel, "pressure", 8)){
+ ident.fields.type = pressure;
+ channel += 8;
+ }
+ else if(!strncmp(channel, "pitch", 5)){
+ ident.fields.type = pitchbend;
+ }
+ else if(!strncmp(channel, "aftertouch", 10)){
+ ident.fields.type = aftertouch;
+ }
+ else{
+ fprintf(stderr, "Unknown MIDI channel control type in %s\n", spec);
+ return NULL;
+ }
+ }
+
ident.fields.control = strtoul(channel, NULL, 10);
if(ident.label){
- return mm_channel(instance, ident.label, 1);
+ return mm_channel(inst, ident.label, 1);
}
return NULL;
@@ -163,20 +199,19 @@ static channel* midi_channel(instance* instance, char* spec){
static int midi_set(instance* inst, size_t num, channel** c, channel_value* v){
size_t u;
snd_seq_event_t ev;
- midi_instance_data* data;
+ midi_instance_data* data = (midi_instance_data*) inst->impl;
midi_channel_ident ident = {
.label = 0
};
for(u = 0; u < num; u++){
- data = (midi_instance_data*) c[u]->instance->impl;
ident.label = c[u]->ident;
snd_seq_ev_clear(&ev);
snd_seq_ev_set_source(&ev, data->port);
snd_seq_ev_set_subs(&ev);
snd_seq_ev_set_direct(&ev);
-
+
switch(ident.fields.type){
case note:
snd_seq_ev_set_noteon(&ev, ident.fields.channel, ident.fields.control, v[u].normalised * 127.0);
@@ -184,6 +219,15 @@ static int midi_set(instance* inst, size_t num, channel** c, channel_value* v){
case cc:
snd_seq_ev_set_controller(&ev, ident.fields.channel, ident.fields.control, v[u].normalised * 127.0);
break;
+ case pressure:
+ snd_seq_ev_set_keypress(&ev, ident.fields.channel, ident.fields.control, v[u].normalised * 127.0);
+ break;
+ case pitchbend:
+ snd_seq_ev_set_pitchbend(&ev, ident.fields.channel, (v[u].normalised * 16383.0) - 8192);
+ break;
+ case aftertouch:
+ snd_seq_ev_set_chanpress(&ev, ident.fields.channel, v[u].normalised * 127.0);
+ break;
case nrpn:
//FIXME set to nrpn output
break;
@@ -201,6 +245,7 @@ static int midi_handle(size_t num, managed_fd* fds){
instance* inst = NULL;
channel* changed = NULL;
channel_value val;
+ char* event_type = NULL;
midi_channel_ident ident = {
.label = 0
};
@@ -210,16 +255,39 @@ static int midi_handle(size_t num, managed_fd* fds){
}
while(snd_seq_event_input(sequencer, &ev) > 0){
+ event_type = NULL;
ident.label = 0;
switch(ev->type){
case SND_SEQ_EVENT_NOTEON:
case SND_SEQ_EVENT_NOTEOFF:
- case SND_SEQ_EVENT_KEYPRESS:
case SND_SEQ_EVENT_NOTE:
ident.fields.type = note;
ident.fields.channel = ev->data.note.channel;
ident.fields.control = ev->data.note.note;
val.normalised = (double)ev->data.note.velocity / 127.0;
+ if(ev->type == SND_SEQ_EVENT_NOTEOFF){
+ val.normalised = 0;
+ }
+ event_type = "note";
+ break;
+ case SND_SEQ_EVENT_KEYPRESS:
+ ident.fields.type = pressure;
+ ident.fields.channel = ev->data.note.channel;
+ ident.fields.control = ev->data.note.note;
+ val.normalised = (double)ev->data.note.velocity / 127.0;
+ event_type = "pressure";
+ break;
+ case SND_SEQ_EVENT_CHANPRESS:
+ ident.fields.type = aftertouch;
+ ident.fields.channel = ev->data.control.channel;
+ val.normalised = (double)ev->data.control.value / 127.0;
+ event_type = "aftertouch";
+ break;
+ case SND_SEQ_EVENT_PITCHBEND:
+ ident.fields.type = pitchbend;
+ ident.fields.channel = ev->data.control.channel;
+ val.normalised = ((double)ev->data.control.value + 8192) / 16383.0;
+ event_type = "pitch";
break;
case SND_SEQ_EVENT_CONTROLLER:
ident.fields.type = cc;
@@ -227,6 +295,7 @@ static int midi_handle(size_t num, managed_fd* fds){
ident.fields.control = ev->data.control.param;
val.raw.u64 = ev->data.control.value;
val.normalised = (double)ev->data.control.value / 127.0;
+ event_type = "cc";
break;
case SND_SEQ_EVENT_CONTROL14:
case SND_SEQ_EVENT_NONREGPARAM:
@@ -255,13 +324,22 @@ static int midi_handle(size_t num, managed_fd* fds){
return 1;
}
}
+
+ if(midi_config.detect && event_type){
+ if(ident.fields.type == pitchbend || ident.fields.type == aftertouch){
+ fprintf(stderr, "Incoming MIDI data on channel %s.ch%d.%s\n", inst->name, ident.fields.channel, event_type);
+ }
+ else{
+ fprintf(stderr, "Incoming MIDI data on channel %s.ch%d.%s%d\n", inst->name, ident.fields.channel, event_type, ident.fields.control);
+ }
+ }
}
free(ev);
return 0;
}
static int midi_start(){
- size_t n, p;
+ size_t n = 0, p;
int nfds, rv = 1;
struct pollfd* pfds = NULL;
instance** inst = NULL;
@@ -279,6 +357,21 @@ static int midi_start(){
return 0;
}
+ //connect to the sequencer
+ if(snd_seq_open(&sequencer, "default", SND_SEQ_OPEN_DUPLEX, 0) < 0){
+ fprintf(stderr, "Failed to open ALSA sequencer\n");
+ goto bail;
+ }
+
+ snd_seq_nonblock(sequencer, 1);
+ fprintf(stderr, "MIDI client ID is %d\n", snd_seq_client_id(sequencer));
+
+ //update the sequencer client name
+ if(snd_seq_set_client_name(sequencer, sequencer_name ? sequencer_name : "MIDIMonster") < 0){
+ fprintf(stderr, "Failed to set MIDI client name to %s\n", sequencer_name);
+ goto bail;
+ }
+
//create all ports
for(p = 0; p < n; p++){
data = (midi_instance_data*) inst[p]->impl;
@@ -312,13 +405,13 @@ static int midi_start(){
}
//register all fds to core
- nfds = snd_seq_poll_descriptors_count(sequencer, POLLIN | POLLOUT);
+ nfds = snd_seq_poll_descriptors_count(sequencer, POLLIN | POLLOUT);
pfds = calloc(nfds, sizeof(struct pollfd));
if(!pfds){
fprintf(stderr, "Failed to allocate memory\n");
goto bail;
}
- nfds = snd_seq_poll_descriptors(sequencer, pfds, nfds, POLLIN | POLLOUT);
+ nfds = snd_seq_poll_descriptors(sequencer, pfds, nfds, POLLIN | POLLOUT);
fprintf(stderr, "MIDI backend registering %d descriptors to core\n", nfds);
for(p = 0; p < nfds; p++){
@@ -355,12 +448,17 @@ static int midi_shutdown(){
free(inst);
//close midi
- snd_seq_close(sequencer);
- sequencer = NULL;
+ if(sequencer){
+ snd_seq_close(sequencer);
+ sequencer = NULL;
+ }
//free configuration cache
snd_config_update_free_global();
+ free(sequencer_name);
+ sequencer_name = NULL;
+
fprintf(stderr, "MIDI backend shut down\n");
return 0;
}