diff options
author | cbdev <cb@cbcdn.com> | 2021-06-30 03:03:48 +0200 |
---|---|---|
committer | cbdev <cb@cbcdn.com> | 2021-06-30 03:03:48 +0200 |
commit | bc3d80e9e6c038c87a64432586670c663a23e53d (patch) | |
tree | 5a28b0004a7f3492455316f34bb2c783e670f944 /backends/winmidi.c | |
parent | 8a0a413f1dd5593189dd6b651babcff9b2495451 (diff) | |
parent | f16f7db86662fcdbf45b6373257c90c824b0b4b0 (diff) | |
download | midimonster-bc3d80e9e6c038c87a64432586670c663a23e53d.tar.gz midimonster-bc3d80e9e6c038c87a64432586670c663a23e53d.tar.bz2 midimonster-bc3d80e9e6c038c87a64432586670c663a23e53d.zip |
Merge branch 'master' into debian/master
Diffstat (limited to 'backends/winmidi.c')
-rw-r--r-- | backends/winmidi.c | 218 |
1 files changed, 177 insertions, 41 deletions
diff --git a/backends/winmidi.c b/backends/winmidi.c index 030062d..649af2e 100644 --- a/backends/winmidi.c +++ b/backends/winmidi.c @@ -74,7 +74,7 @@ static int winmidi_configure(char* option, char* value){ static int winmidi_configure_instance(instance* inst, char* option, char* value){ winmidi_instance_data* data = (winmidi_instance_data*) inst->impl; - if(!strcmp(option, "read")){ + if(!strcmp(option, "read") || !strcmp(option, "source")){ if(data->read){ LOGPF("Instance %s already connected to an input device", inst->name); return 1; @@ -82,7 +82,7 @@ static int winmidi_configure_instance(instance* inst, char* option, char* value) data->read = strdup(value); return 0; } - if(!strcmp(option, "write")){ + else if(!strcmp(option, "write") || !strcmp(option, "target")){ if(data->write){ LOGPF("Instance %s already connected to an output device", inst->name); return 1; @@ -90,6 +90,13 @@ static int winmidi_configure_instance(instance* inst, char* option, char* value) data->write = strdup(value); return 0; } + else if(!strcmp(option, "epn-tx")){ + data->epn_tx_short = 0; + if(!strcmp(value, "short")){ + data->epn_tx_short = 1; + } + return 0; + } LOGPF("Unknown instance configuration option %s on instance %s", option, inst->name); return 1; @@ -148,12 +155,23 @@ static channel* winmidi_channel(instance* inst, char* spec, uint8_t flags){ ident.fields.type = pressure; next_token += 8; } + else if(!strncmp(next_token, "rpn", 3)){ + ident.fields.type = rpn; + next_token += 3; + } + else if(!strncmp(next_token, "nrpn", 4)){ + ident.fields.type = nrpn; + next_token += 4; + } else if(!strncmp(next_token, "pitch", 5)){ ident.fields.type = pitchbend; } 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 %s", spec); return NULL; @@ -167,11 +185,7 @@ static channel* winmidi_channel(instance* inst, char* spec, uint8_t flags){ return NULL; } -static int winmidi_set(instance* inst, size_t num, channel** c, channel_value* v){ - winmidi_instance_data* data = (winmidi_instance_data*) inst->impl; - winmidi_channel_ident ident = { - .label = 0 - }; +static void winmidi_tx(HMIDIOUT port, uint8_t type, uint8_t channel, uint8_t control, uint16_t value){ union { struct { uint8_t status; @@ -183,6 +197,28 @@ static int winmidi_set(instance* inst, size_t num, channel** c, channel_value* v } output = { .dword = 0 }; + + output.components.status = type | channel; + output.components.data1 = control; + output.components.data2 = value & 0x7F; + + if(type == pitchbend){ + output.components.data1 = value & 0x7F; + output.components.data2 = (value >> 7) & 0x7F; + } + else if(type == aftertouch || type == program){ + output.components.data1 = value; + output.components.data2 = 0; + } + + midiOutShortMsg(port, output.dword); +} + +static int winmidi_set(instance* inst, size_t num, channel** c, channel_value* v){ + winmidi_instance_data* data = (winmidi_instance_data*) inst->impl; + winmidi_channel_ident ident = { + .label = 0 + }; size_t u; if(!data->device_out){ @@ -193,20 +229,29 @@ static int winmidi_set(instance* inst, size_t num, channel** c, channel_value* v for(u = 0; u < num; u++){ ident.label = c[u]->ident; - //build output message - output.components.status = ident.fields.type | ident.fields.channel; - output.components.data1 = ident.fields.control; - output.components.data2 = v[u].normalised * 127.0; - if(ident.fields.type == pitchbend){ - output.components.data1 = ((int)(v[u].normalised * 16384.0)) & 0x7F; - output.components.data2 = (((int)(v[u].normalised * 16384.0)) >> 7) & 0x7F; - } - else if(ident.fields.type == aftertouch){ - output.components.data1 = v[u].normalised * 127.0; - output.components.data2 = 0; + switch(ident.fields.type){ + case rpn: + case nrpn: + //transmit parameter number + winmidi_tx(data->device_out, cc, ident.fields.channel, (ident.fields.type == rpn) ? 101 : 99, (ident.fields.control >> 7) & 0x7F); + winmidi_tx(data->device_out, cc, ident.fields.channel, (ident.fields.type == rpn) ? 100 : 98, ident.fields.control & 0x7F); + + //transmit parameter value + winmidi_tx(data->device_out, cc, ident.fields.channel, 6, (((uint16_t) (v[u].normalised * 16383.0)) >> 7) & 0x7F); + winmidi_tx(data->device_out, cc, ident.fields.channel, 38, ((uint16_t) (v[u].normalised * 16383.0)) & 0x7F); + + if(!data->epn_tx_short){ + //clear active parameter + winmidi_tx(data->device_out, cc, ident.fields.channel, 101, 127); + winmidi_tx(data->device_out, cc, ident.fields.channel, 100, 127); + } + break; + case pitchbend: + winmidi_tx(data->device_out, ident.fields.type, ident.fields.channel, ident.fields.control, v[u].normalised * 16383.0); + break; + default: + winmidi_tx(data->device_out, ident.fields.type, ident.fields.channel, ident.fields.control, v[u].normalised * 127.0); } - - midiOutShortMsg(data->device_out, output.dword); } return 0; @@ -218,12 +263,18 @@ static char* winmidi_type_name(uint8_t typecode){ return "note"; case cc: return "cc"; + case rpn: + return "rpn"; + case nrpn: + return "nrpn"; case pressure: return "pressure"; case aftertouch: return "aftertouch"; case pitchbend: return "pitch"; + case program: + return "program"; } return "unknown"; } @@ -248,7 +299,8 @@ static int winmidi_handle(size_t num, managed_fd* fds){ if(backend_config.detect){ //pretty-print channel-wide events if(backend_config.event[u].channel.fields.type == pitchbend - || backend_config.event[u].channel.fields.type == aftertouch){ + || backend_config.event[u].channel.fields.type == aftertouch + || backend_config.event[u].channel.fields.type == program){ LOGPF("Incoming data on channel %s.ch%d.%s, value %f", backend_config.event[u].inst->name, backend_config.event[u].channel.fields.channel, @@ -275,11 +327,98 @@ static int winmidi_handle(size_t num, managed_fd* fds){ return 0; } -static void CALLBACK winmidi_input_callback(HMIDIIN device, unsigned message, DWORD_PTR inst, DWORD param1, DWORD param2){ +static int winmidi_enqueue_input(instance* inst, winmidi_channel_ident ident, channel_value val){ + EnterCriticalSection(&backend_config.push_events); + if(backend_config.events_alloc <= backend_config.events_active){ + backend_config.event = realloc((void*) backend_config.event, (backend_config.events_alloc + 1) * sizeof(winmidi_event)); + if(!backend_config.event){ + LOG("Failed to allocate memory"); + backend_config.events_alloc = 0; + backend_config.events_active = 0; + LeaveCriticalSection(&backend_config.push_events); + return 1; + } + backend_config.events_alloc++; + } + backend_config.event[backend_config.events_active].inst = inst; + backend_config.event[backend_config.events_active].channel.label = ident.label; + backend_config.event[backend_config.events_active].value = val; + backend_config.events_active++; + LeaveCriticalSection(&backend_config.push_events); + return 0; +} + +//this state machine was copied more-or-less verbatim from the alsa midi implementation - fixes there will need to be integrated +static void winmidi_handle_epn(instance* inst, uint8_t chan, uint16_t control, uint16_t value){ + winmidi_instance_data* data = (winmidi_instance_data*) inst->impl; winmidi_channel_ident ident = { .label = 0 }; channel_value val; + + //switching between nrpn and rpn clears all valid bits + if(((data->epn_status[chan] & EPN_NRPN) && (control == 101 || control == 100)) + || (!(data->epn_status[chan] & EPN_NRPN) && (control == 99 || control == 98))){ + data->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){ + data->epn_status[chan] &= ~EPN_VALUE_HI; + } + + //parameter hi + if(control == 101 || control == 99){ + data->epn_control[chan] &= 0x7F; + data->epn_control[chan] |= value << 7; + data->epn_status[chan] |= EPN_PARAMETER_HI | ((control == 99) ? EPN_NRPN : 0); + if(control == 101 && value == 127){ + data->epn_status[chan] &= ~EPN_PARAMETER_HI; + } + } + + //parameter lo + if(control == 100 || control == 98){ + data->epn_control[chan] &= ~0x7F; + data->epn_control[chan] |= value & 0x7F; + data->epn_status[chan] |= EPN_PARAMETER_LO | ((control == 98) ? EPN_NRPN : 0); + if(control == 100 && value == 127){ + data->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 + && ((data->epn_status[chan] & (EPN_PARAMETER_HI | EPN_PARAMETER_LO)) == (EPN_PARAMETER_HI | EPN_PARAMETER_LO))){ + data->epn_value[chan] = value << 7; + data->epn_status[chan] |= EPN_VALUE_HI; + } + + //value lo, flush the value + if(control == 38 + && data->epn_status[chan] & EPN_VALUE_HI){ + data->epn_value[chan] &= ~0x7F; + data->epn_value[chan] |= value & 0x7F; + data->epn_status[chan] &= ~EPN_VALUE_HI; + + //find the updated channel + ident.fields.type = data->epn_status[chan] & EPN_NRPN ? nrpn : rpn; + ident.fields.channel = chan; + ident.fields.control = data->epn_control[chan]; + val.normalised = (double) data->epn_value[chan] / 16383.0; + + winmidi_enqueue_input(inst, ident, val); + } +} + +static void CALLBACK winmidi_input_callback(HMIDIIN device, unsigned message, DWORD_PTR inst, DWORD param1, DWORD param2){ + winmidi_channel_ident ident = { + .label = 0 + }; + channel_value val = { + 0 + }; union { struct { uint8_t status; @@ -305,18 +444,22 @@ static void CALLBACK winmidi_input_callback(HMIDIIN device, unsigned message, DW ident.fields.type = input.components.status & 0xF0; ident.fields.control = input.components.data1; val.normalised = (double) input.components.data2 / 127.0; + val.raw.u64 = input.components.data2; if(ident.fields.type == 0x80){ ident.fields.type = note; val.normalised = 0; + val.raw.u64 = 0; } else if(ident.fields.type == pitchbend){ ident.fields.control = 0; - val.normalised = (double)((input.components.data2 << 7) | input.components.data1) / 16384.0; + val.normalised = (double) ((input.components.data2 << 7) | input.components.data1) / 16383.0; + val.raw.u64 = input.components.data2 << 7 | input.components.data1; } - else if(ident.fields.type == aftertouch){ + else if(ident.fields.type == aftertouch || ident.fields.type == program){ ident.fields.control = 0; val.normalised = (double) input.components.data1 / 127.0; + val.raw.u64 = input.components.data1; } break; case MIM_LONGDATA: @@ -332,26 +475,19 @@ static void CALLBACK winmidi_input_callback(HMIDIIN device, unsigned message, DW return; } + //pass changes in the (n)rpn CCs to the EPN state machine + if(ident.fields.type == cc + && ((ident.fields.control <= 101 && ident.fields.control >= 98) + || ident.fields.control == 6 + || ident.fields.control == 38)){ + winmidi_handle_epn((instance*) inst, ident.fields.channel, ident.fields.control, val.raw.u64); + } + DBGPF("Incoming message type %d channel %d control %d value %f", ident.fields.type, ident.fields.channel, ident.fields.control, val.normalised); - - EnterCriticalSection(&backend_config.push_events); - if(backend_config.events_alloc <= backend_config.events_active){ - backend_config.event = realloc((void*) backend_config.event, (backend_config.events_alloc + 1) * sizeof(winmidi_event)); - if(!backend_config.event){ - LOG("Failed to allocate memory"); - backend_config.events_alloc = 0; - backend_config.events_active = 0; - LeaveCriticalSection(&backend_config.push_events); - return; - } - backend_config.events_alloc++; + if(winmidi_enqueue_input((instance*) inst, ident, val)){ + LOG("Failed to enqueue incoming data"); } - backend_config.event[backend_config.events_active].inst = (instance*) inst; - backend_config.event[backend_config.events_active].channel.label = ident.label; - backend_config.event[backend_config.events_active].value = val; - backend_config.events_active++; - LeaveCriticalSection(&backend_config.push_events); if(message != MIM_MOREDATA){ //alert the main loop |