aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--README.md67
-rw-r--r--artnet.c68
-rw-r--r--artnet.h5
-rw-r--r--backend.c8
-rw-r--r--config.c6
-rw-r--r--midi.c106
-rw-r--r--midi.h10
-rw-r--r--midimonster.c66
-rw-r--r--midimonster.h8
-rw-r--r--monster.cfg20
10 files changed, 319 insertions, 45 deletions
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 <backend-name>]`,
+an *instance configuration* section, started by `[<backend-name> <instance-name>]` 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 <string.h>
#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 <string.h>
+#include <alsa/asoundlib.h>
+#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 <string.h>
#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