beginning of midi + fixes

This commit is contained in:
Stefano D'Angelo 2024-01-15 21:03:11 +01:00
parent 85e2c1ef14
commit d8379dcb6d
11 changed files with 181 additions and 53 deletions

1
TODO
View File

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

2
notes
View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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" ]
}
}

View File

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

View File

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