aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorcbdev <cb@cbcdn.com>2017-07-11 16:00:57 +0200
committercbdev <cb@cbcdn.com>2017-07-11 16:00:57 +0200
commitabc1700eee53bd6dfd3e1d70736256bb02c0a115 (patch)
treef3ce0ac26ab5f221f3f4bf3ea4dbfba908569b10
parent90d3983b164bf37e08dd143ce343a28e756ebf46 (diff)
downloadmidimonster-abc1700eee53bd6dfd3e1d70736256bb02c0a115.tar.gz
midimonster-abc1700eee53bd6dfd3e1d70736256bb02c0a115.tar.bz2
midimonster-abc1700eee53bd6dfd3e1d70736256bb02c0a115.zip
Implement ArtNet wide (16bit) channel support (Fixes #4)
-rw-r--r--README.md7
-rw-r--r--artnet.c151
-rw-r--r--artnet.h10
-rw-r--r--monster.cfg26
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