aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--backend.c42
-rw-r--r--backend.h1
-rw-r--r--config.c59
-rw-r--r--midimonster.c43
-rw-r--r--midimonster.h41
-rw-r--r--monster.cfg4
6 files changed, 182 insertions, 8 deletions
diff --git a/backend.c b/backend.c
index 1d728d1..65ee748 100644
--- a/backend.c
+++ b/backend.c
@@ -6,6 +6,35 @@ static size_t nbackends = 0;
static backend* backends = NULL;
static size_t ninstances = 0;
static instance** instances = NULL;
+static size_t nchannels = 0;
+static channel** channels = NULL;
+
+channel* mm_channel(instance* i, uint64_t ident){
+ size_t u;
+ for(u = 0; u < nchannels; u++){
+ if(channels[u]->instance == 0 && channels[u]->ident == ident){
+ return channels[u];
+ }
+ }
+
+ channel** new_chan = realloc(channels, (nchannels + 1) * sizeof(channel*));
+ if(!new_chan){
+ fprintf(stderr, "Failed to allocate memory\n");
+ nchannels = 0;
+ return NULL;
+ }
+
+ channels = new_chan;
+ channels[nchannels] = calloc(1, sizeof(channel));
+ if(!channels[nchannels]){
+ fprintf(stderr, "Failed to allocate memory\n");
+ return NULL;
+ }
+
+ channels[nchannels]->instance = i;
+ channels[nchannels]->ident = ident;
+ return channels[nchannels++];
+}
instance* mm_instance(){
instance** new_inst = realloc(instances, (ninstances + 1) * sizeof(instance*));
@@ -65,6 +94,19 @@ void instances_free(){
ninstances = 0;
}
+void channels_free(){
+ size_t u;
+ for(u = 0; u < nchannels; u++){
+ if(channels[u]->impl){
+ channels[u]->instance->backend->channel_free(channels[u]);
+ }
+ free(channels[u]);
+ channels[u] = NULL;
+ }
+ free(channels);
+ nchannels = 0;
+}
+
backend* backend_match(char* name){
size_t u;
for(u = 0; u < nbackends; u++){
diff --git a/backend.h b/backend.h
index 65ca97e..e1ad4ab 100644
--- a/backend.h
+++ b/backend.h
@@ -3,3 +3,4 @@ instance* instance_match(char* name);
int backends_start();
int backends_stop();
void instances_free();
+void channels_free();
diff --git a/config.c b/config.c
index f425fe4..d3605e2 100644
--- a/config.c
+++ b/config.c
@@ -4,15 +4,15 @@
#include "config.h"
#include "backend.h"
-enum {
+static enum {
none,
backend_cfg,
instance_cfg,
map
} parser_state = none;
-backend* current_backend = NULL;
-instance* current_instance = NULL;
+static backend* current_backend = NULL;
+static instance* current_instance = NULL;
static char* config_trim_line(char* in){
ssize_t u;
@@ -28,6 +28,48 @@ static char* config_trim_line(char* in){
return in;
}
+static int config_map(char* to, char* from){
+ char* chanspec_to = to, *chanspec_from = from;
+ instance* instance_to = NULL, *instance_from = NULL;
+ channel* channel_from = NULL, *channel_to = NULL;
+
+ //separate channel spec from instance
+ for(; *chanspec_to && *chanspec_to != '.'; chanspec_to++){
+ }
+
+ for(; *chanspec_from && *chanspec_from != '.'; chanspec_from++){
+ }
+
+ if(!*chanspec_to || !*chanspec_from){
+ fprintf(stderr, "Mapping does not contain a proper instance specification\n");
+ return 1;
+ }
+
+ //terminate
+ *chanspec_to = *chanspec_from = 0;
+ chanspec_to++;
+ chanspec_from++;
+
+ //find matching instances
+ instance_to = instance_match(to);
+ instance_from = instance_match(from);
+
+ if(!instance_to || !instance_from){
+ fprintf(stderr, "No such instance %s\n", instance_from ? to : from);
+ return 1;
+ }
+
+ channel_from = instance_from->backend->channel(instance_from, chanspec_from);
+ channel_to = instance_to->backend->channel(instance_to, chanspec_to);
+
+ if(!channel_from || !channel_to){
+ fprintf(stderr, "Failed to parse channel specifications\n");
+ return 1;
+ }
+
+ return mm_map_channel(channel_from, channel_to);
+}
+
int config_read(char* cfg_file){
int rv = 1;
size_t line_alloc = 0;
@@ -114,17 +156,22 @@ int config_read(char* cfg_file){
*separator = 0;
separator++;
+ line = config_trim_line(line);
+ separator = config_trim_line(separator);
- if(parser_state == backend_cfg && current_backend->conf(config_trim_line(line), config_trim_line(separator))){
+ if(parser_state == backend_cfg && current_backend->conf(line, separator)){
fprintf(stderr, "Failed to configure backend %s\n", current_backend->name);
goto bail;
}
- else if(parser_state == instance_cfg && current_backend->conf_instance(current_instance, config_trim_line(line), config_trim_line(separator))){
+ else if(parser_state == instance_cfg && current_backend->conf_instance(current_instance, line, separator)){
fprintf(stderr, "Failed to configure instance %s\n", current_instance->name);
goto bail;
}
else if(parser_state == map){
- //TODO map two channels
+ if(config_map(line, separator)){
+ fprintf(stderr, "Failed to map channel\n");
+ goto bail;
+ }
}
}
}
diff --git a/midimonster.c b/midimonster.c
index e59b7f9..aeaf04d 100644
--- a/midimonster.c
+++ b/midimonster.c
@@ -8,6 +8,48 @@ int artnet_init();
int midi_init();
int osc_init();
+static size_t mappings = 0;
+static channel_mapping* map = NULL;
+
+int mm_map_channel(channel* from, channel* to){
+ size_t u, m;
+ //find existing source mapping
+ for(u = 0; u < mappings; u++){
+ if(map[u].from == from){
+ break;
+ }
+ }
+
+ //create new entry
+ if(u == mappings){
+ map = realloc(map, (mappings + 1) * sizeof(channel_mapping));
+ if(!map){
+ fprintf(stderr, "Failed to allocate memory\n");
+ return 1;
+ }
+ memset(map + mappings, 0, sizeof(channel_mapping));
+ mappings++;
+ }
+
+ //check whether the target is already mapped
+ for(m = 0; m < map[u].destinations; m++){
+ if(map[u].to[m] == to){
+ return 0;
+ }
+ }
+
+ map[u].to = realloc(map[u].to, (map[u].destinations + 1) * sizeof(channel*));
+ if(!map[u].to){
+ fprintf(stderr, "Failed to allocate memory\n");
+ map[u].destinations = 0;
+ return 1;
+ }
+
+ map[u].to[map[u].destinations] = to;
+ map[u].destinations++;
+ return 0;
+}
+
int usage(char* fn){
fprintf(stderr, "MIDIMonster v0.1\n");
fprintf(stderr, "Usage:\n");
@@ -49,6 +91,7 @@ int main(int argc, char** argv){
bail:
//free all data
backends_stop();
+ channels_free();
instances_free();
return rv;
diff --git a/midimonster.h b/midimonster.h
index 9026222..b2af7a3 100644
--- a/midimonster.h
+++ b/midimonster.h
@@ -13,6 +13,7 @@ struct _backend_instance;
typedef int (*mmbackend_handle_event)(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);
@@ -36,6 +37,7 @@ typedef struct /*_mm_backend*/ {
mmbackend_process_fd process;
mmbackend_start start;
mmbackend_shutdown shutdown;
+ mmbackend_free_channel channel_free;
} backend;
typedef struct _backend_instance {
@@ -58,9 +60,48 @@ typedef struct /*_mm_managed_fd*/ {
void* impl;
} managed_fd;
+typedef struct /*_mm_channel_mapping*/ {
+ channel* from;
+ size_t destinations;
+ channel** to;
+} channel_mapping;
+
+/*
+ * Register a new backend.
+ */
int mm_backend_register(backend b);
+
+/*
+ * Provides a pointer to a newly (zero-)allocated instance.
+ * All instance pointers need to be allocated via this API
+ * in order to be assignable from the configuration parser.
+ * This API should be called from the mmbackend_create_instance
+ * call of your backend.
+ *
+ * Instances returned from this call are freed by midimonster.
+ * The contents of the impl members should be freed in the
+ * mmbackend_shutdown procedure of the backend, eg. by querying
+ * all instances for the backend.
+ */
instance* mm_instance();
+/*
+ * Provides a pointer to a channel structure, pre-filled with
+ * the provided instance reference and identifier.
+ * Will return previous allocations if the provided fields
+ * match.
+ * This API is just a convenience function. The array
+ * of channels is only used for mapping internally,
+ * creating and managing your own channel store is
+ * possible.
+ * For each channel with a non-NULL impl field, the backend
+ * will receive a call to its channel_free function.
+ */
+channel* mm_channel(instance* i, uint64_t ident);
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.
+ */
int mm_backend_instances(char* backend, size_t* n, instance*** i);
+int mm_map_channel(channel* from, channel* to);
#endif
diff --git a/monster.cfg b/monster.cfg
index 4bb7bd2..b638e56 100644
--- a/monster.cfg
+++ b/monster.cfg
@@ -28,5 +28,5 @@ output = true
net = 0
[map]
-artnet.231 = lc1.cc5
-artent.231 = osc.f/channel5/ toggle=127
+net1.231 = lc1.cc5
+;net1.231 = osc.f/channel5/ toggle=127