From abc1700eee53bd6dfd3e1d70736256bb02c0a115 Mon Sep 17 00:00:00 2001 From: cbdev Date: Tue, 11 Jul 2017 16:00:57 +0200 Subject: Implement ArtNet wide (16bit) channel support (Fixes #4) --- README.md | 7 +++ artnet.c | 151 ++++++++++++++++++++++++++++++++++++++++++++++++------------ artnet.h | 10 ++++ monster.cfg | 26 +++++------ 4 files changed, 152 insertions(+), 42 deletions(-) diff --git a/README.md b/README.md index a16644d..6892df8 100644 --- a/README.md +++ b/README.md @@ -86,6 +86,13 @@ Example mapping: net1.231 < net2.123 ``` +A 16-bit channel (spannin anyg two normal channels in the same universe) may be mapped with the syntax +``` +net1.1+2 > net2.5+123 +``` + +A normal channel that is part of a wide channel can not be mapped individually. + #### Known bugs / problems Currently, no keep-alive frames are sent and the minimum inter-frame-time is disregarded. diff --git a/artnet.c b/artnet.c index c041e32..b870700 100644 --- a/artnet.c +++ b/artnet.c @@ -228,20 +228,55 @@ static int artnet_configure_instance(instance* inst, char* option, char* value){ return 1; } - return artnet_parse_addr(host, port, &data->dest_addr, &data->dest_len); + return artnet_parse_addr(host, port, &data->dest_addr, &data->dest_len); } fprintf(stderr, "Unknown ArtNet option %s for instance %s\n", option, inst->name); return 1; } -static channel* artnet_channel(instance* instance, char* spec){ - unsigned channel = strtoul(spec, NULL, 10); - if(channel > 512 || channel < 1){ - fprintf(stderr, "Invalid ArtNet channel %s\n", spec); +static channel* artnet_channel(instance* inst, char* spec){ + artnet_instance_data* data = (artnet_instance_data*) inst->impl; + char* spec_next = spec; + unsigned chan_a = strtoul(spec, &spec_next, 10); + unsigned chan_b = 0; + + //primary channel sanity check + if(!chan_a || chan_a > 512){ + fprintf(stderr, "Invalid ArtNet channel specification %s\n", spec); return NULL; } - return mm_channel(instance, channel - 1, 1); + chan_a--; + + //secondary channel setup + if(*spec_next == '+'){ + chan_b = strtoul(spec_next + 1, NULL, 10); + if(!chan_b || chan_b > 512){ + fprintf(stderr, "Invalid wide-channel spec %s\n", spec); + return NULL; + } + chan_b--; + + //if mapped mode differs, bail + if(IS_ACTIVE(data->data.map[chan_b]) && data->data.map[chan_b] != (MAP_FINE | chan_a)){ + fprintf(stderr, "Fine channel already mapped for ArtNet spec %s\n", spec); + return NULL; + } + + data->data.map[chan_b] = MAP_FINE | chan_a; + } + + //check current map mode + if(IS_ACTIVE(data->data.map[chan_a])){ + if((*spec_next == '+' && data->data.map[chan_a] != (MAP_COARSE | chan_b)) + || (*spec_next != '+' && data->data.map[chan_a] != (MAP_SINGLE | chan_a))){ + fprintf(stderr, "Primary ArtNet channel already mapped at differing mode: %s\n", spec); + return NULL; + } + } + data->data.map[chan_a] = (*spec_next == '+') ? (MAP_COARSE | chan_b) : (MAP_SINGLE | chan_a); + + return mm_channel(inst, chan_a, 1); } static int artnet_set(instance* inst, size_t num, channel** c, channel_value* v){ @@ -255,9 +290,21 @@ static int artnet_set(instance* inst, size_t num, channel** c, channel_value* v) //FIXME maybe introduce minimum frame interval for(u = 0; u < num; u++){ - if(data->data.out[c[u]->ident] != (v[u].normalised * 255.0)){ - mark = 1; + if(IS_WIDE(data->data.map[c[u]->ident])){ + uint32_t val = (v[u].normalised * 0xFFFF); + //test coarse channel + if(data->data.out[c[u]->ident] != (val >> 8)){ + mark = 1; + data->data.out[c[u]->ident] = val >> 8; + } + if(data->data.out[MAPPED_CHANNEL(data->data.map[c[u]->ident])] != (val & 0xFF)){ + mark = 1; + data->data.out[MAPPED_CHANNEL(data->data.map[c[u]->ident])] = val & 0xFF; + } + } + else if(data->data.out[c[u]->ident] != (v[u].normalised * 255.0)){ + mark = 1; data->data.out[c[u]->ident] = v[u].normalised * 255.0; } } @@ -285,17 +332,77 @@ static int artnet_set(instance* inst, size_t num, channel** c, channel_value* v) return 0; } +static inline int artnet_process_frame(instance* inst, artnet_pkt* frame){ + size_t p; + uint16_t max_mark = 0; + uint16_t wide_val = 0; + channel* chan = NULL; + channel_value val; + artnet_instance_data* data = (artnet_instance_data*) inst->impl; + + if(be16toh(frame->length) > 512){ + fprintf(stderr, "Invalid frame channel count\n"); + return 1; + } + + //read data, mark update channels + for(p = 0; p < be16toh(frame->length); p++){ + if(IS_ACTIVE(data->data.map[p]) && frame->data[p] != data->data.in[p]){ + data->data.in[p] = frame->data[p]; + data->data.map[p] |= MAP_MARK; + max_mark = p; + } + } + + //generate events + for(p = 0; p < max_mark; p++){ + if(data->data.map[p] & MAP_MARK){ + data->data.map[p] &= ~MAP_MARK; + if(IS_ACTIVE(data->data.map[p])){ + if(IS_WIDE(data->data.map[p]) && data->data.map[p] & MAP_FINE){ + chan = mm_channel(inst, MAPPED_CHANNEL(data->data.map[p]), 0); + } + else{ + chan = mm_channel(inst, p, 0); + } + + if(!chan){ + fprintf(stderr, "Active channel %zu not known to core\n", p); + return 1; + } + + if(IS_WIDE(data->data.map[p])){ + data->data.map[MAPPED_CHANNEL(data->data.map[p])] &= ~MAP_MARK; + wide_val = data->data.in[p] << ((data->data.map[p] & MAP_COARSE) ? 8 : 0); + wide_val |= data->data.in[MAPPED_CHANNEL(data->data.map[p])] << ((data->data.map[p] & MAP_COARSE) ? 0 : 8); + + val.raw.u64 = wide_val; + val.normalised = (double) wide_val / (double) 0xFFFF; + } + else{ + //single channel + val.raw.u64 = data->data.in[p]; + val.normalised = (double) data->data.in[p] / 255.0; + } + + if(mm_channel_event(chan, val)){ + fprintf(stderr, "Failed to push ArtNet channel event to core\n"); + return 1; + } + } + } + } + return 0; +} + static int artnet_handle(size_t num, managed_fd* fds){ - size_t u, p; + size_t u; ssize_t bytes_read; char recv_buf[ARTNET_RECV_BUF]; artnet_instance_id inst_id = { .label = 0 }; instance* inst = NULL; - channel* chan = NULL; - channel_value val; - artnet_instance_data* data; artnet_pkt* frame = (artnet_pkt*) recv_buf; if(!num){ //early exit @@ -305,29 +412,15 @@ static int artnet_handle(size_t num, managed_fd* fds){ for(u = 0; u < num; u++){ do{ bytes_read = recv(fds[u].fd, recv_buf, sizeof(recv_buf), 0); - if(bytes_read > sizeof(artnet_hdr)){ + if(bytes_read > 0 && bytes_read > sizeof(artnet_hdr)){ if(!memcmp(frame->magic, "Art-Net\0", 8) && be16toh(frame->opcode) == OpDmx){ //find matching instance inst_id.fields.fd_index = ((uint64_t) fds[u].impl) & 0xFF; inst_id.fields.net = frame->net; inst_id.fields.uni = frame->universe; inst = mm_instance_find(BACKEND_NAME, inst_id.label); - if(inst){ - data = (artnet_instance_data*) inst->impl; - - //read data, notify of changes - for(p = 0; p < be16toh(frame->length); p++){ - if(frame->data[p] != data->data.in[p]){ - data->data.in[p] = frame->data[p]; - chan = mm_channel(inst, p, 0); - val.raw.u64 = frame->data[p]; - val.normalised = (double)frame->data[p] / 255.0; - if(chan && mm_channel_event(chan, val)){ - fprintf(stderr, "Failed to push ArtNet channel event to core\n"); - return 1; - } - } - } + if(inst && artnet_process_frame(inst, frame)){ + fprintf(stderr, "Failed to process ArtNet frame\n"); } } } diff --git a/artnet.h b/artnet.h index 3633e81..6b4dee4 100644 --- a/artnet.h +++ b/artnet.h @@ -16,11 +16,21 @@ static int artnet_shutdown(); #define ARTNET_RECV_BUF 4096 #define ARTNET_KEEPALIVE_INTERVAL 15e5 +#define MAP_COARSE 0x0200 +#define MAP_FINE 0x0400 +#define MAP_SINGLE 0x0800 +#define MAP_MARK 0x1000 +#define MAPPED_CHANNEL(a) ((a) & 0x01FF) +#define IS_ACTIVE(a) ((a) & 0xFE00) +#define IS_WIDE(a) ((a) & 0x0600) +#define IS_SINGLE(a) ((a) & MAP_SINGLE) + typedef struct /*_artnet_universe_model*/ { uint8_t last_frame; uint8_t seq; uint8_t in[512]; uint8_t out[512]; + uint16_t map[512]; } artnet_universe; typedef struct /*_artnet_instance_model*/ { diff --git a/monster.cfg b/monster.cfg index 171252f..c459262 100644 --- a/monster.cfg +++ b/monster.cfg @@ -5,20 +5,20 @@ name = MIDIMonster bind = * net = 0 -[midi in] -read = nano - [artnet net1] -uni = 5 -dest = 192.168.178.255 - -[artnet net2] -iface = 1 -uni = 5 +iface = 0 +uni = 0 dest = 255.255.255.255 -[map] +[osc osc1] +bind = * 8000 +dest = learn@8001 +/1/fader1 = f 0.0 1.0 +/1/fader2 = f 0.0 1.0 +/1/fader3 = f 0.0 1.0 +/1/fader4 = f 0.0 1.0 +/1/xy = ff 0.0 1.0 0.0 1.0 -in.cc0.1 > net1.1 -in.cc0.2 > net2.1 -net2.1 > in.note0.0 +[map] +osc1./1/xy:0 <> net1.1+2 +osc1./1/xy:1 <> net1.3+4 -- cgit v1.2.3