vst3 split internal parameter data and possibly fixed a bunch of issues

This commit is contained in:
Stefano D'Angelo 2025-01-16 20:58:23 +01:00
parent 50f5b4b378
commit 84888a6433
3 changed files with 293 additions and 98 deletions

View File

@ -192,7 +192,7 @@ static struct Steinberg_Vst_ParameterInfo parameterInfo[DATA_PRODUCT_PARAMETERS_
# define DATA_PARAM_INTEGER (1<<2) # define DATA_PARAM_INTEGER (1<<2)
# define DATA_PARAM_MAP_LOG (1<<3) # define DATA_PARAM_MAP_LOG (1<<3)
static struct { typedef struct {
size_t index; size_t index;
double min; double min;
double max; double max;
@ -200,8 +200,11 @@ static struct {
uint32_t flags; uint32_t flags;
double mapK; double mapK;
// scalePoints? // scalePoints?
} parameterData[DATA_PRODUCT_PARAMETERS_N] = { } ParameterData;
{{~it.product.parameters.filter(x => !x.isLatency) :p:i}}
# if DATA_PRODUCT_PARAMETERS_IN_N > 0
static ParameterData parameterInData[DATA_PRODUCT_PARAMETERS_IN_N] = {
{{~it.product.parameters.filter(x => x.direction == "input") :p:i}}
{ {
/* .index = */ {{=p.paramIndex}}, /* .index = */ {{=p.paramIndex}},
/* .min = */ {{=p.minimum.toExponential()}}, /* .min = */ {{=p.minimum.toExponential()}},
@ -212,6 +215,30 @@ static struct {
}, },
{{~}} {{~}}
}; };
# endif
# if DATA_PRODUCT_PARAMETERS_OUT_N > 0
static ParameterData parameterOutData[DATA_PRODUCT_PARAMETERS_OUT_N] = {
{{~it.product.parameters.filter(x => !x.isLatency && x.direction == "output") :p:i}}
{
/* .index = */ {{=p.paramIndex}},
/* .min = */ {{=p.minimum.toExponential()}},
/* .max = */ {{=p.maximum.toExponential()}},
/* .def = */ {{=p.defaultValue.toExponential()}},
/* .flags = */ 0{{?p.toggled}} | DATA_PARAM_TOGGLED{{?}}{{?p.integer}} | DATA_PARAM_INTEGER{{?}}{{?p.map == "logarithmic"}} | DATA_PARAM_MAP_LOG{{?}},
/* .mapK = */ {{?p.map == "logarithmic"}}{{=Number(2.0 * Math.log(Math.sqrt(p.maximum * p.minimum) / Math.abs(p.minimum))).toExponential()}}{{??}}0.0{{?}}
},
{{~}}
};
static size_t parameterOutDataToInfoIndex[DATA_PRODUCT_PARAMETERS_OUT_N] = {
{{=it.product.parameters.filter(x => !x.isLatency && x.direction == "output").map(x => x.paramInfoIndex).join(", ")}}
};
# endif
static size_t parameterInfoToDataIndex[DATA_PRODUCT_PARAMETERS_N] = {
{{=it.product.parameters.filter(x => !x.isLatency).map(x => x.paramDataIndex).join(", ")}}
};
# endif # endif
@ -230,11 +257,13 @@ static struct {
* Parameter indices/ids: * Parameter indices/ids:
* *
* parameterInfo.id: hash of parameter id (+ extra sometimes), univocally identifies parameter across plugin versions (a la lv2:symbol) * parameterInfo.id: hash of parameter id (+ extra sometimes), univocally identifies parameter across plugin versions (a la lv2:symbol)
* parameterGetIndexById(): returns parameterData's array index based on id (parameterInfo.id) * parameterGetIndexById(): returns parameterInfo array index based on id (parameterInfo.id)
* parameterData.index/p.paramIndex: Tibia parameter index, as used in plugin.h * parameterInfoToDataIndex: maps parameterInfo array indices to parameter(In/Out)Data indices
* latency out parameter is never added to parameterInfo and parameterData (specially handled) * parameterOutDataToInfoIndex: maps parameterOutData indices to parameterInfo array indices
* parameter(In/Out)Data.index/p.paramIndex: Tibia parameter index, as used in plugin.h
* latency out parameter is never added to parameterInfo and parameter(In/Out)Data (specially handled)
*/ */
{{?it.product.state && it.product.state.dspCustom}} {{?it.product.state && it.product.state.dspCustom}}
#define STATE_DSP_CUSTOM #define DATA_STATE_DSP_CUSTOM
{{?}} {{?}}

View File

@ -57,7 +57,6 @@
// https://github.com/rubberduck-vba/Rubberduck/wiki/COM-in-plain-C // https://github.com/rubberduck-vba/Rubberduck/wiki/COM-in-plain-C
// https://devblogs.microsoft.com/oldnewthing/20040205-00/?p=40733 // https://devblogs.microsoft.com/oldnewthing/20040205-00/?p=40733
//#define TIBIA_TRACE
#ifdef TIBIA_TRACE #ifdef TIBIA_TRACE
# define TRACE(...) printf(__VA_ARGS__); fflush(stdout); # define TRACE(...) printf(__VA_ARGS__); fflush(stdout);
#else #else
@ -160,6 +159,7 @@ static double clamp(double x, double m, double M) {
return x < m ? m : (x > M ? M : x); return x < m ? m : (x > M ? M : x);
} }
#if DATA_PRODUCT_PARAMETERS_N + DATA_PRODUCT_BUSES_MIDI_INPUT_N > 0
static int parameterGetIndexById(Steinberg_Vst_ParamID id) { static int parameterGetIndexById(Steinberg_Vst_ParamID id) {
for (int i = 0; i < DATA_PRODUCT_PARAMETERS_N + 3 * DATA_PRODUCT_BUSES_MIDI_INPUT_N; i++) for (int i = 0; i < DATA_PRODUCT_PARAMETERS_N + 3 * DATA_PRODUCT_BUSES_MIDI_INPUT_N; i++)
if (parameterInfo[i].id == id) if (parameterInfo[i].id == id)
@ -167,19 +167,67 @@ static int parameterGetIndexById(Steinberg_Vst_ParamID id) {
return -1; return -1;
} }
static double parameterMap(int i, double v) { # if DATA_PRODUCT_PARAMETERS_N > 0
return parameterData[i].flags & DATA_PARAM_MAP_LOG ? parameterData[i].min * exp(parameterData[i].mapK * v) : parameterData[i].min + (parameterData[i].max - parameterData[i].min) * v; static double parameterMap(const ParameterData *data, double v) {
return data->flags & DATA_PARAM_MAP_LOG ? data->min * exp(data->mapK * v) : data->min + (data->max - data->min) * v;
} }
static double parameterUnmap(int i, double v) { static double parameterUnmap(const ParameterData *data, double v) {
return parameterData[i].flags & DATA_PARAM_MAP_LOG ? log(v / parameterData[i].min) / parameterData[i].mapK : (v - parameterData[i].min) / (parameterData[i].max - parameterData[i].min); return data->flags & DATA_PARAM_MAP_LOG ? log(v / data->min) / data->mapK : (v - data->min) / (data->max - data->min);
} }
static double parameterAdjust(int i, double v) { static double parameterAdjust(const ParameterData *data, double v) {
v = parameterData[i].flags & (DATA_PARAM_BYPASS | DATA_PARAM_TOGGLED) ? (v >= 0.5 ? 1.0 : 0.0) v = data->flags & (DATA_PARAM_BYPASS | DATA_PARAM_TOGGLED) ? (v >= 0.5 ? 1.0 : 0.0)
: (parameterData[i].flags & DATA_PARAM_INTEGER ? (int32_t)(v + (v >= 0.0 ? 0.5 : -0.5)) : v); : (data->flags & DATA_PARAM_INTEGER ? (int32_t)(v + (v >= 0.0 ? 0.5 : -0.5)) : v);
return clamp(v, parameterData[i].min, parameterData[i].max); return clamp(v, data->min, data->max);
} }
# endif
static ParameterData *parameterGetDataByIndex(size_t index) {
# if DATA_PRODUCT_BUSES_MIDI_INPUT_N > 0
# if DATA_PRODUCT_PARAMETERS_N > 0
if (index >= DATA_PRODUCT_PARAMETERS_N)
# endif
return NULL;
# endif
# if DATA_PRODUCT_PARAMETERS_N > 0
# if (DATA_PRODUCT_PARAMETERS_IN_N > 0) && (DATA_PRODUCT_PARAMETERS_OUT_N > 0)
ParameterData *p = (parameterInfo[index].flags & Steinberg_Vst_ParameterInfo_ParameterFlags_kIsReadOnly ? parameterOutData : parameterInData) + parameterInfoToDataIndex[index];
# elif DATA_PRODUCT_PARAMETERS_IN_N > 0
ParameterData *p = parameterInData + parameterInfoToDataIndex[index];
# else
ParameterData *p = parameterOutData + parameterInfoToDataIndex[index];
# endif
return p;
# endif
}
static double parameterMapByIndex(size_t index, double v) {
# if DATA_PRODUCT_PARAMETERS_N > 0
ParameterData *p = parameterGetDataByIndex(index);
# if DATA_PRODUCT_BUSES_MIDI_INPUT_N > 0
if (p == NULL)
return v;
# endif
return parameterMap(p, v);
# else
return v;
# endif
}
static double parameterUnmapByIndex(size_t index, double v) {
# if DATA_PRODUCT_PARAMETERS_N > 0
ParameterData *p = parameterGetDataByIndex(index);
# if DATA_PRODUCT_BUSES_MIDI_INPUT_N > 0
if (p == NULL)
return v;
# endif
return parameterUnmap(p, v);
# else
return v;
# endif
}
#endif
typedef struct pluginInstance { typedef struct pluginInstance {
Steinberg_Vst_IComponentVtbl * vtblIComponent; Steinberg_Vst_IComponentVtbl * vtblIComponent;
@ -189,8 +237,11 @@ typedef struct pluginInstance {
Steinberg_FUnknown * context; Steinberg_FUnknown * context;
plugin p; plugin p;
float sampleRate; float sampleRate;
#if DATA_PRODUCT_PARAMETERS_N > 0 #if DATA_PRODUCT_PARAMETERS_IN_N > 0
float parameters[DATA_PRODUCT_PARAMETERS_N]; float parametersIn[DATA_PRODUCT_PARAMETERS_IN_N];
#endif
#if DATA_PRODUCT_PARAMETERS_OUT_N > 0
float parametersOut[DATA_PRODUCT_PARAMETERS_OUT_N];
#endif #endif
#if DATA_PRODUCT_CHANNELS_AUDIO_INPUT_N > 0 #if DATA_PRODUCT_CHANNELS_AUDIO_INPUT_N > 0
const float * inputs[DATA_PRODUCT_CHANNELS_AUDIO_INPUT_N]; const float * inputs[DATA_PRODUCT_CHANNELS_AUDIO_INPUT_N];
@ -215,7 +266,7 @@ typedef struct pluginInstance {
char midiOutputsActive[DATA_PRODUCT_BUSES_MIDI_OUTPUT_N]; char midiOutputsActive[DATA_PRODUCT_BUSES_MIDI_OUTPUT_N];
#endif #endif
void * mem; void * mem;
#ifdef STATE_DSP_CUSTOM #ifdef DATA_STATE_DSP_CUSTOM
struct Steinberg_IBStream * state; struct Steinberg_IBStream * state;
#endif #endif
} pluginInstance; } pluginInstance;
@ -223,7 +274,7 @@ typedef struct pluginInstance {
static Steinberg_Vst_IComponentVtbl pluginVtblIComponent; static Steinberg_Vst_IComponentVtbl pluginVtblIComponent;
static Steinberg_Vst_IAudioProcessorVtbl pluginVtblIAudioProcessor; static Steinberg_Vst_IAudioProcessorVtbl pluginVtblIAudioProcessor;
#ifdef STATE_DSP_CUSTOM #ifdef DATA_STATE_DSP_CUSTOM
static int pluginWriteStateCb(void *handle, const char *data, size_t length) { static int pluginWriteStateCb(void *handle, const char *data, size_t length) {
pluginInstance *p = (pluginInstance *)handle; pluginInstance *p = (pluginInstance *)handle;
size_t written = 0; size_t written = 0;
@ -246,8 +297,9 @@ static void pluginLoadParameterCb(void *handle, size_t index, float value) {
i--; i--;
# endif # endif
pluginInstance *p = (pluginInstance *)handle; pluginInstance *p = (pluginInstance *)handle;
p->parameters[i] = parameterAdjust(i, value); size_t ii = parameterInfoToDataIndex[i];
plugin_set_parameter(&p->p, index, p->parameters[i]); p->parametersIn[ii] = parameterAdjust(parameterInData + ii, value);
plugin_set_parameter(&p->p, index, p->parametersIn[ii]);
} }
# endif # endif
#endif #endif
@ -319,7 +371,7 @@ static Steinberg_tresult pluginInitialize(void *thisInterface, struct Steinberg_
/* .format = */ "vst3", /* .format = */ "vst3",
/* .get_bindir = */ getBindirCb, /* .get_bindir = */ getBindirCb,
/* .get_datadir = */ getDatadirCb, /* .get_datadir = */ getDatadirCb,
#ifdef STATE_DSP_CUSTOM #ifdef DATA_STATE_DSP_CUSTOM
/* .write_state = */ pluginWriteStateCb, /* .write_state = */ pluginWriteStateCb,
# if DATA_PRODUCT_PARAMETERS_IN_N > 0 # if DATA_PRODUCT_PARAMETERS_IN_N > 0
/* .load_parameter = */ pluginLoadParameterCb /* .load_parameter = */ pluginLoadParameterCb
@ -332,13 +384,16 @@ static Steinberg_tresult pluginInitialize(void *thisInterface, struct Steinberg_
#endif #endif
}; };
plugin_init(&p->p, &cbs); plugin_init(&p->p, &cbs);
#if DATA_PRODUCT_PARAMETERS_N > 0 #if DATA_PRODUCT_PARAMETERS_IN_N > 0
for (size_t i = 0; i < DATA_PRODUCT_PARAMETERS_N; i++) { for (size_t i = 0; i < DATA_PRODUCT_PARAMETERS_IN_N; i++) {
p->parameters[i] = parameterData[i].def; p->parametersIn[i] = parameterInData[i].def;
if (!(parameterInfo[i].flags & Steinberg_Vst_ParameterInfo_ParameterFlags_kIsReadOnly)) plugin_set_parameter(&p->p, parameterInData[i].index, parameterInData[i].def);
plugin_set_parameter(&p->p, parameterData[i].index, parameterData[i].def);
} }
#endif #endif
#if DATA_PRODUCT_PARAMETERS_OUT_N > 0
for (size_t i = 0; i < DATA_PRODUCT_PARAMETERS_OUT_N; i++)
p->parametersOut[i] = parameterOutData[i].def;
#endif
#if DATA_PRODUCT_BUSES_AUDIO_INPUT_N + DATA_PRODUCT_BUSES_AUDIO_OUTPUT_N + DATA_PRODUCT_BUSES_MIDI_INPUT_N + DATA_PRODUCT_BUSES_MIDI_OUTPUT_N > 0 #if DATA_PRODUCT_BUSES_AUDIO_INPUT_N + DATA_PRODUCT_BUSES_AUDIO_OUTPUT_N + DATA_PRODUCT_BUSES_MIDI_INPUT_N + DATA_PRODUCT_BUSES_MIDI_OUTPUT_N > 0
p->neededBusesActive = 0; p->neededBusesActive = 0;
# if DATA_PRODUCT_BUSES_AUDIO_INPUT_N > 0 # if DATA_PRODUCT_BUSES_AUDIO_INPUT_N > 0
@ -554,7 +609,7 @@ static Steinberg_tresult pluginSetState(void* thisInterface, struct Steinberg_IB
TRACE("plugin set state\n"); TRACE("plugin set state\n");
if (state == NULL) if (state == NULL)
return Steinberg_kResultFalse; return Steinberg_kResultFalse;
#ifdef STATE_DSP_CUSTOM #ifdef DATA_STATE_DSP_CUSTOM
pluginInstance *p = (pluginInstance *)((char *)thisInterface - offsetof(pluginInstance, vtblIComponent)); pluginInstance *p = (pluginInstance *)((char *)thisInterface - offsetof(pluginInstance, vtblIComponent));
if (state->lpVtbl->seek(state, 0, Steinberg_IBStream_IStreamSeekMode_kIBSeekEnd, NULL) != Steinberg_kResultOk) if (state->lpVtbl->seek(state, 0, Steinberg_IBStream_IStreamSeekMode_kIBSeekEnd, NULL) != Steinberg_kResultOk)
return Steinberg_kResultFalse; return Steinberg_kResultFalse;
@ -580,11 +635,9 @@ static Steinberg_tresult pluginSetState(void* thisInterface, struct Steinberg_IB
plugin_state_load(&p->p, data, length); plugin_state_load(&p->p, data, length);
free(data); free(data);
#else #else
# if DATA_PRODUCT_PARAMETERS_N > 0 # if DATA_PRODUCT_PARAMETERS_IN_N > 0
pluginInstance *p = (pluginInstance *)((char *)thisInterface - offsetof(pluginInstance, vtblIComponent)); pluginInstance *p = (pluginInstance *)((char *)thisInterface - offsetof(pluginInstance, vtblIComponent));
for (size_t i = 0; i < DATA_PRODUCT_PARAMETERS_N; i++) { for (size_t i = 0; i < DATA_PRODUCT_PARAMETERS_IN_N; i++) {
if (parameterInfo[i].flags & Steinberg_Vst_ParameterInfo_ParameterFlags_kIsReadOnly)
continue;
union { float f; uint32_t u; } v; union { float f; uint32_t u; } v;
Steinberg_int32 n; Steinberg_int32 n;
state->lpVtbl->read(state, &v, 4, &n); state->lpVtbl->read(state, &v, 4, &n);
@ -592,8 +645,8 @@ static Steinberg_tresult pluginSetState(void* thisInterface, struct Steinberg_IB
return Steinberg_kResultFalse; return Steinberg_kResultFalse;
if (IS_BIG_ENDIAN) if (IS_BIG_ENDIAN)
v.u = SWAP_UINT32(v.u); v.u = SWAP_UINT32(v.u);
p->parameters[i] = parameterAdjust(i, v.f); p->parametersIn[i] = parameterAdjust(parameterInData + i, v.f);
plugin_set_parameter(&p->p, parameterData[i].index, p->parameters[i]); plugin_set_parameter(&p->p, parameterInData[i].index, p->parametersIn[i]);
} }
# endif # endif
#endif #endif
@ -604,19 +657,17 @@ static Steinberg_tresult pluginGetState(void* thisInterface, struct Steinberg_IB
TRACE("plugin get state\n"); TRACE("plugin get state\n");
if (state == NULL) if (state == NULL)
return Steinberg_kResultFalse; return Steinberg_kResultFalse;
#ifdef STATE_DSP_CUSTOM #ifdef DATA_STATE_DSP_CUSTOM
pluginInstance *p = (pluginInstance *)((char *)thisInterface - offsetof(pluginInstance, vtblIComponent)); pluginInstance *p = (pluginInstance *)((char *)thisInterface - offsetof(pluginInstance, vtblIComponent));
p->state = state; p->state = state;
if (plugin_state_save(&p->p) != 0) if (plugin_state_save(&p->p) != 0)
return Steinberg_kResultFalse; return Steinberg_kResultFalse;
#else #else
# if DATA_PRODUCT_PARAMETERS_N > 0 # if DATA_PRODUCT_PARAMETERS_IN_N > 0
pluginInstance *p = (pluginInstance *)((char *)thisInterface - offsetof(pluginInstance, vtblIComponent)); pluginInstance *p = (pluginInstance *)((char *)thisInterface - offsetof(pluginInstance, vtblIComponent));
for (size_t i = 0; i < DATA_PRODUCT_PARAMETERS_N; i++) { for (size_t i = 0; i < DATA_PRODUCT_PARAMETERS_IN_N; i++) {
if (parameterInfo[i].flags & Steinberg_Vst_ParameterInfo_ParameterFlags_kIsReadOnly)
continue;
union { float f; uint32_t u; } v; union { float f; uint32_t u; } v;
v.f = p->parameters[i]; v.f = p->parametersIn[i];
if (IS_BIG_ENDIAN) if (IS_BIG_ENDIAN)
v.u = SWAP_UINT32(v.u); v.u = SWAP_UINT32(v.u);
Steinberg_int32 n; Steinberg_int32 n;
@ -749,7 +800,7 @@ static Steinberg_tresult pluginSetProcessing(void* thisInterface, Steinberg_TBoo
} }
static void processParams(pluginInstance *p, struct Steinberg_Vst_ProcessData *data, char before) { 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_PRODUCT_PARAMETERS_IN_N + DATA_PRODUCT_BUSES_MIDI_INPUT_N > 0
if (data->inputParameterChanges == NULL) if (data->inputParameterChanges == NULL)
return; return;
Steinberg_int32 n = data->inputParameterChanges->lpVtbl->getParameterCount(data->inputParameterChanges); Steinberg_int32 n = data->inputParameterChanges->lpVtbl->getParameterCount(data->inputParameterChanges);
@ -783,6 +834,8 @@ static void processParams(pluginInstance *p, struct Steinberg_Vst_ProcessData *d
case 1: case 1:
{ {
// pitch bend change // pitch bend change
// MIDI spec: 0 = max down bend, 8192 = no bend, 16383 = max up bend
// to make it symmetrical we have max down bend = 1 instead
uint16_t x = (uint16_t)(16382.0 * v) + 1; uint16_t x = (uint16_t)(16382.0 * v) + 1;
data[0] = 0xe0 /* | channel */; data[0] = 0xe0 /* | channel */;
data[1] = (x >> 7) & 0x7f; data[1] = (x >> 7) & 0x7f;
@ -802,11 +855,14 @@ static void processParams(pluginInstance *p, struct Steinberg_Vst_ProcessData *d
continue; continue;
} }
# endif # endif
v = parameterAdjust(pi, parameterMap(pi, v)); # if DATA_PRODUCT_PARAMETERS_IN_N > 0
if (v != p->parameters[pi]) { size_t ii = parameterInfoToDataIndex[pi];
p->parameters[pi] = v; v = parameterAdjust(parameterInData + ii, parameterMap(parameterInData + ii, v));
plugin_set_parameter(&p->p, parameterData[pi].index, p->parameters[pi]); if (v != p->parametersIn[ii]) {
p->parametersIn[ii] = v;
plugin_set_parameter(&p->p, parameterInData[ii].index, v);
} }
# endif
} }
#endif #endif
} }
@ -897,22 +953,20 @@ static Steinberg_tresult pluginProcess(void* thisInterface, struct Steinberg_Vst
processParams(p, data, 0); processParams(p, data, 0);
#if DATA_PRODUCT_PARAMETERS_N > 0 #if DATA_PRODUCT_PARAMETERS_OUT_N > 0
for (Steinberg_int32 i = 0; i < DATA_PRODUCT_PARAMETERS_N; i++) { for (Steinberg_int32 i = 0; i < DATA_PRODUCT_PARAMETERS_OUT_N; i++) {
if (!(parameterInfo[i].flags & Steinberg_Vst_ParameterInfo_ParameterFlags_kIsReadOnly)) float v = plugin_get_parameter(&p->p, parameterOutData[i].index);
if (v == p->parametersOut[i])
continue; continue;
float v = plugin_get_parameter(&p->p, parameterData[i].index); p->parametersOut[i] = v;
if (v == p->parameters[i])
continue;
p->parameters[i] = v;
if (data->outputParameterChanges == NULL) if (data->outputParameterChanges == NULL)
continue; continue;
Steinberg_Vst_ParamID id = parameterInfo[i].id; Steinberg_Vst_ParamID id = parameterInfo[parameterOutDataToInfoIndex[i]].id;
Steinberg_int32 index; Steinberg_int32 index;
struct Steinberg_Vst_IParamValueQueue *q = data->outputParameterChanges->lpVtbl->addParameterData(data->outputParameterChanges, &id, &index); struct Steinberg_Vst_IParamValueQueue *q = data->outputParameterChanges->lpVtbl->addParameterData(data->outputParameterChanges, &id, &index);
if (q == NULL) if (q == NULL)
continue; continue;
q->lpVtbl->addPoint(q, data->numSamples - 1, parameterUnmap(i, v), &index); q->lpVtbl->addPoint(q, data->numSamples - 1, parameterUnmap(parameterOutData + i, v), &index);
} }
#endif #endif
@ -995,8 +1049,14 @@ typedef struct controller {
#endif #endif
Steinberg_uint32 refs; Steinberg_uint32 refs;
Steinberg_FUnknown * context; Steinberg_FUnknown * context;
#if DATA_PRODUCT_PARAMETERS_N + DATA_PRODUCT_BUSES_MIDI_INPUT_N > 0 #if DATA_PRODUCT_PARAMETERS_IN_N > 0
double parameters[DATA_PRODUCT_PARAMETERS_N + 3 * DATA_PRODUCT_BUSES_MIDI_INPUT_N]; double parametersIn[DATA_PRODUCT_PARAMETERS_IN_N];
#endif
#if DATA_PRODUCT_PARAMETERS_OUT_N > 0
double parametersOut[DATA_PRODUCT_PARAMETERS_OUT_N];
#endif
#if DATA_PRODUCT_BUSES_MIDI_INPUT_N > 0
double parametersMidiIn[3 * DATA_PRODUCT_BUSES_MIDI_INPUT_N];
#endif #endif
struct Steinberg_Vst_IComponentHandler * componentHandler; struct Steinberg_Vst_IComponentHandler * componentHandler;
#ifdef DATA_UI #ifdef DATA_UI
@ -1007,6 +1067,26 @@ typedef struct controller {
static Steinberg_Vst_IEditControllerVtbl controllerVtblIEditController; static Steinberg_Vst_IEditControllerVtbl controllerVtblIEditController;
#if DATA_PRODUCT_PARAMETERS_N > 0
static void controllerGetParamDataValuePtrs(controller *ctrl, size_t index, ParameterData **p, double **pv) {
# if (DATA_PRODUCT_PARAMETERS_IN_N > 0) && (DATA_PRODUCT_PARAMETERS_OUT_N > 0)
if (parameterInfo[index].flags & Steinberg_Vst_ParameterInfo_ParameterFlags_kIsReadOnly) {
*p = parameterOutData + parameterInfoToDataIndex[index];
*pv = ctrl->parametersOut + parameterInfoToDataIndex[index];
} else {
*p = parameterInData + parameterInfoToDataIndex[index];
*pv = ctrl->parametersIn + parameterInfoToDataIndex[index];
}
# elif DATA_PRODUCT_PARAMETER_IN_N > 0
*p = parameterInData + paramterInfoToDataIndex[index];
*pv = ctrl->parametersIn + parameterInfoToDataIndex[index];
# else
*p = parameterOutData + paramterInfoToDataIndex[index];
*pv = ctrl->parametersOut + parameterInfoToDataIndex[index];
# endif
}
#endif
#ifdef DATA_UI #ifdef DATA_UI
# ifdef __linux__ # ifdef __linux__
@ -1168,16 +1248,31 @@ static Steinberg_tresult plugViewIsPlatformTypeSupported(void* thisInterface, St
} }
# if DATA_PRODUCT_PARAMETERS_N > 0 # if DATA_PRODUCT_PARAMETERS_N > 0
static void plugViewUpdateParameter(plugView *view, size_t index) { # if DATA_PRODUCT_PARAMETERS_IN_N > 0
static void plugViewUpdateParameterIn(plugView *view, size_t index) {
if (view->ui) if (view->ui)
plugin_ui_set_parameter(view->ui, parameterData[index].index, view->ctrl->parameters[index]); plugin_ui_set_parameter(view->ui, parameterInData[index].index, view->ctrl->parametersIn[index]);
} }
# endif
# if DATA_PRODUCT_PARAMETERS_OUT_N > 0
static void plugViewUpdateParameterOut(plugView *view, size_t index) {
if (view->ui)
plugin_ui_set_parameter(view->ui, parameterOutData[index].index, view->ctrl->parametersOut[index]);
}
# endif
static void plugViewUpdateAllParameters(plugView *view) { static void plugViewUpdateAllParameters(plugView *view) {
if (view->ui == NULL) if (view->ui == NULL)
return; return;
for (size_t i = 0; i < DATA_PRODUCT_PARAMETERS_N; i++) # if DATA_PRODUCT_PARAMETERS_IN_N > 0
plugin_ui_set_parameter(view->ui, parameterData[i].index, view->ctrl->parameters[i]); for (size_t i = 0; i < DATA_PRODUCT_PARAMETERS_IN_N; i++)
plugin_ui_set_parameter(view->ui, parameterInData[i].index, view->ctrl->parametersIn[i]);
# endif
# if DATA_PRODUCT_PARAMETERS_OUT_N > 0
for (size_t i = 0; i < DATA_PRODUCT_PARAMETERS_OUT_N; i++)
plugin_ui_set_parameter(view->ui, parameterOutData[i].index, view->ctrl->parametersOut[i]);
# endif
} }
static void plugViewSetParameterBeginCb(void *handle, size_t index) { static void plugViewSetParameterBeginCb(void *handle, size_t index) {
@ -1201,8 +1296,12 @@ static void plugViewSetParameterCb(void *handle, size_t index, float value) {
index = index >= DATA_PARAM_LATENCY_INDEX ? index - 1 : index; index = index >= DATA_PARAM_LATENCY_INDEX ? index - 1 : index;
# endif # endif
plugView *v = (plugView *)handle; plugView *v = (plugView *)handle;
v->ctrl->parameters[index] = parameterAdjust(index, value); // let Reaper find the updated value
v->ctrl->componentHandler->lpVtbl->performEdit(v->ctrl->componentHandler, parameterInfo[index].id, parameterUnmap(index, v->ctrl->parameters[index])); ParameterData *p;
double *pv;
controllerGetParamDataValuePtrs(v->ctrl, index, &p, &pv);
*pv = parameterAdjust(p, value); // let Reaper find the updated value
v->ctrl->componentHandler->lpVtbl->performEdit(v->ctrl->componentHandler, parameterInfo[index].id, parameterUnmap(p, *pv));
} }
static void plugViewSetParameterEndCb(void *handle, size_t index) { static void plugViewSetParameterEndCb(void *handle, size_t index) {
@ -1568,15 +1667,19 @@ static Steinberg_tresult controllerInitialize(void* thisInterface, struct Steinb
if (c->context != NULL) if (c->context != NULL)
return Steinberg_kResultFalse; return Steinberg_kResultFalse;
c->context = context; c->context = context;
#if DATA_PRODUCT_PARAMETERS_N > 0 #if DATA_PRODUCT_PARAMETERS_IN_N > 0
for (int i = 0; i < DATA_PRODUCT_PARAMETERS_N; i++) for (int i = 0; i < DATA_PRODUCT_PARAMETERS_IN_N; i++)
c->parameters[i] = parameterData[i].def; c->parametersIn[i] = parameterInData[i].def;
#endif
#if DATA_PRODUCT_PARAMETERS_OUT_N > 0
for (int i = 0; i < DATA_PRODUCT_PARAMETERS_OUT_N; i++)
c->parametersOut[i] = parameterOutData[i].def;
#endif #endif
#if DATA_PRODUCT_BUSES_MIDI_INPUT_N > 0 #if DATA_PRODUCT_BUSES_MIDI_INPUT_N > 0
for (int i = DATA_PRODUCT_PARAMETERS_N; i < DATA_PRODUCT_PARAMETERS_N + 3 * DATA_PRODUCT_BUSES_MIDI_INPUT_N; i += 3) { for (int i = 0; i < 3 * DATA_PRODUCT_BUSES_MIDI_INPUT_N; i += 3) {
c->parameters[i] = 0.0; c->parametersMidiIn[i] = 0.0;
c->parameters[i + 1] = 0.5; c->parametersMidiIn[i + 1] = 0.5;
c->parameters[i + 2] = 0.0; c->parametersMidiIn[i + 2] = 0.0;
} }
#endif #endif
return Steinberg_kResultOk; return Steinberg_kResultOk;
@ -1593,11 +1696,9 @@ static Steinberg_tresult controllerSetComponentState(void* thisInterface, struct
TRACE("controller set component state %p %p\n", thisInterface, (void *)state); TRACE("controller set component state %p %p\n", thisInterface, (void *)state);
if (state == NULL) if (state == NULL)
return Steinberg_kResultFalse; return Steinberg_kResultFalse;
#if DATA_PRODUCT_PARAMETERS_N > 0 #if DATA_PRODUCT_PARAMETERS_IN_N > 0
controller *c = (controller *)((char *)thisInterface - offsetof(controller, vtblIEditController)); controller *c = (controller *)((char *)thisInterface - offsetof(controller, vtblIEditController));
for (size_t i = 0; i < DATA_PRODUCT_PARAMETERS_N; i++) { for (size_t i = 0; i < DATA_PRODUCT_PARAMETERS_IN_N; i++) {
if (parameterInfo[i].flags & Steinberg_Vst_ParameterInfo_ParameterFlags_kIsReadOnly)
continue;
union { float f; uint32_t u; } v; union { float f; uint32_t u; } v;
Steinberg_int32 n; Steinberg_int32 n;
state->lpVtbl->read(state, &v, 4, &n); state->lpVtbl->read(state, &v, 4, &n);
@ -1605,7 +1706,7 @@ static Steinberg_tresult controllerSetComponentState(void* thisInterface, struct
return Steinberg_kResultFalse; return Steinberg_kResultFalse;
if (IS_BIG_ENDIAN) if (IS_BIG_ENDIAN)
v.u = SWAP_UINT32(v.u); v.u = SWAP_UINT32(v.u);
c->parameters[i] = parameterAdjust(i, v.f); c->parametersIn[i] = parameterAdjust(parameterInData + i, v.f);
} }
#endif #endif
TRACE(" ok\n"); TRACE(" ok\n");
@ -1613,14 +1714,13 @@ static Steinberg_tresult controllerSetComponentState(void* thisInterface, struct
} }
static Steinberg_tresult controllerSetState(void* thisInterface, struct Steinberg_IBStream* state) { static Steinberg_tresult controllerSetState(void* thisInterface, struct Steinberg_IBStream* state) {
// TODO: we need this implemented for Reaper, need to re-check this
TRACE("controller set state\n"); TRACE("controller set state\n");
if (state == NULL) if (state == NULL)
return Steinberg_kResultFalse; return Steinberg_kResultFalse;
#if DATA_PRODUCT_PARAMETERS_N > 0 #if DATA_PRODUCT_PARAMETERS_IN_N > 0
controller *c = (controller *)((char *)thisInterface - offsetof(controller, vtblIEditController)); controller *c = (controller *)((char *)thisInterface - offsetof(controller, vtblIEditController));
for (size_t i = 0; i < DATA_PRODUCT_PARAMETERS_N; i++) { for (size_t i = 0; i < DATA_PRODUCT_PARAMETERS_IN_N; i++) {
if (parameterInfo[i].flags & Steinberg_Vst_ParameterInfo_ParameterFlags_kIsReadOnly)
continue;
union { float f; uint32_t u; } v; union { float f; uint32_t u; } v;
Steinberg_int32 n; Steinberg_int32 n;
state->lpVtbl->read(state, &v, 4, &n); state->lpVtbl->read(state, &v, 4, &n);
@ -1628,11 +1728,11 @@ static Steinberg_tresult controllerSetState(void* thisInterface, struct Steinber
return Steinberg_kResultFalse; return Steinberg_kResultFalse;
if (IS_BIG_ENDIAN) if (IS_BIG_ENDIAN)
v.u = SWAP_UINT32(v.u); v.u = SWAP_UINT32(v.u);
c->parameters[i] = parameterAdjust(i, v.f); c->parametersIn[i] = parameterAdjust(parameterInData + i, v.f);
# ifdef DATA_UI # ifdef DATA_UI
if (c->views) if (c->views)
for (size_t j = 0; j < c->viewsCount; j++) for (size_t j = 0; j < c->viewsCount; j++)
plugViewUpdateParameter(c->views[j], i); plugViewUpdateParameterIn(c->views[j], i);
# endif # endif
} }
#endif #endif
@ -1640,16 +1740,15 @@ static Steinberg_tresult controllerSetState(void* thisInterface, struct Steinber
} }
static Steinberg_tresult controllerGetState(void* thisInterface, struct Steinberg_IBStream* state) { static Steinberg_tresult controllerGetState(void* thisInterface, struct Steinberg_IBStream* state) {
// TODO: we need this implemented for Reaper, need to re-check this
TRACE("controller get state\n"); TRACE("controller get state\n");
if (state == NULL) if (state == NULL)
return Steinberg_kResultFalse; return Steinberg_kResultFalse;
#if DATA_PRODUCT_PARAMETERS_N > 0 #if DATA_PRODUCT_PARAMETERS_IN_N > 0
controller *c = (controller *)((char *)thisInterface - offsetof(controller, vtblIEditController)); controller *c = (controller *)((char *)thisInterface - offsetof(controller, vtblIEditController));
for (size_t i = 0; i < DATA_PRODUCT_PARAMETERS_N; i++) { for (size_t i = 0; i < DATA_PRODUCT_PARAMETERS_IN_N; i++) {
if (parameterInfo[i].flags & Steinberg_Vst_ParameterInfo_ParameterFlags_kIsReadOnly)
continue;
union { float f; uint32_t u; } v; union { float f; uint32_t u; } v;
v.f = c->parameters[i]; v.f = c->parametersIn[i];
if (IS_BIG_ENDIAN) if (IS_BIG_ENDIAN)
v.u = SWAP_UINT32(v.u); v.u = SWAP_UINT32(v.u);
Steinberg_int32 n; Steinberg_int32 n;
@ -1672,10 +1771,16 @@ static Steinberg_tresult controllerGetParameterInfo(void* thisInterface, Steinbe
(void)thisInterface; (void)thisInterface;
TRACE("controller get parameter info\n"); TRACE("controller get parameter info\n");
#if DATA_PRODUCT_PARAMETERS_N + DATA_PRODUCT_BUSES_MIDI_INPUT_N > 0
if (paramIndex < 0 || paramIndex >= DATA_PRODUCT_PARAMETERS_N + 3 * DATA_PRODUCT_BUSES_MIDI_INPUT_N) if (paramIndex < 0 || paramIndex >= DATA_PRODUCT_PARAMETERS_N + 3 * DATA_PRODUCT_BUSES_MIDI_INPUT_N)
return Steinberg_kResultFalse; return Steinberg_kResultFalse;
*info = parameterInfo[paramIndex]; *info = parameterInfo[paramIndex];
return Steinberg_kResultTrue; return Steinberg_kResultTrue;
#else
(void)paramIndex;
(void)info;
return Steinberg_kResultFalse;
#endif
} }
static void dToStr(double v, Steinberg_Vst_String128 s, int precision) { static void dToStr(double v, Steinberg_Vst_String128 s, int precision) {
@ -1724,11 +1829,18 @@ static Steinberg_tresult controllerGetParamStringByValue(void* thisInterface, St
(void)thisInterface; (void)thisInterface;
TRACE("controller get param string by value\n"); TRACE("controller get param string by value\n");
#if DATA_PRODUCT_PARAMETERS_N + DATA_PRODUCT_BUSES_MIDI_INPUT_N > 0
int pi = parameterGetIndexById(id); int pi = parameterGetIndexById(id);
if (pi >= DATA_PRODUCT_PARAMETERS_N + 3 * DATA_PRODUCT_BUSES_MIDI_INPUT_N || pi < 0) if (pi >= DATA_PRODUCT_PARAMETERS_N + 3 * DATA_PRODUCT_BUSES_MIDI_INPUT_N || pi < 0)
return Steinberg_kResultFalse; return Steinberg_kResultFalse;
dToStr(pi >= DATA_PRODUCT_PARAMETERS_N ? valueNormalized : parameterMap(pi, valueNormalized), string, 2); dToStr(parameterMapByIndex(pi, valueNormalized), string, 2);
return Steinberg_kResultTrue; return Steinberg_kResultTrue;
#else
(void)id;
(void)valueNormalized;
(void)string;
return Steinberg_kResultFalse;
#endif
} }
void TCharToD(Steinberg_Vst_TChar* s, double *v) { void TCharToD(Steinberg_Vst_TChar* s, double *v) {
@ -1763,29 +1875,48 @@ static Steinberg_tresult controllerGetParamValueByString(void* thisInterface, St
(void)thisInterface; (void)thisInterface;
TRACE("controller get param value by string\n"); TRACE("controller get param value by string\n");
#if DATA_PRODUCT_PARAMETERS_N + DATA_PRODUCT_BUSES_MIDI_INPUT_N > 0
int pi = parameterGetIndexById(id); int pi = parameterGetIndexById(id);
if (pi >= DATA_PRODUCT_PARAMETERS_N + 3 * DATA_PRODUCT_BUSES_MIDI_INPUT_N || pi < 0) if (pi >= DATA_PRODUCT_PARAMETERS_N + 3 * DATA_PRODUCT_BUSES_MIDI_INPUT_N || pi < 0)
return Steinberg_kResultFalse; return Steinberg_kResultFalse;
double v; double v;
TCharToD(string, &v); TCharToD(string, &v);
*valueNormalized = pi >= DATA_PRODUCT_PARAMETERS_N ? v : parameterUnmap(pi, v); *valueNormalized = parameterUnmapByIndex(pi, v);
return Steinberg_kResultTrue; return Steinberg_kResultTrue;
#else
(void)id;
(void)string;
(void)valueNormalized;
return Steinberg_kResultFalse;
#endif
} }
static Steinberg_Vst_ParamValue controllerNormalizedParamToPlain(void* thisInterface, Steinberg_Vst_ParamID id, Steinberg_Vst_ParamValue valueNormalized) { static Steinberg_Vst_ParamValue controllerNormalizedParamToPlain(void* thisInterface, Steinberg_Vst_ParamID id, Steinberg_Vst_ParamValue valueNormalized) {
(void)thisInterface; (void)thisInterface;
TRACE("controller normalized param to plain\n"); TRACE("controller normalized param to plain\n");
#if DATA_PRODUCT_PARAMETERS_N > 0
int pi = parameterGetIndexById(id); int pi = parameterGetIndexById(id);
return pi >= DATA_PRODUCT_PARAMETERS_N ? valueNormalized : parameterMap(pi, valueNormalized); return parameterMapByIndex(pi, valueNormalized);
#else
(void)id;
(void)valueNormalized;
return 0.0;
#endif
} }
static Steinberg_Vst_ParamValue controllerPlainParamToNormalized(void* thisInterface, Steinberg_Vst_ParamID id, Steinberg_Vst_ParamValue plainValue) { static Steinberg_Vst_ParamValue controllerPlainParamToNormalized(void* thisInterface, Steinberg_Vst_ParamID id, Steinberg_Vst_ParamValue plainValue) {
(void)thisInterface; (void)thisInterface;
TRACE("controller plain param to normalized\n"); TRACE("controller plain param to normalized\n");
#if DATA_PRODUCT_PARAMETERS_N > 0
int pi = parameterGetIndexById(id); int pi = parameterGetIndexById(id);
return pi >= DATA_PRODUCT_PARAMETERS_N ? plainValue : parameterUnmap(pi, plainValue); return parameterUnmapByIndex(pi, plainValue);
#else
(void)id;
(void)plainValue;
return 0.0;
#endif
} }
static Steinberg_Vst_ParamValue controllerGetParamNormalized(void* thisInterface, Steinberg_Vst_ParamID id) { static Steinberg_Vst_ParamValue controllerGetParamNormalized(void* thisInterface, Steinberg_Vst_ParamID id) {
@ -1794,10 +1925,15 @@ static Steinberg_Vst_ParamValue controllerGetParamNormalized(void* thisInterface
controller *c = (controller *)((char *)thisInterface - offsetof(controller, vtblIEditController)); controller *c = (controller *)((char *)thisInterface - offsetof(controller, vtblIEditController));
int pi = parameterGetIndexById(id); int pi = parameterGetIndexById(id);
# if DATA_PRODUCT_BUSES_MIDI_INPUT_N > 0 # if DATA_PRODUCT_BUSES_MIDI_INPUT_N > 0
return pi >= DATA_PRODUCT_PARAMETERS_N ? c->parameters[pi] : parameterUnmap(pi, c->parameters[pi]); # if DATA_PRODUCT_PARAMETERS_N > 0
# else if (pi >= DATA_PRODUCT_PARAMETERS_N)
return parameterUnmap(pi, c->parameters[pi]);
# endif # endif
return c->parametersMidiIn[pi - DATA_PRODUCT_PARAMETERS_N];
# endif
ParameterData *p;
double *pv;
controllerGetParamDataValuePtrs(c, pi, &p, &pv);
return parameterUnmap(p, *pv);
#else #else
(void)thisInterface; (void)thisInterface;
(void)id; (void)id;
@ -1812,12 +1948,29 @@ static Steinberg_tresult controllerSetParamNormalized(void* thisInterface, Stein
if (pi >= DATA_PRODUCT_PARAMETERS_N + 3 * DATA_PRODUCT_BUSES_MIDI_INPUT_N || pi < 0) if (pi >= DATA_PRODUCT_PARAMETERS_N + 3 * DATA_PRODUCT_BUSES_MIDI_INPUT_N || pi < 0)
return Steinberg_kResultFalse; return Steinberg_kResultFalse;
controller *c = (controller *)((char *)thisInterface - offsetof(controller, vtblIEditController)); controller *c = (controller *)((char *)thisInterface - offsetof(controller, vtblIEditController));
c->parameters[pi] = pi >= DATA_PRODUCT_PARAMETERS_N ? value : parameterAdjust(pi, parameterMap(pi, value)); # if DATA_PRODUCT_BUSES_MIDI_INPUT_N > 0
# ifdef DATA_UI if (pi >= DATA_PRODUCT_PARAMETERS_N) {
c->parametersMidiIn[pi - DATA_PRODUCT_PARAMETERS_N] = value;
} else {
# endif
# if DATA_PRODUCT_PARAMETERS_N > 0
ParameterData *p;
double *pv;
controllerGetParamDataValuePtrs(c, pi, &p, &pv);
*pv = parameterAdjust(p, parameterMap(p, value));
# endif
# if DATA_PRODUCT_BUSES_MIDI_INPUT_N > 0
}
# endif
# if defined(DATA_UI) && (DATA_PRODUCT_PARAMETERS_N > 0)
if (pi < DATA_PRODUCT_PARAMETERS_N) if (pi < DATA_PRODUCT_PARAMETERS_N)
for (size_t i = 0; i < c->viewsCount; i++) for (size_t i = 0; i < c->viewsCount; i++)
if(c->views[i]) if(c->views[i]) {
plugViewUpdateParameter(c->views[i], pi); if (parameterInfo[pi].flags & Steinberg_Vst_ParameterInfo_ParameterFlags_kIsReadOnly)
plugViewUpdateParameterOut(c->views[i], pi);
else
plugViewUpdateParameterIn(c->views[i], pi);
}
# endif # endif
return Steinberg_kResultTrue; return Steinberg_kResultTrue;
#else #else

View File

@ -52,8 +52,21 @@ module.exports = function (data, api, outputCommon, outputData) {
} }
}; };
for (var i = 0; i < data.product.parameters.length; i++) var inIdx = 0;
var outIdx = 0;
for (var i = 0; i < data.product.parameters.length; i++) {
data.product.parameters[i].paramIndex = i; data.product.parameters[i].paramIndex = i;
if (data.product.parameters[i].isLatency)
continue;
data.product.parameters[i].paramInfoIndex = inIdx + outIdx;
if (data.product.parameters[i].direction == "input") {
data.product.parameters[i].paramDataIndex = inIdx;
inIdx++;
} else {
data.product.parameters[i].paramDataIndex = outIdx;
outIdx++;
}
}
} }
api.copyFile(`data${sep}PkgInfo`, `data${sep}PkgInfo`); api.copyFile(`data${sep}PkgInfo`, `data${sep}PkgInfo`);