From c47b0765a8605b9afcc6205a6d2396ccbe3d5e05 Mon Sep 17 00:00:00 2001 From: cbdev Date: Sun, 4 Jun 2017 22:49:27 +0200 Subject: Implement channel mapping --- backend.c | 42 ++++++++++++++++++++++++++++++++++++++++++ backend.h | 1 + config.c | 59 +++++++++++++++++++++++++++++++++++++++++++++++++++++------ midimonster.c | 43 +++++++++++++++++++++++++++++++++++++++++++ midimonster.h | 41 +++++++++++++++++++++++++++++++++++++++++ monster.cfg | 4 ++-- 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 -- cgit v1.2.3