aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorcbdev <cb@cbcdn.com>2021-01-08 23:03:11 +0100
committercbdev <cb@cbcdn.com>2021-01-08 23:03:11 +0100
commit00ba26c238a2e75c5b7d2e32469eae02179efde9 (patch)
tree594304fd9d745cd3ab9612635b66873a8c531393
parent41cb85a842a696e1183e1d55116c99b63099fde3 (diff)
downloadmidimonster-00ba26c238a2e75c5b7d2e32469eae02179efde9.tar.gz
midimonster-00ba26c238a2e75c5b7d2e32469eae02179efde9.tar.bz2
midimonster-00ba26c238a2e75c5b7d2e32469eae02179efde9.zip
Implement EPN reception for the jack backend
-rw-r--r--TODO1
-rw-r--r--backends/jack.c72
-rw-r--r--backends/jack.h10
-rw-r--r--backends/jack.md7
-rw-r--r--backends/winmidi.c2
5 files changed, 88 insertions, 4 deletions
diff --git a/TODO b/TODO
index 5662479..2ab5f10 100644
--- a/TODO
+++ b/TODO
@@ -5,6 +5,7 @@ make event collectors threadsafe to stop marshalling data...
collect & check backend API version
move all connection establishment to _start to be able to hot-stop/start all backends
event deduplication in core?
+move all typenames to _t
per-channel filters
* invert
diff --git a/backends/jack.c b/backends/jack.c
index a3caf73..176144f 100644
--- a/backends/jack.c
+++ b/backends/jack.c
@@ -101,6 +101,68 @@ static void mmjack_process_midiout(void* buffer, size_t sample_offset, uint8_t t
}
}
+//this state machine was copied more-or-less verbatim from the alsa midi implementation - fixes there will need to be integrated
+static void mmjack_handle_epn(mmjack_port* port, uint8_t chan, uint16_t control, uint16_t value){
+ mmjack_channel_ident ident = {
+ .label = 0
+ };
+
+ //switching between nrpn and rpn clears all valid bits
+ if(((port->epn_status[chan] & EPN_NRPN) && (control == 101 || control == 100))
+ || (!(port->epn_status[chan] & EPN_NRPN) && (control == 99 || control == 98))){
+ port->epn_status[chan] &= ~(EPN_NRPN | EPN_PARAMETER_LO | EPN_PARAMETER_HI);
+ }
+
+ //setting an address always invalidates the value valid bits
+ if(control >= 98 && control <= 101){
+ port->epn_status[chan] &= ~EPN_VALUE_HI;
+ }
+
+ //parameter hi
+ if(control == 101 || control == 99){
+ port->epn_control[chan] &= 0x7F;
+ port->epn_control[chan] |= value << 7;
+ port->epn_status[chan] |= EPN_PARAMETER_HI | ((control == 99) ? EPN_NRPN : 0);
+ if(control == 101 && value == 127){
+ port->epn_status[chan] &= ~EPN_PARAMETER_HI;
+ }
+ }
+
+ //parameter lo
+ if(control == 100 || control == 98){
+ port->epn_control[chan] &= ~0x7F;
+ port->epn_control[chan] |= value & 0x7F;
+ port->epn_status[chan] |= EPN_PARAMETER_LO | ((control == 98) ? EPN_NRPN : 0);
+ if(control == 100 && value == 127){
+ port->epn_status[chan] &= ~EPN_PARAMETER_LO;
+ }
+ }
+
+ //value hi, clears low, mark as update candidate
+ if(control == 6
+ //check if parameter is set before accepting value update
+ && ((port->epn_status[chan] & (EPN_PARAMETER_HI | EPN_PARAMETER_LO)) == (EPN_PARAMETER_HI | EPN_PARAMETER_LO))){
+ port->epn_value[chan] = value << 7;
+ port->epn_status[chan] |= EPN_VALUE_HI;
+ }
+
+ //value lo, flush the value
+ if(control == 38
+ && port->epn_status[chan] & EPN_VALUE_HI){
+ port->epn_value[chan] &= ~0x7F;
+ port->epn_value[chan] |= value & 0x7F;
+ port->epn_status[chan] &= ~EPN_VALUE_HI;
+
+ //find the updated channel
+ ident.fields.sub_type = port->epn_status[chan] & EPN_NRPN ? midi_nrpn : midi_rpn;
+ ident.fields.sub_channel = chan;
+ ident.fields.sub_control = port->epn_control[chan];
+
+ //ident.fields.port set on output in mmjack_handle_midi
+ mmjack_midiqueue_append(port, ident, port->epn_value[chan]);
+ }
+}
+
static int mmjack_process_midi(instance* inst, mmjack_port* port, size_t nframes, size_t* mark){
mmjack_instance_data* data = (mmjack_instance_data*) inst->impl;
void* buffer = jack_port_get_buffer(port->port, nframes);
@@ -113,7 +175,6 @@ static int mmjack_process_midi(instance* inst, mmjack_port* port, size_t nframes
if(port->input){
if(event_count){
DBGPF("Reading %u MIDI events from port %s", event_count, port->name);
- //TODO (n)rpn RX
for(u = 0; u < event_count; u++){
ident.label = 0;
//read midi data from stream
@@ -135,6 +196,15 @@ static int mmjack_process_midi(instance* inst, mmjack_port* port, size_t nframes
ident.fields.sub_control = 0;
value = event.buffer[1];
}
+
+ //forward the EPN CCs to the EPN state machine
+ if(ident.fields.sub_type == midi_cc
+ && ((ident.fields.sub_control <= 101 && ident.fields.sub_control >= 98)
+ || ident.fields.sub_control == 6
+ || ident.fields.sub_control == 38)){
+ mmjack_handle_epn(port, ident.fields.sub_channel, ident.fields.sub_control, value);
+ }
+
//append midi data
mmjack_midiqueue_append(port, ident, value);
}
diff --git a/backends/jack.h b/backends/jack.h
index ca62ea5..762282b 100644
--- a/backends/jack.h
+++ b/backends/jack.h
@@ -16,6 +16,11 @@ static int mmjack_shutdown(size_t n, instance** inst);
#define JACK_DEFAULT_SERVER_NAME "default"
#define JACK_MIDIQUEUE_CHUNK 10
+#define EPN_NRPN 8
+#define EPN_PARAMETER_HI 4
+#define EPN_PARAMETER_LO 2
+#define EPN_VALUE_HI 1
+
enum /*mmjack_midi_channel_type*/ {
midi_none = 0,
midi_note = 0x90,
@@ -59,10 +64,15 @@ typedef struct /*_mmjack_port_data*/ {
double min;
uint8_t mark;
double last;
+
size_t queue_len;
size_t queue_alloc;
mmjack_midiqueue* queue;
+ uint16_t epn_control[16];
+ uint16_t epn_value[16];
+ uint8_t epn_status[16];
+
pthread_mutex_t lock;
} mmjack_port;
diff --git a/backends/jack.md b/backends/jack.md
index 3d426f3..4ff77f6 100644
--- a/backends/jack.md
+++ b/backends/jack.md
@@ -83,8 +83,11 @@ by the MIDIMonster
#### Known bugs / problems
-Extended parameter numbers (`rpn` and `nrpn` control types) can currently only be transmitted, not properly
-received as such. Support for this functionality is planned.
+MIDI extended parameter numbers (EPNs, the `rpn` and `nrpn` control types) will also generate events on the controls (CC 101 through
+98, 38 and 6) that are used as the lower layer transport. When using EPNs, mapping those controls is probably not useful.
+
+EPN control types support only the full 14-bit transfer encoding, not the shorter variant transmitting only the 7
+high-order bits. This may be changed if there is sufficient interest in the functionality.
While JACK has rudimentary capabilities for transporting OSC messages, configuring and parsing such channels
with this backend would take a great amount of dedicated syntax & code. CV ports can provide fine-grained single
diff --git a/backends/winmidi.c b/backends/winmidi.c
index 66456e8..d12dc71 100644
--- a/backends/winmidi.c
+++ b/backends/winmidi.c
@@ -402,7 +402,7 @@ static void winmidi_handle_epn(instance* inst, uint8_t chan, uint16_t control, u
ident.fields.control = data->epn_control[chan];
val.normalised = (double) data->epn_value[chan] / 16383.0;
- winmidi_enqueue_input(inst, ident,val);
+ winmidi_enqueue_input(inst, ident, val);
}
}