diff options
-rw-r--r-- | Makefile | 2 | ||||
-rw-r--r-- | README.md | 2 | ||||
-rw-r--r-- | backends/maweb.c | 92 | ||||
-rw-r--r-- | backends/maweb.md | 6 |
4 files changed, 58 insertions, 44 deletions
@@ -56,5 +56,5 @@ run: valgrind --leak-check=full --show-leak-kinds=all ./midimonster sanitize: export CC = clang -sanitize: export CFLAGS = -g -Wall -Wpedantic -fsanitize=address -fsanitize=undefined -fno-omit-frame-pointer +sanitize: export CFLAGS += -g -Wall -Wpedantic -fsanitize=address -fsanitize=undefined -fno-omit-frame-pointer sanitize: midimonster backends @@ -11,6 +11,7 @@ Currently, the MIDIMonster supports the following protocols: * OpenSoundControl (OSC) * evdev input devices (Linux) * Open Lighting Architecture (OLA) +* MA Lighting Web Remote with additional flexibility provided by a Lua scripting environment. @@ -121,6 +122,7 @@ special information. These documentation files are located in the `backends/` di * [`ola` backend documentation](backends/ola.md) * [`osc` backend documentation](backends/osc.md) * [`lua` backend documentation](backends/lua.md) +* [`maweb` backend documentation](backends/maweb.md) ## Building diff --git a/backends/maweb.c b/backends/maweb.c index 9ba04fa..4e6fad1 100644 --- a/backends/maweb.c +++ b/backends/maweb.c @@ -266,14 +266,17 @@ static channel* maweb_channel(instance* inst, char* spec){ ident.fields.index--; ident.fields.page--; - //check if the channel is already known + //check if the (exec/meta) channel is already known for(n = 0; n < data->input_channels; n++){ - if(data->input_channel[n].label == ident.label){ + if(data->input_channel[n].fields.page == ident.fields.page + && data->input_channel[n].fields.index == ident.fields.index){ break; } } - if(n == data->input_channels){ + //FIXME only register channels that are mapped as outputs + //only register exec channels for updates + if(n == data->input_channels && ident.fields.type != cmdline_button){ 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"); @@ -324,9 +327,9 @@ static int maweb_send_frame(instance* inst, maweb_operation op, uint8_t* payload 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; + channel_value evt; maweb_channel_ident ident = { - .fields.page = page, + .fields.page = page - 1, .fields.index = json_obj_int(payload, "iExec", 191) }; @@ -335,10 +338,11 @@ static int maweb_process_playback(instance* inst, int64_t page, maweb_channel_ty //ignore unused buttons return 0; } - fprintf(stderr, "maweb missing exec block data on exec %d\n", ident.fields.index); + fprintf(stderr, "maweb missing exec block data on exec %ld.%d\n", page, ident.fields.index); return 1; } + //the bottomButtons key has an additional subentry if(metatype == 3){ exec_blocks += json_obj_offset(payload + exec_blocks, "items"); } @@ -363,7 +367,7 @@ static int maweb_process_playback(instance* inst, int64_t page, maweb_channel_ty 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)); + DBGPF("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++; } @@ -416,7 +420,7 @@ static int maweb_process_playbacks(instance* inst, int64_t page, char* payload, group++; } updates_inflight--; - fprintf(stderr, "maweb playback message processing done, %lu updates inflight\n", updates_inflight); + DBGPF("maweb playback message processing done, %lu updates inflight\n", updates_inflight); return 0; } @@ -425,45 +429,53 @@ static int maweb_request_playbacks(instance* inst){ 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; + char item_indices[1024] = "[300,400,500]", item_counts[1024] = "[16,16,16]", item_types[1024] = "[3,3,3]"; + size_t page_index = 0, view = 3, 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; } + //don't quote me on this whole segment for(channel = 0; channel < data->input_channels; channel++){ - offsets[0] = offsets[1] = offsets[2] = 0; + offsets[0] = offsets[1] = offsets[2] = 1; 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; + //blocks 0, 100 & 200 have 21 execs and need to be queried from fader view + view = (data->input_channel[channel].fields.index >= 300) ? 3 : 2; + + for(channel_offset = 1; channel + channel_offset <= data->input_channels; channel_offset++){ + channels = channel + channel_offset - 1; + //find end for this exec block + for(; channel + channel_offset < data->input_channels; channel_offset++){ + if(data->input_channel[channel + channel_offset].fields.page != page_index + || (data->input_channel[channels].fields.index / 100) != (data->input_channel[channel + channel_offset].fields.index / 100)){ + break; + } + } + + //add request block for the exec block + offsets[0] += snprintf(item_indices + offsets[0], sizeof(item_indices) - offsets[0], "%d,", data->input_channel[channels].fields.index); + offsets[1] += snprintf(item_counts + offsets[1], sizeof(item_counts) - offsets[1], "%d,", data->input_channel[channel + channel_offset - 1].fields.index - data->input_channel[channels].fields.index + 1); + offsets[2] += snprintf(item_types + offsets[2], sizeof(item_types) - offsets[2], "%d,", (data->input_channel[channels].fields.index < 100) ? 2 : 3); + + //send on page boundary, metamode boundary, last channel + if(channel + channel_offset >= data->input_channels + || data->input_channel[channel + channel_offset].fields.page != page_index + || (data->input_channel[channel].fields.index < 300) != (data->input_channel[channel + channel_offset].fields.index < 300)){ + break; + } + } + + //terminate arrays (overwriting the last array separator) + offsets[0] += snprintf(item_indices + offsets[0] - 1, sizeof(item_indices) - offsets[0], "]"); + offsets[1] += snprintf(item_counts + offsets[1] - 1, sizeof(item_counts) - offsets[1], "]"); + offsets[2] += snprintf(item_types + offsets[2] - 1, sizeof(item_types) - offsets[2], "]"); } else{ + //for the ma, the view equals the exec type requested (we can query all button execs from button view, all fader execs from fader view) 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); @@ -476,8 +488,12 @@ static int maweb_request_playbacks(instance* inst){ 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; } + + //advance base channel + channel += channel_offset - 1; + + //send current request snprintf(xmit_buffer, sizeof(xmit_buffer), "{" "\"requestType\":\"playbacks\"," @@ -497,7 +513,7 @@ static int maweb_request_playbacks(instance* inst){ view, data->session); rv |= maweb_send_frame(inst, ws_text, (uint8_t*) xmit_buffer, strlen(xmit_buffer)); - //fprintf(stderr, "req: %s\n", xmit_buffer); + DBGPF("maweb poll request: %s\n", xmit_buffer); updates_inflight++; } @@ -530,7 +546,7 @@ static int maweb_handle_message(instance* inst, char* payload, size_t payload_le } } - fprintf(stderr, "maweb message (%lu): %s\n", payload_length, payload); + DBGPF("maweb message (%lu): %s\n", payload_length, payload); if(json_obj(payload, "session") == JSON_NUMBER){ data->session = json_obj_int(payload, "session", data->session); fprintf(stderr, "maweb session id is now %ld\n", data->session); diff --git a/backends/maweb.md b/backends/maweb.md index f3b40b6..9fe4790 100644 --- a/backends/maweb.md +++ b/backends/maweb.md @@ -109,8 +109,4 @@ Data input from the console is done by actively querying the state of all mapped 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 -* Command line events are sent, but I'm not sure they're being handled yet -* For the dot2, currently only the Core & F-Wings are supported for input from the console, not the B-Wings +Command line events are sent, but I'm not sure they're being handled yet. |