aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorcbdev <cb@cbcdn.com>2017-06-07 19:54:59 +0200
committercbdev <cb@cbcdn.com>2017-06-07 19:54:59 +0200
commit00c273f6ce28fe267c483b20d1edf4630f5b741f (patch)
tree56b4cf130aa4a83d474efbcbc9cdaed7f4120d64
parent351167055885207b57a6054fd33bec38e8946e1b (diff)
downloadmidimonster-00c273f6ce28fe267c483b20d1edf4630f5b741f.tar.gz
midimonster-00c273f6ce28fe267c483b20d1edf4630f5b741f.tar.bz2
midimonster-00c273f6ce28fe267c483b20d1edf4630f5b741f.zip
ArtNet output
-rw-r--r--artnet.c125
-rw-r--r--artnet.h44
-rw-r--r--midi.c2
-rw-r--r--monster.cfg8
4 files changed, 167 insertions, 12 deletions
diff --git a/artnet.c b/artnet.c
index a15743b..80db7f3 100644
--- a/artnet.c
+++ b/artnet.c
@@ -4,6 +4,8 @@
#include <sys/socket.h>
#include <netdb.h>
#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
#define BACKEND_NAME "artnet"
static uint8_t default_net = 0;
@@ -66,6 +68,25 @@ static int artnet_listener(char* host, char* port){
return fd;
}
+static int artnet_parse_addr(char* host, char* port, struct sockaddr_storage* addr, socklen_t* len){
+ struct addrinfo* head;
+ struct addrinfo hints = {
+ .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));
+ return 1;
+ }
+
+ memcpy(addr, head->ai_addr, head->ai_addrlen);
+ *len = head->ai_addrlen;
+
+ freeaddrinfo(head);
+ return 0;
+}
int artnet_init(){
backend artnet = {
@@ -133,6 +154,7 @@ static instance* artnet_instance(){
}
static int artnet_configure_instance(instance* instance, char* option, char* value){
+ char* separator;
artnet_instance_data* data = (artnet_instance_data*) instance->impl;
if(!strcmp(option, "net")){
@@ -152,6 +174,20 @@ static int artnet_configure_instance(instance* instance, char* option, char* val
}
return 0;
}
+ else if(!strcmp(option, "dest")){
+ for(separator = value; *separator && *separator != ' '; separator++){
+ }
+
+ if(!*separator){
+ fprintf(stderr, "No port supplied in destination address\n");
+ return 1;
+ }
+
+ *separator = 0;
+ separator++;
+
+ return artnet_parse_addr(value, separator, &data->dest_addr, &data->dest_len);
+ }
fprintf(stderr, "Unknown ArtNet instance option %s\n", option);
return 1;
@@ -167,18 +203,79 @@ static channel* artnet_channel(instance* instance, char* spec){
}
static int artnet_set(instance* inst, size_t num, channel** c, channel_value* v){
- //TODO
- return 1;
+ 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);
+ return 0;
+ }
+
+ //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;
+
+ data->data.out[c[u]->ident] = v[u].normalised * 255.0;
+ }
+ }
+
+ if(mark){
+ //output frame
+ artnet_output_pkt frame = {
+ .magic = {'A', 'r', 't', '-', 'N', 'e', 't', 0x00},
+ .opcode = htobe16(OpDmx),
+ .version = htobe16(ARTNET_VERSION),
+ .sequence = data->data.seq++,
+ .port = 0,
+ .universe = data->uni,
+ .net = data->net,
+ .length = htobe16(512),
+ .data = {}
+ };
+ memcpy(frame.data, data->data.out, 512);
+
+ if(sendto(artnet_fd, &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));
+ }
+ }
+
+ return 0;
}
static int artnet_handle(size_t num, managed_fd* fds){
- //TODO
+ size_t u;
+ ssize_t bytes_read;
+ char recv_buf[ARTNET_RECV_BUF];
+ if(!num){
+ //early exit
+ return 0;
+ }
+
+ for(u = 0; u < num; u++){
+ do{
+ bytes_read = recv(fds[u].fd, recv_buf, sizeof(recv_buf), 0);
+ if(bytes_read > sizeof(artnet_header)){
+ fprintf(stderr, "Possible artnet data\n");
+ }
+ } while(bytes_read > 0);
+
+ if(bytes_read < 0 && errno != EAGAIN){
+ fprintf(stderr, "ArtNet failed to receive data: %s\n", strerror(errno));
+ }
+
+ if(bytes_read == 0){
+ fprintf(stderr, "ArtNet listener closed\n");
+ return 1;
+ }
+ }
+
return 0;
}
static int artnet_start(){
size_t n, u, p;
- int rv = 1;
+ int rv = 1i, flags;
instance** inst = NULL;
artnet_instance_data* data_a, *data_b;
artnet_instance_id id = {
@@ -215,6 +312,18 @@ static int artnet_start(){
id.fields.uni = data_a->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;
@@ -234,7 +343,15 @@ static int artnet_start(){
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;
}
diff --git a/artnet.h b/artnet.h
index eb60115..fa8d16e 100644
--- a/artnet.h
+++ b/artnet.h
@@ -1,3 +1,4 @@
+#include <sys/socket.h>
#include "midimonster.h"
/*
@@ -16,16 +17,24 @@ static int artnet_handle(size_t num, managed_fd* fds);
static int artnet_start();
static int artnet_shutdown();
+#define ARTNET_PORT "6454"
+#define ARTNET_VERSION 14
+#define ARTNET_RECV_BUF 4096
+#define ARTNET_KEEPALIVE_INTERVAL 15e5
+
typedef struct /*_artnet_universe_model*/ {
uint8_t last_frame;
- uint8_t data[512];
+ uint8_t seq;
+ uint8_t in[512];
+ uint8_t out[512];
} artnet_universe;
typedef struct /*_artnet_instance_model*/ {
uint8_t net;
uint8_t uni;
uint8_t mode;
- char* dest;
+ struct sockaddr_storage dest_addr;
+ socklen_t dest_len;
artnet_universe data;
} artnet_instance_data;
@@ -41,3 +50,34 @@ enum {
output = 1,
mark = 2
};
+
+typedef struct /*_artnet_pkt_header*/ {
+ uint8_t magic[8];
+ uint16_t opcode;
+ uint16_t version;
+} artnet_header;
+
+typedef struct /*_artnet_input_pkt*/ {
+ uint8_t sequence;
+ uint8_t port;
+ uint8_t universe;
+ uint8_t net;
+ uint16_t length;
+} artnet_input_pkt;
+
+typedef struct /*_artnet_output_pkt*/ {
+ uint8_t magic[8];
+ uint16_t opcode;
+ uint16_t version;
+ uint8_t sequence;
+ uint8_t port;
+ uint8_t universe;
+ uint8_t net;
+ uint16_t length;
+ uint8_t data[512];
+} artnet_output_pkt;
+
+enum artnet_pkt_opcode {
+ OpDmx = 0x0050
+};
+
diff --git a/midi.c b/midi.c
index da71a32..9235c89 100644
--- a/midi.c
+++ b/midi.c
@@ -314,7 +314,7 @@ static int midi_start(){
}
nfds = snd_seq_poll_descriptors(sequencer, pfds, nfds, POLLIN | POLLOUT);
- fprintf(stderr, "Registering %d descriptors to core\n", nfds);
+ fprintf(stderr, "MIDI backend registering %d descriptors to core\n", nfds);
for(p = 0; p < nfds; p++){
if(mm_manage_fd(pfds[p].fd, BACKEND_NAME, 1, NULL)){
goto bail;
diff --git a/monster.cfg b/monster.cfg
index 1da33c6..561649a 100644
--- a/monster.cfg
+++ b/monster.cfg
@@ -12,8 +12,8 @@ read = BCF
write = BCF
[midi lc1]
-read = Launch Control
-write = Launch Control
+;read = Launch Control
+;write = Launch Control
[midi xlate]
@@ -22,13 +22,11 @@ net = 1
uni = 1
output = true
-[artnet foo]
-net = 0
-
[map]
bcf.cc0.81 = lc1.cc0.1
lc1.cc0.1 = bcf.cc0.81
bcf.cc0.82 = lc1.note0.9
+net1.15 = bcf.cc0.82
;net1.256 = lc1.note0.9
;net1.257 = bcf.cc0.81
;net1.231 = foo.1