From 4e604b493e0dc855b6ea6978e5cf8e2de5d2b8d5 Mon Sep 17 00:00:00 2001 From: cbdev Date: Sun, 28 Jul 2019 23:40:40 +0200 Subject: Fix evdev relative axes, add detect option --- backends/evdev.h | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'backends/evdev.h') diff --git a/backends/evdev.h b/backends/evdev.h index c6e3a25..d719631 100644 --- a/backends/evdev.h +++ b/backends/evdev.h @@ -24,10 +24,18 @@ static int evdev_shutdown(); #define UINPUT_MAX_NAME_SIZE 512 #endif +typedef struct /*_evdev_relative_axis_config*/ { + int code; + int64_t max; + int64_t current; +} evdev_relaxis_config; + typedef struct /*_evdev_instance_model*/ { int input_fd; struct libevdev* input_ev; int exclusive; + size_t relative_axes; + evdev_relaxis_config* relative_axis; int output_enabled; #ifndef EVDEV_NO_UINPUT -- cgit v1.2.3 From a23ce7718ab61e73586d2738329d6bbace0b764b Mon Sep 17 00:00:00 2001 From: cbdev Date: Thu, 1 Aug 2019 20:54:17 +0200 Subject: Implement evdev relative axis inversion --- backends/evdev.c | 15 ++++++++++++++- backends/evdev.h | 1 + backends/evdev.md | 8 +++++++- 3 files changed, 22 insertions(+), 2 deletions(-) (limited to 'backends/evdev.h') diff --git a/backends/evdev.c b/backends/evdev.c index 565eb17..7a7913d 100644 --- a/backends/evdev.c +++ b/backends/evdev.c @@ -205,8 +205,13 @@ static int evdev_configure_instance(instance* inst, char* option, char* value) { fprintf(stderr, "Failed to allocate memory\n"); return 1; } + data->relative_axis[data->relative_axes].inverted = 0; data->relative_axis[data->relative_axes].code = libevdev_event_code_from_name(EV_REL, option + 8); - data->relative_axis[data->relative_axes].max = strtoul(value, &next_token, 0); + data->relative_axis[data->relative_axes].max = strtoll(value, &next_token, 0); + if(data->relative_axis[data->relative_axes].max < 0){ + data->relative_axis[data->relative_axes].max *= -1; + data->relative_axis[data->relative_axes].inverted = 1; + } data->relative_axis[data->relative_axes].current = strtoul(next_token, NULL, 0); if(data->relative_axis[data->relative_axes].code < 0){ fprintf(stderr, "Failed to configure relative axis extents for %s.%s\n", inst->name, option + 8); @@ -313,6 +318,9 @@ static int evdev_push_event(instance* inst, evdev_instance_data* data, struct in case EV_REL: for(axis = 0; axis < data->relative_axes; axis++){ if(data->relative_axis[axis].code == event.code){ + if(data->relative_axis[axis].inverted){ + event.value *= -1; + } data->relative_axis[axis].current = clamp(data->relative_axis[axis].current + event.value, data->relative_axis[axis].max, 0); val.normalised = (double) data->relative_axis[axis].current / (double) data->relative_axis[axis].max; break; @@ -374,6 +382,11 @@ static int evdev_handle(size_t num, managed_fd* fds){ read_flags = LIBEVDEV_READ_FLAG_SYNC; } + //exclude synchronization events + if(ev.type == EV_SYN){ + continue; + } + //handle event if(evdev_push_event(inst, data, ev)){ return 1; diff --git a/backends/evdev.h b/backends/evdev.h index d719631..f89e362 100644 --- a/backends/evdev.h +++ b/backends/evdev.h @@ -25,6 +25,7 @@ static int evdev_shutdown(); #endif typedef struct /*_evdev_relative_axis_config*/ { + uint8_t inverted; int code; int64_t max; int64_t current; diff --git a/backends/evdev.md b/backends/evdev.md index 995b44c..88dfc85 100644 --- a/backends/evdev.md +++ b/backends/evdev.md @@ -38,12 +38,18 @@ If an axis is not used for output, this configuration can be omitted. For real devices, all of these parameters for every axis can be found by running `evtest` on the device. To use the input from relative axes in absolute-value based protocols, the backend needs a reference frame to -convert the relative movements to absolute values. +convert the relative movements to absolute values. To invert the mapping of the relative axis, specify the `max` value +as a negative number, for example: + +``` +relaxis.REL_X = -1024 512 +``` If relative axes are used without specifying their extents, the channel will generate normalized values of `0`, `0.5` and `1` for any input less than, equal to and greater than `0`, respectively. As for output, only the values `-1`, `0` and `1` are generated for the same interval. + #### Channel specification A channel is specified by its event type and event code, separated by `.`. For a complete list of event types and codes -- cgit v1.2.3 From 48bf96602023b2ead855f13477b6f5e26b663b45 Mon Sep 17 00:00:00 2001 From: cbdev Date: Sat, 10 Aug 2019 20:30:07 +0200 Subject: Clean up & check unions --- backends/artnet.c | 5 +++++ backends/evdev.c | 14 +++++--------- backends/evdev.h | 9 +++++++++ backends/midi.c | 14 +++++--------- backends/midi.h | 10 ++++++++++ backends/osc.c | 13 +++++-------- backends/osc.h | 8 ++++++++ backends/sacn.c | 5 +++++ 8 files changed, 52 insertions(+), 26 deletions(-) (limited to 'backends/evdev.h') diff --git a/backends/artnet.c b/backends/artnet.c index 7f3f08c..8e47d4f 100644 --- a/backends/artnet.c +++ b/backends/artnet.c @@ -54,6 +54,11 @@ int init(){ .shutdown = artnet_shutdown }; + if(sizeof(artnet_instance_id) != sizeof(uint64_t)){ + fprintf(stderr, "ArtNet instance identification union out of bounds\n"); + return 1; + } + //register backend if(mm_backend_register(artnet)){ fprintf(stderr, "Failed to register ArtNet backend\n"); diff --git a/backends/evdev.c b/backends/evdev.c index 7a7913d..bd2098d 100644 --- a/backends/evdev.c +++ b/backends/evdev.c @@ -18,15 +18,6 @@ #define BACKEND_NAME "evdev" -typedef union { - struct { - uint32_t pad; - uint16_t type; - uint16_t code; - } fields; - uint64_t label; -} evdev_channel_ident; - static struct { uint8_t detect; } evdev_config = { @@ -46,6 +37,11 @@ int init(){ .shutdown = evdev_shutdown }; + if(sizeof(evdev_channel_ident) != sizeof(uint64_t)){ + fprintf(stderr, "evdev channel identification union out of bounds\n"); + return 1; + } + if(mm_backend_register(evdev)){ fprintf(stderr, "Failed to register evdev backend\n"); return 1; diff --git a/backends/evdev.h b/backends/evdev.h index f89e362..b26664b 100644 --- a/backends/evdev.h +++ b/backends/evdev.h @@ -44,3 +44,12 @@ typedef struct /*_evdev_instance_model*/ { struct libevdev_uinput* output_ev; #endif } evdev_instance_data; + +typedef union { + struct { + uint32_t pad; + uint16_t type; + uint16_t code; + } fields; + uint64_t label; +} evdev_channel_ident; \ No newline at end of file diff --git a/backends/midi.c b/backends/midi.c index 9c6ba80..5b8e561 100644 --- a/backends/midi.c +++ b/backends/midi.c @@ -4,15 +4,6 @@ #define BACKEND_NAME "midi" static snd_seq_t* sequencer = NULL; -typedef union { - struct { - uint8_t pad[5]; - uint8_t type; - uint8_t channel; - uint8_t control; - } fields; - uint64_t label; -} midi_channel_ident; enum /*_midi_channel_type*/ { none = 0, @@ -44,6 +35,11 @@ int init(){ .shutdown = midi_shutdown }; + if(sizeof(midi_channel_ident) != sizeof(uint64_t)){ + fprintf(stderr, "MIDI channel identification union out of bounds\n"); + return 1; + } + if(snd_seq_open(&sequencer, "default", SND_SEQ_OPEN_DUPLEX, 0) < 0){ fprintf(stderr, "Failed to open ALSA sequencer\n"); return 1; diff --git a/backends/midi.h b/backends/midi.h index 556706f..5ec17ea 100644 --- a/backends/midi.h +++ b/backends/midi.h @@ -15,3 +15,13 @@ typedef struct /*_midi_instance_data*/ { char* read; char* write; } midi_instance_data; + +typedef union { + struct { + uint8_t pad[5]; + uint8_t type; + uint8_t channel; + uint8_t control; + } fields; + uint64_t label; +} midi_channel_ident; \ No newline at end of file diff --git a/backends/osc.c b/backends/osc.c index 77bbde4..beb5527 100644 --- a/backends/osc.c +++ b/backends/osc.c @@ -13,14 +13,6 @@ #define osc_align(a) ((((a) / 4) + (((a) % 4) ? 1 : 0)) * 4) #define BACKEND_NAME "osc" -typedef union { - struct { - uint32_t channel; - uint32_t parameter; - } fields; - uint64_t label; -} osc_channel_ident; - static struct { uint8_t detect; } osc_global_config = { @@ -40,6 +32,11 @@ int init(){ .shutdown = osc_shutdown }; + if(sizeof(osc_channel_ident) != sizeof(uint64_t)){ + fprintf(stderr, "OSC channel identification union out of bounds\n"); + return 1; + } + //register backend if(mm_backend_register(osc)){ fprintf(stderr, "Failed to register OSC backend\n"); diff --git a/backends/osc.h b/backends/osc.h index b2aaea7..ab19463 100644 --- a/backends/osc.h +++ b/backends/osc.h @@ -66,3 +66,11 @@ typedef struct /*_osc_instance_data*/ { //peer fd int fd; } osc_instance_data; + +typedef union { + struct { + uint32_t channel; + uint32_t parameter; + } fields; + uint64_t label; +} osc_channel_ident; \ No newline at end of file diff --git a/backends/sacn.c b/backends/sacn.c index 2f418e5..470e3bd 100644 --- a/backends/sacn.c +++ b/backends/sacn.c @@ -44,6 +44,11 @@ int init(){ .shutdown = sacn_shutdown }; + if(sizeof(sacn_instance_id) != sizeof(uint64_t)){ + fprintf(stderr, "sACN instance identification union out of bounds\n"); + return 1; + } + //register the backend if(mm_backend_register(sacn)){ fprintf(stderr, "Failed to register sACN backend\n"); -- cgit v1.2.3 From 8b016f61a4b3d3be0c7b1e311209ab991276af0c Mon Sep 17 00:00:00 2001 From: cbdev Date: Thu, 22 Aug 2019 21:13:48 +0200 Subject: Implement input for the maweb backend (with a few limitations) --- README.md | 2 +- backends/Makefile | 4 +- backends/evdev.h | 3 +- backends/libmmbackend.c | 241 ++++++++++++++++++++++++++++++-- backends/libmmbackend.h | 33 ++--- backends/maweb.c | 358 ++++++++++++++++++++++++++++++++++++++++-------- backends/maweb.h | 14 +- backends/maweb.md | 36 +++-- backends/midi.h | 3 +- backends/osc.h | 3 +- 10 files changed, 582 insertions(+), 115 deletions(-) (limited to 'backends/evdev.h') diff --git a/README.md b/README.md index 4d8fe18..40f8fbb 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ on any other (or the same) supported protocol, for example to: * Translate MIDI Control Changes into Notes ([Example configuration](configs/unifest-17.cfg)) * Translate MIDI Notes into ArtNet or sACN ([Example configuration](configs/launchctl-sacn.cfg)) * Translate OSC messages into MIDI ([Example configuration](configs/midi-osc.cfg)) -* Dynamically route and modify events using the Lua programming language ([Example configuration](configs/lua.cfg) and [Script](configs/demo.lua)) to create your own lighting controller or run effects on TouchOSC (Flying faders demo [configuration](configs/flying-faders.cfg) and [script](configs/flying-faders.lua)) +* Dynamically generate, route and modify events using the Lua programming language ([Example configuration](configs/lua.cfg) and [Script](configs/demo.lua)) to create your own lighting controller or run effects on TouchOSC (Flying faders demo [configuration](configs/flying-faders.cfg) and [script](configs/flying-faders.lua)) * Use an OSC app as a simple lighting controller via ArtNet or sACN * Visualize ArtNet data using OSC tools * Control lighting fixtures or DAWs using gamepad controllers, trackballs, etc ([Example configuration](configs/evdev.cfg)) diff --git a/backends/Makefile b/backends/Makefile index 582655c..5c5b677 100644 --- a/backends/Makefile +++ b/backends/Makefile @@ -1,7 +1,7 @@ .PHONY: all clean full LINUX_BACKENDS = midi.so evdev.so -WINDOWS_BACKENDS = artnet.dll osc.dll loopback.dll sacn.dll -BACKENDS = artnet.so osc.so loopback.so sacn.so lua.so +WINDOWS_BACKENDS = artnet.dll osc.dll loopback.dll sacn.dll maweb.dll +BACKENDS = artnet.so osc.so loopback.so sacn.so lua.so maweb.so OPTIONAL_BACKENDS = ola.so BACKEND_LIB = libmmbackend.o diff --git a/backends/evdev.h b/backends/evdev.h index b26664b..48bd0ab 100644 --- a/backends/evdev.h +++ b/backends/evdev.h @@ -52,4 +52,5 @@ typedef union { uint16_t code; } fields; uint64_t label; -} evdev_channel_ident; \ No newline at end of file +} evdev_channel_ident; + diff --git a/backends/libmmbackend.c b/backends/libmmbackend.c index c98cfe3..ccbeb52 100644 --- a/backends/libmmbackend.c +++ b/backends/libmmbackend.c @@ -208,10 +208,11 @@ size_t json_validate(char* json, size_t length){ size_t json_validate_string(char* json, size_t length){ size_t string_length = 0, offset; - for(offset = 0; json[offset] && offset < length && json[offset] != '"'; offset++){ + //skip leading whitespace + for(offset = 0; json[offset] && offset < length && isspace(json[offset]); offset++){ } - if(offset == length){ + if(offset == length || json[offset] != '"'){ return 0; } @@ -230,17 +231,122 @@ size_t json_validate_string(char* json, size_t length){ } size_t json_validate_array(char* json, size_t length){ - //TODO + size_t offset = 0; + + //skip leading whitespace + for(offset = 0; json[offset] && offset < length && isspace(json[offset]); offset++){ + } + + if(offset == length || json[offset] != '['){ + return 0; + } + + for(offset++; offset < length; offset++){ + offset += json_validate(json + offset, length - offset); + + //skip trailing whitespace, find terminator + for(; offset < length && isspace(json[offset]); offset++){ + } + + if(json[offset] == ','){ + continue; + } + + if(json[offset] == ']'){ + return offset + 1; + } + + break; + } + return 0; } size_t json_validate_object(char* json, size_t length){ - //TODO + size_t offset = 0; + + //skip whitespace + for(offset = 0; json[offset] && isspace(json[offset]); offset++){ + } + + if(offset == length || json[offset] != '{'){ + return 0; + } + + for(offset++; offset < length; offset++){ + if(json_identify(json + offset, length - offset) != JSON_STRING){ + //still could be an empty object... + for(; offset < length && isspace(json[offset]); offset++){ + } + if(json[offset] == '}'){ + return offset + 1; + } + return 0; + } + offset += json_validate(json + offset, length - offset); + + //find value separator + for(; offset < length && isspace(json[offset]); offset++){ + } + + if(json[offset] != ':'){ + return 0; + } + + offset++; + offset += json_validate(json + offset, length - offset); + + //skip trailing whitespace + for(; json[offset] && isspace(json[offset]); offset++){ + } + + if(json[offset] == '}'){ + return offset + 1; + } + else if(json[offset] != ','){ + return 0; + } + } return 0; } size_t json_validate_value(char* json, size_t length){ - //TODO + size_t offset = 0, value_length; + + //skip leading whitespace + for(offset = 0; json[offset] && offset < length && isspace(json[offset]); offset++){ + } + + if(offset == length){ + return 0; + } + + //match complete values + if(length - offset >= 4 && !strncmp(json + offset, "null", 4)){ + return offset + 4; + } + else if(length - offset >= 4 && !strncmp(json + offset, "true", 4)){ + return offset + 4; + } + else if(length - offset >= 5 && !strncmp(json + offset, "false", 5)){ + return offset + 5; + } + + if(json[offset] == '-' || isdigit(json[offset])){ + //json number parsing is dumb. + for(value_length = 1; offset + value_length < length && + (isdigit(json[offset + value_length]) + || json[offset + value_length] == '+' + || json[offset + value_length] == '-' + || json[offset + value_length] == '.' + || tolower(json[offset + value_length]) == 'e'); value_length++){ + } + + if(value_length > 0){ + return offset + value_length; + } + } + return 0; } @@ -284,13 +390,51 @@ size_t json_obj_offset(char* json, char* key){ //add length of value offset += json_validate(json + offset, strlen(json + offset)); - //find comma or closing brace - for(; json[offset] && json[offset] != ',' && json[offset] != '}'; offset++){ + //skip trailing whitespace + for(; json[offset] && isspace(json[offset]); offset++){ } if(json[offset] == ','){ offset++; + continue; + } + + break; + } + + return 0; +} + +size_t json_array_offset(char* json, uint64_t key){ + size_t offset = 0, index = 0; + + //skip leading whitespace + for(offset = 0; json[offset] && isspace(json[offset]); offset++){ + } + + if(json[offset] != '['){ + return 0; + } + + for(offset++; index <= key; offset++){ + //skip whitespace + for(; json[offset] && isspace(json[offset]); offset++){ + } + + if(index == key){ + return offset; + } + + offset += json_validate(json + offset, strlen(json + offset)); + + //skip trailing whitespace, find terminator + for(; json[offset] && isspace(json[offset]); offset++){ } + + if(json[offset] != ','){ + break; + } + index++; } return 0; @@ -304,6 +448,14 @@ json_type json_obj(char* json, char* key){ return JSON_INVALID; } +json_type json_array(char* json, uint64_t key){ + size_t offset = json_array_offset(json, key); + if(offset){ + return json_identify(json + offset, strlen(json + offset)); + } + return JSON_INVALID; +} + uint8_t json_obj_bool(char* json, char* key, uint8_t fallback){ size_t offset = json_obj_offset(json, key); if(offset){ @@ -317,6 +469,19 @@ uint8_t json_obj_bool(char* json, char* key, uint8_t fallback){ return fallback; } +uint8_t json_array_bool(char* json, uint64_t key, uint8_t fallback){ + size_t offset = json_array_offset(json, key); + if(offset){ + if(!strncmp(json + offset, "true", 4)){ + return 1; + } + if(!strncmp(json + offset, "false", 5)){ + return 0; + } + } + return fallback; +} + int64_t json_obj_int(char* json, char* key, int64_t fallback){ char* next_token = NULL; int64_t result; @@ -332,7 +497,7 @@ int64_t json_obj_int(char* json, char* key, int64_t fallback){ double json_obj_double(char* json, char* key, double fallback){ char* next_token = NULL; - int64_t result; + double result; size_t offset = json_obj_offset(json, key); if(offset){ result = strtod(json + offset, &next_token); @@ -343,6 +508,32 @@ double json_obj_double(char* json, char* key, double fallback){ return fallback; } +int64_t json_array_int(char* json, uint64_t key, int64_t fallback){ + char* next_token = NULL; + int64_t result; + size_t offset = json_array_offset(json, key); + if(offset){ + result = strtol(json + offset, &next_token, 10); + if(next_token != json + offset){ + return result; + } + } + return fallback; +} + +double json_array_double(char* json, uint64_t key, double fallback){ + char* next_token = NULL; + double result; + size_t offset = json_array_offset(json, key); + if(offset){ + result = strtod(json + offset, &next_token); + if(next_token != json + offset){ + return result; + } + } + return fallback; +} + char* json_obj_str(char* json, char* key, size_t* length){ size_t offset = json_obj_offset(json, key), raw_length; if(offset){ @@ -356,15 +547,37 @@ char* json_obj_str(char* json, char* key, size_t* length){ } char* json_obj_strdup(char* json, char* key){ - size_t offset = json_obj_offset(json, key), raw_length; - char* rv = NULL; + size_t len = 0; + char* value = json_obj_str(json, key, &len), *rv = NULL; + if(len){ + rv = calloc(len + 1, sizeof(char)); + if(rv){ + memcpy(rv, value, len); + } + } + return rv; +} + +char* json_array_str(char* json, uint64_t key, size_t* length){ + size_t offset = json_array_offset(json, key), raw_length; if(offset){ raw_length = json_validate_string(json + offset, strlen(json + offset)); - rv = calloc(raw_length - 1, sizeof(char)); - if(rv){ - memcpy(rv, json + offset + 1, raw_length - 2); + if(length){ + *length = raw_length - 2; } - return rv; + return json + offset + 1; } return NULL; } + +char* json_array_strdup(char* json, uint64_t key){ + size_t len = 0; + char* value = json_array_str(json, key, &len), *rv = NULL; + if(len){ + rv = calloc(len + 1, sizeof(char)); + if(rv){ + memcpy(rv, value, len); + } + } + return rv; +} diff --git a/backends/libmmbackend.h b/backends/libmmbackend.h index aa0ac0c..5749119 100644 --- a/backends/libmmbackend.h +++ b/backends/libmmbackend.h @@ -78,49 +78,50 @@ json_type json_identify(char* json, size_t length); * Returns the length of a detected JSON document, 0 otherwise (ie. parse failures) */ size_t json_validate(char* json, size_t length); - size_t json_validate_string(char* json, size_t length); - size_t json_validate_array(char* json, size_t length); - size_t json_validate_object(char* json, size_t length); - size_t json_validate_value(char* json, size_t length); /* * Calculate offset for value of `key` - * Assumes a zero-terminated, validated JSON object as input + * Assumes a zero-terminated, validated JSON object / array as input * Returns offset on success, 0 on failure */ size_t json_obj_offset(char* json, char* key); +size_t json_array_offset(char* json, uint64_t key); /* - * Check for for a key within a JSON object - * Assumes a zero-terminated, validated JSON object as input + * Check for for a key within a JSON object / index within an array + * Assumes a zero-terminated, validated JSON object / array as input * Returns type of value */ json_type json_obj(char* json, char* key); - -//json_type json_array(char* json, size_t index) +json_type json_array(char* json, uint64_t key); /* - * Fetch boolean value for an object key - * Assumes a zero-terminated, validated JSON object as input + * Fetch boolean value for an object / array key + * Assumes a zero-terminated, validated JSON object / array as input */ uint8_t json_obj_bool(char* json, char* key, uint8_t fallback); +uint8_t json_array_bool(char* json, uint64_t key, uint8_t fallback); /* - * Fetch integer/double value for an object key - * Assumes a zero-terminated validated JSON object as input + * Fetch integer/double value for an object / array key + * Assumes a zero-terminated validated JSON object / array as input */ int64_t json_obj_int(char* json, char* key, int64_t fallback); double json_obj_double(char* json, char* key, double fallback); +int64_t json_array_int(char* json, uint64_t key, int64_t fallback); +double json_array_double(char* json, uint64_t key, double fallback); /* - * Fetch a string value for an object key - * Assumes a zero-terminated validated JSON object as input - * json_obj_strdup returns a newly-allocated buffer containing + * Fetch a string value for an object / array key + * Assumes a zero-terminated validated JSON object / array as input + * json_*_strdup returns a newly-allocated buffer containing * only the requested value */ char* json_obj_str(char* json, char* key, size_t* length); char* json_obj_strdup(char* json, char* key); +char* json_array_str(char* json, uint64_t key, size_t* length); +char* json_array_strdup(char* json, uint64_t key); diff --git a/backends/maweb.c b/backends/maweb.c index 79e223f..07595be 100644 --- a/backends/maweb.c +++ b/backends/maweb.c @@ -14,7 +14,13 @@ #define WS_FLAG_FIN 0x80 #define WS_FLAG_MASK 0x80 +//TODO test using different pages simultaneously +//TODO test dot2 button virtual faders in fader view + static uint64_t last_keepalive = 0; +static uint64_t update_interval = 50; +static uint64_t last_update = 0; +static uint64_t updates_inflight = 0; static char* cmdline_keys[] = { "SET", @@ -94,7 +100,8 @@ int init(){ .handle = maweb_set, .process = maweb_handle, .start = maweb_start, - .shutdown = maweb_shutdown + .shutdown = maweb_shutdown, + .interval = maweb_interval }; if(sizeof(maweb_channel_ident) != sizeof(uint64_t)){ @@ -110,8 +117,27 @@ int init(){ return 0; } +static int channel_comparator(const void* raw_a, const void* raw_b){ + maweb_channel_ident* a = (maweb_channel_ident*) raw_a; + maweb_channel_ident* b = (maweb_channel_ident*) raw_b; + + if(a->fields.page != b->fields.page){ + return a->fields.page - b->fields.page; + } + return a->fields.index - b->fields.index; +} + +static uint32_t maweb_interval(){ + return update_interval - (last_update % update_interval); +} + static int maweb_configure(char* option, char* value){ - fprintf(stderr, "The maweb backend does not take any global configuration\n"); + if(!strcmp(option, "interval")){ + update_interval = strtoul(value, NULL, 10); + return 0; + } + + fprintf(stderr, "Unknown maweb backend configuration option %s\n", option); return 1; } @@ -187,6 +213,7 @@ static instance* maweb_instance(){ } static channel* maweb_channel(instance* inst, char* spec){ + maweb_instance_data* data = (maweb_instance_data*) inst->impl; maweb_channel_ident ident = { .label = 0 }; @@ -214,7 +241,7 @@ static channel* maweb_channel(instance* inst, char* spec){ next_token += 5; } else if(!strncmp(next_token, "flash", 5)){ - ident.fields.type = exec_flash; + ident.fields.type = exec_button; next_token += 5; } else if(!strncmp(next_token, "button", 6)){ @@ -238,6 +265,24 @@ static channel* maweb_channel(instance* inst, char* spec){ //actually, those are zero-indexed... ident.fields.index--; ident.fields.page--; + + //check if the channel is already known + for(n = 0; n < data->input_channels; n++){ + if(data->input_channel[n].label == ident.label){ + break; + } + } + + if(n == data->input_channels){ + data->input_channel = realloc(data->input_channel, (data->input_channels + 1) * sizeof(maweb_channel_ident)); + if(!data->input_channel){ + fprintf(stderr, "Failed to allocate memory\n"); + return NULL; + } + data->input_channel[n].label = ident.label; + data->input_channels++; + } + return mm_channel(inst, ident.label, 1); } fprintf(stderr, "Failed to parse maweb channel spec %s\n", spec); @@ -276,11 +321,216 @@ static int maweb_send_frame(instance* inst, maweb_operation op, uint8_t* payload return 0; } +static int maweb_process_playback(instance* inst, int64_t page, maweb_channel_type metatype, char* payload, size_t payload_length){ + size_t exec_blocks = json_obj_offset(payload, (metatype == 2) ? "executorBlocks" : "bottomButtons"), offset, block = 0, control; + channel* chan = NULL; + channel_value evt; + maweb_channel_ident ident = { + .fields.page = page, + .fields.index = json_obj_int(payload, "iExec", 191) + }; + + if(!exec_blocks){ + if(metatype == 3){ + //ignore unused buttons + return 0; + } + fprintf(stderr, "maweb missing exec block data on exec %d\n", ident.fields.index); + return 1; + } + + if(metatype == 3){ + exec_blocks += json_obj_offset(payload + exec_blocks, "items"); + } + + //TODO detect unused faders + //TODO state tracking for fader values / exec run state + + //iterate over executor blocks + for(offset = json_array_offset(payload + exec_blocks, block); offset; offset = json_array_offset(payload + exec_blocks, block)){ + control = exec_blocks + offset + json_obj_offset(payload + exec_blocks + offset, "fader"); + ident.fields.type = exec_fader; + chan = mm_channel(inst, ident.label, 0); + if(chan){ + evt.normalised = json_obj_double(payload + control, "v", 0.0); + mm_channel_event(chan, evt); + } + + ident.fields.type = exec_button; + chan = mm_channel(inst, ident.label, 0); + if(chan){ + evt.normalised = json_obj_int(payload, "isRun", 0); + mm_channel_event(chan, evt); + } + + //printf("maweb page %ld exec %d value %f running %lu\n", page, ident.fields.index, json_obj_double(payload + control, "v", 0.0), json_obj_int(payload, "isRun", 0)); + ident.fields.index++; + block++; + } + + return 0; +} + +static int maweb_process_playbacks(instance* inst, int64_t page, char* payload, size_t payload_length){ + size_t base_offset = json_obj_offset(payload, "itemGroups"), group_offset, subgroup_offset, item_offset; + uint64_t group = 0, subgroup, item, metatype; + + if(!page){ + fprintf(stderr, "maweb received playbacks for invalid page\n"); + return 0; + } + + if(!base_offset){ + fprintf(stderr, "maweb playback data missing item key\n"); + return 0; + } + + //iterate .itemGroups + for(group_offset = json_array_offset(payload + base_offset, group); + group_offset; + group_offset = json_array_offset(payload + base_offset, group)){ + metatype = json_obj_int(payload + base_offset + group_offset, "itemsType", 0); + //iterate .itemGroups.items + //FIXME this is problematic if there is no "items" key + group_offset = group_offset + json_obj_offset(payload + base_offset + group_offset, "items"); + if(group_offset){ + subgroup = 0; + group_offset += base_offset; + for(subgroup_offset = json_array_offset(payload + group_offset, subgroup); + subgroup_offset; + subgroup_offset = json_array_offset(payload + group_offset, subgroup)){ + //iterate .itemGroups.items[n] + item = 0; + subgroup_offset += group_offset; + for(item_offset = json_array_offset(payload + subgroup_offset, item); + item_offset; + item_offset = json_array_offset(payload + subgroup_offset, item)){ + maweb_process_playback(inst, page, metatype, + payload + subgroup_offset + item_offset, + payload_length - subgroup_offset - item_offset); + item++; + } + subgroup++; + } + } + group++; + } + updates_inflight--; + fprintf(stderr, "maweb playback message processing done, %lu updates inflight\n", updates_inflight); + return 0; +} + +static int maweb_request_playbacks(instance* inst){ + maweb_instance_data* data = (maweb_instance_data*) inst->impl; + char xmit_buffer[MAWEB_XMIT_CHUNK]; + int rv = 0; + + char item_indices[1024] = "[0,100,200]", item_counts[1024] = "[21,21,21]", item_types[1024] = "[2,3,3]"; + //char item_indices[1024] = "[300,400]", item_counts[1024] = "[18,18]", item_types[1024] = "[3,3]"; + size_t page_index = 0, view = 2, channel = 0, offsets[3], channel_offset, channels; + + if(updates_inflight){ + fprintf(stderr, "maweb skipping update request, %lu updates still inflight\n", updates_inflight); + return 0; + } + + for(channel = 0; channel < data->input_channels; channel++){ + offsets[0] = offsets[1] = offsets[2] = 0; + page_index = data->input_channel[channel].fields.page; + if(data->peer_type == peer_dot2){ + //TODO implement poll segmentation for dot + //"\"startIndex\":[0,100,200]," + //"\"itemsCount\":[21,21,21]," + //"\"itemsType\":[2,3,3]," + //"\"view\":2," + //view = (data->input_channel[channel].fields.index >= 300) ? 3 : 2; + //observed + //"startIndex":[300,400,500,600,700,800], + //"itemsCount":[13,13,13,13,13,13] + //"itemsType":[3,3,3,3,3,3] + /*fprintf(stderr, "range start at %lu.%lu (%lu/%lu) end at %lu.%lu (%lu/%lu)\n", + page_index, + data->input_channel[channel].fields.index, + channel, + data->input_channels, + page_index, + data->input_channel[channel + channel_offset - 1].fields.index, + channel + channel_offset - 1, + data->input_channels + );*/ + //only send one request currently + channel = data->input_channels; + } + else{ + view = (data->input_channel[channel].fields.index >= 100) ? 3 : 2; + //for the ma, the view equals the exec type + snprintf(item_types, sizeof(item_types), "[%lu]", view); + //this channel must be included, so it must be in range for the first startindex + snprintf(item_indices, sizeof(item_indices), "[%d]", (data->input_channel[channel].fields.index / 5) * 5); + + for(channel_offset = 1; channel + channel_offset < data->input_channels + && data->input_channel[channel].fields.page == data->input_channel[channel + channel_offset].fields.page + && data->input_channel[channel].fields.index / 100 == data->input_channel[channel + channel_offset].fields.index / 100; channel_offset++){ + } + + channels = data->input_channel[channel + channel_offset - 1].fields.index - (data->input_channel[channel].fields.index / 5) * 5; + + + snprintf(item_counts, sizeof(item_indices), "[%lu]", ((channels / 5) * 5 + 5)); + channel += channel_offset - 1; + } + snprintf(xmit_buffer, sizeof(xmit_buffer), + "{" + "\"requestType\":\"playbacks\"," + "\"startIndex\":%s," + "\"itemsCount\":%s," + "\"pageIndex\":%lu," + "\"itemsType\":%s," + "\"view\":%lu," + "\"execButtonViewMode\":2," //extended + "\"buttonsViewMode\":0," //get vfader for button execs + "\"session\":%lu" + "}", + item_indices, + item_counts, + page_index, + item_types, + view, + data->session); + rv |= maweb_send_frame(inst, ws_text, (uint8_t*) xmit_buffer, strlen(xmit_buffer)); + //fprintf(stderr, "req: %s\n", xmit_buffer); + updates_inflight++; + } + + return rv; +} + static int maweb_handle_message(instance* inst, char* payload, size_t payload_length){ char xmit_buffer[MAWEB_XMIT_CHUNK]; char* field; maweb_instance_data* data = (maweb_instance_data*) inst->impl; + //query this early to save on unnecessary parser passes with stupid-huge data messages + if(json_obj(payload, "responseType") == JSON_STRING){ + field = json_obj_str(payload, "responseType", NULL); + if(!strncmp(field, "login", 5)){ + if(json_obj_bool(payload, "result", 0)){ + fprintf(stderr, "maweb login successful\n"); + data->login = 1; + } + else{ + fprintf(stderr, "maweb login failed\n"); + data->login = 0; + } + } + if(!strncmp(field, "playbacks", 9)){ + if(maweb_process_playbacks(inst, json_obj_int(payload, "iPage", 0), payload, payload_length)){ + fprintf(stderr, "maweb failed to handle/request input data\n"); + } + return 0; + } + } + fprintf(stderr, "maweb message (%lu): %s\n", payload_length, payload); if(json_obj(payload, "session") == JSON_NUMBER){ data->session = json_obj_int(payload, "session", data->session); @@ -294,7 +544,6 @@ static int maweb_handle_message(instance* inst, char* payload, size_t payload_le (data->peer_type == peer_dot2) ? "remote" : data->user, data->pass, data->session); maweb_send_frame(inst, ws_text, (uint8_t*) xmit_buffer, strlen(xmit_buffer)); } - if(json_obj(payload, "status") && json_obj(payload, "appType")){ fprintf(stderr, "maweb connection established\n"); field = json_obj_str(payload, "appType", NULL); @@ -307,31 +556,6 @@ static int maweb_handle_message(instance* inst, char* payload, size_t payload_le maweb_send_frame(inst, ws_text, (uint8_t*) "{\"session\":0}", 13); } - if(json_obj(payload, "responseType") == JSON_STRING){ - field = json_obj_str(payload, "responseType", NULL); - if(!strncmp(field, "login", 5)){ - if(json_obj_bool(payload, "result", 0)){ - fprintf(stderr, "maweb login successful\n"); - data->login = 1; - } - else{ - fprintf(stderr, "maweb login failed\n"); - data->login = 0; - } - } - else if(!strncmp(field, "getdata", 7)){ - //FIXME stupid keepalive logic - snprintf(xmit_buffer, sizeof(xmit_buffer), - "{\"requestType\":\"getdata\"," - "\"data\":\"set,clear,solo,high\"," - "\"realtime\":true," - "\"maxRequests\":10," - ",\"session\":%ld}", - data->session); - maweb_send_frame(inst, ws_text, (uint8_t*) xmit_buffer, strlen(xmit_buffer)); - } - } - return 0; } @@ -376,7 +600,7 @@ static ssize_t maweb_handle_lines(instance* inst, ssize_t bytes_read){ maweb_instance_data* data = (maweb_instance_data*) inst->impl; size_t n, begin = 0; - for(n = 0; n < bytes_read - 2; n++){ + for(n = 0; n < bytes_read - 1; n++){ if(!strncmp((char*) data->buffer + data->offset + n, "\r\n", 2)){ if(data->state == ws_new){ if(!strncmp((char*) data->buffer, "HTTP/1.1 101", 12)){ @@ -397,7 +621,7 @@ static ssize_t maweb_handle_lines(instance* inst, ssize_t bytes_read){ } } - return begin; + return data->offset + begin; } static ssize_t maweb_handle_ws(instance* inst, ssize_t bytes_read){ @@ -507,6 +731,7 @@ static int maweb_handle_fd(instance* inst){ if(bytes_handled < 0){ bytes_handled = data->offset + bytes_read; + data->offset = 0; //TODO close, reopen fprintf(stderr, "maweb failed to handle incoming data\n"); return 1; @@ -517,8 +742,6 @@ static int maweb_handle_fd(instance* inst){ memmove(data->buffer, data->buffer + bytes_handled, (data->offset + bytes_read) - bytes_handled); - //FIXME this might be somewhat borked - bytes_read -= data->offset; bytes_handled -= data->offset; bytes_read -= bytes_handled; data->offset = 0; @@ -551,30 +774,10 @@ static int maweb_set(instance* inst, size_t num, channel** c, channel_value* v){ "\"type\":1," "\"session\":%ld" "}", ident.fields.index, ident.fields.page, v[n].normalised, data->session); - fprintf(stderr, "maweb out %s\n", xmit_buffer); maweb_send_frame(inst, ws_text, (uint8_t*) xmit_buffer, strlen(xmit_buffer)); break; case exec_upper: case exec_lower: - case exec_flash: - snprintf(xmit_buffer, sizeof(xmit_buffer), - "{\"requestType\":\"playbacks_userInput\"," - //"\"cmdline\":\"\"," - "\"execIndex\":%d," - "\"pageIndex\":%d," - "\"buttonId\":%d," - "\"pressed\":%s," - "\"released\":%s," - "\"type\":0," - "\"session\":%ld" - "}", ident.fields.index, ident.fields.page, - (data->peer_type == peer_dot2) ? (ident.fields.type - 3) : (exec_flash - ident.fields.type), - (v[n].normalised > 0.9) ? "true" : "false", - (v[n].normalised > 0.9) ? "false" : "true", - data->session); - fprintf(stderr, "maweb out %s\n", xmit_buffer); - maweb_send_frame(inst, ws_text, (uint8_t*) xmit_buffer, strlen(xmit_buffer)); - break; case exec_button: snprintf(xmit_buffer, sizeof(xmit_buffer), "{\"requestType\":\"playbacks_userInput\"," @@ -586,13 +789,11 @@ static int maweb_set(instance* inst, size_t num, channel** c, channel_value* v){ "\"released\":%s," "\"type\":0," "\"session\":%ld" - "}", ident.fields.index, - ident.fields.page, - 0, + "}", ident.fields.index, ident.fields.page, + (data->peer_type == peer_dot2 && ident.fields.type == exec_upper) ? 0 : (ident.fields.type - exec_button), (v[n].normalised > 0.9) ? "true" : "false", (v[n].normalised > 0.9) ? "false" : "true", data->session); - fprintf(stderr, "maweb out %s\n", xmit_buffer); maweb_send_frame(inst, ws_text, (uint8_t*) xmit_buffer, strlen(xmit_buffer)); break; case cmdline_button: @@ -602,7 +803,6 @@ static int maweb_set(instance* inst, size_t num, channel** c, channel_value* v){ "\"value\":%d" "}", cmdline_keys[ident.fields.index], (v[n].normalised > 0.9) ? 1 : 0); - fprintf(stderr, "maweb out %s\n", xmit_buffer); maweb_send_frame(inst, ws_text, (uint8_t*) xmit_buffer, strlen(xmit_buffer)); break; default: @@ -638,6 +838,29 @@ static int maweb_keepalive(){ return 0; } +static int maweb_poll(){ + size_t n, u; + instance** inst = NULL; + maweb_instance_data* data = NULL; + + //fetch all defined instances + if(mm_backend_instances(BACKEND_NAME, &n, &inst)){ + fprintf(stderr, "Failed to fetch instance list\n"); + return 1; + } + + //send data polls for logged-in instances + for(u = 0; u < n; u++){ + data = (maweb_instance_data*) inst[u]->impl; + if(data->login){ + maweb_request_playbacks(inst[u]); + } + } + + free(inst); + return 0; +} + static int maweb_handle(size_t num, managed_fd* fds){ size_t n = 0; int rv = 0; @@ -646,17 +869,24 @@ static int maweb_handle(size_t num, managed_fd* fds){ rv |= maweb_handle_fd((instance*) fds[n].impl); } + //FIXME all keepalive processing allocates temporary buffers, this might an optimization target if(last_keepalive && mm_timestamp() - last_keepalive >= MAWEB_CONNECTION_KEEPALIVE){ rv |= maweb_keepalive(); last_keepalive = mm_timestamp(); } + if(last_update && mm_timestamp() - last_update >= update_interval){ + rv |= maweb_poll(); + last_update = mm_timestamp(); + } + return rv; } static int maweb_start(){ size_t n, u; instance** inst = NULL; + maweb_instance_data* data = NULL; //fetch all defined instances if(mm_backend_instances(BACKEND_NAME, &n, &inst)){ @@ -665,6 +895,10 @@ static int maweb_start(){ } for(u = 0; u < n; u++){ + //sort channels + data = (maweb_instance_data*) inst[u]->impl; + qsort(data->input_channel, data->input_channels, sizeof(maweb_channel_ident), channel_comparator); + if(maweb_connect(inst[u])){ fprintf(stderr, "Failed to open connection to MA Web Remote for instance %s\n", inst[u]->name); return 1; @@ -678,8 +912,8 @@ static int maweb_start(){ fprintf(stderr, "maweb backend registering %lu descriptors to core\n", n); - //initialize keepalive timeout - last_keepalive = mm_timestamp(); + //initialize timeouts + last_keepalive = last_update = mm_timestamp(); return 0; } @@ -713,6 +947,10 @@ static int maweb_shutdown(){ data->offset = data->allocated = 0; data->state = ws_new; + + free(data->input_channel); + data->input_channel = NULL; + data->input_channels = 0; } free(inst); diff --git a/backends/maweb.h b/backends/maweb.h index 5f59cc1..a868426 100644 --- a/backends/maweb.h +++ b/backends/maweb.h @@ -9,22 +9,22 @@ static int maweb_set(instance* inst, size_t num, channel** c, channel_value* v); static int maweb_handle(size_t num, managed_fd* fds); static int maweb_start(); static int maweb_shutdown(); +static uint32_t maweb_interval(); //Default login password: MD5("midimonster") #define MAWEB_DEFAULT_PASSWORD "2807623134739142b119aff358f8a219" #define MAWEB_DEFAULT_PORT "80" #define MAWEB_RECV_CHUNK 1024 -#define MAWEB_XMIT_CHUNK 2048 +#define MAWEB_XMIT_CHUNK 4096 #define MAWEB_FRAME_HEADER_LENGTH 16 #define MAWEB_CONNECTION_KEEPALIVE 10000 typedef enum /*_maweb_channel_type*/ { type_unset = 0, exec_fader = 1, - exec_button = 2, - exec_upper = 3, - exec_lower = 4, - exec_flash = 5, + exec_button = 2, //gma: 0 dot: 0 + exec_lower = 3, //gma: 1 dot: 1 + exec_upper = 4, //gma: 2 dot: 0 cmdline_button } maweb_channel_type; @@ -69,6 +69,10 @@ typedef struct /*_maweb_instance_data*/ { int64_t session; maweb_peer_type peer_type; + //need to keep an internal registry to optimize data polls + size_t input_channels; + maweb_channel_ident* input_channel; + int fd; maweb_state state; size_t offset; diff --git a/backends/maweb.md b/backends/maweb.md index d713d82..fe430db 100644 --- a/backends/maweb.md +++ b/backends/maweb.md @@ -2,8 +2,7 @@ This backend connects directly with the integrated *MA Web Remote* of MA Lighting consoles and OnPC instances (GrandMA2 / GrandMA2 OnPC / GrandMA Dot2 / GrandMA Dot2 OnPC). -It grants read-write access to the console's playback faders and buttons as well as write access to -the command line buttons. +It grants read-write access to the console's playback controls as well as write access to the command line. #### Setting up the console @@ -16,7 +15,9 @@ Web Remote. Set a web remote password using the option below the activation sett #### Global configuration -The `maweb` backend does not take any global configuration. +| Option | Example value | Default value | Description | +|---------------|-----------------------|-----------------------|---------------------------------------------------------------| +| `interval` | `100` | `50` | Query interval for input data polling (in msec) | #### Instance configuration @@ -28,39 +29,44 @@ The `maweb` backend does not take any global configuration. #### Channel specification -Currently, three types of channels can be assigned +Currently, three types of MA controls can be assigned, with each having some subcontrols + +* Fader executor +* Button executor +* Command line buttons ##### Executors * For the GrandMA2, executors are arranged in pages, with each page having 90 fader executors (numbered 1 through 90) and 90 button executors (numbered 101 through 190). - * A fader executor consists of a `fader`, two buttons above it (`upper`, `lower`) and one `flash` button below it. - * A button executor consists of a `button` control. + * A fader executor consists of a `fader`, two buttons above it (`upper`, `lower`) and one `button` below it. + * A button executor consists of a `button` control and a virtual `fader` (visible on the console in the "Action Buttons" view). * For the dot2, executors are also arranged in pages, but the controls are non-obviously numbered. * For the faders, they are numerically right-to-left from the Core Fader section (Faders 6 to 1) over the F-Wing 1 (Faders 13 to 6) to F-Wing 2 (Faders 21 to 14). * Above the fader sections are two rows of 21 `button` executors, numbered 122 through 101 (upper row) and 222 through 201 (lower row), in the same order as the faders are. * Fader executors have two buttons below them (`upper` and `lower`). - * The button executor section consists of six rows of 18 buttons, divided into two button wings. Buttons on the wings + * The button executor section consists of six rows of 16 buttons, divided into two button wings. Buttons on the wings are once again numbered right-to-left. - * B-Wing 1 has `button` executors 308 to 301 (top row), 408 to 401 (second row), and so on until 808 through 801 (bottom row) + * B-Wing 1 has `button` controls 308 to 301 (top row), 408 to 401 (second row), and so on until 808 through 801 (bottom row) * B-Wing 2 has 316 to 309 (top row) through 816 to 809 (bottom row) When creating a new show, only the first page is created and active. Additional pages have to be created explicitly within -the console before being usable. +the console before being usable. `fader` controls, when mapped as outputs from the MA, output their value, `button` controls +output 1 when the corresponding executor is running, 0 otherwise. These controls can be addressed like ``` mw1.page1.fader5 > mw1.page1.upper5 -mw1.page3.lower3 > mw1.page2.flash2 +mw1.page3.lower3 > mw1.page2.button2 ``` A button executor can likewise be mapped using the syntax ``` -mw1.page2.button103 > mw1.page3.button101 +mw1.page2.button103 > mw1.page3.fader101 mw1.page2.button803 > mw1.page3.button516 ``` @@ -99,10 +105,12 @@ Since this may be a problem on some platforms, the backend can be built with thi to set arbitrary passwords. The backend will always try to log in with the default password `midimonster` in this case. The user name is still configurable. +Data input from the console is done by actively querying the state of all mapped controls, which is resource-intensive if done +at low latency. A lower input interval value will produce data with lower latency, at the cost of network & CPU usage. +Higher values will make the input "step" more, but will not consume as many CPU cycles and network bandwidth. + This backend is currently in active development. It therefore has some limitations: * It outputs a lot of debug information -* It currently is write-only, channel events are only sent to the MA, not generated by it -* Fader executors (and their buttons) seem to work, I haven't tested button executors yet. * Command line events are sent, but I'm not sure they're being handled yet -* I have so far only tested it with GradMA2 OnPC +* For the dot2, currently only the Core & F-Wings are supported for input from the console, not the B-Wings diff --git a/backends/midi.h b/backends/midi.h index 5ec17ea..6c3fcf9 100644 --- a/backends/midi.h +++ b/backends/midi.h @@ -24,4 +24,5 @@ typedef union { uint8_t control; } fields; uint64_t label; -} midi_channel_ident; \ No newline at end of file +} midi_channel_ident; + diff --git a/backends/osc.h b/backends/osc.h index ab19463..dd5afb0 100644 --- a/backends/osc.h +++ b/backends/osc.h @@ -73,4 +73,5 @@ typedef union { uint32_t parameter; } fields; uint64_t label; -} osc_channel_ident; \ No newline at end of file +} osc_channel_ident; + -- cgit v1.2.3 From 1107a91861189d28d771d02d721d61b403aac38a Mon Sep 17 00:00:00 2001 From: cbdev Date: Wed, 4 Dec 2019 01:21:14 +0100 Subject: Explicitly mark the backend init symbol visible --- backends/artnet.c | 2 +- backends/artnet.h | 2 +- backends/evdev.c | 2 +- backends/evdev.h | 2 +- backends/jack.c | 4 +++- backends/jack.h | 2 +- backends/loopback.c | 2 +- backends/loopback.h | 2 +- backends/lua.c | 2 +- backends/lua.h | 2 +- backends/maweb.c | 2 +- backends/maweb.h | 2 +- backends/midi.c | 2 +- backends/midi.h | 2 +- backends/ola.cpp | 2 +- backends/ola.h | 2 +- backends/osc.c | 2 +- backends/osc.h | 2 +- backends/sacn.c | 2 +- backends/sacn.h | 2 +- backends/winmidi.c | 2 +- backends/winmidi.h | 2 +- midimonster.h | 10 ++++++++++ 23 files changed, 34 insertions(+), 22 deletions(-) (limited to 'backends/evdev.h') diff --git a/backends/artnet.c b/backends/artnet.c index e01ac94..8a62a43 100644 --- a/backends/artnet.c +++ b/backends/artnet.c @@ -41,7 +41,7 @@ static int artnet_listener(char* host, char* port){ return 0; } -int init(){ +MM_PLUGIN_API int init(){ backend artnet = { .name = BACKEND_NAME, .conf = artnet_configure, diff --git a/backends/artnet.h b/backends/artnet.h index f5aa745..cce11d1 100644 --- a/backends/artnet.h +++ b/backends/artnet.h @@ -3,7 +3,7 @@ #endif #include "midimonster.h" -int init(); +MM_PLUGIN_API int init(); static int artnet_configure(char* option, char* value); static int artnet_configure_instance(instance* instance, char* option, char* value); static instance* artnet_instance(); diff --git a/backends/evdev.c b/backends/evdev.c index b19cda8..dd2231b 100644 --- a/backends/evdev.c +++ b/backends/evdev.c @@ -24,7 +24,7 @@ static struct { .detect = 0 }; -int init(){ +MM_PLUGIN_API int init(){ backend evdev = { .name = BACKEND_NAME, .conf = evdev_configure, diff --git a/backends/evdev.h b/backends/evdev.h index 48bd0ab..30ce892 100644 --- a/backends/evdev.h +++ b/backends/evdev.h @@ -8,7 +8,7 @@ * disabled by building with -DEVDEV_NO_UINPUT */ -int init(); +MM_PLUGIN_API int init(); static int evdev_configure(char* option, char* value); static int evdev_configure_instance(instance* instance, char* option, char* value); static instance* evdev_instance(); diff --git a/backends/jack.c b/backends/jack.c index b3aacd4..e8a63bc 100644 --- a/backends/jack.c +++ b/backends/jack.c @@ -4,6 +4,8 @@ #include #include +#define DEBUG + #include "jack.h" #include #include @@ -27,7 +29,7 @@ static struct /*_mmjack_backend_cfg*/ { .jack_shutdown = 0 }; -int init(){ +MM_PLUGIN_API int init(){ backend mmjack = { .name = BACKEND_NAME, .conf = mmjack_configure, diff --git a/backends/jack.h b/backends/jack.h index dd59cd2..5598042 100644 --- a/backends/jack.h +++ b/backends/jack.h @@ -2,7 +2,7 @@ #include #include -int init(); +MM_PLUGIN_API int init(); static int mmjack_configure(char* option, char* value); static int mmjack_configure_instance(instance* inst, char* option, char* value); static instance* mmjack_instance(); diff --git a/backends/loopback.c b/backends/loopback.c index 083a312..0a45bde 100644 --- a/backends/loopback.c +++ b/backends/loopback.c @@ -3,7 +3,7 @@ #define BACKEND_NAME "loopback" -int init(){ +MM_PLUGIN_API int init(){ backend loopback = { .name = BACKEND_NAME, .conf = loopback_configure, diff --git a/backends/loopback.h b/backends/loopback.h index c73ca20..a08417b 100644 --- a/backends/loopback.h +++ b/backends/loopback.h @@ -1,6 +1,6 @@ #include "midimonster.h" -int init(); +MM_PLUGIN_API int init(); static int loopback_configure(char* option, char* value); static int loopback_configure_instance(instance* inst, char* option, char* value); static instance* loopback_instance(); diff --git a/backends/lua.c b/backends/lua.c index 3555e72..0b47b2c 100644 --- a/backends/lua.c +++ b/backends/lua.c @@ -19,7 +19,7 @@ static int timer_fd = -1; static uint64_t last_timestamp; #endif -int init(){ +MM_PLUGIN_API int init(){ backend lua = { #ifndef MMBACKEND_LUA_TIMERFD .interval = lua_interval, diff --git a/backends/lua.h b/backends/lua.h index f2583a8..e187a8e 100644 --- a/backends/lua.h +++ b/backends/lua.h @@ -9,7 +9,7 @@ #define MMBACKEND_LUA_TIMERFD #endif -int init(); +MM_PLUGIN_API int init(); static int lua_configure(char* option, char* value); static int lua_configure_instance(instance* inst, char* option, char* value); static instance* lua_instance(); diff --git a/backends/maweb.c b/backends/maweb.c index c495512..08156f2 100644 --- a/backends/maweb.c +++ b/backends/maweb.c @@ -72,7 +72,7 @@ static maweb_command_key cmdline_keys[] = { {"GO_MINUS_SMALL", 50}, {"PAUSE_SMALL", 51}, {"GO_PLUS_SMALL", 52} }; -int init(){ +MM_PLUGIN_API int init(){ backend maweb = { .name = BACKEND_NAME, .conf = maweb_configure, diff --git a/backends/maweb.h b/backends/maweb.h index 14e4755..9091cda 100644 --- a/backends/maweb.h +++ b/backends/maweb.h @@ -1,6 +1,6 @@ #include "midimonster.h" -int init(); +MM_PLUGIN_API int init(); static int maweb_configure(char* option, char* value); static int maweb_configure_instance(instance* inst, char* option, char* value); static instance* maweb_instance(); diff --git a/backends/midi.c b/backends/midi.c index cde278b..f380f59 100644 --- a/backends/midi.c +++ b/backends/midi.c @@ -23,7 +23,7 @@ static struct { .detect = 0 }; -int init(){ +MM_PLUGIN_API int init(){ backend midi = { .name = BACKEND_NAME, .conf = midi_configure, diff --git a/backends/midi.h b/backends/midi.h index 6c3fcf9..b9934f1 100644 --- a/backends/midi.h +++ b/backends/midi.h @@ -1,6 +1,6 @@ #include "midimonster.h" -int init(); +MM_PLUGIN_API int init(); static int midi_configure(char* option, char* value); static int midi_configure_instance(instance* instance, char* option, char* value); static instance* midi_instance(); diff --git a/backends/ola.cpp b/backends/ola.cpp index 632cef7..d069a8c 100644 --- a/backends/ola.cpp +++ b/backends/ola.cpp @@ -11,7 +11,7 @@ static ola::io::SelectServer* ola_select = NULL; static ola::OlaCallbackClient* ola_client = NULL; -int init(){ +MM_PLUGIN_API int init(){ backend ola = { .name = BACKEND_NAME, .conf = ola_configure, diff --git a/backends/ola.h b/backends/ola.h index c943d52..1637495 100644 --- a/backends/ola.h +++ b/backends/ola.h @@ -4,7 +4,7 @@ extern "C" { #undef min #undef max - int init(); + MM_PLUGIN_API int init(); static int ola_configure(char* option, char* value); static int ola_configure_instance(instance* instance, char* option, char* value); static instance* ola_instance(); diff --git a/backends/osc.c b/backends/osc.c index bffbba8..d9f9139 100644 --- a/backends/osc.c +++ b/backends/osc.c @@ -19,7 +19,7 @@ static struct { .detect = 0 }; -int init(){ +MM_PLUGIN_API int init(){ backend osc = { .name = BACKEND_NAME, .conf = osc_configure, diff --git a/backends/osc.h b/backends/osc.h index dd5afb0..86be285 100644 --- a/backends/osc.h +++ b/backends/osc.h @@ -7,7 +7,7 @@ #define OSC_RECV_BUF 8192 #define OSC_XMIT_BUF 8192 -int init(); +MM_PLUGIN_API int init(); static int osc_configure(char* option, char* value); static int osc_configure_instance(instance* inst, char* option, char* value); static instance* osc_instance(); diff --git a/backends/sacn.c b/backends/sacn.c index 6e1b20b..d8b3eb3 100644 --- a/backends/sacn.c +++ b/backends/sacn.c @@ -31,7 +31,7 @@ static struct /*_sacn_global_config*/ { .last_announce = 0 }; -int init(){ +MM_PLUGIN_API int init(){ backend sacn = { .name = BACKEND_NAME, .conf = sacn_configure, diff --git a/backends/sacn.h b/backends/sacn.h index 7af2a36..631d3a4 100644 --- a/backends/sacn.h +++ b/backends/sacn.h @@ -1,6 +1,6 @@ #include "midimonster.h" -int init(); +MM_PLUGIN_API int init(); static int sacn_configure(char* option, char* value); static int sacn_configure_instance(instance* instance, char* option, char* value); static instance* sacn_instance(); diff --git a/backends/winmidi.c b/backends/winmidi.c index ffca3b4..bda5401 100644 --- a/backends/winmidi.c +++ b/backends/winmidi.c @@ -23,7 +23,7 @@ static struct { //TODO receive feedback socket until EAGAIN -int init(){ +MM_PLUGIN_API int init(){ backend winmidi = { .name = BACKEND_NAME, .conf = winmidi_configure, diff --git a/backends/winmidi.h b/backends/winmidi.h index e4abda1..ffa6a26 100644 --- a/backends/winmidi.h +++ b/backends/winmidi.h @@ -1,6 +1,6 @@ #include "midimonster.h" -int init(); +MM_PLUGIN_API int init(); static int winmidi_configure(char* option, char* value); static int winmidi_configure_instance(instance* inst, char* option, char* value); static instance* winmidi_instance(); diff --git a/midimonster.h b/midimonster.h index 3922b03..1192d6a 100644 --- a/midimonster.h +++ b/midimonster.h @@ -5,6 +5,7 @@ #include #include +/* API call attributes and visibilities */ #ifndef MM_API #ifdef _WIN32 #define MM_API __attribute__((dllimport)) @@ -13,6 +14,15 @@ #endif #endif +/* Some build systems may apply the -fvisibility=hidden parameter from the core build to the backends, so mark the init function visible */ +#ifndef MM_PLUGIN_API + #ifdef _WIN32 + #define MM_PLUGIN_API __attribute__((dllexport)) + #else + #define MM_PLUGIN_API __attribute__((visibility ("default"))) + #endif +#endif + /* Straight-forward min / max macros */ #define max(a,b) (((a) > (b)) ? (a) : (b)) #define min(a,b) (((a) < (b)) ? (a) : (b)) -- cgit v1.2.3 From 3eada28582b144519e95a44ee3adc3f46d39036e Mon Sep 17 00:00:00 2001 From: cbdev Date: Thu, 5 Dec 2019 21:05:14 +0100 Subject: Add flags parameter to channel parser plugin API (Fixes #31) --- backends/artnet.c | 2 +- backends/artnet.h | 2 +- backends/evdev.c | 2 +- backends/evdev.h | 2 +- backends/jack.c | 2 +- backends/jack.h | 2 +- backends/loopback.c | 2 +- backends/loopback.h | 2 +- backends/lua.c | 2 +- backends/lua.h | 2 +- backends/maweb.c | 2 +- backends/maweb.h | 2 +- backends/midi.c | 2 +- backends/midi.h | 2 +- backends/ola.cpp | 2 +- backends/ola.h | 2 +- backends/osc.c | 2 +- backends/osc.h | 2 +- backends/sacn.c | 2 +- backends/sacn.h | 2 +- backends/winmidi.c | 2 +- backends/winmidi.h | 2 +- config.c | 8 ++++---- midimonster.h | 15 ++++++++++++--- 24 files changed, 38 insertions(+), 29 deletions(-) (limited to 'backends/evdev.h') diff --git a/backends/artnet.c b/backends/artnet.c index 8a62a43..57eb7b1 100644 --- a/backends/artnet.c +++ b/backends/artnet.c @@ -156,7 +156,7 @@ static int artnet_configure_instance(instance* inst, char* option, char* value){ return 1; } -static channel* artnet_channel(instance* inst, char* spec){ +static channel* artnet_channel(instance* inst, char* spec, uint8_t flags){ artnet_instance_data* data = (artnet_instance_data*) inst->impl; char* spec_next = spec; unsigned chan_a = strtoul(spec, &spec_next, 10); diff --git a/backends/artnet.h b/backends/artnet.h index cce11d1..f6a6709 100644 --- a/backends/artnet.h +++ b/backends/artnet.h @@ -7,7 +7,7 @@ MM_PLUGIN_API int init(); 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 channel* artnet_channel(instance* instance, char* spec, uint8_t flags); 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(); diff --git a/backends/evdev.c b/backends/evdev.c index dd2231b..0da5ae6 100644 --- a/backends/evdev.c +++ b/backends/evdev.c @@ -249,7 +249,7 @@ static int evdev_configure_instance(instance* inst, char* option, char* value) { return 1; } -static channel* evdev_channel(instance* inst, char* spec){ +static channel* evdev_channel(instance* inst, char* spec, uint8_t flags){ #ifndef EVDEV_NO_UINPUT evdev_instance_data* data = (evdev_instance_data*) inst->impl; #endif diff --git a/backends/evdev.h b/backends/evdev.h index 30ce892..6504416 100644 --- a/backends/evdev.h +++ b/backends/evdev.h @@ -12,7 +12,7 @@ MM_PLUGIN_API int init(); static int evdev_configure(char* option, char* value); static int evdev_configure_instance(instance* instance, char* option, char* value); static instance* evdev_instance(); -static channel* evdev_channel(instance* instance, char* spec); +static channel* evdev_channel(instance* instance, char* spec, uint8_t flags); static int evdev_set(instance* inst, size_t num, channel** c, channel_value* v); static int evdev_handle(size_t num, managed_fd* fds); static int evdev_start(); diff --git a/backends/jack.c b/backends/jack.c index 926f800..e7bed04 100644 --- a/backends/jack.c +++ b/backends/jack.c @@ -409,7 +409,7 @@ static int mmjack_parse_midispec(mmjack_channel_ident* ident, char* spec){ return 0; } -static channel* mmjack_channel(instance* inst, char* spec){ +static channel* mmjack_channel(instance* inst, char* spec, uint8_t flags){ mmjack_instance_data* data = (mmjack_instance_data*) inst->impl; mmjack_channel_ident ident = { .label = 0 diff --git a/backends/jack.h b/backends/jack.h index 5598042..a7f3e8b 100644 --- a/backends/jack.h +++ b/backends/jack.h @@ -6,7 +6,7 @@ MM_PLUGIN_API int init(); static int mmjack_configure(char* option, char* value); static int mmjack_configure_instance(instance* inst, char* option, char* value); static instance* mmjack_instance(); -static channel* mmjack_channel(instance* inst, char* spec); +static channel* mmjack_channel(instance* inst, char* spec, uint8_t flags); static int mmjack_set(instance* inst, size_t num, channel** c, channel_value* v); static int mmjack_handle(size_t num, managed_fd* fds); static int mmjack_start(); diff --git a/backends/loopback.c b/backends/loopback.c index 0a45bde..41e6f85 100644 --- a/backends/loopback.c +++ b/backends/loopback.c @@ -49,7 +49,7 @@ static instance* loopback_instance(){ return i; } -static channel* loopback_channel(instance* inst, char* spec){ +static channel* loopback_channel(instance* inst, char* spec, uint8_t flags){ size_t u; loopback_instance_data* data = (loopback_instance_data*) inst->impl; diff --git a/backends/loopback.h b/backends/loopback.h index a08417b..ee51c66 100644 --- a/backends/loopback.h +++ b/backends/loopback.h @@ -4,7 +4,7 @@ MM_PLUGIN_API int init(); static int loopback_configure(char* option, char* value); static int loopback_configure_instance(instance* inst, char* option, char* value); static instance* loopback_instance(); -static channel* loopback_channel(instance* inst, char* spec); +static channel* loopback_channel(instance* inst, char* spec, uint8_t flags); static int loopback_set(instance* inst, size_t num, channel** c, channel_value* v); static int loopback_handle(size_t num, managed_fd* fds); static int loopback_start(); diff --git a/backends/lua.c b/backends/lua.c index 0b47b2c..40e6613 100644 --- a/backends/lua.c +++ b/backends/lua.c @@ -330,7 +330,7 @@ static instance* lua_instance(){ return inst; } -static channel* lua_channel(instance* inst, char* spec){ +static channel* lua_channel(instance* inst, char* spec, uint8_t flags){ size_t u; lua_instance_data* data = (lua_instance_data*) inst->impl; diff --git a/backends/lua.h b/backends/lua.h index e187a8e..4ea5b0a 100644 --- a/backends/lua.h +++ b/backends/lua.h @@ -13,7 +13,7 @@ MM_PLUGIN_API int init(); static int lua_configure(char* option, char* value); static int lua_configure_instance(instance* inst, char* option, char* value); static instance* lua_instance(); -static channel* lua_channel(instance* inst, char* spec); +static channel* lua_channel(instance* inst, char* spec, uint8_t flags); static int lua_set(instance* inst, size_t num, channel** c, channel_value* v); static int lua_handle(size_t num, managed_fd* fds); static int lua_start(); diff --git a/backends/maweb.c b/backends/maweb.c index 08156f2..d008cc0 100644 --- a/backends/maweb.c +++ b/backends/maweb.c @@ -229,7 +229,7 @@ static instance* maweb_instance(){ return inst; } -static channel* maweb_channel(instance* inst, char* spec){ +static channel* maweb_channel(instance* inst, char* spec, uint8_t flags){ maweb_instance_data* data = (maweb_instance_data*) inst->impl; maweb_channel_data chan = { 0 diff --git a/backends/maweb.h b/backends/maweb.h index 9091cda..05095f8 100644 --- a/backends/maweb.h +++ b/backends/maweb.h @@ -4,7 +4,7 @@ MM_PLUGIN_API int init(); static int maweb_configure(char* option, char* value); static int maweb_configure_instance(instance* inst, char* option, char* value); static instance* maweb_instance(); -static channel* maweb_channel(instance* inst, char* spec); +static channel* maweb_channel(instance* inst, char* spec, uint8_t flags); static int maweb_set(instance* inst, size_t num, channel** c, channel_value* v); static int maweb_handle(size_t num, managed_fd* fds); static int maweb_start(); diff --git a/backends/midi.c b/backends/midi.c index f380f59..92776ca 100644 --- a/backends/midi.c +++ b/backends/midi.c @@ -110,7 +110,7 @@ static int midi_configure_instance(instance* inst, char* option, char* value){ return 1; } -static channel* midi_channel(instance* inst, char* spec){ +static channel* midi_channel(instance* inst, char* spec, uint8_t flags){ midi_channel_ident ident = { .label = 0 }; diff --git a/backends/midi.h b/backends/midi.h index b9934f1..4e16f90 100644 --- a/backends/midi.h +++ b/backends/midi.h @@ -4,7 +4,7 @@ MM_PLUGIN_API int 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 channel* midi_channel(instance* instance, char* spec, uint8_t flags); 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(); diff --git a/backends/ola.cpp b/backends/ola.cpp index d069a8c..c13e8f9 100644 --- a/backends/ola.cpp +++ b/backends/ola.cpp @@ -68,7 +68,7 @@ static int ola_configure_instance(instance* inst, char* option, char* value){ return 1; } -static channel* ola_channel(instance* inst, char* spec){ +static channel* ola_channel(instance* inst, char* spec, uint8_t flags){ ola_instance_data* data = (ola_instance_data*) inst->impl; char* spec_next = spec; unsigned chan_a = strtoul(spec, &spec_next, 10); diff --git a/backends/ola.h b/backends/ola.h index 1637495..0c42bac 100644 --- a/backends/ola.h +++ b/backends/ola.h @@ -8,7 +8,7 @@ extern "C" { static int ola_configure(char* option, char* value); static int ola_configure_instance(instance* instance, char* option, char* value); static instance* ola_instance(); - static channel* ola_channel(instance* instance, char* spec); + static channel* ola_channel(instance* instance, char* spec, uint8_t flags); static int ola_set(instance* inst, size_t num, channel** c, channel_value* v); static int ola_handle(size_t num, managed_fd* fds); static int ola_start(); diff --git a/backends/osc.c b/backends/osc.c index d9f9139..757ad89 100644 --- a/backends/osc.c +++ b/backends/osc.c @@ -577,7 +577,7 @@ static instance* osc_instance(){ return inst; } -static channel* osc_map_channel(instance* inst, char* spec){ +static channel* osc_map_channel(instance* inst, char* spec, uint8_t flags){ size_t u, p; osc_instance_data* data = (osc_instance_data*) inst->impl; osc_channel_ident ident = { diff --git a/backends/osc.h b/backends/osc.h index 86be285..6f3b923 100644 --- a/backends/osc.h +++ b/backends/osc.h @@ -11,7 +11,7 @@ MM_PLUGIN_API int init(); static int osc_configure(char* option, char* value); static int osc_configure_instance(instance* inst, char* option, char* value); static instance* osc_instance(); -static channel* osc_map_channel(instance* inst, char* spec); +static channel* osc_map_channel(instance* inst, char* spec, uint8_t flags); static int osc_set(instance* inst, size_t num, channel** c, channel_value* v); static int osc_handle(size_t num, managed_fd* fds); static int osc_start(); diff --git a/backends/sacn.c b/backends/sacn.c index d8b3eb3..2229b8a 100644 --- a/backends/sacn.c +++ b/backends/sacn.c @@ -198,7 +198,7 @@ static instance* sacn_instance(){ return inst; } -static channel* sacn_channel(instance* inst, char* spec){ +static channel* sacn_channel(instance* inst, char* spec, uint8_t flags){ sacn_instance_data* data = (sacn_instance_data*) inst->impl; char* spec_next = spec; diff --git a/backends/sacn.h b/backends/sacn.h index 631d3a4..1d3268c 100644 --- a/backends/sacn.h +++ b/backends/sacn.h @@ -4,7 +4,7 @@ MM_PLUGIN_API int init(); static int sacn_configure(char* option, char* value); static int sacn_configure_instance(instance* instance, char* option, char* value); static instance* sacn_instance(); -static channel* sacn_channel(instance* instance, char* spec); +static channel* sacn_channel(instance* instance, char* spec, uint8_t flags); static int sacn_set(instance* inst, size_t num, channel** c, channel_value* v); static int sacn_handle(size_t num, managed_fd* fds); static int sacn_start(); diff --git a/backends/winmidi.c b/backends/winmidi.c index b274c06..790257b 100644 --- a/backends/winmidi.c +++ b/backends/winmidi.c @@ -110,7 +110,7 @@ static instance* winmidi_instance(){ return i; } -static channel* winmidi_channel(instance* inst, char* spec){ +static channel* winmidi_channel(instance* inst, char* spec, uint8_t flags){ char* next_token = NULL; winmidi_channel_ident ident = { .label = 0 diff --git a/backends/winmidi.h b/backends/winmidi.h index 8c2d76b..985c46a 100644 --- a/backends/winmidi.h +++ b/backends/winmidi.h @@ -4,7 +4,7 @@ MM_PLUGIN_API int init(); static int winmidi_configure(char* option, char* value); static int winmidi_configure_instance(instance* inst, char* option, char* value); static instance* winmidi_instance(); -static channel* winmidi_channel(instance* inst, char* spec); +static channel* winmidi_channel(instance* inst, char* spec, uint8_t flags); static int winmidi_set(instance* inst, size_t num, channel** c, channel_value* v); static int winmidi_handle(size_t num, managed_fd* fds); static int winmidi_start(); diff --git a/config.c b/config.c index 8e7e581..0b9173e 100644 --- a/config.c +++ b/config.c @@ -175,7 +175,7 @@ static int config_glob_scan(instance* inst, channel_spec* spec){ return 0; } -static channel* config_glob_resolve(instance* inst, channel_spec* spec, uint64_t n){ +static channel* config_glob_resolve(instance* inst, channel_spec* spec, uint64_t n, uint8_t map_direction){ size_t glob = 0, glob_length; ssize_t bytes = 0; uint64_t current_value = 0; @@ -216,7 +216,7 @@ static channel* config_glob_resolve(instance* inst, channel_spec* spec, uint64_t } } - result = inst->backend->channel(inst, resolved_spec); + result = inst->backend->channel(inst, resolved_spec, map_direction); if(spec->globs && !result){ fprintf(stderr, "Failed to match multichannel evaluation %s to a channel\n", resolved_spec); } @@ -294,8 +294,8 @@ static int config_map(char* to_raw, char* from_raw){ //iterate, resolve globs and map rv = 0; for(n = 0; !rv && n < max(spec_from.channels, spec_to.channels); n++){ - channel_from = config_glob_resolve(instance_from, &spec_from, min(n, spec_from.channels)); - channel_to = config_glob_resolve(instance_to, &spec_to, min(n, spec_to.channels)); + channel_from = config_glob_resolve(instance_from, &spec_from, min(n, spec_from.channels), mmchannel_input); + channel_to = config_glob_resolve(instance_to, &spec_to, min(n, spec_to.channels), mmchannel_output); if(!channel_from || !channel_to){ rv = 1; diff --git a/midimonster.h b/midimonster.h index 1192d6a..5ce0c73 100644 --- a/midimonster.h +++ b/midimonster.h @@ -87,8 +87,11 @@ struct _managed_fd; * Parse instance configuration from the user-supplied configuration * file. Returning a non-zero value fails config parsing. * * mmbackend_channel - * Parse a channel-spec to be mapped to/from. Returning NULL signals an - * out-of-memory condition and terminates the program. + * Parse a channel-spec to be mapped to/from. The `falgs` parameter supplies + * additional information to the parser, such as whether the channel is being + * queried for use as input (to the MIDIMonster core) and/or output + * (from the MIDIMonster core) channel (on a per-query basis). + * Returning NULL signals an out-of-memory condition and terminates the program. * * mmbackend_start * Called after all instances have been created and all mappings * have been set up. Only backends for which instances have been configured @@ -121,7 +124,7 @@ struct _managed_fd; */ 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 struct _backend_channel* (*mmbackend_parse_channel)(struct _backend_instance* instance, char* spec, uint8_t flags); 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); @@ -130,6 +133,12 @@ typedef int (*mmbackend_start)(); typedef uint32_t (*mmbackend_interval)(); typedef int (*mmbackend_shutdown)(); +/* Bit masks for the `flags` parameter to mmbackend_parse_channel */ +typedef enum { + mmchannel_input = 0x1, + mmchannel_output = 0x2 +} mmbe_channel_flags; + /* Channel event value, .normalised is used by backends to determine channel values */ typedef struct _channel_value { union { -- cgit v1.2.3