From 8ff86335bc9f5233564a0f791174a9cc49ae2df4 Mon Sep 17 00:00:00 2001 From: cbdev Date: Sun, 10 Jan 2021 19:10:01 +0100 Subject: Implement program change control for the rtpmidi backend (#79) --- backends/rtpmidi.c | 23 +++++++++++++++-------- backends/rtpmidi.h | 3 ++- backends/rtpmidi.md | 4 +++- 3 files changed, 20 insertions(+), 10 deletions(-) diff --git a/backends/rtpmidi.c b/backends/rtpmidi.c index 7c5aa69..8d5525c 100644 --- a/backends/rtpmidi.c +++ b/backends/rtpmidi.c @@ -427,6 +427,8 @@ static char* rtpmidi_type_name(uint8_t type){ return "aftertouch"; case pitchbend: return "pitch"; + case program: + return "program"; } return "unknown"; } @@ -552,7 +554,7 @@ static int rtpmidi_peer_applecommand(instance* inst, size_t peer, uint8_t contro memcpy(&dest_addr, &(data->peer[peer].dest), min(sizeof(dest_addr), data->peer[peer].dest_len)); if(control){ - //calculate remote control port from data port + //calculate remote control port from data port ((struct sockaddr_in*) &dest_addr)->sin_port = htobe16(be16toh(((struct sockaddr_in*) &dest_addr)->sin_port) - 1); } @@ -715,6 +717,9 @@ static channel* rtpmidi_channel(instance* inst, char* spec, uint8_t flags){ else if(!strncmp(next_token, "aftertouch", 10)){ ident.fields.type = aftertouch; } + else if(!strncmp(next_token, "program", 7)){ + ident.fields.type = program; + } else{ LOGPF("Unknown control type in spec %s", spec); return NULL; @@ -761,11 +766,11 @@ static int rtpmidi_set(instance* inst, size_t num, channel** c, channel_value* v 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; + payload[2] = ((int)(v[u].normalised * 16383.0)) & 0x7F; + payload[3] = (((int)(v[u].normalised * 16383.0)) >> 7) & 0x7F; } - //channel-wide aftertouch is only 2 bytes - else if(ident.fields.type == aftertouch){ + //channel-wides aftertouch and program are only 2 bytes + else if(ident.fields.type == aftertouch || ident.fields.type == program){ payload[2] = payload[3]; payload -= 1; offset -= 1; @@ -996,7 +1001,7 @@ static int rtpmidi_parse(instance* inst, uint8_t* frame, size_t bytes){ ident.fields.channel = midi_status & 0x0F; //single byte command - if(ident.fields.type == aftertouch){ + if(ident.fields.type == aftertouch || ident.fields.type == program){ ident.fields.control = 0; val.normalised = (double) frame[offset] / 127.0; offset++; @@ -1010,7 +1015,7 @@ static int rtpmidi_parse(instance* inst, uint8_t* frame, size_t bytes){ if(ident.fields.type == pitchbend){ ident.fields.control = 0; - val.normalised = (double)((frame[offset] << 7) | frame[offset - 1]) / 16384.0; + val.normalised = (double)((frame[offset] << 7) | frame[offset - 1]) / 16383.0; } else{ ident.fields.control = frame[offset - 1]; @@ -1030,7 +1035,9 @@ static int rtpmidi_parse(instance* inst, uint8_t* frame, size_t bytes){ ident.fields.type, ident.fields.channel, ident.fields.control, val.normalised); if(cfg.detect){ - if(ident.fields.type == pitchbend || ident.fields.type == aftertouch){ + if(ident.fields.type == pitchbend + || ident.fields.type == aftertouch + || ident.fields.type == program){ LOGPF("Incoming data on channel %s.ch%d.%s, value %f", inst->name, ident.fields.channel, rtpmidi_type_name(ident.fields.type), val.normalised); diff --git a/backends/rtpmidi.h b/backends/rtpmidi.h index 7e6eccc..5f1621e 100644 --- a/backends/rtpmidi.h +++ b/backends/rtpmidi.h @@ -35,8 +35,9 @@ static int rtpmidi_shutdown(size_t n, instance** inst); enum /*_rtpmidi_channel_type*/ { none = 0, note = 0x90, - cc = 0xB0, pressure = 0xA0, + cc = 0xB0, + program = 0xC0, aftertouch = 0xD0, pitchbend = 0xE0 }; diff --git a/backends/rtpmidi.md b/backends/rtpmidi.md index 82548bf..9f56f3d 100644 --- a/backends/rtpmidi.md +++ b/backends/rtpmidi.md @@ -63,11 +63,12 @@ The `rtpmidi` backend supports mapping different MIDI events to MIDIMonster chan * `pressure` - Note pressure/aftertouch messages * `aftertouch` - Channel-wide aftertouch messages * `pitch` - Channel pitchbend messages +* `program` - Channel program change messages A MIDIMonster channel is specified using the syntax `channel.`. The shorthand `ch` may be used instead of the word `channel` (Note that `channel` here refers to the MIDI channel number). -The `pitch` and `aftertouch` events are channel-wide, thus they can be specified as `channel.`. +The `pitch`, `aftertouch` program messages/events are channel-wide, thus they can be specified as `channel.`. MIDI channels range from `0` to `15`. Each MIDI channel consists of 128 notes (numbered `0` through `127`), which additionally each have a pressure control, 128 CC's (numbered likewise), a channel pressure control (also called @@ -80,6 +81,7 @@ rmidi1.ch0.note9 > rmidi2.channel1.cc4 rmidi1.channel15.pressure1 > rmidi1.channel0.note0 rmidi1.ch1.aftertouch > rmidi2.ch2.cc0 rmidi1.ch0.pitch > rmidi2.ch1.pitch +rmidi2.ch15.note1 > rmidi2.ch2.program ``` #### Known bugs / problems -- cgit v1.2.3