aboutsummaryrefslogtreecommitdiffhomepage
path: root/backends
diff options
context:
space:
mode:
authorcbdev <cb@cbcdn.com>2019-12-13 22:43:22 +0100
committercbdev <cb@cbcdn.com>2019-12-13 22:43:22 +0100
commitd17023d77e90a8665965da50685dcf4a30f3eb37 (patch)
tree5f060f6cb28485d15361b452e761d13629f2c8f7 /backends
parent60adf2c4fe53e935e6de359ef1c01d0a91ab7480 (diff)
downloadmidimonster-d17023d77e90a8665965da50685dcf4a30f3eb37.tar.gz
midimonster-d17023d77e90a8665965da50685dcf4a30f3eb37.tar.bz2
midimonster-d17023d77e90a8665965da50685dcf4a30f3eb37.zip
Basic rtpmidi output
Diffstat (limited to 'backends')
-rw-r--r--backends/rtpmidi.c112
-rw-r--r--backends/rtpmidi.h7
2 files changed, 113 insertions, 6 deletions
diff --git a/backends/rtpmidi.c b/backends/rtpmidi.c
index 612ac6f..40e2cb2 100644
--- a/backends/rtpmidi.c
+++ b/backends/rtpmidi.c
@@ -338,13 +338,108 @@ static channel* rtpmidi_channel(instance* inst, char* spec, uint8_t flags){
}
static int rtpmidi_set(instance* inst, size_t num, channel** c, channel_value* v){
- //TODO
- return 1;
+ rtpmidi_instance_data* data = (rtpmidi_instance_data*) inst->impl;
+ uint8_t frame[RTPMIDI_PACKET_BUFFER] = "";
+ rtpmidi_header* rtp_header = (rtpmidi_header*) frame;
+ rtpmidi_command_header* command_header = (rtpmidi_command_header*) (frame + sizeof(rtpmidi_header));
+ size_t offset = sizeof(rtpmidi_header) + sizeof(rtpmidi_command_header), u = 0;
+ uint8_t* payload = frame + offset;
+ rtpmidi_channel_ident ident;
+
+ rtp_header->vpxccmpt = RTPMIDI_HEADER_MAGIC;
+ rtp_header->sequence = htobe16(data->sequence++);
+ rtp_header->timestamp = 0; //TODO calculate appropriate timestamps
+ rtp_header->ssrc = htobe32(data->ssrc);
+
+ //midi command section header
+ //TODO enable the journal bit here
+ command_header->flags = 0xA0; //extended length header, first entry in list has dtime
+
+ //midi list
+ for(u = 0; u < num; u++){
+ ident.label = c[u]->ident;
+
+ //encode timestamp
+ payload[0] = 0;
+
+ //encode midi command
+ payload[1] = ident.fields.type | ident.fields.channel;
+ payload[2] = ident.fields.control;
+ payload[3] = v[u].normalised * 127.0;
+
+ if(ident.fields.type == pitchbend){
+ payload[2] = ((int)(v[u].normalised * 16384.0)) & 0x7F;
+ payload[3] = (((int)(v[u].normalised * 16384.0)) >> 7) & 0x7F;
+ }
+ //channel-wide aftertouch is only 2 bytes
+ else if(ident.fields.type == aftertouch){
+ payload[2] = payload[3];
+ payload -= 1;
+ offset -= 1;
+ }
+
+ payload += 4;
+ offset += 4;
+ }
+
+ //update command section length
+ //FIXME this might overrun, might check the number of events at some point
+ command_header->flags |= (((offset - sizeof(rtpmidi_header) - sizeof(rtpmidi_command_header)) & 0x0F00) >> 8);
+ command_header->length = ((offset - sizeof(rtpmidi_header) - sizeof(rtpmidi_command_header)) & 0xFF);
+
+ //TODO journal section
+
+ for(u = 0; u < data->peers; u++){
+ sendto(data->fd, frame, offset, 0, (struct sockaddr*) &data->peer[u].dest, data->peer[u].dest_len);
+ }
+
+ return 0;
+}
+
+static int rtpmidi_handle_data(instance* inst){
+ size_t u;
+ rtpmidi_instance_data* data = (rtpmidi_instance_data*) inst->impl;
+ uint8_t frame[RTPMIDI_PACKET_BUFFER] = "";
+ struct sockaddr_storage sock_addr;
+ socklen_t sock_len = sizeof(sock_addr);
+ rtpmidi_header* rtp_header = (rtpmidi_header*) frame;
+ ssize_t bytes_recv = recvfrom(data->fd, frame, sizeof(frame), 0, (struct sockaddr*) &sock_addr, &sock_len);
+
+ //TODO receive until EAGAIN
+ //FIXME might want to filter data input from sources that are not registered peers
+ if(rtp_header->vpxccmpt != RTPMIDI_HEADER_MAGIC){
+ fprintf(stderr, "rtpmidi instance %s received frame with invalid header magic\n", inst->name);
+ return 0;
+ }
+
+ //try to learn peers
+ if(data->learn_peers){
+ for(u = 0; u < data->peers; u++){
+ if(data->peer[u].dest_len == sock_len
+ && !memcmp(&data->peer[u].dest, &sock_addr, sock_len)){
+ break;
+ }
+ }
+
+ if(u == data->peers){
+ fprintf(stderr, "rtpmidi instance %s learned new peer\n", inst->name);
+ return rtpmidi_push_peer(data, sock_addr, sock_len);
+ }
+ }
+ return 0;
+}
+
+static int rtpmidi_handle_control(instance* inst){
+ rtpmidi_instance_data* data = (rtpmidi_instance_data*) inst->impl;
+
+ return 0;
}
static int rtpmidi_handle(size_t num, managed_fd* fds){
size_t u;
int rv = 0;
+ instance* inst = NULL;
+ rtpmidi_instance_data* data = NULL;
//TODO handle mDNS discovery frames
@@ -357,7 +452,18 @@ static int rtpmidi_handle(size_t num, managed_fd* fds){
//TODO handle mDNS discovery input
}
else{
- //TODO handle rtp/control input
+ //handle rtp/control input
+ inst = (instance*) fds[u].impl;
+ data = (rtpmidi_instance_data*) inst->impl;
+ if(fds[u].fd == data->fd){
+ rv |= rtpmidi_handle_data(inst);
+ }
+ else if(fds[u].fd == data->control_fd){
+ rv |= rtpmidi_handle_control(inst);
+ }
+ else{
+ fprintf(stderr, "rtpmidi signaled descriptor not recognized\n");
+ }
}
}
diff --git a/backends/rtpmidi.h b/backends/rtpmidi.h
index ddb7bed..b56000a 100644
--- a/backends/rtpmidi.h
+++ b/backends/rtpmidi.h
@@ -13,7 +13,7 @@ static int rtpmidi_handle(size_t num, managed_fd* fds);
static int rtpmidi_start(size_t n, instance** inst);
static int rtpmidi_shutdown(size_t n, instance** inst);
-#define RTPMIDI_RECV_BUF 4096
+#define RTPMIDI_PACKET_BUFFER 8192
#define RTPMIDI_DEFAULT_HOST "::"
#define RTPMIDI_MDNS_PORT "5353"
#define RTPMIDI_HEADER_MAGIC htobe16(0x80E1)
@@ -58,6 +58,7 @@ typedef struct /*_rtmidi_instance_data*/ {
size_t peers;
rtpmidi_peer* peer;
uint32_t ssrc;
+ uint16_t sequence;
//apple-midi config
char* session_name;
@@ -103,6 +104,6 @@ typedef struct /*_rtp_midi_header*/ {
typedef struct /*_rtp_midi_command*/ {
uint8_t flags;
- uint8_t additional_length;
-} rtpmidi_command;
+ uint8_t length;
+} rtpmidi_command_header;
#pragma pack(pop)