reworked midi

This commit is contained in:
Stefano D'Angelo 2024-01-20 11:18:40 +01:00
parent 1102d9f186
commit 4269699af4
10 changed files with 177 additions and 257 deletions

4
TODO
View File

@ -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

View File

@ -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);
}
}
}

View File

@ -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++) {

View File

@ -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
},
{{~}}
};

View File

@ -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:

View File

@ -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; });

View File

@ -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

View File

@ -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

View File

@ -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]);
};
}

View File

@ -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);
}