From 5d0ee3b301ca012c81adbf57042f7054ee45808c Mon Sep 17 00:00:00 2001 From: cbdev Date: Sun, 4 Jun 2017 02:30:17 +0200 Subject: Add readme, flesh out backends --- README.md | 67 +++++++++++++++++++++++++++++++++++++ artnet.c | 68 ++++++++++++++++++++++++++----------- artnet.h | 5 +++ backend.c | 8 ++--- config.c | 6 ---- midi.c | 106 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++- midi.h | 10 +++++- midimonster.c | 66 +++++++++++++++++++++++++++++++++++- midimonster.h | 8 ++--- monster.cfg | 20 ++++++----- 10 files changed, 319 insertions(+), 45 deletions(-) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..83b6f52 --- /dev/null +++ b/README.md @@ -0,0 +1,67 @@ +# The MIDIMonster + +Named for it's scary math, the MIDIMonster is a universal translation +tool between multi-channel absolute-value-based control and/or bus protocols, +such as MIDI, DMX/ArtNet and OSC. + +It allows the user to translate channels on one protocol into channels on another +(or the same) protocol, eg + +* Translate MIDI Control Changes into Notes +* Translate MIDI Notes into ArtNet +* Translate OSC messages into MIDI + +## Configuration + +Each protocol supported by MIDIMonster is implemented by a *backend*, which takes +global protocol-specific options and provides *instance*s, which can be configured further. + +The configuration is stored in a file with a format very similar to the common +INI file format. A section is started by a header in `[]` braces, followed by +lines of the form `option = value`. + +A section may either be a *backend configuration* section, started by `[backend ]`, +an *instance configuration* section, started by `[ ]` or a *mapping* +section started by `[map]`. + +The options accepted by the implemented backends are documented in the next section. + +### The `artnet` backend + +#### Global configuration + +| Option | Example value | Default value | Description | +|---------------|-----------------------|-----------------------|-----------------------| +| `bind` | `127.0.0.1 6454` | *none* | What address and port to bind the ArtNet socket to | +| `net` | `0` | `0` | The default net to use | + +#### Instance configuration + +### The `midi` backend + +#### Global configuration + +#### Instance configuration + +### The `osc` backend + +#### Global configuration + +#### Instance configuration + +## Building + +This section will explain how to build the provided sources to be able to run +`midimonster`. + +### Prerequisites + +In order to build the MIDIMonster, you'll need some libraries that provide +support for the protocols to translate. + +* libasound2-dev +* A C compiler + +### Building + +Just running `make` in the source directory should do the trick. diff --git a/artnet.c b/artnet.c index a245cf1..a1f52d7 100644 --- a/artnet.c +++ b/artnet.c @@ -1,12 +1,12 @@ #include #include "artnet.h" -size_t ninstances = 0; -instance* instances = NULL; +#define BACKEND_NAME "artnet" +static uint8_t default_net = 0; int artnet_init(){ backend artnet = { - .name = "artnet", + .name = BACKEND_NAME, .conf = artnet_configure, .create = artnet_instance, .conf_instance = artnet_configure_instance, @@ -17,7 +17,7 @@ int artnet_init(){ }; //register backend - if(!mm_backend_register(artnet)){ + if(mm_backend_register(artnet)){ fprintf(stderr, "Failed to register ArtNet backend\n"); return 1; } @@ -25,50 +25,78 @@ int artnet_init(){ } static int artnet_configure(char* option, char* value){ - fprintf(stderr, "ArtNet backend configured: %s -> %s\n", option, value); - return 0; + if(!strcmp(option, "bind")){ + //TODO create socket, hand over to be managed (unregister previous socket?) + return 0; + } + else if(!strcmp(option, "net")){ + //configure default net + default_net = strtoul(value, NULL, 10); + return 0; + } + fprintf(stderr, "Unknown ArtNet backend option %s\n", option); + return 1; } static instance* artnet_instance(){ - fprintf(stderr, "Creating new ArtNet instance\n"); - instances = realloc(instances, (ninstances + 1) * sizeof(instance)); - if(!instances){ + instance* inst = mm_instance(); + if(!inst){ + return NULL; + } + + inst->impl = calloc(1, sizeof(artnet_instance_data)); + if(!inst->impl){ fprintf(stderr, "Failed to allocate memory\n"); - ninstances = 0; return NULL; } - memset(instances + ninstances, 0, sizeof(instance)); - ninstances++; - return instances + (ninstances - 1); + + return inst; } static int artnet_configure_instance(instance* instance, char* option, char* value){ - fprintf(stderr, "ArtNet instance configured: %s -> %s\n", option, value); + artnet_instance_data* data = (artnet_instance_data*) instance->impl; + + if(!strcmp(option, "net")){ + data->net = strtoul(value, NULL, 10); + return 0; + } + else if(!strcmp(option, "uni")){ + data->uni = strtoul(value, NULL, 10); + return 0; + } + + fprintf(stderr, "Unknown ArtNet instance option %s\n", option); return 1; } static channel* artnet_channel(instance* instance, char* spec){ fprintf(stderr, "Parsing ArtNet channelspec %s\n", spec); + //TODO return NULL; } static int artnet_set(size_t num, channel* c, channel_value* v){ + //TODO return 1; } static int artnet_handle(size_t num, int* fd, void** data){ + //TODO return 1; } static int artnet_shutdown(){ - size_t u; - - for(u = 0; u < ninstances; u++){ - mm_instance_free(instances + u); + size_t n, p; + instance** inst = NULL; + if(mm_backend_instances(BACKEND_NAME, &n, &inst)){ + fprintf(stderr, "Failed to fetch instance list\n"); + return 1; } - free(instances); - ninstances = 0; + for(p = 0; p < n; p++){ + free(inst[p]->impl); + } + free(inst); fprintf(stderr, "ArtNet backend shut down\n"); return 0; } diff --git a/artnet.h b/artnet.h index a7742f3..03106bf 100644 --- a/artnet.h +++ b/artnet.h @@ -8,3 +8,8 @@ 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_shutdown(); + +typedef struct /*_artnet_instance_model*/ { + uint8_t net; + uint8_t uni; +} artnet_instance_data; diff --git a/backend.c b/backend.c index e1e143f..673fe9b 100644 --- a/backend.c +++ b/backend.c @@ -15,21 +15,21 @@ backend* backend_match(char* name){ return NULL; } -backend* mm_backend_register(backend b){ +int mm_backend_register(backend b){ if(!backend_match(b.name)){ backends = realloc(backends, (nbackends + 1) * sizeof(backend)); if(!backends){ fprintf(stderr, "Failed to allocate memory\n"); nbackends = 0; - return NULL; + return 1; } backends[nbackends] = b; nbackends++; fprintf(stderr, "Registered backend %s\n", b.name); - return backends + (nbackends - 1); + return 0; } - return NULL; + return 1; } int backends_stop(){ diff --git a/config.c b/config.c index de6767d..b87394f 100644 --- a/config.c +++ b/config.c @@ -130,9 +130,3 @@ bail: free(line_raw); return rv; } - -void mm_instance_free(instance* inst){ - free(inst->name); - inst->name = NULL; - inst->backend = NULL; -} diff --git a/midi.c b/midi.c index c817bd2..a59ebeb 100644 --- a/midi.c +++ b/midi.c @@ -1,3 +1,107 @@ -int midi_start(){ +#include +#include +#include "midi.h" + +#define BACKEND_NAME "midi" +static snd_seq_t* sequencer = NULL; + +int midi_init(){ + backend midi = { + .name = BACKEND_NAME, + .conf = midi_configure, + .create = midi_instance, + .conf_instance = midi_configure_instance, + .channel = midi_channel, + .handle = midi_set, + .process = midi_handle, + .shutdown = midi_shutdown + }; + + if(snd_seq_open(&sequencer, "default", SND_SEQ_OPEN_DUPLEX, 0) < 0){ + fprintf(stderr, "Failed to open ALSA sequencer\n"); + return 1; + } + + //register backend + if(mm_backend_register(midi)){ + fprintf(stderr, "Failed to register MIDI backend\n"); + return 1; + } + 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; + } + return 0; + } + + fprintf(stderr, "Unknown MIDI backend option %s\n", option); + return 1; +} + +static instance* midi_instance(){ + return mm_instance(); +} + +static int midi_configure_instance(instance* instance, char* option, char* value){ + if(!strcmp(option, "device")){ + //open i/o device + return 0; + } + else if(!strcmp(option, "port")){ + //create midi port + return 0; + } + else if(!strcmp(option, "mode")){ + //configure open mode + //FIXME needed? + return 0; + } + + fprintf(stderr, "Unknown MIDI instance option %s\n", option); + return 1; +} + +static channel* midi_channel(instance* instance, char* spec){ + fprintf(stderr, "Parsing MIDI channelspec %s\n", spec); + //TODO + return NULL; +} + +static int midi_set(size_t num, channel* c, channel_value* v){ + //TODO return 1; } + +static int midi_handle(size_t num, int* fd, void** data){ + //TODO + return 1; +} + +static int midi_shutdown(){ + size_t n, p; + instance** inst = 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++){ + free(inst[p]->impl); + } + free(inst); + + //close midi + snd_seq_close(sequencer); + sequencer = NULL; + + //free configuration cache + snd_config_update_free_global(); + + fprintf(stderr, "MIDI backend shut down\n"); + return 0; +} diff --git a/midi.h b/midi.h index eef2413..abad971 100644 --- a/midi.h +++ b/midi.h @@ -1,2 +1,10 @@ #include "midimonster.h" -int midi_start(); + +int midi_init(); +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_shutdown(); diff --git a/midimonster.c b/midimonster.c index 5d7ec7f..765c1ce 100644 --- a/midimonster.c +++ b/midimonster.c @@ -1,3 +1,4 @@ +#include #include "midimonster.h" #include "config.h" #include "backend.h" @@ -7,6 +8,67 @@ int artnet_init(); int midi_init(); int osc_init(); +static size_t ninstances = 0; +static instance** instances = NULL; + +instance* mm_instance(){ + instance** new_inst = realloc(instances, (ninstances + 1) * sizeof(instance*)); + if(!new_inst){ + //TODO free + fprintf(stderr, "Failed to allocate memory\n"); + ninstances = 0; + return NULL; + } + instances = new_inst; + instances[ninstances] = calloc(1, sizeof(instance)); + if(!instances[ninstances]){ + fprintf(stderr, "Failed to allocate memory\n"); + return NULL; + } + + return instances[ninstances++]; +} + +int mm_backend_instances(char* name, size_t* ninst, instance*** inst){ + backend* b = backend_match(name); + size_t n = 0, u; + //count number of affected instances + for(u = 0; u < ninstances; u++){ + if(instances[u]->backend == b){ + n++; + } + } + + *ninst = n; + *inst = calloc(n, sizeof(instance*)); + if(!*inst){ + fprintf(stderr, "Failed to allocate memory\n"); + return 1; + } + + n = 0; + for(u = 0; u < ninstances; u++){ + if(instances[u]->backend == b){ + (*inst)[n] = instances[u]; + n++; + } + } + return 0; +} + +static void instances_free(){ + size_t u; + for(u = 0; u < ninstances; u++){ + free(instances[u]->name); + instances[u]->name = NULL; + instances[u]->backend = NULL; + free(instances[u]); + instances[u] = NULL; + } + free(instances); + ninstances = 0; +} + int usage(char* fn){ fprintf(stderr, "MIDIMonster v0.1\n"); fprintf(stderr, "Usage:\n"); @@ -23,7 +85,7 @@ int main(int argc, char** argv){ //initialize backends //TODO replace this with loading shared objects - if(artnet_init() /*|| midi_init() || osc_init()*/){ + if(artnet_init() || midi_init() /* || osc_init()*/){ fprintf(stderr, "Failed to initialize a backend\n"); goto bail; } @@ -32,6 +94,7 @@ int main(int argc, char** argv){ if(config_read(cfg_file)){ fprintf(stderr, "Failed to read configuration file %s\n", cfg_file); backends_stop(); + instances_free(); return usage(argv[0]); } @@ -41,6 +104,7 @@ int main(int argc, char** argv){ bail: //free all data backends_stop(); + instances_free(); return rv; } diff --git a/midimonster.h b/midimonster.h index 8c323f1..86c2d30 100644 --- a/midimonster.h +++ b/midimonster.h @@ -55,9 +55,9 @@ typedef struct /*_mm_managed_fd*/ { void* impl; } managed_fd; -backend* mm_backend_register(backend b); -int mm_manage_fd(int fd, backend* b, int manage, void* impl); +int mm_backend_register(backend b); +instance* mm_instance(); +int mm_manage_fd(int fd, char* backend, int manage, void* impl); int mm_channel_event(channel* c, channel_value v); - -void mm_instance_free(instance* i); +int mm_backend_instances(char* backend, size_t* n, instance*** i); #endif diff --git a/monster.cfg b/monster.cfg index 8d3b0ad..ccb0d11 100644 --- a/monster.cfg +++ b/monster.cfg @@ -2,26 +2,30 @@ name = MIDIMonster [backend artnet] +bind = 127.0.0.1 6454 +net = 0 -[backend osc] +;[backend osc] [midi indisc] -name = MIDIMonster Input 1 -mode = input,output,create +port = MIDIMonster Input 1 +;mode = input,output,create [midi lc1] device = 20:0 -mode = input,output +;mode = input,output [midi xlate] -name = Translation Output 1 -mode = input,output,create +port = Translation Output 1 +;mode = input,output,create [artnet net1] -mode = input,output -net = 0 +net = 1 uni = 1 +[artnet foo] +net = 0 + [map] artnet.231 = lc1.cc5 artent.231 = osc.f/channel5/ toggle=127 -- cgit v1.2.3