aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorcbdev <cb@cbcdn.com>2020-08-07 22:34:31 +0200
committercbdev <cb@cbcdn.com>2020-08-07 22:34:31 +0200
commit39dfd02d5daa8ce7cf749f6235cf6450b2171214 (patch)
treef5e14528fb68c0ebe4a462a34ce11d694ef2a870
parent7f2f36ddaa9c8311404248c69cc686bac6264ceb (diff)
downloadmidimonster-39dfd02d5daa8ce7cf749f6235cf6450b2171214.tar.gz
midimonster-39dfd02d5daa8ce7cf749f6235cf6450b2171214.tar.bz2
midimonster-39dfd02d5daa8ce7cf749f6235cf6450b2171214.zip
Implement detect mode for artnet (#70)
-rw-r--r--backends/artnet.c128
-rw-r--r--backends/artnet.h1
-rw-r--r--backends/artnet.md3
-rw-r--r--backends/sacn.c1
-rw-r--r--backends/sacn.h2
-rw-r--r--backends/sacn.md2
6 files changed, 82 insertions, 55 deletions
diff --git a/backends/artnet.c b/backends/artnet.c
index e07ea52..4a7907d 100644
--- a/backends/artnet.c
+++ b/backends/artnet.c
@@ -9,14 +9,19 @@
#define MAX_FDS 255
-static uint32_t next_frame = 0;
-static uint8_t default_net = 0;
-static size_t artnet_fds = 0;
-static artnet_descriptor* artnet_fd = NULL;
+static struct {
+ uint32_t next_frame;
+ uint8_t default_net;
+ size_t fds;
+ artnet_descriptor* fd;
+ uint8_t detect;
+} global_cfg = {
+ 0
+};
static int artnet_listener(char* host, char* port){
int fd;
- if(artnet_fds >= MAX_FDS){
+ if(global_cfg.fds >= MAX_FDS){
LOG("Backend descriptor limit reached");
return -1;
}
@@ -27,18 +32,19 @@ static int artnet_listener(char* host, char* port){
}
//store fd
- artnet_fd = realloc(artnet_fd, (artnet_fds + 1) * sizeof(artnet_descriptor));
- if(!artnet_fd){
+ global_cfg.fd = realloc(global_cfg.fd, (global_cfg.fds + 1) * sizeof(artnet_descriptor));
+ if(!global_cfg.fd){
close(fd);
+ global_cfg.fds = 0;
LOG("Failed to allocate memory");
return -1;
}
- LOGPF("Interface %" PRIsize_t " bound to %s port %s", artnet_fds, host, port);
- artnet_fd[artnet_fds].fd = fd;
- artnet_fd[artnet_fds].output_instances = 0;
- artnet_fd[artnet_fds].output_instance = NULL;
- artnet_fds++;
+ LOGPF("Interface %" PRIsize_t " bound to %s port %s", global_cfg.fds, host, port);
+ global_cfg.fd[global_cfg.fds].fd = fd;
+ global_cfg.fd[global_cfg.fds].output_instances = 0;
+ global_cfg.fd[global_cfg.fds].output_instance = NULL;
+ global_cfg.fds++;
return 0;
}
@@ -70,8 +76,8 @@ MM_PLUGIN_API int init(){
}
static uint32_t artnet_interval(){
- if(next_frame){
- return next_frame;
+ if(global_cfg.next_frame){
+ return global_cfg.next_frame;
}
return ARTNET_KEEPALIVE_INTERVAL;
}
@@ -80,7 +86,7 @@ static int artnet_configure(char* option, char* value){
char* host = NULL, *port = NULL, *fd_opts = NULL;
if(!strcmp(option, "net")){
//configure default net
- default_net = strtoul(value, NULL, 0);
+ global_cfg.default_net = strtoul(value, NULL, 0);
return 0;
}
else if(!strcmp(option, "bind")){
@@ -97,6 +103,16 @@ static int artnet_configure(char* option, char* value){
}
return 0;
}
+ else if(!strcmp(option, "detect")){
+ global_cfg.detect = 0;
+ if(!strcmp(value, "on")){
+ global_cfg.detect = 1;
+ }
+ else if(!strcmp(value, "verbose")){
+ global_cfg.detect = 2;
+ }
+ return 0;
+ }
LOGPF("Unknown backend option %s", option);
return 1;
@@ -111,7 +127,7 @@ static int artnet_instance(instance* inst){
return 1;
}
- data->net = default_net;
+ data->net = global_cfg.default_net;
for(u = 0; u < sizeof(data->data.channel) / sizeof(channel); u++){
data->data.channel[u].ident = u;
data->data.channel[u].instance = inst;
@@ -136,7 +152,7 @@ static int artnet_configure_instance(instance* inst, char* option, char* value){
else if(!strcmp(option, "iface") || !strcmp(option, "interface")){
data->fd_index = strtoul(value, NULL, 0);
- if(data->fd_index >= artnet_fds){
+ if(data->fd_index >= global_cfg.fds){
LOGPF("Invalid interface configured for instance %s", inst->name);
return 1;
}
@@ -223,7 +239,7 @@ static int artnet_transmit(instance* inst, artnet_output_universe* output){
};
memcpy(frame.data, data->data.out, 512);
- if(sendto(artnet_fd[data->fd_index].fd, (uint8_t*) &frame, sizeof(frame), 0, (struct sockaddr*) &data->dest_addr, data->dest_len) < 0){
+ if(sendto(global_cfg.fd[data->fd_index].fd, (uint8_t*) &frame, sizeof(frame), 0, (struct sockaddr*) &data->dest_addr, data->dest_len) < 0){
#ifdef _WIN32
if(WSAGetLastError() != WSAEWOULDBLOCK){
#else
@@ -234,8 +250,8 @@ static int artnet_transmit(instance* inst, artnet_output_universe* output){
}
//reschedule frame output
output->mark = 1;
- if(!next_frame || next_frame > ARTNET_SYNTHESIZE_MARGIN){
- next_frame = ARTNET_SYNTHESIZE_MARGIN;
+ if(!global_cfg.next_frame || global_cfg.next_frame > ARTNET_SYNTHESIZE_MARGIN){
+ global_cfg.next_frame = ARTNET_SYNTHESIZE_MARGIN;
}
return 0;
}
@@ -279,22 +295,22 @@ static int artnet_set(instance* inst, size_t num, channel** c, channel_value* v)
if(mark){
//find last frame time
- for(u = 0; u < artnet_fd[data->fd_index].output_instances; u++){
- if(artnet_fd[data->fd_index].output_instance[u].label == inst->ident){
+ for(u = 0; u < global_cfg.fd[data->fd_index].output_instances; u++){
+ if(global_cfg.fd[data->fd_index].output_instance[u].label == inst->ident){
break;
}
}
- frame_delta = mm_timestamp() - artnet_fd[data->fd_index].output_instance[u].last_frame;
+ frame_delta = mm_timestamp() - global_cfg.fd[data->fd_index].output_instance[u].last_frame;
//check output rate limit, request next frame
if(frame_delta < ARTNET_FRAME_TIMEOUT){
- artnet_fd[data->fd_index].output_instance[u].mark = 1;
- if(!next_frame || next_frame > (ARTNET_FRAME_TIMEOUT - frame_delta)){
- next_frame = (ARTNET_FRAME_TIMEOUT - frame_delta);
+ global_cfg.fd[data->fd_index].output_instance[u].mark = 1;
+ if(!global_cfg.next_frame || global_cfg.next_frame > (ARTNET_FRAME_TIMEOUT - frame_delta)){
+ global_cfg.next_frame = (ARTNET_FRAME_TIMEOUT - frame_delta);
}
return 0;
}
- return artnet_transmit(inst, artnet_fd[data->fd_index].output_instance + u);
+ return artnet_transmit(inst, global_cfg.fd[data->fd_index].output_instance + u);
}
return 0;
@@ -307,6 +323,11 @@ static inline int artnet_process_frame(instance* inst, artnet_pkt* frame){
channel_value val;
artnet_instance_data* data = (artnet_instance_data*) inst->impl;
+ if(!data->last_input && global_cfg.detect){
+ LOGPF("Valid data on instance %s (Net %d Universe %d): %d channels", inst->name, data->net, data->uni, be16toh(frame->length));
+ }
+ data->last_input = mm_timestamp();
+
if(be16toh(frame->length) > 512){
LOGPF("Invalid frame channel count: %d", be16toh(frame->length));
return 1;
@@ -366,23 +387,23 @@ static int artnet_handle(size_t num, managed_fd* fds){
artnet_pkt* frame = (artnet_pkt*) recv_buf;
//transmit keepalive & synthesized frames
- next_frame = 0;
- for(u = 0; u < artnet_fds; u++){
- for(c = 0; c < artnet_fd[u].output_instances; c++){
- synthesize_delta = timestamp - artnet_fd[u].output_instance[c].last_frame;
- if((artnet_fd[u].output_instance[c].mark
+ global_cfg.next_frame = 0;
+ for(u = 0; u < global_cfg.fds; u++){
+ for(c = 0; c < global_cfg.fd[u].output_instances; c++){
+ synthesize_delta = timestamp - global_cfg.fd[u].output_instance[c].last_frame;
+ if((global_cfg.fd[u].output_instance[c].mark
&& synthesize_delta >= ARTNET_FRAME_TIMEOUT + ARTNET_SYNTHESIZE_MARGIN) //synthesize next frame
|| synthesize_delta >= ARTNET_KEEPALIVE_INTERVAL){ //keepalive timeout
- inst = mm_instance_find(BACKEND_NAME, artnet_fd[u].output_instance[c].label);
+ inst = mm_instance_find(BACKEND_NAME, global_cfg.fd[u].output_instance[c].label);
if(inst){
- artnet_transmit(inst, artnet_fd[u].output_instance + c);
+ artnet_transmit(inst, global_cfg.fd[u].output_instance + c);
}
}
//update next_frame
- if(artnet_fd[u].output_instance[c].mark
- && (!next_frame || next_frame > ARTNET_FRAME_TIMEOUT + ARTNET_SYNTHESIZE_MARGIN - synthesize_delta)){
- next_frame = ARTNET_FRAME_TIMEOUT + ARTNET_SYNTHESIZE_MARGIN - synthesize_delta;
+ if(global_cfg.fd[u].output_instance[c].mark
+ && (!global_cfg.next_frame || global_cfg.next_frame > ARTNET_FRAME_TIMEOUT + ARTNET_SYNTHESIZE_MARGIN - synthesize_delta)){
+ global_cfg.next_frame = ARTNET_FRAME_TIMEOUT + ARTNET_SYNTHESIZE_MARGIN - synthesize_delta;
}
}
}
@@ -400,6 +421,9 @@ static int artnet_handle(size_t num, managed_fd* fds){
if(inst && artnet_process_frame(inst, frame)){
LOG("Failed to process frame");
}
+ else if(!inst && global_cfg.detect > 1){
+ LOGPF("Received data for unconfigured universe %d (net %d) on descriptor %" PRIsize_t, frame->universe, frame->net, (((uint64_t) fds[u].impl) & 0xFF));
+ }
}
}
} while(bytes_read > 0);
@@ -429,7 +453,7 @@ static int artnet_start(size_t n, instance** inst){
.label = 0
};
- if(!artnet_fds){
+ if(!global_cfg.fds){
LOG("Failed to start backend: no descriptors bound");
return 1;
}
@@ -452,23 +476,23 @@ static int artnet_start(size_t n, instance** inst){
//if enabled for output, add to keepalive tracking
if(data->dest_len){
- artnet_fd[data->fd_index].output_instance = realloc(artnet_fd[data->fd_index].output_instance, (artnet_fd[data->fd_index].output_instances + 1) * sizeof(artnet_output_universe));
+ global_cfg.fd[data->fd_index].output_instance = realloc(global_cfg.fd[data->fd_index].output_instance, (global_cfg.fd[data->fd_index].output_instances + 1) * sizeof(artnet_output_universe));
- if(!artnet_fd[data->fd_index].output_instance){
+ if(!global_cfg.fd[data->fd_index].output_instance){
LOG("Failed to allocate memory");
goto bail;
}
- artnet_fd[data->fd_index].output_instance[artnet_fd[data->fd_index].output_instances].label = id.label;
- artnet_fd[data->fd_index].output_instance[artnet_fd[data->fd_index].output_instances].last_frame = 0;
- artnet_fd[data->fd_index].output_instance[artnet_fd[data->fd_index].output_instances].mark = 0;
+ global_cfg.fd[data->fd_index].output_instance[global_cfg.fd[data->fd_index].output_instances].label = id.label;
+ global_cfg.fd[data->fd_index].output_instance[global_cfg.fd[data->fd_index].output_instances].last_frame = 0;
+ global_cfg.fd[data->fd_index].output_instance[global_cfg.fd[data->fd_index].output_instances].mark = 0;
- artnet_fd[data->fd_index].output_instances++;
+ global_cfg.fd[data->fd_index].output_instances++;
}
}
- LOGPF("Registering %" PRIsize_t " descriptors to core", artnet_fds);
- for(u = 0; u < artnet_fds; u++){
- if(mm_manage_fd(artnet_fd[u].fd, BACKEND_NAME, 1, (void*) u)){
+ LOGPF("Registering %" PRIsize_t " descriptors to core", global_cfg.fds);
+ for(u = 0; u < global_cfg.fds; u++){
+ if(mm_manage_fd(global_cfg.fd[u].fd, BACKEND_NAME, 1, (void*) u)){
goto bail;
}
}
@@ -485,11 +509,13 @@ static int artnet_shutdown(size_t n, instance** inst){
free(inst[p]->impl);
}
- for(p = 0; p < artnet_fds; p++){
- close(artnet_fd[p].fd);
- free(artnet_fd[p].output_instance);
+ for(p = 0; p < global_cfg.fds; p++){
+ close(global_cfg.fd[p].fd);
+ free(global_cfg.fd[p].output_instance);
}
- free(artnet_fd);
+ free(global_cfg.fd);
+ global_cfg.fd = NULL;
+ global_cfg.fds = 0;
LOG("Backend shut down");
return 0;
diff --git a/backends/artnet.h b/backends/artnet.h
index a517aa0..ecd775e 100644
--- a/backends/artnet.h
+++ b/backends/artnet.h
@@ -47,6 +47,7 @@ typedef struct /*_artnet_instance_model*/ {
socklen_t dest_len;
artnet_universe data;
size_t fd_index;
+ uint64_t last_input;
} artnet_instance_data;
typedef union /*_artnet_instance_id*/ {
diff --git a/backends/artnet.md b/backends/artnet.md
index 383203d..73f598a 100644
--- a/backends/artnet.md
+++ b/backends/artnet.md
@@ -9,8 +9,9 @@ Art-Netâ„¢ Designed by and Copyright Artistic Licence Holdings Ltd.
| Option | Example value | Default value | Description |
|---------------|-----------------------|-----------------------|-----------------------|
-| `bind` | `127.0.0.1 6454` | none | Binds a network address to listen for data. This option may be set multiple times, with each interface being assigned an index starting from 0 to be used with the `interface` instance configuration option. At least one interface is required for transmission. |
+| `bind` | `127.0.0.1 6454` | none | Binds a network address to listen for data. This option may be set multiple times, with each interface being assigned an index starting from 0 to be used with the `interface` instance configuration option. At least one interface is required for transmission. |
| `net` | `0` | `0` | The default net to use |
+| `detect` | `on`, `verbose` | `off` | Output additional information on received data packets to help with configuring complex scenarios |
#### Instance configuration
diff --git a/backends/sacn.c b/backends/sacn.c
index 247dfc7..0ea7b58 100644
--- a/backends/sacn.c
+++ b/backends/sacn.c
@@ -1,5 +1,4 @@
#define BACKEND_NAME "sacn"
-#define DEBUG
#include <string.h>
#include <sys/types.h>
diff --git a/backends/sacn.h b/backends/sacn.h
index 0c44ebc..0f24538 100644
--- a/backends/sacn.h
+++ b/backends/sacn.h
@@ -40,7 +40,7 @@ typedef struct /*_sacn_universe_model*/ {
} sacn_universe;
typedef struct /*_sacn_instance_model*/ {
- uint32_t last_input;
+ uint64_t last_input;
uint16_t uni;
uint8_t realtime;
uint8_t xmit_prio;
diff --git a/backends/sacn.md b/backends/sacn.md
index 3bc5b72..b7686e0 100644
--- a/backends/sacn.md
+++ b/backends/sacn.md
@@ -11,7 +11,7 @@ containing all write-enabled universes.
| `name` | `sACN source` | `MIDIMonster` | sACN source name |
| `cid` | `0xAA 0xBB 0xCC` ... | `MIDIMonster` | Source CID (16 bytes) |
| `bind` | `0.0.0.0 5568` | none | Binds a network address to listen for data. This option may be set multiple times, with each descriptor being assigned an index starting from 0 to be used with the `interface` instance configuration option. At least one descriptor is required for operation. |
-| `detect` | `on`, `verbose` | `off` | Output additional information on incoming and outgoing data packets to help with configuring complex scenarios. |
+| `detect` | `on`, `verbose` | `off` | Output additional information on received data packets to help with configuring complex scenarios. |
The `bind` configuration value can be extended by the keyword `local` to allow software on the
local host to process the sACN output frames from the MIDIMonster (e.g. `bind = 0.0.0.0 5568 local`).