reworked midi
This commit is contained in:
parent
1102d9f186
commit
4269699af4
4
TODO
4
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
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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++) {
|
||||
|
@ -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
|
||||
},
|
||||
{{~}}
|
||||
};
|
||||
|
@ -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:
|
||||
|
@ -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; });
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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]);
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user