From 1cbf63996a51a67bdd221257d74adec61f03ea32 Mon Sep 17 00:00:00 2001 From: cbdev Date: Tue, 6 Jun 2017 00:08:06 +0200 Subject: Implement MIDI backend event fetching --- artnet.c | 6 ++-- artnet.h | 4 +-- backend.c | 66 +++++++++++++++++++++++++++++++++++++--- backend.h | 3 ++ midi.c | 98 ++++++++++++++++++++++++++++++++++++++++++++++++++++++----- midi.h | 4 +-- midimonster.c | 37 +++++++++++++++++++--- midimonster.h | 27 +++++++++++----- monster.cfg | 17 +++++------ 9 files changed, 223 insertions(+), 39 deletions(-) diff --git a/artnet.c b/artnet.c index 598fed8..f2a2fa5 100644 --- a/artnet.c +++ b/artnet.c @@ -107,14 +107,14 @@ static channel* artnet_channel(instance* instance, char* spec){ return mm_channel(instance, channel, 1); } -static int artnet_set(size_t num, channel* c, channel_value* v){ +static int artnet_set(instance* inst, size_t num, channel* c, channel_value* v){ //TODO return 1; } -static int artnet_handle(size_t num, int* fd, void** data){ +static int artnet_handle(size_t num, managed_fd* fds){ //TODO - return 1; + return 0; } static int artnet_start(){ diff --git a/artnet.h b/artnet.h index 4976938..00a75a7 100644 --- a/artnet.h +++ b/artnet.h @@ -5,8 +5,8 @@ static int artnet_configure(char* option, char* value); static int artnet_configure_instance(instance* instance, char* option, char* value); static instance* artnet_instance(); static channel* artnet_channel(instance* instance, char* spec); -static int artnet_set(size_t num, channel* c, channel_value* v); -static int artnet_handle(size_t num, int* fd, void** data); +static int artnet_set(instance* inst, size_t num, channel* c, channel_value* v); +static int artnet_handle(size_t num, managed_fd* fds); static int artnet_start(); static int artnet_shutdown(); diff --git a/backend.c b/backend.c index 2eb6fcf..1b297d4 100644 --- a/backend.c +++ b/backend.c @@ -9,10 +9,37 @@ static instance** instances = NULL; static size_t nchannels = 0; static channel** channels = NULL; +int backends_handle(size_t nfds, managed_fd* fds){ + size_t u, p, n; + int rv = 0; + managed_fd xchg; + + for(u = 0; u < nbackends && !rv; u++){ + n = 0; + + for(p = 0; p < nfds; p++){ + if(fds[p].backend == backends + u){ + xchg = fds[n]; + fds[n] = fds[p]; + fds[p] = xchg; + n++; + } + } + + rv |= backends[u].process(n, fds); + } + return rv; +} + +int backends_notify(size_t nev, channel* c, channel_value* v){ + //TODO + return 1; +} + channel* mm_channel(instance* i, uint64_t ident, uint8_t create){ size_t u; for(u = 0; u < nchannels; u++){ - if(channels[u]->instance == 0 && channels[u]->ident == ident){ + if(channels[u]->instance == i && channels[u]->ident == ident){ return channels[u]; } } @@ -58,6 +85,22 @@ instance* mm_instance(){ return instances[ninstances++]; } +instance* mm_instance_find(char* name, uint64_t ident){ + size_t u; + backend* b = backend_match(name); + if(!b){ + return NULL; + } + + for(u = 0; u < ninstances; u++){ + if(instances[u]->backend == b && instances[u]->ident == ident){ + return instances[u]; + } + } + + return NULL; +} + int mm_backend_instances(char* name, size_t* ninst, instance*** inst){ backend* b = backend_match(name); size_t n = 0, u; @@ -132,12 +175,25 @@ instance* instance_match(char* name){ } struct timeval backend_timeout(){ - //TODO fetch minimum poll interval from backends + size_t u; + uint32_t res, secs = 1, msecs = 0; + + for(u = 0; u < nbackends; u++){ + if(backends[u].interval){ + res = backends[u].interval(); + if((res / 1000) <= secs){ + secs = res / 1000; + if((res % 1000) < msecs){ + msecs = res % 1000; + } + } + } + } + struct timeval tv = { - 0, - 10000 + secs, + msecs }; - return tv; } diff --git a/backend.h b/backend.h index 16cf77e..e2eaac5 100644 --- a/backend.h +++ b/backend.h @@ -1,5 +1,8 @@ #include +int backends_handle(size_t nfds, managed_fd* fds); +int backends_notify(size_t nev, channel* c, channel_value* v); + backend* backend_match(char* name); instance* instance_match(char* name); struct timeval backend_timeout(); diff --git a/midi.c b/midi.c index ca1f66b..857460f 100644 --- a/midi.c +++ b/midi.c @@ -14,6 +14,7 @@ enum /*_midi_channel_type*/ { none = 0, note, cc, + nrpn, sysmsg }; @@ -125,6 +126,10 @@ static channel* midi_channel(instance* instance, char* spec){ ident.fields.type = note; channel = spec + 4; } + else if(!strncmp(spec, "nrpn", 4)){ + ident.fields.type = nrpn; + channel = spec + 4; + } else{ fprintf(stderr, "Unknown MIDI channel specification %s\n", spec); return NULL; @@ -153,14 +158,84 @@ static channel* midi_channel(instance* instance, char* spec){ return NULL; } -static int midi_set(size_t num, channel* c, channel_value* v){ - //TODO - return 1; +static int midi_set(instance* inst, size_t num, channel* c, channel_value* v){ + size_t u; + midi_instance_data* data; + + for(u = 0; u < num; u++){ + data = (midi_instance_data*) c[u].instance->impl; + //TODO write out event + } + + return 0; } -static int midi_handle(size_t num, int* fd, void** data){ - //TODO - return 1; +static int midi_handle(size_t num, managed_fd* fds){ + snd_seq_event_t* ev = NULL; + instance* inst = NULL; + channel* changed = NULL; + channel_value val; + union { + struct { + uint8_t pad[5]; + uint8_t type; + uint8_t channel; + uint8_t control; + } fields; + uint64_t label; + } ident = { + .label = 0 + }; + + while(snd_seq_event_input(sequencer, &ev) > 0){ + 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; + break; + case SND_SEQ_EVENT_CONTROLLER: + ident.fields.type = cc; + ident.fields.channel = ev->data.control.channel; + ident.fields.control = ev->data.control.param; + val.raw.u64 = ev->data.control.value; + val.normalised = (double)ev->data.control.value / 127.0; + break; + case SND_SEQ_EVENT_CONTROL14: + case SND_SEQ_EVENT_NONREGPARAM: + case SND_SEQ_EVENT_REGPARAM: + //FIXME value calculation + ident.fields.type = nrpn; + ident.fields.channel = ev->data.control.channel; + ident.fields.control = ev->data.control.param; + break; + default: + fprintf(stderr, "Ignored MIDI event of unsupported type\n"); + continue; + } + + inst = mm_instance_find(BACKEND_NAME, ev->dest.port); + if(!inst){ + //FIXME might want to return failure + fprintf(stderr, "Delivered MIDI event did not match any instance\n"); + continue; + } + + changed = mm_channel(inst, ident.label, 0); + if(changed){ + if(mm_channel_event(changed, val)){ + free(ev); + return 1; + } + } + } + free(ev); + return 0; } static int midi_start(){ @@ -180,6 +255,7 @@ static int midi_start(){ for(p = 0; p < n; p++){ data = (midi_instance_data*) inst[p]->impl; data->port = snd_seq_create_simple_port(sequencer, inst[p]->name, SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SUBS_WRITE | SND_SEQ_PORT_CAP_READ | SND_SEQ_PORT_CAP_SUBS_READ, SND_SEQ_PORT_TYPE_MIDI_GENERIC); + inst[p]->ident = data->port; //make connections if(data->write){ @@ -218,7 +294,9 @@ static int midi_start(){ fprintf(stderr, "Registering %d descriptors to core\n", nfds); for(p = 0; p < nfds; p++){ - mm_manage_fd(pfds[p].fd, BACKEND_NAME, 1, NULL); + if(mm_manage_fd(pfds[p].fd, BACKEND_NAME, 1, NULL)){ + goto bail; + } } rv = 0; @@ -232,12 +310,18 @@ bail: static int midi_shutdown(){ size_t n, p; instance** inst = NULL; + midi_instance_data* data = NULL; if(mm_backend_instances(BACKEND_NAME, &n, &inst)){ fprintf(stderr, "Failed to fetch instance list\n"); return 1; } for(p = 0; p < n; p++){ + data = (midi_instance_data*) inst[p]->impl; + free(data->read); + free(data->write); + data->read = NULL; + data->write = NULL; free(inst[p]->impl); } free(inst); diff --git a/midi.h b/midi.h index 7a13f7c..dc997bc 100644 --- a/midi.h +++ b/midi.h @@ -5,8 +5,8 @@ static int midi_configure(char* option, char* value); static int midi_configure_instance(instance* instance, char* option, char* value); static instance* midi_instance(); static channel* midi_channel(instance* instance, char* spec); -static int midi_set(size_t num, channel* c, channel_value* v); -static int midi_handle(size_t num, int* fd, void** data); +static int midi_set(instance* inst, size_t num, channel* c, channel_value* v); +static int midi_handle(size_t num, managed_fd* fds); static int midi_start(); static int midi_shutdown(); diff --git a/midimonster.c b/midimonster.c index 2c3013d..33858fd 100644 --- a/midimonster.c +++ b/midimonster.c @@ -133,6 +133,12 @@ void fds_free(){ fd = NULL; } +int mm_channel_event(channel* c, channel_value v){ + //TODO + fprintf(stderr, "Stub implementation: Channel on %s at value %f\n", c->instance->name, v.normalised); + return 0; +} + int usage(char* fn){ fprintf(stderr, "MIDIMonster v0.1\n"); fprintf(stderr, "Usage:\n"); @@ -143,7 +149,8 @@ int usage(char* fn){ int main(int argc, char** argv){ fd_set all_fds, read_fds; struct timeval tv; - size_t u; + size_t u, n; + managed_fd* signaled_fds; int rv = EXIT_FAILURE, error, maxfd = -1; char* cfg_file = DEFAULT_CFG; if(argc > 1){ @@ -176,6 +183,13 @@ int main(int argc, char** argv){ signal(SIGINT, signal_handler); + //allocate data buffers + signaled_fds = calloc(fds, sizeof(managed_fd)); + if(!signaled_fds){ + fprintf(stderr, "Failed to allocate memory\n"); + goto bail; + } + //create initial fd set FD_ZERO(&all_fds); for(u = 0; u < fds; u++){ @@ -195,14 +209,29 @@ int main(int argc, char** argv){ fprintf(stderr, "select failed: %s\n", strerror(errno)); break; } - //TODO process all backends, collect events - //TODO push all events to backends - //FIXME notify non-fd backends + + //find all signaled fds + n = 0; + for(u = 0; u < fds; u++){ + if(fd[u].fd >= 0 && FD_ISSET(fd[u].fd, &read_fds)){ + signaled_fds[n] = fd[u]; + n++; + } + } + + //run backend processing, collect events + if(backends_handle(n, signaled_fds)){ + fprintf(stderr, "Backends failed to handle input\n"); + goto bail; + } + + //TODO push collected events to target backends } rv = EXIT_SUCCESS; bail: //free all data + free(signaled_fds); backends_stop(); channels_free(); instances_free(); diff --git a/midimonster.h b/midimonster.h index d548528..bd42d8f 100644 --- a/midimonster.h +++ b/midimonster.h @@ -10,21 +10,24 @@ struct _channel_value; struct _backend_channel; struct _backend_instance; +struct _managed_fd; -typedef int (*mmbackend_handle_event)(size_t channels, struct _backend_channel* c, struct _channel_value* v); +typedef int (*mmbackend_handle_event)(struct _backend_instance* inst, size_t channels, struct _backend_channel* c, struct _channel_value* v); typedef struct _backend_instance* (*mmbackend_create_instance)(); typedef struct _backend_channel* (*mmbackend_parse_channel)(struct _backend_instance* instance, char* spec); typedef void (*mmbackend_free_channel)(struct _backend_channel* c); typedef int (*mmbackend_configure)(char* option, char* value); typedef int (*mmbackend_configure_instance)(struct _backend_instance* instance, char* option, char* value); -typedef int (*mmbackend_process_fd)(size_t fds, int* fd, void** impl); +typedef int (*mmbackend_process_fd)(size_t nfds, struct _managed_fd* fds); typedef int (*mmbackend_start)(); -typedef uint32_t (*mmbackend_max_interval)(); +typedef uint32_t (*mmbackend_interval)(); typedef int (*mmbackend_shutdown)(); typedef struct _channel_value { - double raw_double; - uint64_t raw_u64; + union { + double dbl; + uint64_t u64; + } raw; double normalised; } channel_value; @@ -39,11 +42,12 @@ typedef struct /*_mm_backend*/ { mmbackend_start start; mmbackend_shutdown shutdown; mmbackend_free_channel channel_free; + mmbackend_interval interval; } backend; typedef struct _backend_instance { backend* backend; - uint64_t ident; /*FIXME needed? identification provided by name*/ + uint64_t ident; void* impl; char* name; } instance; @@ -56,7 +60,7 @@ typedef struct _backend_channel { //FIXME might be replaced by struct pollfd //FIXME who frees impl -typedef struct /*_mm_managed_fd*/ { +typedef struct _managed_fd { int fd; backend* backend; void* impl; @@ -86,6 +90,14 @@ int mm_backend_register(backend b); * all instances for the backend. */ instance* mm_instance(); +/* + * Finds an instance matching the specified backend and identifier. + * Since setting an identifier for an instance is optional, + * this may not work depending on the backend. + * Instance identifiers may for example be set in the backends + * mmbackend_start call. + */ +instance* mm_instance_find(char* backend, uint64_t ident); /* * Provides a pointer to a channel structure, pre-filled with * the provided instance reference and identifier. @@ -109,6 +121,7 @@ int mm_manage_fd(int fd, char* backend, int manage, void* impl); int mm_channel_event(channel* c, channel_value v); /* * Query all active instances for a given backend. + * *i will need to be freed by the caller. */ int mm_backend_instances(char* backend, size_t* n, instance*** i); int mm_map_channel(channel* from, channel* to); diff --git a/monster.cfg b/monster.cfg index eb65a07..3ff2e3f 100644 --- a/monster.cfg +++ b/monster.cfg @@ -7,17 +7,13 @@ net = 0 ;[backend osc] -[midi indisc] -port = MIDIMonster Input 1 -;mode = input,output,create +[midi bcf] +read = BCF [midi lc1] -device = 20:0 -;mode = input,output +read = Launch Control [midi xlate] -port = Translation Output 1 -;mode = input,output,create [artnet net1] net = 1 @@ -28,6 +24,9 @@ output = true net = 0 [map] -net1.231 = foo.1 -net1.255 = lc1.cc1.1 +net1.255 = lc1.cc0.1 +net1.256 = lc1.note0.9 +net1.257 = bcf.cc0.81 +;net1.231 = foo.1 +;net1.255 = lc1.cc0.1 ;net1.231 = osc.f/channel5/ toggle=127 -- cgit v1.2.3