aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--README.md8
-rw-r--r--artnet.c94
-rw-r--r--artnet.h9
-rw-r--r--monster.cfg9
-rw-r--r--sacn.c113
-rw-r--r--sacn.h2
6 files changed, 169 insertions, 66 deletions
diff --git a/README.md b/README.md
index 6bfcad6..1f1979d 100644
--- a/README.md
+++ b/README.md
@@ -138,7 +138,8 @@ 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.
+The minimum inter-frame-time is disregarded, as the packet rate is determined by the rate of incoming
+channel events.
### The `sacn` backend
@@ -186,11 +187,10 @@ A normal channel that is part of a wide channel can not be mapped individually.
#### Known bugs / problems
-No keepalive frames are sent at this time. This will be implemented in the near future.
-
The DMX start code of transmitted and received universes is fixed as `0`.
-The limit on packet transmission rate mandated by section 6.6.1 of the sACN specification is disregarded.
+The (upper) limit on packet transmission rate mandated by section 6.6.1 of the sACN specification is disregarded.
+The rate of packet transmission is influenced by the rate of incoming mapped events on the instance.
Universe synchronization is currently not supported, though this feature may be implemented in the future.
diff --git a/artnet.c b/artnet.c
index 474d0a6..c16e630 100644
--- a/artnet.c
+++ b/artnet.c
@@ -13,7 +13,7 @@
static uint8_t default_net = 0;
static size_t artnet_fds = 0;
-static int* artnet_fd = NULL;
+static artnet_descriptor* artnet_fd = NULL;
static int artnet_listener(char* host, char* port){
int fd = -1, status, yes = 1, flags;
@@ -81,14 +81,17 @@ static int artnet_listener(char* host, char* port){
}
//store fd
- artnet_fd = realloc(artnet_fd, (artnet_fds + 1) * sizeof(int));
+ artnet_fd = realloc(artnet_fd, (artnet_fds + 1) * sizeof(artnet_descriptor));
if(!artnet_fd){
fprintf(stderr, "Failed to allocate memory\n");
return -1;
}
fprintf(stderr, "ArtNet backend interface %zu bound to %s port %s\n", artnet_fds, host, port);
- artnet_fd[artnet_fds] = fd;
+ artnet_fd[artnet_fds].fd = fd;
+ artnet_fd[artnet_fds].output_instances = 0;
+ artnet_fd[artnet_fds].output_instance = NULL;
+ artnet_fd[artnet_fds].last_frame = NULL;
artnet_fds++;
return 0;
}
@@ -279,6 +282,36 @@ static channel* artnet_channel(instance* inst, char* spec){
return mm_channel(inst, chan_a, 1);
}
+static int artnet_transmit(instance* inst){
+ size_t u;
+ artnet_instance_data* data = (artnet_instance_data*) inst->impl;
+ //output frame
+ artnet_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 = {0}
+ };
+ memcpy(frame.data, data->data.out, 512);
+
+ if(sendto(artnet_fd[data->fd_index].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));
+ }
+
+ //update last frame timestamp
+ for(u = 0; u < artnet_fd[data->fd_index].output_instances; u++){
+ if(artnet_fd[data->fd_index].output_instance[u].label == inst->ident){
+ artnet_fd[data->fd_index].last_frame[u] = mm_timestamp();
+ }
+ }
+ return 0;
+}
+
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;
@@ -310,23 +343,7 @@ static int artnet_set(instance* inst, size_t num, channel** c, channel_value* v)
}
if(mark){
- //output frame
- artnet_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 = {0}
- };
- memcpy(frame.data, data->data.out, 512);
-
- 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));
- }
+ return artnet_transmit(inst);
}
return 0;
@@ -393,7 +410,8 @@ static inline int artnet_process_frame(instance* inst, artnet_pkt* frame){
}
static int artnet_handle(size_t num, managed_fd* fds){
- size_t u;
+ size_t u, c;
+ uint64_t timestamp = mm_timestamp();
ssize_t bytes_read;
char recv_buf[ARTNET_RECV_BUF];
artnet_instance_id inst_id = {
@@ -401,6 +419,19 @@ static int artnet_handle(size_t num, managed_fd* fds){
};
instance* inst = NULL;
artnet_pkt* frame = (artnet_pkt*) recv_buf;
+
+ //transmit keepalive frames
+ for(u = 0; u < artnet_fds; u++){
+ for(c = 0; c < artnet_fd[u].output_instances; c++){
+ if(timestamp - artnet_fd[u].last_frame[c] >= ARTNET_KEEPALIVE_INTERVAL){
+ inst = mm_instance_find(BACKEND_NAME, artnet_fd[u].output_instance[c].label);
+ if(inst){
+ artnet_transmit(inst);
+ }
+ }
+ }
+ }
+
if(!num){
//early exit
return 0;
@@ -476,11 +507,26 @@ static int artnet_start(){
goto bail;
}
}
+
+ //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_instance_id));
+ artnet_fd[data->fd_index].last_frame = realloc(artnet_fd[data->fd_index].last_frame, (artnet_fd[data->fd_index].output_instances + 1) * sizeof(uint64_t));
+
+ if(!artnet_fd[data->fd_index].output_instance || !artnet_fd[data->fd_index].last_frame){
+ fprintf(stderr, "Failed to allocate memory\n");
+ goto bail;
+ }
+ artnet_fd[data->fd_index].output_instance[artnet_fd[data->fd_index].output_instances] = id;
+ artnet_fd[data->fd_index].last_frame[artnet_fd[data->fd_index].output_instances] = 0;
+
+ artnet_fd[data->fd_index].output_instances++;
+ }
}
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)){
+ if(mm_manage_fd(artnet_fd[u].fd, BACKEND_NAME, 1, (void*) u)){
goto bail;
}
}
@@ -505,7 +551,9 @@ static int artnet_shutdown(){
free(inst);
for(p = 0; p < artnet_fds; p++){
- close(artnet_fd[p]);
+ close(artnet_fd[p].fd);
+ free(artnet_fd[p].output_instance);
+ free(artnet_fd[p].last_frame);
}
free(artnet_fd);
diff --git a/artnet.h b/artnet.h
index a2fd388..90aedd5 100644
--- a/artnet.h
+++ b/artnet.h
@@ -14,7 +14,7 @@ static int artnet_shutdown();
#define ARTNET_PORT "6454"
#define ARTNET_VERSION 14
#define ARTNET_RECV_BUF 4096
-#define ARTNET_KEEPALIVE_INTERVAL 15e5
+#define ARTNET_KEEPALIVE_INTERVAL 2000
#define MAP_COARSE 0x0200
#define MAP_FINE 0x0400
@@ -50,6 +50,13 @@ typedef union /*_artnet_instance_id*/ {
uint64_t label;
} artnet_instance_id;
+typedef struct /*_artnet_fd*/ {
+ int fd;
+ size_t output_instances;
+ artnet_instance_id* output_instance;
+ uint64_t* last_frame;
+} artnet_descriptor;
+
#pragma pack(push, 1)
typedef struct /*_artnet_hdr*/ {
uint8_t magic[8];
diff --git a/monster.cfg b/monster.cfg
index 236bd9a..8412e8c 100644
--- a/monster.cfg
+++ b/monster.cfg
@@ -5,6 +5,13 @@ name = MIDIMonster
name = sACN source
bind = 0.0.0.0
+[backend artnet]
+bind = 0.0.0.0
+
+[artnet art]
+universe = 1
+dest = 10.2.2.255
+
[evdev in]
input = /dev/input/event14
@@ -19,3 +26,5 @@ in.EV_ABS.ABS_X > midi.cc0.0
in.EV_ABS.ABS_Y > midi.cc0.1
in.EV_ABS.ABS_X > sacn.1+2
in.EV_ABS.ABS_Y > sacn.3
+in.EV_ABS.ABS_X > art.1+2
+in.EV_ABS.ABS_Y > art.3
diff --git a/sacn.c b/sacn.c
index 417fc32..9a3202d 100644
--- a/sacn.c
+++ b/sacn.c
@@ -126,6 +126,7 @@ static int sacn_listener(char* host, char* port, uint8_t fd_flags){
global_cfg.fd[global_cfg.fds].flags = fd_flags;
global_cfg.fd[global_cfg.fds].universes = 0;
global_cfg.fd[global_cfg.fds].universe = NULL;
+ global_cfg.fd[global_cfg.fds].last_frame = NULL;
global_cfg.fds++;
return 0;
}
@@ -323,6 +324,55 @@ static channel* sacn_channel(instance* inst, char* spec){
return mm_channel(inst, chan_a, 1);
}
+static int sacn_transmit(instance* inst){
+ size_t u;
+ sacn_instance_data* data = (sacn_instance_data*) inst->impl;
+ sacn_data_pdu pdu = {
+ .root = {
+ .preamble_size = htobe16(0x10),
+ .postamble_size = 0,
+ .magic = { 0 }, //memcpy'd
+ .flags = htobe16(0x7000 | 0x026e),
+ .vector = htobe32(ROOT_E131_DATA),
+ .sender_cid = { 0 }, //memcpy'd
+ .frame_flags = htobe16(0x7000 | 0x0258),
+ .frame_vector = htobe32(FRAME_E131_DATA)
+ },
+ .data = {
+ .source_name = "", //memcpy'd
+ .priority = data->xmit_prio,
+ .sync_addr = 0,
+ .sequence = data->data.last_seq++,
+ .options = 0,
+ .universe = htobe16(data->uni),
+ .flags = htobe16(0x7000 | 0x0205),
+ .vector = DMP_SET_PROPERTY,
+ .format = 0xA1,
+ .startcode_offset = 0,
+ .address_increment = htobe16(1),
+ .channels = htobe16(513),
+ .data = { 0 } //memcpy'd
+ }
+ };
+
+ memcpy(pdu.root.magic, SACN_PDU_MAGIC, sizeof(pdu.root.magic));
+ memcpy(pdu.root.sender_cid, global_cfg.cid, sizeof(pdu.root.sender_cid));
+ memcpy(pdu.data.source_name, global_cfg.source_name, sizeof(pdu.data.source_name));
+ memcpy((((uint8_t*)pdu.data.data) + 1), data->data.out, 512);
+
+ if(sendto(global_cfg.fd[data->fd_index].fd, &pdu, sizeof(pdu), 0, (struct sockaddr*) &data->dest_addr, data->dest_len) < 0){
+ fprintf(stderr, "Failed to output sACN frame for instance %s: %s\n", inst->name, strerror(errno));
+ }
+
+ //update last transmit timestamp
+ for(u = 0; u < global_cfg.fd[data->fd_index].universes; u++){
+ if(global_cfg.fd[data->fd_index].universe[u] == data->uni){
+ global_cfg.fd[data->fd_index].last_frame[u] = mm_timestamp();
+ }
+ }
+ return 0;
+}
+
static int sacn_set(instance* inst, size_t num, channel** c, channel_value* v){
size_t u, mark = 0;
sacn_instance_data* data = (sacn_instance_data*) inst->impl;
@@ -358,42 +408,7 @@ static int sacn_set(instance* inst, size_t num, channel** c, channel_value* v){
//send packet if required
if(mark){
- sacn_data_pdu pdu = {
- .root = {
- .preamble_size = htobe16(0x10),
- .postamble_size = 0,
- .magic = { 0 }, //memcpy'd
- .flags = htobe16(0x7000 | 0x026e),
- .vector = htobe32(ROOT_E131_DATA),
- .sender_cid = { 0 }, //memcpy'd
- .frame_flags = htobe16(0x7000 | 0x0258),
- .frame_vector = htobe32(FRAME_E131_DATA)
- },
- .data = {
- .source_name = "", //memcpy'd
- .priority = data->xmit_prio,
- .sync_addr = 0,
- .sequence = data->data.last_seq++,
- .options = 0,
- .universe = htobe16(data->uni),
- .flags = htobe16(0x7000 | 0x0205),
- .vector = DMP_SET_PROPERTY,
- .format = 0xA1,
- .startcode_offset = 0,
- .address_increment = htobe16(1),
- .channels = htobe16(513),
- .data = { 0 } //memcpy'd
- }
- };
-
- memcpy(pdu.root.magic, SACN_PDU_MAGIC, sizeof(pdu.root.magic));
- memcpy(pdu.root.sender_cid, global_cfg.cid, sizeof(pdu.root.sender_cid));
- memcpy(pdu.data.source_name, global_cfg.source_name, sizeof(pdu.data.source_name));
- memcpy((((uint8_t*)pdu.data.data) + 1), data->data.out, 512);
-
- if(sendto(global_cfg.fd[data->fd_index].fd, &pdu, sizeof(pdu), 0, (struct sockaddr*) &data->dest_addr, data->dest_len) < 0){
- fprintf(stderr, "Failed to output sACN frame for instance %s: %s\n", inst->name, strerror(errno));
- }
+ sacn_transmit(inst);
}
return 0;
@@ -526,7 +541,8 @@ static void sacn_discovery(size_t fd){
}
static int sacn_handle(size_t num, managed_fd* fds){
- size_t u;
+ size_t u, c;
+ uint64_t timestamp = mm_timestamp();
ssize_t bytes_read;
char recv_buf[SACN_RECV_BUF];
instance* inst = NULL;
@@ -543,7 +559,21 @@ static int sacn_handle(size_t num, managed_fd* fds){
sacn_discovery(u);
}
}
- global_cfg.last_announce = mm_timestamp();
+ global_cfg.last_announce = timestamp;
+ }
+
+ //check for keepalive frames
+ for(u = 0; u < global_cfg.fds; u++){
+ for(c = 0; c < global_cfg.fd[u].universes; c++){
+ if(timestamp - global_cfg.fd[u].last_frame[c] >= SACN_KEEPALIVE_INTERVAL){
+ instance_id.fields.fd_index = u;
+ instance_id.fields.uni = global_cfg.fd[u].universe[c];
+ inst = mm_instance_find(BACKEND_NAME, instance_id.label);
+ if(inst){
+ sacn_transmit(inst);
+ }
+ }
+ }
}
//early exit
@@ -664,6 +694,12 @@ static int sacn_start(){
fprintf(stderr, "sACN backend registering %zu descriptors to core\n", global_cfg.fds);
for(u = 0; u < global_cfg.fds; u++){
+ //allocate memory for storing last frame transmission timestamp
+ global_cfg.fd[u].last_frame = calloc(global_cfg.fd[u].universes, sizeof(uint64_t));
+ if(!global_cfg.fd[u].last_frame){
+ fprintf(stderr, "Failed to allocate memory\n");
+ goto bail;
+ }
if(mm_manage_fd(global_cfg.fd[u].fd, BACKEND_NAME, 1, (void*) u)){
goto bail;
}
@@ -692,6 +728,7 @@ static int sacn_shutdown(){
for(p = 0; p < global_cfg.fds; p++){
close(global_cfg.fd[p].fd);
free(global_cfg.fd[p].universe);
+ free(global_cfg.fd[p].last_frame);
}
free(global_cfg.fd);
fprintf(stderr, "sACN backend shut down\n");
diff --git a/sacn.h b/sacn.h
index 74549ba..e7106f7 100644
--- a/sacn.h
+++ b/sacn.h
@@ -13,6 +13,7 @@ static int sacn_shutdown();
#define SACN_PORT "5568"
#define SACN_RECV_BUF 8192
+#define SACN_KEEPALIVE_INTERVAL 2000
#define SACN_DISCOVERY_TIMEOUT 9000
#define SACN_PDU_MAGIC "ASC-E1.17\0\0\0"
@@ -59,6 +60,7 @@ typedef struct /*_sacn_socket*/ {
uint8_t flags;
size_t universes;
uint16_t* universe;
+ uint64_t* last_frame;
} sacn_fd;
#pragma pack(push, 1)