aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--README.md9
-rw-r--r--TODO4
-rw-r--r--artnet.c225
-rw-r--r--artnet.h14
4 files changed, 124 insertions, 128 deletions
diff --git a/README.md b/README.md
index 4ed1641..a16644d 100644
--- a/README.md
+++ b/README.md
@@ -11,6 +11,7 @@ It allows the user to translate channels on one protocol into channels on anothe
* Translate MIDI Notes into ArtNet
* Translate OSC messages into MIDI
* Use an OSC app as a simple lighting controller via ArtNet
+* Visualize ArtNet data using OSC servers
## Usage
@@ -50,7 +51,7 @@ output eachothers events.
The last line is a shorter way to create a bi-directional mapping.
-An example configuration file can be found in [unifest-17.cfg](unifest-17.cfg).
+An example configuration file can be found in [configs/unifest-17.cfg](configs/unifest-17.cfg).
## Backend documentation
This section documents the configuration options supported by the various backends.
@@ -64,7 +65,7 @@ fixture control.
| Option | Example value | Default value | Description |
|---------------|-----------------------|-----------------------|-----------------------|
-| `bind` | `127.0.0.1 6454` | *none* | What address and port to bind the ArtNet socket to |
+| `bind` | `127.0.0.1 6454` | 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 `iface` instance configuration option |
| `net` | `0` | `0` | The default net to use |
#### Instance configuration
@@ -73,8 +74,8 @@ fixture control.
|---------------|-----------------------|-----------------------|-----------------------|
| `net` | `0` | `0` | ArtNet net to use |
| `uni` | `0` | `0` | ArtNet universe to use|
-| `output` | `true` | `false` | Controls whether ArtNet frames for this universe are output (otherwise the universe is input-only) |
-| `dest` | `10.2.2.2` | `255.255.255.255` | Destination address for sent ArtNet frames |
+| `dest` | `10.2.2.2` | none | Destination address for sent ArtNet frames. Setting this enables the universe for output |
+| `iface` | `1` | `0` | The bound address to use for data input/output |
#### Channel specification
diff --git a/TODO b/TODO
index e654306..8176dd3 100644
--- a/TODO
+++ b/TODO
@@ -1,2 +1,6 @@
Wide channels (DMX/MIDI)
Note source in channel value struct
+Optimize core channel search (store backend offset)
+OSC backend transmit alignment fails on multifader
+Function generator
+Printing backend
diff --git a/artnet.c b/artnet.c
index 55e4e85..c041e32 100644
--- a/artnet.c
+++ b/artnet.c
@@ -5,21 +5,18 @@
#include <netdb.h>
#include <unistd.h>
#include <fcntl.h>
+#include <ctype.h>
#include <errno.h>
+#define MAX_FDS 255
#define BACKEND_NAME "artnet"
+
static uint8_t default_net = 0;
-static struct {
- char* host;
- char* port;
-} bind_info = {
- .host = NULL,
- .port = NULL
-};
-int artnet_fd = -1;
+static size_t artnet_fds = 0;
+static int* artnet_fd = NULL;
static int artnet_listener(char* host, char* port){
- int fd = -1, status, yes = 1;
+ int fd = -1, status, yes = 1, flags;
struct addrinfo hints = {
.ai_family = AF_UNSPEC,
.ai_socktype = SOCK_DGRAM,
@@ -28,6 +25,11 @@ static int artnet_listener(char* host, char* port){
struct addrinfo* info;
struct addrinfo* addr_it;
+ if(artnet_fds >= MAX_FDS){
+ fprintf(stderr, "ArtNet backend descriptor limit reached\n");
+ return -1;
+ }
+
status = getaddrinfo(host, port, &hints, &info);
if(status){
fprintf(stderr, "Failed to get socket info for %s port %s: %s\n", host, port, gai_strerror(status));
@@ -70,7 +72,25 @@ static int artnet_listener(char* host, char* port){
fprintf(stderr, "Failed to create listening socket for %s port %s\n", host, port);
return -1;
}
- return fd;
+
+ //set nonblocking
+ flags = fcntl(fd, F_GETFL, 0);
+ if(fcntl(fd, F_SETFL, flags | O_NONBLOCK) < 0){
+ fprintf(stderr, "Failed to set ArtNet descriptor nonblocking\n");
+ return -1;
+ }
+
+ //store fd
+ artnet_fd = realloc(artnet_fd, (artnet_fds + 1) * sizeof(int));
+ if(!artnet_fd){
+ fprintf(stderr, "Failed to allocate memory\n");
+ return -1;
+ }
+
+ fprintf(stderr, "ArtNet backend descriptor %zu bound to %s port %s\n", artnet_fds, host, port);
+ artnet_fd[artnet_fds] = fd;
+ artnet_fds++;
+ return 0;
}
static int artnet_parse_addr(char* host, char* port, struct sockaddr_storage* addr, socklen_t* len){
@@ -79,7 +99,7 @@ static int artnet_parse_addr(char* host, char* port, struct sockaddr_storage* ad
.ai_family = AF_UNSPEC,
.ai_socktype = SOCK_DGRAM
};
-
+
int error = getaddrinfo(host, port, &hints, &head);
if(error || !head){
fprintf(stderr, "Failed to parse address %s port %s: %s\n", host, port, gai_strerror(error));
@@ -93,6 +113,30 @@ static int artnet_parse_addr(char* host, char* port, struct sockaddr_storage* ad
return 0;
}
+static int artnet_separate_hostspec(char* in, char** host, char** port){
+ size_t u;
+
+ if(!in || !host || !port){
+ return 1;
+ }
+
+ for(u = 0; in[u] && !isspace(in[u]); u++){
+ }
+
+ //guess
+ *host = in;
+
+ if(in[u]){
+ in[u] = 0;
+ *port = in + u + 1;
+ }
+ else{
+ //no port given
+ *port = ARTNET_PORT;
+ }
+ return 0;
+}
+
int init(){
backend artnet = {
.name = BACKEND_NAME,
@@ -115,25 +159,22 @@ int init(){
}
static int artnet_configure(char* option, char* value){
- char* separator = value;
- if(!strcmp(option, "bind")){
- for(; *separator && *separator != ' '; separator++){
+ char* host = NULL, *port = NULL;
+ if(!strcmp(option, "net")){
+ //configure default net
+ default_net = strtoul(value, NULL, 0);
+ return 0;
+ }
+ else if(!strcmp(option, "bind")){
+ if(artnet_separate_hostspec(value, &host, &port)){
+ fprintf(stderr, "Not a valid ArtNet bind address: %s\n", value);
+ return 1;
}
- if(*separator){
- *separator = 0;
- separator++;
- free(bind_info.port);
- bind_info.port = strdup(separator);
+ if(artnet_listener(host, port)){
+ fprintf(stderr, "Failed to bind ArtNet descriptor: %s\n", value);
+ return 1;
}
-
- free(bind_info.host);
- bind_info.host = strdup(value);
- return 0;
- }
- else if(!strcmp(option, "net")){
- //configure default net
- default_net = strtoul(value, NULL, 10);
return 0;
}
fprintf(stderr, "Unknown ArtNet backend option %s\n", option);
@@ -141,60 +182,56 @@ static int artnet_configure(char* option, char* value){
}
static instance* artnet_instance(){
+ artnet_instance_data* data = NULL;
instance* inst = mm_instance();
if(!inst){
return NULL;
}
- inst->impl = calloc(1, sizeof(artnet_instance_data));
- if(!inst->impl){
+ data = calloc(1, sizeof(artnet_instance_data));
+ if(!data){
fprintf(stderr, "Failed to allocate memory\n");
return NULL;
}
- artnet_instance_data* data = (artnet_instance_data*) inst->impl;
+ data->fd_index = 0;
data->net = default_net;
+ inst->impl = data;
return inst;
}
-static int artnet_configure_instance(instance* instance, char* option, char* value){
- char* separator;
- artnet_instance_data* data = (artnet_instance_data*) instance->impl;
+static int artnet_configure_instance(instance* inst, char* option, char* value){
+ char* host = NULL, *port = NULL;
+ artnet_instance_data* data = (artnet_instance_data*) inst->impl;
if(!strcmp(option, "net")){
- data->net = strtoul(value, NULL, 10);
+ data->net = strtoul(value, NULL, 0);
return 0;
}
else if(!strcmp(option, "uni")){
- data->uni = strtoul(value, NULL, 10);
+ data->uni = strtoul(value, NULL, 0);
return 0;
}
- else if(!strcmp(option, "output")){
- if(!strcmp(value, "true")){
- data->mode |= output;
- }
- else{
- data->mode &= ~output;
+ else if(!strcmp(option, "iface")){
+ data->fd_index = strtoul(value, NULL, 0);
+
+ if(data->fd_index >= artnet_fds){
+ fprintf(stderr, "Invalid interface configured for ArtNet instance %s\n", inst->name);
+ return 1;
}
return 0;
}
else if(!strcmp(option, "dest")){
- for(separator = value; *separator && *separator != ' '; separator++){
- }
-
- if(!*separator){
- fprintf(stderr, "No port supplied in destination address\n");
+ if(artnet_separate_hostspec(value, &host, &port)){
+ fprintf(stderr, "Not a valid ArtNet destination for instance %s\n", inst->name);
return 1;
}
- *separator = 0;
- separator++;
-
- return artnet_parse_addr(value, separator, &data->dest_addr, &data->dest_len);
+ return artnet_parse_addr(host, port, &data->dest_addr, &data->dest_len);
}
- fprintf(stderr, "Unknown ArtNet instance option %s\n", option);
+ fprintf(stderr, "Unknown ArtNet option %s for instance %s\n", option, inst->name);
return 1;
}
@@ -210,9 +247,9 @@ static channel* artnet_channel(instance* instance, char* spec){
static int artnet_set(instance* inst, size_t num, channel** c, channel_value* v){
size_t u, mark = 0;
artnet_instance_data* data = (artnet_instance_data*) inst->impl;
-
- if(!(data->mode & output)){
- fprintf(stderr, "ArtNet instance %s not enabled for output\n", inst->name);
+
+ if(!data->dest_len){
+ fprintf(stderr, "ArtNet instance %s not enabled for output (%zu channel events)\n", inst->name, num);
return 0;
}
@@ -240,7 +277,7 @@ static int artnet_set(instance* inst, size_t num, channel** c, channel_value* v)
};
memcpy(frame.data, data->data.out, 512);
- if(sendto(artnet_fd, &frame, sizeof(frame), 0, (struct sockaddr*) &data->dest_addr, data->dest_len) < 0){
+ if(sendto(artnet_fd[data->fd_index], &frame, sizeof(frame), 0, (struct sockaddr*) &data->dest_addr, data->dest_len) < 0){
fprintf(stderr, "Failed to output ArtNet frame for instance %s: %s\n", inst->name, strerror(errno));
}
}
@@ -271,6 +308,7 @@ static int artnet_handle(size_t num, managed_fd* fds){
if(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);
@@ -310,26 +348,13 @@ static int artnet_handle(size_t num, managed_fd* fds){
static int artnet_start(){
size_t n, u, p;
- int rv = 1, flags;
+ int rv = 1;
instance** inst = NULL;
- artnet_instance_data* data_a, *data_b;
+ artnet_instance_data* data;
artnet_instance_id id = {
.label = 0
};
- if(!bind_info.host){
- bind_info.host = strdup("127.0.0.1");
- }
-
- if(!bind_info.port){
- bind_info.port = strdup("6454");
- }
-
- if(!bind_info.host || !bind_info.port){
- fprintf(stderr, "Failed to allocate memory\n");
- return 1;
- }
-
//fetch all defined instances
if(mm_backend_instances(BACKEND_NAME, &n, &inst)){
fprintf(stderr, "Failed to fetch instance list\n");
@@ -341,58 +366,35 @@ static int artnet_start(){
return 0;
}
+ if(!artnet_fds){
+ fprintf(stderr, "No ArtNet descriptors bound\n");
+ return 1;
+ }
+
for(u = 0; u < n; u++){
- data_a = (artnet_instance_data*) inst[u]->impl;
+ data = (artnet_instance_data*) inst[u]->impl;
//set instance identifier
- id.fields.net = data_a->net;
- id.fields.uni = data_a->uni;
+ id.fields.fd_index = data->fd_index;
+ id.fields.net = data->net;
+ id.fields.uni = data->uni;
inst[u]->ident = id.label;
- //set destination address if not provided
- if(!data_a->dest_len){
- struct sockaddr_in bcast4 = {
- .sin_addr.s_addr = htobe32(-1),
- .sin_port = htobe16(strtoul(ARTNET_PORT, NULL, 10)),
- .sin_family = PF_INET
- };
-
- memcpy(&(data_a->dest_addr), &bcast4, sizeof(bcast4));
- data_a->dest_len = sizeof(bcast4);
- }
-
- //check for duplicate instances
- for(p = u + 1; p < n; p++){
- data_b = (artnet_instance_data*) inst[p]->impl;
- //FIXME might want to include destination in duplicate check
- if(data_a->net == data_b->net
- && data_a->uni == data_b->uni){
+ //check for duplicates
+ for(p = 0; p < u; p++){
+ if(inst[u]->ident == inst[p]->ident){
fprintf(stderr, "Universe specified multiple times, use one instance: %s - %s\n", inst[u]->name, inst[p]->name);
goto bail;
}
}
}
- //open socket
- artnet_fd = artnet_listener(bind_info.host, bind_info.port);
- if(artnet_fd < 0){
- fprintf(stderr, "Failed to open ArtNet listener socket\n");
- goto bail;
- }
- fprintf(stderr, "Listening for ArtNet data on %s port %s\n", bind_info.host, bind_info.port);
-
- //set nonblocking
- flags = fcntl(artnet_fd, F_GETFL, 0);
- if(fcntl(artnet_fd, F_SETFL, flags | O_NONBLOCK) < 0){
- fprintf(stderr, "Failed to set ArtNet input nonblocking\n");
- goto bail;
- }
-
- fprintf(stderr, "ArtNet backend registering 1 descriptors to core\n");
- if(mm_manage_fd(artnet_fd, BACKEND_NAME, 1, NULL)){
- goto bail;
+ fprintf(stderr, "ArtNet backend registering %zu descriptors to core\n", artnet_fds);
+ for(u = 0; u < artnet_fds; u++){
+ if(mm_manage_fd(artnet_fd[u], BACKEND_NAME, 1, (void*) u)){
+ goto bail;
+ }
}
- //TODO parse all universe destinations
rv = 0;
bail:
free(inst);
@@ -411,9 +413,8 @@ static int artnet_shutdown(){
free(inst[p]->impl);
}
free(inst);
+ free(artnet_fd);
- free(bind_info.host);
- free(bind_info.port);
fprintf(stderr, "ArtNet backend shut down\n");
return 0;
}
diff --git a/artnet.h b/artnet.h
index 0d61627..3633e81 100644
--- a/artnet.h
+++ b/artnet.h
@@ -1,12 +1,6 @@
#include <sys/socket.h>
#include "midimonster.h"
-/*
- * TODO
- * bind per instance
- * destination per instance
- */
-
int init();
static int artnet_configure(char* option, char* value);
static int artnet_configure_instance(instance* instance, char* option, char* value);
@@ -32,25 +26,21 @@ typedef struct /*_artnet_universe_model*/ {
typedef struct /*_artnet_instance_model*/ {
uint8_t net;
uint8_t uni;
- uint8_t mode;
struct sockaddr_storage dest_addr;
socklen_t dest_len;
artnet_universe data;
+ size_t fd_index;
} artnet_instance_data;
typedef union /*_artnet_instance_id*/ {
struct {
+ uint8_t fd_index;
uint8_t net;
uint8_t uni;
} fields;
uint64_t label;
} artnet_instance_id;
-enum {
- output = 1,
- mark = 2
-};
-
typedef struct /*_artnet_hdr*/ {
uint8_t magic[8];
uint16_t opcode;