diff --git a/notes b/notes index bf81a43..5f13ff8 100644 --- a/notes +++ b/notes @@ -47,16 +47,16 @@ product { VST3: TBD LV2: manifest.ttl lv2:port, run() (set parameter) defaultValue: - default value, number, mapped, required for non-bypass input + default value, number, mapped, required for non-bypass VST3: ParameterInfo defaultNormalizedValue, controller initialize LV2: manifest.ttl lv2:port lv2:default, activate() (set initial parameter) minimum: - minimum value, number, mapped, required for non-bypass input + minimum value, number, mapped, required for non-bypass VST3: ParameterInfo stepCount, defaultNormalizedValue, controller get/set parameter (value clamped) LV2: manifest.ttl lv2:port lv2:minimum, run() (set parameter, value clamped) LV2: maximum: - maximum value, number, mapped, required for non-bypass input + maximum value, number, mapped, required for non-bypass VST3: ParameterInfo stepCount, defaultNormalizedValue, controller get/set parameter (value clamped) LV2: manifest.ttl lv2:port lv2:maximum, run() (set parameter, value clamped) toggled: diff --git a/templates/lv2/data/.manifest.ttl.swp b/templates/lv2/data/.manifest.ttl.swp deleted file mode 100644 index d8cb275..0000000 Binary files a/templates/lv2/data/.manifest.ttl.swp and /dev/null differ diff --git a/templates/lv2/src/.data.h.swp b/templates/lv2/src/.data.h.swp deleted file mode 100644 index c73b409..0000000 Binary files a/templates/lv2/src/.data.h.swp and /dev/null differ diff --git a/templates/lv2/src/.lv2.c.swp b/templates/lv2/src/.lv2.c.swp deleted file mode 100644 index 32f0bce..0000000 Binary files a/templates/lv2/src/.lv2.c.swp and /dev/null differ diff --git a/templates/lv2/src/data.h b/templates/lv2/src/data.h index ec84ceb..ee7454b 100644 --- a/templates/lv2/src/data.h +++ b/templates/lv2/src/data.h @@ -1,7 +1,7 @@ #define DATA_LV2_URI "{{=it.tibia.CGetUTF8StringLiteral(it.tibia.lv2.expandURI(it.lv2.uri))}}" -#define DATA_PRODUCT_AUDIO_INPUT_CHANNELS_N {{=it.product.buses.filter(x => x.type == "audio" && x.direction == "input").reduce((s, x) => s += x.channels, 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, 0)}} +#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_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 3bb2ca4..19086b6 100644 --- a/templates/lv2/src/lv2.c +++ b/templates/lv2/src/lv2.c @@ -4,6 +4,11 @@ #include "data.h" #include "plugin.h" +#if defined(__i386__) || defined(__x86_64__) +#include +#include +#endif + typedef struct { plugin p; #if DATA_PRODUCT_AUDIO_INPUT_CHANNELS_N > 0 @@ -104,8 +109,27 @@ static void run(LV2_Handle instance, uint32_t sample_count) { } #endif +#if defined(__aarch64__) + uint64_t fpcr; + __asm__ __volatile__ ("mrs %0, fpcr" : "=r"(fpcr)); + __asm__ __volatile__ ("msr fpcr, %0" :: "r"(fpcr | 0x1000000)); // enable FZ +#elif defined(__i386__) || defined(__x86_64__) + const unsigned int flush_zero_mode = _MM_GET_FLUSH_ZERO_MODE(); + const unsigned int denormals_zero_mode = _MM_GET_DENORMALS_ZERO_MODE(); + + _MM_SET_FLUSH_ZERO_MODE(_MM_FLUSH_ZERO_ON); + _MM_SET_DENORMALS_ZERO_MODE(_MM_DENORMALS_ZERO_ON); +#endif + plugin_process(&i->p, i->x, i->y, sample_count); +#if defined(__aarch64__) + __asm__ __volatile__ ("msr fpcr, %0" : : "r"(fpcr)); +#elif defined(__i386__) || defined(__x86_64__) + _MM_SET_FLUSH_ZERO_MODE(flush_zero_mode); + _MM_SET_DENORMALS_ZERO_MODE(denormals_zero_mode); +#endif + #if DATA_PRODUCT_CONTROL_OUTPUTS_N > 0 for (uint32_t j = 0; j < DATA_PRODUCT_CONTROL_OUTPUTS_N; j++) { uint32_t k = param_out_index[j]; diff --git a/templates/lv2/tibia-index.js b/templates/lv2/tibia-index.js index f888b34..039347d 100644 --- a/templates/lv2/tibia-index.js +++ b/templates/lv2/tibia-index.js @@ -67,16 +67,22 @@ module.exports = function (data, api) { var audioBuses = data.product.buses.filter(x => x.type == "audio"); for (var i = 0; i < audioBuses.length; i++) { var b = audioBuses[i]; - for (var j = 0; j < b.channels; j++) { + 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] + "_" + j; + 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)); - api.generateFileFromTemplateFile(`data${sep}manifest.ttl`, `data${sep}manifest.ttl`, data); api.copyFile(`src${sep}lv2.c`, `src${sep}lv2.c`); api.generateFileFromTemplateFile(`src${sep}data.h`, `src${sep}data.h`, data); diff --git a/templates/vst3/src/data.h b/templates/vst3/src/data.h index 6a161b5..f1bc137 100644 --- a/templates/vst3/src/data.h +++ b/templates/vst3/src/data.h @@ -39,10 +39,10 @@ static struct Steinberg_Vst_BusInfo busInfoAudioInput[DATA_PRODUCT_BUSES_AUDIO_I { /* .mediaType = */ Steinberg_Vst_MediaTypes_kAudio, /* .direction = */ Steinberg_Vst_BusDirections_kInput, - /* .channelCount = */ {{=b.channels}}, + /* .channelCount = */ {{=b.channels == "mono" ? 1 : 2}}, /* .name = */ { {{~Array.from(b.name) :c}}0x{{=c.charCodeAt(0).toString(16)}}, {{~}}0 }, - /* .busType = */ {{?b.aux}}Steinberg_Vst_BusTypes_kAux{{??}}Steinberg_Vst_BusTypes_kMain{{?}}, - /* .flags = */ {{?b.cv}}Steinberg_Vst_BusInfo_BusFlags_kIsControlVoltage{{??}}Steinberg_Vst_BusInfo_BusFlags_kDefaultActive{{?}} + /* .busType = */ {{?b.sidechain}}Steinberg_Vst_BusTypes_kAux{{??}}Steinberg_Vst_BusTypes_kMain{{?}}, + /* .flags = */ 0{{?b.cv}} | Steinberg_Vst_BusInfo_BusFlags_kIsControlVoltage{{?}}{{?!b.optional}} | Steinberg_Vst_BusInfo_BusFlags_kDefaultActive{{?}} }, {{~}} }; @@ -54,10 +54,10 @@ static struct Steinberg_Vst_BusInfo busInfoAudioOutput[DATA_PRODUCT_BUSES_AUDIO_ { /* .mediaType = */ Steinberg_Vst_MediaTypes_kAudio, /* .direction = */ Steinberg_Vst_BusDirections_kOutput, - /* .channelCount = */ {{=b.channels}}, + /* .channelCount = */ {{=b.channels == "mono" ? 1 : 2}}, /* .name = */ { {{~Array.from(b.name) :c}}0x{{=c.charCodeAt(0).toString(16)}}, {{~}}0 }, - /* .busType = */ {{?b.aux}}Steinberg_Vst_BusTypes_kAux{{??}}Steinberg_Vst_BusTypes_kMain{{?}}, - /* .flags = */ {{?b.cv}}Steinberg_Vst_BusInfo_BusFlags_kIsControlVoltage{{??}}Steinberg_Vst_BusInfo_BusFlags_kDefaultActive{{?}} + /* .busType = */ {{?b.sidechain}}Steinberg_Vst_BusTypes_kAux{{??}}Steinberg_Vst_BusTypes_kMain{{?}}, + /* .flags = */ 0{{?b.cv}} | Steinberg_Vst_BusInfo_BusFlags_kIsControlVoltage{{?}}{{?!b.optional}} | Steinberg_Vst_BusInfo_BusFlags_kDefaultActive{{?}} }, {{~}} }; @@ -69,10 +69,10 @@ static struct Steinberg_Vst_BusInfo busInfoEventInput[DATA_PRODUCT_BUSES_EVENT_I { /* .mediaType = */ Steinberg_Vst_MediaTypes_kEvent, /* .direction = */ Steinberg_Vst_BusDirections_kInput, - /* .channelCount = */ {{=b.channels}}, + /* .channelCount = */ 1, /* .name = */ { {{~Array.from(b.name) :c}}0x{{=c.charCodeAt(0).toString(16)}}, {{~}}0 }, /* .busType = */ Steinberg_Vst_BusTypes_kMain, - /* .flags = */ Steinberg_Vst_BusInfo_BusFlags_kDefaultActive + /* .flags = */ 0{{?!b.optional}} | Steinberg_Vst_BusInfo_BusFlags_kDefaultActive{{?}} }, {{~}} }; @@ -84,10 +84,10 @@ static struct Steinberg_Vst_BusInfo busInfoAudioInput[DATA_PRODUCT_BUSES_EVENT_O { /* .mediaType = */ Steinberg_Vst_MediaTypes_kEvent, /* .direction = */ Steinberg_Vst_BusDirections_kOutput, - /* .channelCount = */ {{=b.channels}}, + /* .channelCount = */ 1, /* .name = */ { {{~Array.from(b.name) :c}}0x{{=c.charCodeAt(0).toString(16)}}, {{~}}0 }, /* .busType = */ Steinberg_Vst_BusTypes_kMain, - /* .flags = */ Steinberg_Vst_BusInfo_BusFlags_kDefaultActive + /* .flags = */ 0{{?!b.optional}} | Steinberg_Vst_BusInfo_BusFlags_kDefaultActive{{?}} }, {{~}} }; diff --git a/templates/vst3/src/vst3.c b/templates/vst3/src/vst3.c index fc54380..706f321 100644 --- a/templates/vst3/src/vst3.c +++ b/templates/vst3/src/vst3.c @@ -16,6 +16,29 @@ # define TRACE(...) printf(__VA_ARGS__) #endif +#if defined(__i386__) || defined(__x86_64__) +#include +#include +#endif + +static double clamp(double x, double m, double M) { + return x < m ? m : (x > M ? M : x); +} + +static double parameterMap(Steinberg_Vst_ParamID id, double v) { + return parameterData[id].min + (parameterData[id].max - parameterData[id].min) * v; +} + +static double parameterUnmap(Steinberg_Vst_ParamID id, double v) { + return (v - parameterData[id].min) / (parameterData[id].max - parameterData[id].min); +} + +static double parameterAdjust(Steinberg_Vst_ParamID id, double v) { + v = parameterData[id].flags & (DATA_PARAM_BYPASS | DATA_PARAM_TOGGLED) ? v >= 0.5 ? 1.0 : 0.0 + : (parameterData[id].flags & DATA_PARAM_INTEGER ? (int32_t)(v + 0.5) : v); + return clamp(v, parameterData[id].min, parameterData[id].max); +} + typedef struct pluginInstance { Steinberg_Vst_IComponentVtbl * vtblIComponent; Steinberg_Vst_IAudioProcessorVtbl * vtblIAudioProcessor; @@ -93,7 +116,13 @@ static Steinberg_tresult pluginInitialize(void *thisInterface, struct Steinberg_ return Steinberg_kResultFalse; p->context = context; plugin_init(&p->p); - // TODO: set param defaults +#if DATA_PRODUCT_PARAMETERS_N > 0 + for (size_t i = 0; i < DATA_PRODUCT_PARAMETERS_N; i++) { + p->parameters[i] = parameterData[i].def; + if (!(parameterInfo[i].flags & Steinberg_Vst_ParameterInfo_ParameterFlags_kIsReadOnly)) + plugin_set_parameter(&p->p, parameterData[i].index, parameterData[i].def); + } +#endif return Steinberg_kResultOk; } @@ -189,14 +218,12 @@ static Steinberg_tresult pluginActivateBus(void* thisInterface, Steinberg_Vst_Me #if DATA_PRODUCT_BUSES_AUDIO_INPUT_N > 0 if (index >= DATA_PRODUCT_BUSES_AUDIO_INPUT_N) return Steinberg_kInvalidArgument; - // TBD return Steinberg_kResultTrue; #endif } else if (dir == Steinberg_Vst_BusDirections_kOutput) { #if DATA_PRODUCT_BUSES_AUDIO_OUTPUT_N > 0 if (index >= DATA_PRODUCT_BUSES_AUDIO_OUTPUT_N) return Steinberg_kInvalidArgument; - // TBD return Steinberg_kResultTrue; #endif } @@ -205,14 +232,12 @@ static Steinberg_tresult pluginActivateBus(void* thisInterface, Steinberg_Vst_Me #if DATA_PRODUCT_BUSES_EVENT_INPUT_N > 0 if (index >= DATA_PRODUCT_BUSES_AUDIO_INPUT_N) return Steinberg_kInvalidArgument; - // TBD 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) return Steinberg_kInvalidArgument; - // TBD return Steinberg_kResultTrue; #endif } @@ -225,10 +250,7 @@ static Steinberg_tresult pluginSetActive(void* thisInterface, Steinberg_TBool st pluginInstance *p = (pluginInstance *)thisInterface; if (state) { plugin_set_sample_rate(&p->p, p->sampleRate); - // TODO: mem plugin_reset(&p->p); - } else { - // TODO: mem } return Steinberg_kResultOk; } @@ -254,9 +276,8 @@ static Steinberg_tresult pluginSetState(void* thisInterface, struct Steinberg_I return Steinberg_kResultFalse; if (IS_BIG_ENDIAN) v.u = SWAP_UINT32(v.u); - p->parameters[i] = v.f; - //XXX - plugin_set_parameter(&p->p, i, v.f); + p->parameters[i] = parameterAdjust(i, v.f); + plugin_set_parameter(&p->p, parameterData[i].index, p->parameters[i]); } #endif return Steinberg_kResultOk; @@ -323,15 +344,46 @@ static Steinberg_uint32 pluginIAudioProcessorRelease(void *thisInterface) { static Steinberg_tresult pluginSetBusArrangements(void* thisInterface, Steinberg_Vst_SpeakerArrangement* inputs, Steinberg_int32 numIns, Steinberg_Vst_SpeakerArrangement* outputs, Steinberg_int32 numOuts) { TRACE("plugin IAudioProcessor set bus arrangements\n"); + if (numIns < 0 || numOuts < 0) + return Steinberg_kInvalidArgument; if (numIns != DATA_PRODUCT_BUSES_AUDIO_INPUT_N || numOuts != DATA_PRODUCT_BUSES_AUDIO_OUTPUT_N) return Steinberg_kResultFalse; + +#if DATA_PRODUCT_BUSES_AUDIO_INPUT_N > 0 + for (Steinberg_int32 i = 0; i < numIns; i++) + if ((busInfoAudioInput[i].channelCount == 1 && inputs[i] != Steinberg_Vst_SpeakerArr_kMono) + || (busInfoAudioInput[i].channelCount == 2 && inputs[i] != Steinberg_Vst_SpeakerArr_kStereo)) + return Steinberg_kResultFalse; +#endif + +#if DATA_PRODUCT_BUSES_AUDIO_OUTPUT_N > 0 + for (Steinberg_int32 i = 0; i < numOuts; i++) + if ((busInfoAudioOutput[i].channelCount == 1 && outputs[i] != Steinberg_Vst_SpeakerArr_kMono) + || (busInfoAudioOutput[i].channelCount == 2 && outputs[i] != Steinberg_Vst_SpeakerArr_kStereo)) + return Steinberg_kResultFalse; +#endif + return Steinberg_kResultTrue; } static Steinberg_tresult pluginGetBusArrangement(void* thisInterface, Steinberg_Vst_BusDirection dir, Steinberg_int32 index, Steinberg_Vst_SpeakerArrangement* arr) { TRACE("plugin IAudioProcessor get bus arrangement\n"); - //TBD! - return Steinberg_kNotImplemented; + +#if DATA_PRODUCT_BUSES_AUDIO_INPUT_N > 0 + if (dir == Steinberg_Vst_BusDirections_kInput && index >= 0 && index < DATA_PRODUCT_BUSES_AUDIO_INPUT_N) { + *arr = busInfoAudioInput[index].channelCount == 1 ? Steinberg_Vst_SpeakerArr_kMono : Steinberg_Vst_SpeakerArr_kStereo; + return Steinberg_kResultTrue; + } +#endif + +#if DATA_PRODUCT_BUSES_AUDIO_OUTPUT_N > 0 + if (dir == Steinberg_Vst_BusDirections_kOutput && index >= 0 && index < DATA_PRODUCT_BUSES_AUDIO_OUTPUT_N) { + *arr = busInfoAudioOutput[index].channelCount == 1 ? Steinberg_Vst_SpeakerArr_kMono : Steinberg_Vst_SpeakerArr_kStereo; + return Steinberg_kResultTrue; + } +#endif + + return Steinberg_kInvalidArgument; } static Steinberg_tresult pluginCanProcessSampleSize(void* thisInterface, Steinberg_int32 symbolicSampleSize) { @@ -359,8 +411,30 @@ static Steinberg_tresult pluginSetProcessing(void* thisInterface, Steinberg_TBoo static Steinberg_tresult pluginProcess(void* thisInterface, struct Steinberg_Vst_ProcessData* data) { TRACE("plugin IAudioProcessor process\n"); + +#if defined(__aarch64__) + uint64_t fpcr; + __asm__ __volatile__ ("mrs %0, fpcr" : "=r"(fpcr)); + __asm__ __volatile__ ("msr fpcr, %0" :: "r"(fpcr | 0x1000000)); // enable FZ +#elif defined(__i386__) || defined(__x86_64__) + const unsigned int flush_zero_mode = _MM_GET_FLUSH_ZERO_MODE(); + const unsigned int denormals_zero_mode = _MM_GET_DENORMALS_ZERO_MODE(); + + _MM_SET_FLUSH_ZERO_MODE(_MM_FLUSH_ZERO_ON); + _MM_SET_DENORMALS_ZERO_MODE(_MM_DENORMALS_ZERO_ON); +#endif + //TBD + +#if defined(__aarch64__) + __asm__ __volatile__ ("msr fpcr, %0" : : "r"(fpcr)); +#elif defined(__i386__) || defined(__x86_64__) + _MM_SET_FLUSH_ZERO_MODE(flush_zero_mode); + _MM_SET_DENORMALS_ZERO_MODE(denormals_zero_mode); +#endif + // IComponentHandler::restartComponent (kLatencyChanged), see https://steinbergmedia.github.io/vst3_dev_portal/pages/Technical+Documentation/Workflow+Diagrams/Get+Latency+Call+Sequence.html + return Steinberg_kResultOk; } @@ -482,24 +556,6 @@ static Steinberg_tresult controllerTerminate(void* thisInterface) { return Steinberg_kResultOk; } -static double clamp(double x, double m, double M) { - return x < m ? m : (x > M ? M : x); -} - -static double parameterMap(Steinberg_Vst_ParamID id, double v) { - return parameterData[id].min + (parameterData[id].max - parameterData[id].min) * v; -} - -static double parameterUnmap(Steinberg_Vst_ParamID id, double v) { - return (v - parameterData[id].min) / (parameterData[id].max - parameterData[id].min); -} - -static double parameterAdjust(Steinberg_Vst_ParamID id, double v) { - v = parameterData[id].flags & (DATA_PARAM_BYPASS | DATA_PARAM_TOGGLED) ? v >= 0.5 ? 1.0 : 0.0 - : (parameterData[id].flags & DATA_PARAM_INTEGER ? (int32_t)(v + 0.5) : v); - return clamp(v, parameterData[id].min, parameterData[id].max); -} - static Steinberg_tresult controllerSetComponentState(void* thisInterface, struct Steinberg_IBStream* state) { TRACE("controller set component state %p %p\n", thisInterface, (void *)state); if (state == NULL) diff --git a/test/product.json b/test/product.json index 40bab97..4128589 100644 --- a/test/product.json +++ b/test/product.json @@ -8,7 +8,7 @@ { "type": "audio", "direction": "input", - "channels": 1, + "channels": "mono", "name": "Input", "shortName": "Input", "sidechain": false, @@ -18,7 +18,7 @@ { "type": "audio", "direction": "output", - "channels": 1, + "channels": "mono", "name": "Output", "shortName": "Output", "sidechain": false,