From 468826463b3f9f1e85c077b2f3bbf3d5d25bf744 Mon Sep 17 00:00:00 2001 From: Stefano D'Angelo Date: Thu, 11 Jan 2024 06:54:34 +0100 Subject: [PATCH] more vst + update nots --- TODO | 1 + notes | 63 +++++++++++++++++++++++++++ templates/lv2/data/.manifest.ttl.swp | Bin 0 -> 12288 bytes templates/lv2/src/.data.h.swp | Bin 0 -> 12288 bytes templates/lv2/src/.lv2.c.swp | Bin 0 -> 16384 bytes templates/vst3/src/vst3.c | 35 ++++++++++----- test/lv2.json | 2 +- test/plugin.h | 14 +++--- 8 files changed, 95 insertions(+), 20 deletions(-) create mode 100644 templates/lv2/data/.manifest.ttl.swp create mode 100644 templates/lv2/src/.data.h.swp create mode 100644 templates/lv2/src/.lv2.c.swp diff --git a/TODO b/TODO index 0281b58..3d27517 100644 --- a/TODO +++ b/TODO @@ -11,3 +11,4 @@ * lv2: i18n? * #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 diff --git a/notes b/notes index 982585d..bf81a43 100644 --- a/notes +++ b/notes @@ -23,4 +23,67 @@ product { bundleName: VST3: plugin folder name, plugin .dll name, Info.plist LV2: plugin folder name, plugin .dll name, manifest.ttl plugin lv2:binary + buses: ... + parameters: [ + { + name: + parameter name string, required + VST3: ParameterInfo title + LV2: manifest.ttl lv2:port lv2:name + shortName: + parameter short name string, required + VST3: ParameterInfo shortTitle + LV2: manifest.ttl lv2:port lv2:shortName + direction: + "input" or "output", required + VST3: ParameterInfo flags + LV2: manifest.ttl lv2:port a + isBypass: + parameter is bypass/enabled? boolean - lots of implications, default false + VST3: ParameterInfo, controller get/set parameter/state + LV2: manifest.ttl lv2:port, run() (set parameter) + isLatency: + parameter is latency output? boolean - lots of implications, default false + VST3: TBD + LV2: manifest.ttl lv2:port, run() (set parameter) + defaultValue: + default value, number, mapped, required for non-bypass input + 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 + 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 + VST3: ParameterInfo stepCount, defaultNormalizedValue, controller get/set parameter (value clamped) + LV2: manifest.ttl lv2:port lv2:maximum, run() (set parameter, value clamped) + toggled: + parameter is on/off? boolean, default false + VST3: ParameterInfo stepCount, controller set parameter/state + LV2: manifest.ttl lv2:port lv2:portProperty lv2:toggled, run() (set parameter) + optional: + parameter is optionally connected? boolean, default false + VST3: not used + LV2: manifest.ttl lv2:port lv2:portProperty lv2:connectionOptional + integer: + parameter values are integers? boolean, default false + VST3: ParameterInfo stepCount, controller set parameter/state + LV2: manifest.ttl lv2:port lv2:portProperty lv2:integer, run() (set parameter) + scalePoints: + { "label1": value1, "label2", value2, ... } + labeled values, default none + VST3: TBD + LV2: manifest.ttl lv2:port lv2:scalePoint + list: + parameter is a list (using scalePoints values)? default false + VST3: TBD + LV2: manifest.ttl lv2:port lv2:enumeration - run() (set parameter) TBD? + unit: + unit of measure (from predefined list, see tibia-index.js), default "" + VST3: ParameterInfo units + LV2: manifest.ttl lv2:port units:unit + } + ] } diff --git a/templates/lv2/data/.manifest.ttl.swp b/templates/lv2/data/.manifest.ttl.swp new file mode 100644 index 0000000000000000000000000000000000000000..d8cb2752b9c654fdae11de93ca912a673a73dc13 GIT binary patch literal 12288 zcmeI2O^6&t6vrz{qLUaE6-B+YBQCp`?U{*2Vw+@>tO*Ik?2?Z~*leI?x@NZ4{-QtD z9hcc$42UL3AO{a3qJoGw%_W}1gLnuYqGu1mgBKAIa}YuQuc~`KcJ||tQ=kTZJ6$!e zUcdUk*RMOI>(!a#uUUKT-3*_H8GG*YkHml5&$GX#7)w&ow(m{d|(m%Ne+Kk|4gl8U#uyW;YcS9~7GrbtpdO}zmJ zW?&RB3fzGLJJ}1nYgKaaZ21}M(MQhSfiJVvC}0#Y3K#{90!9I&fKk9GU=+C96iA~j z>;i(mCl7of|K76dJ0F@aqkvJsC}0#Y3K#{90!9I&fKk9GU=%P47zOS^1-Q%D7xyye zZ$|5{+xDKv@w?PO_f@9!C@b80+-3C8^&%hPX24{c`ZtrC5Pw*A+ z!K+{|cpB^ge?P$3E$|)q61)#0a2}ih4tNs$zJswF;5zseTm_fFn_wQ)!8CBd2-pVx z*v{Bb;2L-bw7>+|3w|y!b{WLr92fx>*aCjLALjw@f*CLkCcsnRF|ZZ%{-)GtK|u|Se6erTP2gz(iM~Q9QBYFN`i?7 zO{`k$;`PDIODPGFoeyRxEyLM>E3J;tm!zNhdQCpq$Xo_}=6bNExvJ>29SLjyeya@e z(m3?W2w0ysY5Vn1R{?UT21Oz0YE)UCM|q#*zEITyyJBo7Sv^5z2CdQRF01wu%1al~ zM7e8Iwjfy4Jz>yuLx*|+N>at?%2=mX1uKInOA#uCaGZ{(5eDoK7)?!OX>U$ly`>U| zez=0y*e|V^)VuGwQO1o$?Qe3X_XAlU%3K-dQ=JY>agJR~pFdYGT7B8m3!dm(C-7cp z&}es)J7m`@{dKO5HLHA+l;uAc$+60L7?cwIn-+g)+7*dx1-e<$&gr$(B7D_zdr~V` zT(!79HFI4eTNYba`KGp}yG`DeYWc9+e5L{&obGu8w$&lqX0x}GfyT3rhIb(y_ z9L|V1p?e})W99s8b^PVI=g--r`e3YLizU&Zn>6h0MQu8UyO_SZwUZ!>2b>g(&)J(S z*VU+QeTfiX9)Qj^daa?&^J#L@-^r) z(5IkJKp%rX0v&-~1^o!I{Q!z%{5FmgFC>5jkN^@u0!RP}{4WUPxpc&k=*z^IPVWh2 zCg&$jI5lbdUZ6sAC|t#Z!h~8^>BLmWKBtyNa|2JRz;knSV*?&LBH#lhJej=nBIQRa zSEQGGt*2AVH+>ecaYH(MqV*<)fwYEmpiGGYjf7O^tG1$vjSYQm5gXpxt@Ud5ey6?L z-|5*q?N+bTZrbgBZ@=H`+O2n}MW-Y$hIG9hy-ML^TAF2+CX8yucQkEOONWb0cml zuRifT$)ys^EflpLW@(PuWWEmcMp2O%HQA(^(hz!oMQ%Ih&!n2;w0Ms!*_ z@d@A6RnutA7mbmXoRLgDRQ}NQm@*6?dC~wta}6P(alc=Z>E*0~Llr~=1=UFM6kG_& z=Vv>eMWax;a-~{aX_6L1Vp3UcGOItM0%JU=2f(zM^Kshb;D(m{m0SL$$rRSwf=6So$e3z&KtE>tKNitVkyDHDCEnz z-QUw#^yYRRSi*7ZJS%e9U#~xtD=wHUS z7+~|DYWF)0BR4CC*%x4EkE?#~`o*pagd8;m6b0-k3m*tR@tJg>XZ$$+Vnl%`Li#!K E4{%aOYXATM literal 0 HcmV?d00001 diff --git a/templates/lv2/src/.lv2.c.swp b/templates/lv2/src/.lv2.c.swp new file mode 100644 index 0000000000000000000000000000000000000000..32f0bce58a9e8d9e380112fe4bd05bad8ad0a3b8 GIT binary patch literal 16384 zcmeI2UuYaf9LLA{r*3Pl+G?My)24(=a=A-V3eq%{q)FRgau<@jk{Z%wvp1J)bGLik z?j4tw+82>3_CYEX9|V2WA_^iX2o?HL1s|#w#8+#@2gOzp)EB?AJ9Bq?xl7U-QDFj~ z-0kdde>3x)-~8E`o9j6?I!>RC^%49%LdcGxH`uLBC&;f!LR^n!4cp;&R{Rro-N&r5 zGvqjn-BZ2$x~KQ`_M{Joo_k&x#*3`rxN*;#vy8aM@`aq?F*lwo_r=|!$v?}10bR}aA!ybE3hv!ECJw3(2v!5837Faw4_Ke)Y#kPF}xaDf4IZ~|!Hx<<&` zpbVx#0z36wqzyT*Y)TJO9DQq|4uzs)GS`)*N0}>8ivLSd>Kgb-3e!!eWP7Mo zNDN;P;lgbyLzI?%CAe&UOXd*RoWMq;zWhg(cX)H^hp}qLOpMRGnSexHe^2m{~k>S>Ux%d6tyHs!0q7EIO z7@V5o9e@B-a6)3H6vE-~pw=tT!sj8UnGAE$Wq&VqC_vDO%smf;Y!Y}YevlhICU zp^YOV>}ts!@@qeSt7UkWNz0Cvp~aFN89UaeA2sYu4h<~aL}|5qg(4i$M-bLU=9P*z zt)R>0(V%(A9;u)}5B~ACg^o= zSU=9J(yKx}#&5=O|B@{#Mje#88uI%ax>JVv;Z&8QHYoio4KAgT4RxpOZJ3@_#!u*) zHM&q=>g zjNkg9ql1aW$QU2TgSjRMLeaWQGc8K$&oaLEhUuY&^QQAd3){8Uz}IAz3RLd00*$^` zD1W1=U;MutP#7$2W@3&<12zk)x{-vNV8-LaPG>lvWWN{ViVW^rpVwp(pSmm_kbJeU zY?S3gAC>}EtEIr2gA+cmR9OEoudBz>UDTsR9M+XK)F)e*hr+h5M3uWPQDbDST%bnl zP#p4wl9+Oee4J{pwLBJ%b$9rxq&mx4e3}q*7lwL&#Ug@91d6}dl#^s_Ca-#s)2H-; zNXi>IjCc{(I>nr9M~jps^ycE&o|Tab6S!^6zLJk00(z`B!WgA_ODP;w0}>s0Gc{=}b7n=ySY0-~UtO<@oY!@psTOW2T(}lprCH@$ zn1(b6+CyemX_i-GFKC@rqh)=6K^yiHZ>hkrPLZ*OHB_5ro4Hbk(cM9S#};%}hdF=$ui%~h2H@}i@_hd~-s|52uYl8F2~@y1 zh=a}GCf@O{f)Bt&a2m{l=fH8W72FiQzvKSZpHe_6pcGIFCparameters[i] = v.f; + //XXX plugin_set_parameter(&p->p, i, v.f); } #endif @@ -360,6 +360,7 @@ static Steinberg_tresult pluginSetProcessing(void* thisInterface, Steinberg_TBoo static Steinberg_tresult pluginProcess(void* thisInterface, struct Steinberg_Vst_ProcessData* data) { TRACE("plugin IAudioProcessor process\n"); //TBD + // IComponentHandler::restartComponent (kLatencyChanged), see https://steinbergmedia.github.io/vst3_dev_portal/pages/Technical+Documentation/Workflow+Diagrams/Get+Latency+Call+Sequence.html return Steinberg_kResultOk; } @@ -481,6 +482,24 @@ 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) @@ -497,7 +516,7 @@ static Steinberg_tresult controllerSetComponentState(void* thisInterface, struct return Steinberg_kResultFalse; if (IS_BIG_ENDIAN) v.u = SWAP_UINT32(v.u); - c->parameters[i] = v.f; + c->parameters[i] = parameterAdjust(i, v.f); } #endif TRACE(" ok\n"); @@ -569,14 +588,6 @@ static void dToStr(double v, Steinberg_Vst_String128 s, int precision) { s[i] = '\0'; } -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 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) @@ -637,7 +648,7 @@ static Steinberg_Vst_ParamValue controllerGetParamNormalized(void* thisInterface TRACE("controller get param normalized\n"); #if DATA_PRODUCT_PARAMETERS_N > 0 controller *c = (controller *)thisInterface; - return parameterMap(id, c->parameters[id]); + return parameterUnmap(id, c->parameters[id]); #else return 0.0; #endif @@ -649,7 +660,7 @@ static Steinberg_tresult controllerSetParamNormalized(void* thisInterface, Stein if (id >= DATA_PRODUCT_PARAMETERS_N) return Steinberg_kResultFalse; controller *c = (controller *)thisInterface; - c->parameters[id] = parameterMap(id, value); + c->parameters[id] = parameterAdjust(id, parameterMap(id, value)); return Steinberg_kResultTrue; #else return Steinberg_kResultFalse; diff --git a/test/lv2.json b/test/lv2.json index 42006dd..7170e7c 100644 --- a/test/lv2.json +++ b/test/lv2.json @@ -8,6 +8,6 @@ "types": [ "@lv2:AmplifierPlugin" ], "version": "1.0", "busSymbols": [ "input", "output" ], - "parameterSymbols": [ "gain", "bypass" ] + "parameterSymbols": [ "gain", "enabled" ] } } diff --git a/test/plugin.h b/test/plugin.h index 6eb2b64..c2b953d 100644 --- a/test/plugin.h +++ b/test/plugin.h @@ -5,21 +5,21 @@ typedef struct plugin { char bypass; } plugin; -void plugin_init(plugin *instance) { +static void plugin_init(plugin *instance) { instance->gain = 1.f; instance->bypass = 0; } -void plugin_fini(plugin *instance) { +static void plugin_fini(plugin *instance) { } -void plugin_set_sample_rate(plugin *instance, float sample_rate) { +static void plugin_set_sample_rate(plugin *instance, float sample_rate) { } -void plugin_reset(plugin *instance) { +static void plugin_reset(plugin *instance) { } -void plugin_set_parameter(plugin *instance, size_t index, float value) { +static void plugin_set_parameter(plugin *instance, size_t index, float value) { switch (index) { case 0: instance->gain = value; @@ -30,12 +30,12 @@ void plugin_set_parameter(plugin *instance, size_t index, float value) { } } -float plugin_get_parameter(plugin *instance, size_t index) { +static float plugin_get_parameter(plugin *instance, size_t index) { // no output parameters return 0.f; } -void plugin_process(plugin *instance, const float **inputs, float **outputs, size_t n_samples) { +static void plugin_process(plugin *instance, const float **inputs, float **outputs, size_t n_samples) { const float g = instance->bypass ? 1.f : powf(10.f, 0.05f * instance->gain); for (size_t i = 0; i < n_samples; i++) outputs[0][i] = g * inputs[0][i];