diff --git a/TODO b/TODO index 5c093ab..2cf9445 100644 --- a/TODO +++ b/TODO @@ -1,5 +1,3 @@ -* mod wheel - * recursive object merge in tibia * copyrights * dotjs error on undefined or JSON schema @@ -20,6 +18,8 @@ * whole midi properly (see https://github.com/harryhaaren/lv2/blob/master/lv2/lv2plug.in/ns/ext/midi/midi.h, https://www.midi.org/specifications-old/item/table-1-summary-of-midi-message, http://midi.teragonaudio.com/tech/midispec.htm) * LV2 (and raw midi): uneven midi pitch bend, what to do? * VST3: noteOn, noteOff tuning, what to do? (https://steinbergmedia.github.io/vst3_doc/vstinterfaces/structSteinberg_1_1Vst_1_1NoteOnEvent.html, https://steinbergmedia.github.io/vst3_doc/vstinterfaces/structSteinberg_1_1Vst_1_1NoteOffEvent.html) +* VST3: pitch bend/channel pressure channel, what to do? (16x2 parameters?) +* VST3: control change and program change * midi out * vst3 tail, web process return true/false * error checking etc. web, especially processor.js diff --git a/templates/lv2/src/lv2.c b/templates/lv2/src/lv2.c index 8452e39..0e2b3ba 100644 --- a/templates/lv2/src/lv2.c +++ b/templates/lv2/src/lv2.c @@ -204,38 +204,9 @@ static void run(LV2_Handle instance, uint32_t sample_count) { continue; LV2_ATOM_SEQUENCE_FOREACH(i->x_midi[j], ev) { if (ev->body.type == i->uri_midi_MidiEvent) { - const uint8_t * const msg = (const uint8_t *)(ev + 1); - switch (lv2_midi_message_type(msg)) { - case LV2_MIDI_MSG_NOTE_ON: - if (msg[2] == 0) - plugin_note_off(&i->p, midi_in_index[j], msg[1], 64.f / 127.f); - else - plugin_note_on(&i->p, midi_in_index[j], msg[1], (1.f / 127.f) * msg[2]); - break; - case LV2_MIDI_MSG_NOTE_OFF: - plugin_note_off(&i->p, midi_in_index[j], msg[1], (1.f / 127.f) * msg[2]); - break; - case LV2_MIDI_MSG_CONTROLLER: - switch (msg[1]) { - case LV2_MIDI_CTL_ALL_SOUNDS_OFF: - plugin_all_sounds_off(&i->p, midi_in_index[j]); - break; - case LV2_MIDI_CTL_ALL_NOTES_OFF: - plugin_all_notes_off(&i->p, midi_in_index[j]); - break; - default: - break; - } - break; - case LV2_MIDI_MSG_CHANNEL_PRESSURE: - plugin_channel_pressure(&i->p, midi_in_index[j], (1.f / 127.f) * msg[1]); - break; - case LV2_MIDI_MSG_BENDER: - plugin_pitch_bend_change(&i->p, midi_in_index[j], (1.f / 8192.f) * (msg[2] << 7 | msg[1]) - 1.f); - break; - default: - break; - } + const uint8_t * data = (const uint8_t *)(ev + 1); + if ((data[0] & 0xf0) != 0xf0) + plugin_midi_msg_in(&i->p, midi_in_index[j], data); } } } diff --git a/templates/lv2/tibia-index.js b/templates/lv2/tibia-index.js index 1f4fc48..150e19b 100644 --- a/templates/lv2/tibia-index.js +++ b/templates/lv2/tibia-index.js @@ -60,6 +60,36 @@ module.exports = function (data, api) { for (var id in data.lv2.prefixes) data.tibia.lv2.prefixes.push({ id: id, uri: data.lv2.prefixes[id] }); + var buses = data.product.buses; + var audioPorts = []; + var midiPorts = []; + for (var bi = 0; bi < buses.length; bi++) { + var b = buses[bi]; + if (b.type == "audio") { + if (b.channels == "mono") { + var e = { type: "audio", direction: b.direction, name: b.name, sidechain: b.sidechain, cv: b.cv, optional: b.optional, busIndex: bi }; + e.symbol = data.lv2.busSymbols[bi]; + audioPorts.push(e); + } else { + var e = { type: "audio", direction: b.direction, name: b.name + " Left", shortName: b.shortName + " L", sidechain: b.sidechain, cv: b.cv, busIndex: bi }; + e.symbol = data.lv2.busSymbols[bi] + "_L"; + data.tibia.lv2.ports.push(e); + var e = { type: "audio", direction: b.direction, name: b.name + " Right", shortName: b.shortName + " R", sidechain: b.sidechain, cv: b.cv, busIndex: bi }; + e.symbol = data.lv2.busSymbols[bi] + "_R"; + audioPorts.push(e); + } + } else { + var e = { type: "midi", direction: b.direction, name: b.name, sidechain: b.sidechain, control: b.control, optional: b.optional, busIndex: bi }; + e.symbol = data.lv2.busSymbols[bi]; + midiPorts.push(e); + } + } + audioPorts.sort((a, b) => a.direction != b.direction ? (a.direction == "input" ? -1 : 1) : 0); + midiPorts.sort((a, b) => a.direction != b.direction ? (a.direction == "input" ? -1 : 1) : 0); + data.tibia.lv2.ports.push.apply(data.tibia.lv2.ports, audioPorts); + data.tibia.lv2.ports.push.apply(data.tibia.lv2.ports, midiPorts); + + /* var audioBuses = data.product.buses.filter(x => x.type == "audio"); var ports = []; var bi = 0; @@ -91,6 +121,7 @@ module.exports = function (data, api) { } ports.sort((a, b) => a.direction != b.direction ? (a.direction == "input" ? -1 : 1) : 0); data.tibia.lv2.ports.push.apply(data.tibia.lv2.ports, ports); + */ var ports = []; for (var i = 0; i < data.product.parameters.length; i++) { diff --git a/templates/vst3/src/data.h b/templates/vst3/src/data.h index 1e9f1a0..3416467 100644 --- a/templates/vst3/src/data.h +++ b/templates/vst3/src/data.h @@ -96,10 +96,16 @@ static struct Steinberg_Vst_BusInfo busInfoMidiOutput[DATA_PRODUCT_BUSES_MIDI_OU }; #endif +#if DATA_PRODUCT_BUSES_MIDI_INPUT_N > 0 +static uint32_t midiInIndex[DATA_PRODUCT_BUSES_MIDI_INPUT_N] = { + {{~it.product.buses :b:i}}{{?b.type == "midi" && b.direction == "input"}}{{=i}}, {{?}}{{~}} +}; +#endif + #define DATA_PRODUCT_PARAMETERS_N {{=it.product.parameters.filter(x => !x.isLatency).length}} #if DATA_PRODUCT_PARAMETERS_N + DATA_PRODUCT_BUSES_MIDI_INPUT_N > 0 -static struct Steinberg_Vst_ParameterInfo parameterInfo[DATA_PRODUCT_PARAMETERS_N + 2 * DATA_PRODUCT_BUSES_MIDI_INPUT_N] = { +static struct Steinberg_Vst_ParameterInfo parameterInfo[DATA_PRODUCT_PARAMETERS_N + 3 * DATA_PRODUCT_BUSES_MIDI_INPUT_N] = { {{~it.product.parameters.filter(x => !x.isLatency) :p:i}} { /* .id = */ {{=i}}, @@ -124,24 +130,34 @@ static struct Steinberg_Vst_ParameterInfo parameterInfo[DATA_PRODUCT_PARAMETERS_ {{~}} {{~it.product.buses.filter(x => x.type == "midi" && x.direction == "input") :b:i}} { - /* .id = */ {{=it.product.parameters.filter(x => !x.isLatency).length + 2 * i}}, - /* .title = */ { {{~Array.from(b.name + " CP") :c}}0x{{=c.charCodeAt(0).toString(16)}}, {{~}}0 }, - /* .shortTitle = */ { {{~Array.from(b.shortName + " CP") :c}}0x{{=c.charCodeAt(0).toString(16)}}, {{~}}0 }, + /* .id = */ {{=it.product.parameters.filter(x => !x.isLatency).length + 3 * i}}, + /* .title = */ { {{~Array.from(b.name + " Channel Pressure") :c}}0x{{=c.charCodeAt(0).toString(16)}}, {{~}}0 }, + /* .shortTitle = */ { {{~Array.from(b.shortName + " Chan Pres") :c}}0x{{=c.charCodeAt(0).toString(16)}}, {{~}}0 }, /* .units = */ { 0 }, /* .stepCount = */ 0, /* .defaultNormalizedValue = */ 0.0, /* .unitId = */ 0, - /* .flags = */ Steinberg_Vst_ParameterInfo_ParameterFlags_kIsHidden | Steinberg_Vst_ParameterInfo_ParameterFlags_kIsReadOnly + /* .flags = */ Steinberg_Vst_ParameterInfo_ParameterFlags_kIsHidden | Steinberg_Vst_ParameterInfo_ParameterFlags_kCanAutomate }, { - /* .id = */ {{=it.product.parameters.filter(x => !x.isLatency).length + 2 * i + 1}}, - /* .title = */ { {{~Array.from(b.name + " PB") :c}}0x{{=c.charCodeAt(0).toString(16)}}, {{~}}0 }, - /* .shortTitle = */ { {{~Array.from(b.shortName + " PB") :c}}0x{{=c.charCodeAt(0).toString(16)}}, {{~}}0 }, + /* .id = */ {{=it.product.parameters.filter(x => !x.isLatency).length + 3 * i + 1}}, + /* .title = */ { {{~Array.from(b.name + " Pitch Bend") :c}}0x{{=c.charCodeAt(0).toString(16)}}, {{~}}0 }, + /* .shortTitle = */ { {{~Array.from(b.shortName + " Pitch Bend") :c}}0x{{=c.charCodeAt(0).toString(16)}}, {{~}}0 }, /* .units = */ { 0 }, /* .stepCount = */ 0, /* .defaultNormalizedValue = */ 0.5, /* .unitId = */ 0, - /* .flags = */ Steinberg_Vst_ParameterInfo_ParameterFlags_kIsHidden | Steinberg_Vst_ParameterInfo_ParameterFlags_kIsReadOnly + /* .flags = */ Steinberg_Vst_ParameterInfo_ParameterFlags_kIsHidden | Steinberg_Vst_ParameterInfo_ParameterFlags_kCanAutomate + }, + { + /* .id = */ {{=it.product.parameters.filter(x => !x.isLatency).length + 3 * i + 2}}, + /* .title = */ { {{~Array.from(b.name + " Mod Wheel") :c}}0x{{=c.charCodeAt(0).toString(16)}}, {{~}}0 }, + /* .shortTitle = */ { {{~Array.from(b.shortName + " Mod Wheel") :c}}0x{{=c.charCodeAt(0).toString(16)}}, {{~}}0 }, + /* .units = */ { 0 }, + /* .stepCount = */ 0, + /* .defaultNormalizedValue = */ 0.0, + /* .unitId = */ 0, + /* .flags = */ Steinberg_Vst_ParameterInfo_ParameterFlags_kIsHidden | Steinberg_Vst_ParameterInfo_ParameterFlags_kCanAutomate }, {{~}} }; diff --git a/templates/vst3/src/vst3.c b/templates/vst3/src/vst3.c index cd342f5..b5b7ed3 100644 --- a/templates/vst3/src/vst3.c +++ b/templates/vst3/src/vst3.c @@ -484,47 +484,72 @@ static Steinberg_tresult pluginSetProcessing(void* thisInterface, Steinberg_TBoo return Steinberg_kNotImplemented; } +static void processParams(pluginInstance *p, struct Steinberg_Vst_ProcessData *data, char before) { +#if DATA_PRODUCT_PARAMETERS_N + DATA_PRODUCT_BUSES_MIDI_INPUT_N > 0 + if (data->inputParameterChanges == NULL) + return; + Steinberg_int32 n = data->inputParameterChanges->lpVtbl->getParameterCount(data->inputParameterChanges); + for (Steinberg_int32 i = 0; i < n; i++) { + struct Steinberg_Vst_IParamValueQueue *q = data->inputParameterChanges->lpVtbl->getParameterData(data->inputParameterChanges, i); + if (q == NULL) + continue; + Steinberg_int32 c = q->lpVtbl->getPointCount(q); + if (c <= 0) + continue; + Steinberg_int32 o; + Steinberg_Vst_ParamValue v; + if (q->lpVtbl->getPoint(q, before ? 0 : c - 1, &o, &v) != Steinberg_kResultTrue) + continue; + if (before ? o != 0 : o <= 0) + continue; + Steinberg_Vst_ParamID id = q->lpVtbl->getParameterId(q); +# if DATA_PRODUCT_BUSES_MIDI_INPUT_N > 0 + if (id >= DATA_PRODUCT_PARAMETERS_N) { + size_t j = id - DATA_PRODUCT_PARAMETERS_N; + size_t index = j / 3; + uint8_t data[3]; + switch (j & 0x3) { + case 0: + // channel pressure + data[0] = 0xd0 /* | channel */; + data[1] = (uint8_t)(127.0 * v); + data[2] = 0; + break; + case 1: + { + // pitch bend change + uint16_t x = (uint16_t)(16382.0 * v) + 1; + data[0] = 0xe0 /* | channel */; + data[1] = (x >> 7) & 0x7f; + data[2] = x & 0x7f; + } + break; + case 2: + // mod wheel + data[0] = 0xb0 /* | channel */; + data[1] = 1; + data[2] = (uint8_t)(127.0 * v); + break; + } + plugin_midi_msg_in(&p->p, midiInIndex[index], data); + continue; + } +# endif + v = parameterAdjust(id, parameterMap(id, v)); + if (v != p->parameters[id]) { + p->parameters[id] = v; + plugin_set_parameter(&p->p, parameterData[id].index, p->parameters[id]); + } + } +#endif +} + static Steinberg_tresult pluginProcess(void* thisInterface, struct Steinberg_Vst_ProcessData* data) { TRACE("plugin IAudioProcessor process\n"); pluginInstance *p = (pluginInstance *)((char *)thisInterface - offsetof(pluginInstance, vtblIAudioProcessor)); -#if DATA_PRODUCT_PARAMETERS_N + DATA_PRODUCT_BUSES_MIDI_INPUT_N > 0 - if (data->inputParameterChanges != NULL) { - Steinberg_int32 n = data->inputParameterChanges->lpVtbl->getParameterCount(data->inputParameterChanges); - for (Steinberg_int32 i = 0; i < n; i++) { - struct Steinberg_Vst_IParamValueQueue *q = data->inputParameterChanges->lpVtbl->getParameterData(data->inputParameterChanges, i); - if (q == NULL) - continue; - Steinberg_int32 c = q->lpVtbl->getPointCount(q); - if (c <= 0) - continue; - Steinberg_int32 o; - Steinberg_Vst_ParamValue v; - if (q->lpVtbl->getPoint(q, 0, &o, &v) != Steinberg_kResultTrue) - continue; - if (o != 0) - continue; - Steinberg_Vst_ParamID id = q->lpVtbl->getParameterId(q); -# if DATA_PRODUCT_BUSES_MIDI_INPUT_N > 0 - if (id >= DATA_PRODUCT_PARAMETERS_N) { - size_t j = id - DATA_PRODUCT_PARAMETERS_N; - size_t index = j >> 1; - if (j & 0x1) - plugin_pitch_bend_change(&p->p, index, 2.0 * v - 1.0); - else - plugin_channel_pressure(&p->p, index, v); - continue; - } -# endif - v = parameterAdjust(id, parameterMap(id, v)); - if (v != p->parameters[id]) { - p->parameters[id] = v; - plugin_set_parameter(&p->p, parameterData[id].index, p->parameters[id]); - } - } - } -#endif + processParams(p, data, 1); #if DATA_PRODUCT_BUSES_MIDI_INPUT_N > 0 if (data->inputEvents != NULL) { @@ -533,16 +558,24 @@ static Steinberg_tresult pluginProcess(void* thisInterface, struct Steinberg_Vst struct Steinberg_Vst_Event ev; if (data->inputEvents->lpVtbl->getEvent(data->inputEvents, i, &ev) != Steinberg_kResultOk) continue; - size_t index = DATA_PRODUCT_BUSES_AUDIO_INPUT_N + DATA_PRODUCT_BUSES_AUDIO_OUTPUT_N + ev.busIndex; switch (ev.type) { case Steinberg_Vst_Event_EventTypes_kNoteOnEvent: - if (ev.Steinberg_Vst_Event_noteOn.velocity == 0) - plugin_note_off(&p->p, index, ev.Steinberg_Vst_Event_noteOn.pitch, 0.5f); - else - plugin_note_on(&p->p, index, ev.Steinberg_Vst_Event_noteOn.pitch, ev.Steinberg_Vst_Event_noteOn.velocity); + { + const uint8_t data[3] = { 0x90 | ev.Steinberg_Vst_Event_noteOn.channel, ev.Steinberg_Vst_Event_noteOn.pitch, (uint8_t)(127.f * ev.Steinberg_Vst_Event_noteOn.velocity) }; + plugin_midi_msg_in(&p->p, midiInIndex[ev.busIndex], data); + } break; case Steinberg_Vst_Event_EventTypes_kNoteOffEvent: - plugin_note_off(&p->p, index, ev.Steinberg_Vst_Event_noteOff.pitch, ev.Steinberg_Vst_Event_noteOff.velocity); + { + const uint8_t data[3] = { 0x90 | ev.Steinberg_Vst_Event_noteOff.channel, ev.Steinberg_Vst_Event_noteOff.pitch, (uint8_t)(127.f * ev.Steinberg_Vst_Event_noteOff.velocity) }; + plugin_midi_msg_in(&p->p, midiInIndex[ev.busIndex], data); + } + break; + case Steinberg_Vst_Event_EventTypes_kPolyPressureEvent: + { + const uint8_t data[3] = { 0xa0 | ev.Steinberg_Vst_Event_polyPressure.channel, ev.Steinberg_Vst_Event_polyPressure.pitch, (uint8_t)(127.f * ev.Steinberg_Vst_Event_polyPressure.pressure) }; + plugin_midi_msg_in(&p->p, midiInIndex[ev.busIndex], data); + } break; } } @@ -590,42 +623,9 @@ static Steinberg_tresult pluginProcess(void* thisInterface, struct Steinberg_Vst _MM_SET_DENORMALS_ZERO_MODE(denormals_zero_mode); #endif -#if DATA_PRODUCT_PARAMETERS_N + DATA_PRODUCT_BUSES_MIDI_INPUT_N > 0 - if (data->inputParameterChanges != NULL) { - Steinberg_int32 n = data->inputParameterChanges->lpVtbl->getParameterCount(data->inputParameterChanges); - for (Steinberg_int32 i = 0; i < n; i++) { - struct Steinberg_Vst_IParamValueQueue *q = data->inputParameterChanges->lpVtbl->getParameterData(data->inputParameterChanges, i); - if (q == NULL) - continue; - Steinberg_int32 c = q->lpVtbl->getPointCount(q); - if (c <= 0) - continue; - Steinberg_int32 o; - Steinberg_Vst_ParamValue v; - if (q->lpVtbl->getPoint(q, c - 1, &o, &v) != Steinberg_kResultTrue) - continue; - if (o <= 0) - continue; - Steinberg_Vst_ParamID id = q->lpVtbl->getParameterId(q); -# if DATA_PRODUCT_BUSES_MIDI_INPUT_N > 0 - if (id >= DATA_PRODUCT_PARAMETERS_N) { - size_t j = id - DATA_PRODUCT_PARAMETERS_N; - size_t index = j >> 1; - if (j & 0x1) - plugin_pitch_bend_change(&p->p, index, 2.0 * v - 1.0); - else - plugin_channel_pressure(&p->p, index, v); - continue; - } -# endif - v = parameterAdjust(id, parameterMap(id, v)); - if (v != p->parameters[id]) { - p->parameters[id] = v; - plugin_set_parameter(&p->p, parameterData[id].index, p->parameters[id]); - } - } - } + processParams(p, data, 0); +#if DATA_PRODUCT_PARAMETERS_N > 0 for (Steinberg_int32 i = 0; i < DATA_PRODUCT_PARAMETERS_N; i++) { if (!(parameterInfo[i].flags & Steinberg_Vst_ParameterInfo_ParameterFlags_kIsReadOnly)) continue; @@ -706,8 +706,8 @@ typedef struct controller { Steinberg_Vst_IMidiMappingVtbl * vtblIMidiMapping; Steinberg_uint32 refs; Steinberg_FUnknown * context; -#if DATA_PRODUCT_PARAMETERS_N > 0 - double parameters[DATA_PRODUCT_PARAMETERS_N]; +#if DATA_PRODUCT_PARAMETERS_N + DATA_PRODUCT_BUSES_MIDI_INPUT_N > 0 + double parameters[DATA_PRODUCT_PARAMETERS_N + 3 * DATA_PRODUCT_BUSES_MIDI_INPUT_N]; #endif struct Steinberg_Vst_IComponentHandler* componentHandler; } controller; @@ -773,8 +773,8 @@ static Steinberg_tresult controllerInitialize(void* thisInterface, struct Steinb if (c->context != NULL) return Steinberg_kResultFalse; c->context = context; -#if DATA_PRODUCT_PARAMETERS_N > 0 - for (int i = 0; i < DATA_PRODUCT_PARAMETERS_N; i++) +#if DATA_PRODUCT_PARAMETERS_N + DATA_PRODUCT_BUSES_MIDI_INPUT_N > 0 + for (int i = 0; i < DATA_PRODUCT_PARAMETERS_N + 3 * DATA_PRODUCT_BUSES_MIDI_INPUT_N; i++) c->parameters[i] = parameterData[i].def; #endif return Steinberg_kResultOk; @@ -822,12 +822,12 @@ static Steinberg_tresult controllerGetState(void* thisInterface, struct Steinber static Steinberg_int32 controllerGetParameterCount(void* thisInterface) { TRACE("controller get parameter count\n"); - return DATA_PRODUCT_PARAMETERS_N + 2 * DATA_PRODUCT_BUSES_MIDI_INPUT_N; + return DATA_PRODUCT_PARAMETERS_N + 3 * DATA_PRODUCT_BUSES_MIDI_INPUT_N; } static Steinberg_tresult controllerGetParameterInfo(void* thisInterface, Steinberg_int32 paramIndex, struct Steinberg_Vst_ParameterInfo* info) { TRACE("controller get parameter info\n"); - if (paramIndex < 0 || paramIndex >= DATA_PRODUCT_PARAMETERS_N + 2 * DATA_PRODUCT_BUSES_MIDI_INPUT_N) + if (paramIndex < 0 || paramIndex >= DATA_PRODUCT_PARAMETERS_N + 3 * DATA_PRODUCT_BUSES_MIDI_INPUT_N) return Steinberg_kResultFalse; *info = parameterInfo[paramIndex]; return Steinberg_kResultTrue; @@ -877,9 +877,9 @@ static void dToStr(double v, Steinberg_Vst_String128 s, int precision) { static Steinberg_tresult controllerGetParamStringByValue(void* thisInterface, Steinberg_Vst_ParamID id, Steinberg_Vst_ParamValue valueNormalized, Steinberg_Vst_String128 string) { TRACE("controller get param string by value\n"); - if (id >= DATA_PRODUCT_PARAMETERS_N) + if (id >= DATA_PRODUCT_PARAMETERS_N + 3 * DATA_PRODUCT_BUSES_MIDI_INPUT_N) return Steinberg_kResultFalse; - dToStr(parameterMap(id, valueNormalized), string, 2); + dToStr(id >= DATA_PRODUCT_PARAMETERS_N ? valueNormalized : parameterMap(id, valueNormalized), string, 2); return Steinberg_kResultTrue; } @@ -913,29 +913,29 @@ void TCharToD(Steinberg_Vst_TChar* s, double *v) { static Steinberg_tresult controllerGetParamValueByString(void* thisInterface, Steinberg_Vst_ParamID id, Steinberg_Vst_TChar* string, Steinberg_Vst_ParamValue* valueNormalized) { TRACE("controller get param value by string\n"); - if (id >= DATA_PRODUCT_PARAMETERS_N) + if (id >= DATA_PRODUCT_PARAMETERS_N + 3 * DATA_PRODUCT_BUSES_MIDI_INPUT_N) return Steinberg_kResultFalse; double v; TCharToD(string, &v); - *valueNormalized = parameterUnmap(id, v); + *valueNormalized = id >= DATA_PRODUCT_PARAMETERS_N ? v : parameterUnmap(id, v); return Steinberg_kResultTrue; } static Steinberg_Vst_ParamValue controllerNormalizedParamToPlain(void* thisInterface, Steinberg_Vst_ParamID id, Steinberg_Vst_ParamValue valueNormalized) { TRACE("controller normalized param to plain\n"); - return parameterMap(id, valueNormalized); + return id >= DATA_PRODUCT_PARAMETERS_N ? valueNormalized : parameterMap(id, valueNormalized); } static Steinberg_Vst_ParamValue controllerPlainParamToNormalized(void* thisInterface, Steinberg_Vst_ParamID id, Steinberg_Vst_ParamValue plainValue) { TRACE("controller plain param to normalized\n"); - return parameterUnmap(id, plainValue); + return id >= DATA_PRODUCT_PARAMETERS_N ? plainValue : parameterUnmap(id, plainValue); } static Steinberg_Vst_ParamValue controllerGetParamNormalized(void* thisInterface, Steinberg_Vst_ParamID id) { TRACE("controller get param normalized\n"); -#if DATA_PRODUCT_PARAMETERS_N > 0 +#if DATA_PRODUCT_PARAMETERS_N + DATA_PRODUCT_BUSES_MIDI_INPUT_N > 0 controller *c = (controller *)((char *)thisInterface - offsetof(controller, vtblIEditController)); - return parameterUnmap(id, c->parameters[id]); + return id >= DATA_PRODUCT_PARAMETERS_N ? c->parameters[id] : parameterUnmap(id, c->parameters[id]); #else return 0.0; #endif @@ -943,11 +943,11 @@ static Steinberg_Vst_ParamValue controllerGetParamNormalized(void* thisInterface static Steinberg_tresult controllerSetParamNormalized(void* thisInterface, Steinberg_Vst_ParamID id, Steinberg_Vst_ParamValue value) { TRACE("controller set param normalized\n"); -#if DATA_PRODUCT_PARAMETERS_N > 0 - if (id >= DATA_PRODUCT_PARAMETERS_N) +#if DATA_PRODUCT_PARAMETERS_N + DATA_PRODUCT_BUSES_MIDI_INPUT_N > 0 + if (id >= DATA_PRODUCT_PARAMETERS_N + 3 * DATA_PRODUCT_BUSES_MIDI_INPUT_N) return Steinberg_kResultFalse; controller *c = (controller *)((char *)thisInterface - offsetof(controller, vtblIEditController)); - c->parameters[id] = parameterAdjust(id, parameterMap(id, value)); + c->parameters[id] = id >= DATA_PRODUCT_PARAMETERS_N ? value : parameterAdjust(id, parameterMap(id, value)); return Steinberg_kResultTrue; #else return Steinberg_kResultFalse; @@ -1020,11 +1020,15 @@ static Steinberg_tresult controllerGetMidiControllerAssignment(void* thisInterfa return Steinberg_kInvalidArgument; switch (midiControllerNumber) { case Steinberg_Vst_ControllerNumbers_kAfterTouch: - *id = DATA_PRODUCT_PARAMETERS_N + 2 * busIndex; + *id = DATA_PRODUCT_PARAMETERS_N + 3 * busIndex; return Steinberg_kResultTrue; break; case Steinberg_Vst_ControllerNumbers_kPitchBend: - *id = DATA_PRODUCT_PARAMETERS_N + 2 * busIndex + 1; + *id = DATA_PRODUCT_PARAMETERS_N + 3 * busIndex + 1; + return Steinberg_kResultTrue; + break; + case Steinberg_Vst_ControllerNumbers_kCtrlModWheel: + *id = DATA_PRODUCT_PARAMETERS_N + 3 * busIndex + 2; return Steinberg_kResultTrue; break; default: diff --git a/templates/web-demo/src/index.html b/templates/web-demo/src/index.html index c1b4484..01c7cd0 100644 --- a/templates/web-demo/src/index.html +++ b/templates/web-demo/src/index.html @@ -164,46 +164,16 @@ window.addEventListener("load", function (e) { }; if (midi) { - function forwardMIDIMessage(msg) { + function onMIDIMessage(e) { + if ((e.data[0] & 0xf0) == 0xf0) + return; + var msg = { type: "midi", data: e.data }; for (var i = 0; i < demo.Module.data.product.buses.length; i++) { var b = demo.Module.data.product.buses[i]; if (b.type != "midi" || b.direction != "input") continue; - var m = {}; - for (p in msg) - m[p] = msg[p]; - m.index = i; - node.port.postMessage(m); - } - } - - function onMIDIMessage(e) { - switch (e.data[0] & 0xf0) { - case 0x90: - if (e.data[2] != 0) - forwardMIDIMessage({ type: "noteOn", note: e.data[1], velocity: e.data[2] / 127 }); - else - forwardMIDIMessage({ type: "noteOff", note: e.data[1], velocity: 64 / 127 }); - break; - case 0x80: - forwardMIDIMessage({ type: "noteOff", note: e.data[1], velocity: e.data[2] / 127 }); - break; - case 0xb0: - switch(e.data[1]) { - case 120: - forwardMIDIMessage({ type: "allSoundsOff" }); - break; - case 123: - forwardMIDIMessage({ type: "allNotesOff" }); - break; - } - break; - case 0xd0: - forwardMIDIMessage({ type: "channelPressure", value: e.data[1] / 127 }); - break; - case 0xe0: - forwardMIDIMessage({ type: "pitchBendChange", value: (e.data[2] << 7 | e.data[1]) / 8192 - 1 }); - break; + msg.index = i; + node.port.postMessage(msg); } } midi.inputs.forEach(x => { x.onmidimessage = onMIDIMessage; }); diff --git a/templates/web-make/Makefile b/templates/web-make/Makefile index d12cfc2..e64156c 100644 --- a/templates/web-make/Makefile +++ b/templates/web-make/Makefile @@ -19,13 +19,7 @@ LDFLAGS = \ -nostdlib ifeq (${HAS_MIDI_IN}, yes) -LDFLAGS += \ - -Wl,--export=processor_note_on \ - -Wl,--export=processor_note_off \ - -Wl,--export=processor_all_sounds_off \ - -Wl,--export=processor_all_notes_off \ - -Wl,--export=processor_channel_pressure \ - -Wl,--export=processor_pitch_bend_change +LDFLAGS += -Wl,--export=processor_midi_msg_in endif ALL = build/${BUNDLE_NAME}.wasm build/${BUNDLE_NAME}_processor.js build/${BUNDLE_NAME}.js diff --git a/templates/web/src/processor.c b/templates/web/src/processor.c index 87ebfb5..4122b83 100644 --- a/templates/web/src/processor.c +++ b/templates/web/src/processor.c @@ -125,27 +125,8 @@ void processor_process(instance *i, int32_t n_samples) { } #if DATA_PRODUCT_MIDI_INPUTS_N > 0 -void processor_note_on(instance *i, int32_t index, int32_t note, float velocity) { - plugin_note_on(&i->p, index, note, velocity); -} - -void processor_note_off(instance *i, int32_t index, int32_t note, float velocity) { - plugin_note_off(&i->p, index, note, velocity); -} - -void processor_all_sounds_off(instance *i, int32_t index) { - plugin_all_sounds_off(&i->p, index); -} - -void processor_all_notes_off(instance *i, int32_t index) { - plugin_all_notes_off(&i->p, index); -} - -void processor_channel_pressure(instance *i, int32_t index, float value) { - plugin_channel_pressure(&i->p, index, value); -} - -void processor_pitch_bend_change(instance *i, int32_t index, float value) { - plugin_pitch_bend_change(&i->p, index, value); +void processor_midi_msg_in(instance *i, int32_t index, uint8_t data0, uint8_t data1, uint8_t data2) { + uint8_t data[3] = { data0, data1, data2 }; + plugin_midi_msg_in(&i->p, index, data); } #endif diff --git a/templates/web/src/processor.js b/templates/web/src/processor.js index 4cc1c81..bb14d15 100644 --- a/templates/web/src/processor.js +++ b/templates/web/src/processor.js @@ -55,26 +55,8 @@ class Processor extends AudioWorkletProcessor { var self = this; this.port.onmessage = function (e) { - switch (e.data.type) { - case "noteOn": - self.module.processor_note_on(self.instance, e.data.index, e.data.note, e.data.velocity); - break; - case "noteOff": - self.module.processor_note_off(self.instance, e.data.index, e.data.note, e.data.velocity); - break; - case "allSoundsOff": - self.module.processor_all_sounds_off(self.instance, e.data.index); - break; - case "allNotesOff": - self.module.processor_all_notes_off(self.instance, e.data.index); - break; - case "channelPressure": - self.module.processor_channel_pressure(self.instance, e.data.index, e.data.value); - break; - case "pitchBendChange": - self.module.processor_pitch_bend_change(self.instance, e.data.index, e.data.value); - break; - } + if (e.data.type == "midi") + self.module.processor_midi_msg_in(self.instance, e.data.index, e.data.data[0], e.data.data[1], e.data.data[2]); }; } diff --git a/test/plugin.h b/test/plugin.h index cdb57ac..29c9cb8 100644 --- a/test/plugin.h +++ b/test/plugin.h @@ -89,38 +89,9 @@ static void plugin_process(plugin *instance, const float **inputs, float **outpu } } -static void plugin_note_on(plugin *instance, size_t index, uint8_t note, float velocity) { +static void plugin_midi_msg_in(plugin *instance, size_t index, const uint8_t * data) { (void)index; - (void)velocity; - //approx instance->cutoff_k = powf(2.f, (1.f / 12.f) * (note - 60)); - instance->cutoff_k = note < 64 ? (-0.19558034980097166f * note - 2.361735109225749f) / (note - 75.57552349522389f) : (393.95397927344214f - 7.660826245588588f * note) / (note - 139.0755234952239f); -} - -static void plugin_note_off(plugin *instance, size_t index, uint8_t note, float velocity) { - (void)instance; - (void)index; - (void)note; - (void)velocity; -} - -static void plugin_all_sounds_off(plugin *instance, size_t index) { - (void)instance; - (void)index; -} - -static void plugin_all_notes_off(plugin *instance, size_t index) { - (void)instance; - (void)index; -} - -static void plugin_channel_pressure(plugin *instance, size_t index, float value) { - (void)instance; - (void)index; - (void)value; -} - -static void plugin_pitch_bend_change(plugin *instance, size_t index, float value) { - (void)instance; - (void)index; - (void)value; + if ((data[0] & 0xf0) && (data[2] != 0)) + //approx instance->cutoff_k = powf(2.f, (1.f / 12.f) * (note - 60)); + instance->cutoff_k = data[1] < 64 ? (-0.19558034980097166f * data[1] - 2.361735109225749f) / (data[1] - 75.57552349522389f) : (393.95397927344214f - 7.660826245588588f * data[1]) / (data[1] - 139.0755234952239f); }