From d8379dcb6d504b1c2dff677f4ef3b74ae159d2b9 Mon Sep 17 00:00:00 2001 From: Stefano D'Angelo Date: Mon, 15 Jan 2024 21:03:11 +0100 Subject: [PATCH] beginning of midi + fixes --- TODO | 1 + notes | 2 +- templates/lv2/data/manifest.ttl | 8 +++- templates/lv2/src/data.h | 2 + templates/lv2/src/lv2.c | 14 +++++++ templates/lv2/tibia-index.js | 58 +++++++++++++++++--------- templates/vst3/src/data.h | 72 ++++++++++++++++++++++++++------- templates/vst3/src/vst3.c | 28 ++++++------- test/lv2.json | 4 +- test/plugin.h | 11 ++++- test/product.json | 34 ++++++++++++++++ 11 files changed, 181 insertions(+), 53 deletions(-) diff --git a/TODO b/TODO index 3d27517..6ef456f 100644 --- a/TODO +++ b/TODO @@ -12,3 +12,4 @@ * #if > 0 -> dotjs * more mappings, lv2 port props * latency mechanism, see https://steinbergmedia.github.io/vst3_dev_portal/pages/Technical+Documentation/Workflow+Diagrams/Get+Latency+Call+Sequence.html, https://steinbergmedia.github.io/vst3_doc/vstinterfaces/classSteinberg_1_1Vst_1_1IAudioProcessor.html#af8884671ccefe68e0a86e72413a0fcf8, https://steinbergmedia.github.io/vst3_dev_portal/pages/Technical+Documentation/Workflow+Diagrams/Audio+Processor+Call+Sequence.html +* vst3 don't include/link libm if not needed, etc. (reduce to minimum reqs) diff --git a/notes b/notes index b030c1a..fefd908 100644 --- a/notes +++ b/notes @@ -86,7 +86,7 @@ product { LV2: manifest.ttl lv2:port units:unit map: "linear" vs "logarithmic" - VST3: + VST3: many places (requires libm) LV2: manifest.ttl lv2:port lv2:portProperty pprops:logarithmic } ] diff --git a/templates/lv2/data/manifest.ttl b/templates/lv2/data/manifest.ttl index 86b7f9a..c088520 100644 --- a/templates/lv2/data/manifest.ttl +++ b/templates/lv2/data/manifest.ttl @@ -54,7 +54,7 @@ lv2:portProperty lv2:integer ; lv2:portProperty lv2:reportsLatency ; {{??}} - a {{?p.type == "control"}}lv2:ControlPort{{??}}{{?p.cv}}lv2:CVPort{{??}}lv2:AudioPort{{?}}{{?}} , + a {{?p.type == "control"}}lv2:ControlPort{{??p.type == "midi"}}atom:AtomPort{{??}}{{?p.cv}}lv2:CVPort{{??}}lv2:AudioPort{{?}}{{?}} , {{?p.direction == "input"}}lv2:InputPort{{??}}lv2:OutputPort{{?}} ; lv2:name "{{=p.name}}" ; {{?"shortName" in p}} @@ -65,10 +65,16 @@ lv2:minimum {{=p.minimum.toExponential()}} ; lv2:maximum {{=p.maximum.toExponential()}} ; lv2:default {{=p.defaultValue.toExponential()}} ; +{{??p.type == "midi"}} + atom:bufferType atom:Sequence ; + atom:supports midi:MidiEvent ; {{?}} {{?p.sidechain}} lv2:portProperty lv2:isSideChain ; {{?}} +{{?p.control}} + lv2:designation lv2:control ; +{{?}} {{?p.toggled}} lv2:portProperty lv2:toggled ; {{?}} diff --git a/templates/lv2/src/data.h b/templates/lv2/src/data.h index ee7454b..6e59cec 100644 --- a/templates/lv2/src/data.h +++ b/templates/lv2/src/data.h @@ -2,6 +2,8 @@ #define DATA_PRODUCT_AUDIO_INPUT_CHANNELS_N {{=it.product.buses.filter(x => x.type == "audio" && x.direction == "input").reduce((s, x) => s += x.channels == "mono" ? 1 : 2, 0)}} #define DATA_PRODUCT_AUDIO_OUTPUT_CHANNELS_N {{=it.product.buses.filter(x => x.type == "audio" && x.direction == "output").reduce((s, x) => s += x.channels == "mono" ? 1 : 2, 0)}} +#define DATA_PRODUCT_MIDI_INPUTS_N {{=it.product.buses.filter(x => x.type == "midi" && x.direction == "input").length}} +#define DATA_PRODUCT_MIDI_OUTPUTS_N {{=it.product.buses.filter(x => x.type == "midi" && x.direction == "output").length}} #define DATA_PRODUCT_CONTROL_INPUTS_N {{=it.product.parameters.filter(x => x.direction == "input").length}} #define DATA_PRODUCT_CONTROL_OUTPUTS_N {{=it.product.parameters.filter(x => x.direction == "output").length}} diff --git a/templates/lv2/src/lv2.c b/templates/lv2/src/lv2.c index 89b8088..dbc3f7a 100644 --- a/templates/lv2/src/lv2.c +++ b/templates/lv2/src/lv2.c @@ -73,6 +73,20 @@ static void connect_port(LV2_Handle instance, uint32_t port, void * data_locatio } port -= DATA_PRODUCT_AUDIO_OUTPUT_CHANNELS_N; #endif +#if DATA_PRODUCT_MIDI_INPUTS_N > 0 + if (port < DATA_PRODUCT_MIDI_INPUTS_N) { + // TBD + return; + } + port -= DATA_PRODUCT_MIDI_INPUTS_N; +#endif +#if DATA_PRODUCT_MIDI_OUTPUTS_N > 0 + if (port < DATA_PRODUCT_MIDI_OUTPUTS_N) { + // TBD + return; + } + port -= DATA_PRODUCT_MIDI_OUTPUTS_N; +#endif #if (DATA_PRODUCT_CONTROL_INPUTS_N + DATA_PRODUCT_CONTROL_OUTPUTS_N) > 0 i->c[port] = data_location; #endif diff --git a/templates/lv2/tibia-index.js b/templates/lv2/tibia-index.js index e7c80ad..919b877 100644 --- a/templates/lv2/tibia-index.js +++ b/templates/lv2/tibia-index.js @@ -4,9 +4,11 @@ var sep = path.sep; module.exports = function (data, api) { data.tibia.lv2 = { prefixes: [ + { id: "atom", uri: "http://lv2plug.in/ns/ext/atom#" }, { id: "doap", uri: "http://usefulinc.com/ns/doap#" }, { id: "foaf", uri: "http://xmlns.com/foaf/0.1/" }, { id: "lv2", uri: "http://lv2plug.in/ns/lv2core#" }, + { id: "midi", uri: "http://lv2plug.in/ns/ext/midi#" }, { id: "pprops", uri: "http://lv2plug.in/ns/ext/port-props#" }, { id: "rdf", uri: "http://www.w3.org/1999/02/22-rdf-syntax-ns#" }, { id: "rdfs", uri: "http://www.w3.org/2000/01/rdf-schema#" }, @@ -56,33 +58,49 @@ 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 audioBuses = data.product.buses.filter(x => x.type == "audio"); + var ports = []; + var bi = 0; + for (; bi < audioBuses.length; bi++) { + var b = audioBuses[bi]; + if (b.channels == "mono") { + var e = { type: "audio", direction: b.direction, name: b.name, sidechain: b.sidechain, cv: b.cv, optional: b.optional }; + e.symbol = data.lv2.busSymbols[bi]; + ports.push(e); + } else { + var e = { type: "audio", direction: b.direction, name: b.name + " Left", shortName: b.shortName + " L", sidechain: b.sidechain, cv: b.cv }; + 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 }; + e.symbol = data.lv2.busSymbols[bi] + "_R"; + ports.push(e); + } + } + 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 midiBuses = data.product.buses.filter(x => x.type == "midi"); + var ports = []; + for (var i = 0; i < midiBuses.length; i++, bi++) { + var b = midiBuses[i]; + var e = { type: "midi", direction: b.direction, name: b.name, sidechain: b.sidechain, control: b.control, optional: b.optional }; + e.symbol = data.lv2.busSymbols[bi]; + ports.push(e); + } + 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++) { var p = data.product.parameters[i]; var e = Object.create(p); e.type = "control"; e.symbol = data.lv2.parameterSymbols[i]; e.paramIndex = i; - data.tibia.lv2.ports.push(e); + ports.push(e); } - - var audioBuses = data.product.buses.filter(x => x.type == "audio"); - for (var i = 0; i < audioBuses.length; i++) { - var b = audioBuses[i]; - if (b.channels == "mono") { - var e = { type: "audio", direction: b.direction, name: b.name, sidechain: b.sidechain, cv: b.cv }; - e.symbol = data.lv2.busSymbols[i]; - data.tibia.lv2.ports.push(e); - } else { - var e = { type: "audio", direction: b.direction, name: b.name + " Left", shortName: b.shortName + " L", sidechain: b.sidechain, cv: b.cv }; - e.symbol = data.lv2.busSymbols[i] + "_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 }; - e.symbol = data.lv2.busSymbols[i] + "_R"; - data.tibia.lv2.ports.push(e); - } - } - - data.tibia.lv2.ports.sort((a, b) => a.type != b.type ? (a.type == "audio" ? -1 : 1) : (a.direction != b.direction ? (a.direction == "input" ? -1 : 1) : 0)); + 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); api.generateFileFromTemplateFile(`data${sep}manifest.ttl`, `data${sep}manifest.ttl`, data); api.copyFile(`src${sep}lv2.c`, `src${sep}lv2.c`); diff --git a/templates/vst3/src/data.h b/templates/vst3/src/data.h index 2a4db33..dc34ab1 100644 --- a/templates/vst3/src/data.h +++ b/templates/vst3/src/data.h @@ -30,8 +30,8 @@ static const Steinberg_TUID dataControllerCID = SMTG_INLINE_UID(DATA_VST3_CONTRO #define DATA_PRODUCT_BUSES_AUDIO_INPUT_N {{=it.product.buses.filter(x => x.type == "audio" && x.direction == "input").length}} #define DATA_PRODUCT_BUSES_AUDIO_OUTPUT_N {{=it.product.buses.filter(x => x.type == "audio" && x.direction == "output").length}} -#define DATA_PRODUCT_BUSES_EVENT_INPUT_N {{=it.product.buses.filter(x => x.type == "event" && x.direction == "input").length}} -#define DATA_PRODUCT_BUSES_EVENT_OUTPUT_N {{=it.product.buses.filter(x => x.type == "event" && x.direction == "output").length}} +#define DATA_PRODUCT_BUSES_MIDI_INPUT_N {{=it.product.buses.filter(x => x.type == "midi" && x.direction == "input").length}} +#define DATA_PRODUCT_BUSES_MIDI_OUTPUT_N {{=it.product.buses.filter(x => x.type == "midi" && x.direction == "output").length}} #define DATA_PRODUCT_CHANNELS_AUDIO_INPUT_N {{=it.product.buses.filter(x => x.type == "audio" && x.direction == "input").reduce((a, x) => a + (x.channels == "mono" ? 1 : 2), 0)}} #define DATA_PRODUCT_CHANNELS_AUDIO_OUTPUT_N {{=it.product.buses.filter(x => x.type == "audio" && x.direction == "output").reduce((a, x) => a + (x.channels == "mono" ? 1 : 2), 0)}} @@ -66,30 +66,30 @@ static struct Steinberg_Vst_BusInfo busInfoAudioOutput[DATA_PRODUCT_BUSES_AUDIO_ }; #endif -#if DATA_PRODUCT_BUSES_EVENT_INPUT_N > 0 -static struct Steinberg_Vst_BusInfo busInfoEventInput[DATA_PRODUCT_BUSES_EVENT_INPUT_N] = { -{{~it.product.buses.filter(x => x.type == "event" && x.direction == "input") :b}} +#if DATA_PRODUCT_BUSES_MIDI_INPUT_N > 0 +static struct Steinberg_Vst_BusInfo busInfoMidiInput[DATA_PRODUCT_BUSES_MIDI_INPUT_N] = { +{{~it.product.buses.filter(x => x.type == "midi" && x.direction == "input") :b}} { /* .mediaType = */ Steinberg_Vst_MediaTypes_kEvent, /* .direction = */ Steinberg_Vst_BusDirections_kInput, - /* .channelCount = */ 1, + /* .channelCount = */ 16, /* .name = */ { {{~Array.from(b.name) :c}}0x{{=c.charCodeAt(0).toString(16)}}, {{~}}0 }, - /* .busType = */ Steinberg_Vst_BusTypes_kMain, + /* .busType = */ {{?b.sidechain}}Steinberg_Vst_BusTypes_kAux{{??}}Steinberg_Vst_BusTypes_kMain{{?}}, /* .flags = */ 0{{?!b.optional}} | Steinberg_Vst_BusInfo_BusFlags_kDefaultActive{{?}} }, {{~}} }; #endif -#if DATA_PRODUCT_BUSES_EVENT_OUTPUT_N > 0 -static struct Steinberg_Vst_BusInfo busInfoAudioInput[DATA_PRODUCT_BUSES_EVENT_OUTPUT_N] = { -{{~it.product.buses.filter(x => x.type == "event" && x.direction == "output") :b}} +#if DATA_PRODUCT_BUSES_MIDI_OUTPUT_N > 0 +static struct Steinberg_Vst_BusInfo busInfoMidiOutput[DATA_PRODUCT_BUSES_MIDI_OUTPUT_N] = { +{{~it.product.buses.filter(x => x.type == "midi" && x.direction == "output") :b}} { /* .mediaType = */ Steinberg_Vst_MediaTypes_kEvent, /* .direction = */ Steinberg_Vst_BusDirections_kOutput, - /* .channelCount = */ 1, + /* .channelCount = */ 16, /* .name = */ { {{~Array.from(b.name) :c}}0x{{=c.charCodeAt(0).toString(16)}}, {{~}}0 }, - /* .busType = */ Steinberg_Vst_BusTypes_kMain, + /* .busType = */ {{?b.sidechain}}Steinberg_Vst_BusTypes_kAux{{??}}Steinberg_Vst_BusTypes_kMain{{?}}, /* .flags = */ 0{{?!b.optional}} | Steinberg_Vst_BusInfo_BusFlags_kDefaultActive{{?}} }, {{~}} @@ -98,8 +98,8 @@ static struct Steinberg_Vst_BusInfo busInfoAudioInput[DATA_PRODUCT_BUSES_EVENT_O #define DATA_PRODUCT_PARAMETERS_N {{=it.product.parameters.filter(x => !x.isLatency).length}} -#if DATA_PRODUCT_PARAMETERS_N > 0 -static struct Steinberg_Vst_ParameterInfo parameterInfo[DATA_PRODUCT_PARAMETERS_N] = { +#if DATA_PRODUCT_PARAMETERS_N + DATA_PRODUCT_BUSES_MIDI_INPUT_N + DATA_PRODUCT_BUSES_MIDI_OUTPUT_N > 0 +static struct Steinberg_Vst_ParameterInfo parameterInfo[DATA_PRODUCT_PARAMETERS_N + 2 * (DATA_PRODUCT_BUSES_MIDI_INPUT_N + DATA_PRODUCT_BUSES_MIDI_OUTPUT_N)] = { {{~it.product.parameters.filter(x => !x.isLatency) :p:i}} { /* .id = */ {{=i}}, @@ -122,6 +122,50 @@ 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 }, + /* .units = */ { 0 }, + /* .stepCount = */ 0, + /* .defaultNormalizedValue = */ 0.0, + /* .unitId = */ 0, + /* .flags = */ Steinberg_Vst_ParameterInfo_ParameterFlags_kIsHidden | Steinberg_Vst_ParameterInfo_ParameterFlags_kIsReadOnly + }, + { + /* .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 }, + /* .units = */ { 0 }, + /* .stepCount = */ 0, + /* .defaultNormalizedValue = */ 0.0, + /* .unitId = */ 0, + /* .flags = */ Steinberg_Vst_ParameterInfo_ParameterFlags_kIsHidden | Steinberg_Vst_ParameterInfo_ParameterFlags_kIsReadOnly + }, +{{~}} +{{~it.product.buses.filter(x => x.type == "midi" && x.direction == "output") :b:i}} + { + /* .id = */ {{=it.product.parameters.filter(x => !x.isLatency).length + 2 * it.product.buses.filter(x => x.type == "midi" && x.direction == "input").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 }, + /* .units = */ { 0 }, + /* .stepCount = */ 0, + /* .defaultNormalizedValue = */ 0.0, + /* .unitId = */ 0, + /* .flags = */ Steinberg_Vst_ParameterInfo_ParameterFlags_kIsHidden | Steinberg_Vst_ParameterInfo_ParameterFlags_kIsReadOnly + }, + { + /* .id = */ {{=it.product.parameters.filter(x => !x.isLatency).length + 2 * it.product.buses.filter(x => x.type == "midi" && x.direction == "input").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 }, + /* .units = */ { 0 }, + /* .stepCount = */ 0, + /* .defaultNormalizedValue = */ 0.0, + /* .unitId = */ 0, + /* .flags = */ Steinberg_Vst_ParameterInfo_ParameterFlags_kIsHidden | Steinberg_Vst_ParameterInfo_ParameterFlags_kIsReadOnly + }, +{{~}} }; # define DATA_PARAM_BYPASS 1 diff --git a/templates/vst3/src/vst3.c b/templates/vst3/src/vst3.c index 69c6535..bd84954 100644 --- a/templates/vst3/src/vst3.c +++ b/templates/vst3/src/vst3.c @@ -168,9 +168,9 @@ static Steinberg_int32 pluginGetBusCount(void *thisInterface, Steinberg_Vst_Medi return DATA_PRODUCT_BUSES_AUDIO_OUTPUT_N; } else if (type == Steinberg_Vst_MediaTypes_kEvent) { if (dir == Steinberg_Vst_BusDirections_kInput) - return DATA_PRODUCT_BUSES_EVENT_INPUT_N; + return DATA_PRODUCT_BUSES_MIDI_INPUT_N; else if (dir == Steinberg_Vst_BusDirections_kOutput) - return DATA_PRODUCT_BUSES_EVENT_OUTPUT_N; + return DATA_PRODUCT_BUSES_MIDI_OUTPUT_N; } return 0; } @@ -197,17 +197,17 @@ static Steinberg_tresult pluginGetBusInfo(void* thisInterface, Steinberg_Vst_Med } } else if (type == Steinberg_Vst_MediaTypes_kEvent) { if (dir == Steinberg_Vst_BusDirections_kInput) { -#if DATA_PRODUCT_BUSES_EVENT_INPUT_N > 0 - if (index >= DATA_PRODUCT_BUSES_AUDIO_INPUT_N) +#if DATA_PRODUCT_BUSES_MIDI_INPUT_N > 0 + if (index >= DATA_PRODUCT_BUSES_MIDI_INPUT_N) return Steinberg_kInvalidArgument; - *bus = busInfoEventInput[index]; + *bus = busInfoMidiInput[index]; return Steinberg_kResultTrue; #endif } else if (dir == Steinberg_Vst_BusDirections_kOutput) { -#if DATA_PRODUCT_BUSES_EVENT_OUTPUT_N > 0 - if (index >= DATA_PRODUCT_BUSES_AUDIO_OUTPUT_N) +#if DATA_PRODUCT_BUSES_MIDI_OUTPUT_N > 0 + if (index >= DATA_PRODUCT_BUSES_MIDI_OUTPUT_N) return Steinberg_kInvalidArgument; - *bus = busInfoEventOutput[index]; + *bus = busInfoMidiOutput[index]; return Steinberg_kResultTrue; #endif } @@ -240,14 +240,14 @@ static Steinberg_tresult pluginActivateBus(void* thisInterface, Steinberg_Vst_Me } } else if (type == Steinberg_Vst_MediaTypes_kEvent) { if (dir == Steinberg_Vst_BusDirections_kInput) { -#if DATA_PRODUCT_BUSES_EVENT_INPUT_N > 0 - if (index >= DATA_PRODUCT_BUSES_AUDIO_INPUT_N) +#if DATA_PRODUCT_BUSES_MIDI_INPUT_N > 0 + if (index >= DATA_PRODUCT_BUSES_MIDI_INPUT_N) return Steinberg_kInvalidArgument; return Steinberg_kResultTrue; #endif } else if (dir == Steinberg_Vst_BusDirections_kOutput) { -#if DATA_PRODUCT_BUSES_EVENT_OUTPUT_N > 0 - if (index >= DATA_PRODUCT_BUSES_AUDIO_OUTPUT_N) +#if DATA_PRODUCT_BUSES_MIDI_OUTPUT_N > 0 + if (index >= DATA_PRODUCT_BUSES_MIDI_OUTPUT_N) return Steinberg_kInvalidArgument; return Steinberg_kResultTrue; #endif @@ -704,12 +704,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; + return DATA_PRODUCT_PARAMETERS_N + 2 * (DATA_PRODUCT_BUSES_MIDI_INPUT_N + DATA_PRODUCT_BUSES_MIDI_OUTPUT_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) + if (paramIndex < 0 || paramIndex >= DATA_PRODUCT_PARAMETERS_N + 2 * (DATA_PRODUCT_BUSES_MIDI_INPUT_N + DATA_PRODUCT_BUSES_MIDI_OUTPUT_N)) return Steinberg_kResultFalse; *info = parameterInfo[paramIndex]; return Steinberg_kResultTrue; diff --git a/test/lv2.json b/test/lv2.json index 7170e7c..32b585f 100644 --- a/test/lv2.json +++ b/test/lv2.json @@ -7,7 +7,7 @@ "project": "@example:project", "types": [ "@lv2:AmplifierPlugin" ], "version": "1.0", - "busSymbols": [ "input", "output" ], - "parameterSymbols": [ "gain", "enabled" ] + "busSymbols": [ "input", "output", "midi_in", "midi_out" ], + "parameterSymbols": [ "gain", "delay", "cutoff", "enabled" ] } } diff --git a/test/plugin.h b/test/plugin.h index b9176d2..cc1f9e4 100644 --- a/test/plugin.h +++ b/test/plugin.h @@ -7,10 +7,12 @@ typedef struct plugin { float gain; float delay; + float cutoff; char bypass; float * delay_line; size_t delay_line_cur; + float z1; } plugin; static void plugin_init(plugin *instance) { @@ -35,6 +37,7 @@ static void plugin_mem_set(plugin *instance, void *mem) { static void plugin_reset(plugin *instance) { memset(instance->delay_line, 0, instance->delay_line_length * sizeof(float)); instance->delay_line_cur = 0; + instance->z1 = 0.f; } static void plugin_set_parameter(plugin *instance, size_t index, float value) { @@ -46,6 +49,9 @@ static void plugin_set_parameter(plugin *instance, size_t index, float value) { instance->delay = 0.001f * value; break; case 2: + instance->cutoff = value; + break; + case 3: instance->bypass = value >= 0.5f; break; } @@ -62,12 +68,15 @@ static size_t calc_index(size_t cur, size_t delay, size_t len) { static void plugin_process(plugin *instance, const float **inputs, float **outputs, size_t n_samples) { size_t delay = roundf(instance->sample_rate * instance->delay); + const float mA1 = instance->sample_rate / (instance->sample_rate + 6.283185307179586f * instance->cutoff); for (size_t i = 0; i < n_samples; i++) { instance->delay_line[instance->delay_line_cur] = inputs[0][i]; - const float y = instance->delay_line[calc_index(instance->delay_line_cur, delay, instance->delay_line_length)]; + const float x = instance->delay_line[calc_index(instance->delay_line_cur, delay, instance->delay_line_length)]; instance->delay_line_cur++; if (instance->delay_line_cur == instance->delay_line_length) instance->delay_line_cur = 0; + const float y = x + mA1 * (instance->z1 - x); + instance->z1 = y; outputs[0][i] = instance->bypass ? inputs[0][i] : instance->gain * y; } } diff --git a/test/product.json b/test/product.json index 1cfc4fc..2aea107 100644 --- a/test/product.json +++ b/test/product.json @@ -24,6 +24,24 @@ "sidechain": false, "cv": false, "optional": false + }, + { + "type": "midi", + "direction": "input", + "name": "MIDI input", + "shortName": "MIDI input", + "sidechain": true, + "control": true, + "optional": true + }, + { + "type": "midi", + "direction": "output", + "name": "MIDI output", + "shortName": "MIDI output", + "sidechain": true, + "control": true, + "optional": true } ], "parameters": [ @@ -64,6 +82,22 @@ "unit": "ms", "map": "linear" }, + { + "name": "Cutoff", + "shortName": "Cutoff", + "direction": "input", + "isBypass": false, + "isLatency": false, + "defaultValue": 1000.0, + "minimum": 20.0, + "maximum": 20e3, + "toggled": false, + "optional": false, + "integer": false, + "list": false, + "unit": "hz", + "map": "logarithmic" + }, { "name": "Bypass", "shortName": "Bypass",