refined state api and implemented in lv2 and vst3

This commit is contained in:
Stefano D'Angelo 2025-01-20 20:25:39 +01:00
parent 440b49c14d
commit 5663b4389c
4 changed files with 271 additions and 160 deletions

View File

@ -26,16 +26,20 @@ typedef struct {
const char * format;
const char * (*get_bindir)(void *handle);
const char * (*get_datadir)(void *handle);
{{?it.product.state && it.product.state.dspCustom}}
int (*write_state)(void *handle, const char *data, size_t length);
{{?it.product.parameters.find(x => x.direction == "input")}}
void (*load_parameter)(void *handle, size_t index, float value);
{{?}}
void (*lock_state)(void *handle);
void (*unlock_state)(void *handle);
{{?}}
} plugin_callbacks;
{{?it.product.state && it.product.state.dspCustom}}
typedef struct {
void * handle;
void (*lock)(void *handle);
void (*unlock)(void *handle);
int (*write)(void *handle, const char *data, size_t length);
{{?it.product.parameters.find(x => x.direction == "input")}}
void (*set_parameter)(void *handle, size_t index, float value);
{{?}}
} plugin_state_callbacks;
{{?}}
typedef struct {
void * handle;
const char * format;

View File

@ -127,6 +127,7 @@ typedef struct {
#ifdef DATA_STATE_DSP_CUSTOM
LV2_URID uri_atom_Chunk;
LV2_URID uri_state_data;
plugin_state_callbacks state_cbs;
LV2_State_Store_Function state_store;
LV2_State_Handle state_handle;
#endif
@ -138,13 +139,25 @@ static const char * get_bundle_path_cb(void *handle) {
}
#ifdef DATA_STATE_DSP_CUSTOM
static int write_state_cb(void *handle, const char *data, size_t length) {
static void state_lock_cb(void *handle) {
plugin_instance * i = (plugin_instance *)handle;
while (atomic_flag_test_and_set(&i->sync_lock_flag))
yield();
i->synced = 0;
}
static void state_unlock_cb(void *handle) {
plugin_instance * i = (plugin_instance *)handle;
atomic_flag_clear(&i->sync_lock_flag);
}
static int state_write_cb(void *handle, const char *data, size_t length) {
plugin_instance * i = (plugin_instance *)handle;
return i->state_store(i->state_handle, i->uri_state_data, data, length, i->uri_atom_Chunk, LV2_STATE_IS_POD | LV2_STATE_IS_PORTABLE);
}
# if DATA_PRODUCT_CONTROL_INPUTS_N > 0
static void load_parameter_cb(void *handle, size_t index, float value) {
static void state_set_parameter_cb(void *handle, size_t index, float value) {
plugin_instance * i = (plugin_instance *)handle;
size_t idx = index_to_param[index] - CONTROL_INPUT_INDEX_OFFSET;
value = adjust_param(idx, value);
@ -152,18 +165,6 @@ static void load_parameter_cb(void *handle, size_t index, float value) {
i->loaded = 1;
}
# endif
static void lock_state_cb(void *handle) {
plugin_instance * i = (plugin_instance *)handle;
while (atomic_flag_test_and_set(&i->sync_lock_flag))
yield();
i->synced = 0;
}
static void unlock_state_cb(void *handle) {
plugin_instance * i = (plugin_instance *)handle;
atomic_flag_clear(&i->sync_lock_flag);
}
#endif
static LV2_Handle instantiate(const struct LV2_Descriptor * descriptor, double sample_rate, const char * bundle_path, const LV2_Feature * const * features) {
@ -207,15 +208,7 @@ static LV2_Handle instantiate(const struct LV2_Descriptor * descriptor, double s
/* .handle = */ (void *)instance,
/* .format = */ "lv2",
/* .get_bindir = */ get_bundle_path_cb,
/* .get_datadir = */ get_bundle_path_cb,
# ifdef DATA_STATE_DSP_CUSTOM
/* .write_state = */ write_state_cb,
# if DATA_PRODUCT_CONTROL_INPUTS_N > 0
/* .load_parameter = */ load_parameter_cb,
# endif
/* .lock_state = */ lock_state_cb,
/* .unlock_state = */ unlock_state_cb,
# endif
/* .get_datadir = */ get_bundle_path_cb
};
plugin_init(&instance->p, &cbs);
@ -447,7 +440,16 @@ static LV2_State_Status state_save(LV2_Handle instance, LV2_State_Store_Function
}
i->state_store = store;
i->state_handle = handle;
return plugin_state_save(&i->p) == 0 ? LV2_STATE_SUCCESS : LV2_STATE_ERR_UNKNOWN;
plugin_state_callbacks cbs = {
/* .handle = */ (void *)i,
/* .lock = */ state_lock_cb,
/* .unlock = */ state_unlock_cb,
/* .write = */ state_write_cb,
# if DATA_PRODUCT_CONTROL_INPUTS_N > 0
/* .set_parameter = */ NULL
# endif
};
return plugin_state_save(&i->p, &cbs) == 0 ? LV2_STATE_SUCCESS : LV2_STATE_ERR_UNKNOWN;
}
static LV2_State_Status state_restore(LV2_Handle instance, LV2_State_Retrieve_Function retrieve, LV2_State_Handle handle, uint32_t flags, const LV2_Feature * const * features) {
@ -466,8 +468,16 @@ static LV2_State_Status state_restore(LV2_Handle instance, LV2_State_Retrieve_Fu
lv2_log_error(&i->logger, "Cannot restore state since property <%s> could not be retrieved\n", DATA_LV2_URI "#state_data");
return LV2_STATE_ERR_NO_PROPERTY;
}
plugin_state_load(&i->p, data, length);
return LV2_STATE_SUCCESS;
plugin_state_callbacks cbs = {
/* .handle = */ (void *)i,
/* .lock = */ state_lock_cb,
/* .unlock = */ state_unlock_cb,
/* .write = */ NULL,
# if DATA_PRODUCT_CONTROL_INPUTS_N > 0
/* .set_parameter = */ state_set_parameter_cb
# endif
};
return plugin_state_load(&cbs, data, length) == 0 ? LV2_STATE_SUCCESS : LV2_STATE_ERR_UNKNOWN;
}
static const void * extension_data(const char * uri) {

View File

@ -53,6 +53,17 @@
#include <pmmintrin.h>
#endif
#if DATA_PRODUCT_PARAMETERS_IN_N > 0
# include <stdatomic.h>
# if defined(_WIN32) || defined(__CYGWIN__)
# include <processthreadsapi.h>
# define yield SwitchToThread
# else
# include <sched.h>
# define yield sched_yield
# endif
#endif
// COM in C doc:
// https://github.com/rubberduck-vba/Rubberduck/wiki/COM-in-plain-C
// https://devblogs.microsoft.com/oldnewthing/20040205-00/?p=40733
@ -229,6 +240,30 @@ static double parameterUnmapByIndex(size_t index, double v) {
}
#endif
static int stateRead(struct Steinberg_IBStream * state, char ** data, Steinberg_int64 * length) {
if (state->lpVtbl->seek(state, 0, Steinberg_IBStream_IStreamSeekMode_kIBSeekEnd, NULL) != Steinberg_kResultOk)
return -1;
if (state->lpVtbl->tell(state, length) != Steinberg_kResultOk)
return -1;
if (state->lpVtbl->seek(state, 0, Steinberg_IBStream_IStreamSeekMode_kIBSeekSet, NULL) != Steinberg_kResultOk)
return -1;
*data = *length > 0 ? malloc(*length) : NULL;
if (*length > 0 && *data == NULL)
return -1;
Steinberg_int64 read = 0;
while (read < *length) {
Steinberg_int32 r = (*length - read) <= 0x7fffffff ? *length - read : 0x7fffffff;
Steinberg_int32 n;
state->lpVtbl->read(state, *data + read, *length, &n);
if (n != r) {
free(*data);
return -1;
}
read += n;
}
return 0;
}
typedef struct pluginInstance {
Steinberg_Vst_IComponentVtbl * vtblIComponent;
Steinberg_Vst_IAudioProcessorVtbl * vtblIAudioProcessor;
@ -239,6 +274,10 @@ typedef struct pluginInstance {
float sampleRate;
#if DATA_PRODUCT_PARAMETERS_IN_N > 0
float parametersIn[DATA_PRODUCT_PARAMETERS_IN_N];
float parametersInSync[DATA_PRODUCT_PARAMETERS_IN_N];
atomic_flag syncLockFlag;
char synced;
char loaded;
#endif
#if DATA_PRODUCT_PARAMETERS_OUT_N > 0
float parametersOut[DATA_PRODUCT_PARAMETERS_OUT_N];
@ -266,16 +305,25 @@ typedef struct pluginInstance {
char midiOutputsActive[DATA_PRODUCT_BUSES_MIDI_OUTPUT_N];
#endif
void * mem;
#ifdef DATA_STATE_DSP_CUSTOM
struct Steinberg_IBStream * state;
#endif
} pluginInstance;
static Steinberg_Vst_IComponentVtbl pluginVtblIComponent;
static Steinberg_Vst_IAudioProcessorVtbl pluginVtblIAudioProcessor;
#ifdef DATA_STATE_DSP_CUSTOM
static int pluginWriteStateCb(void *handle, const char *data, size_t length) {
static void pluginStateLockCb(void *handle) {
pluginInstance *p = (pluginInstance *)handle;
while (atomic_flag_test_and_set(&p->syncLockFlag))
yield();
p->synced = 0;
}
static void pluginStateUnlockCb(void *handle) {
pluginInstance *p = (pluginInstance *)handle;
atomic_flag_clear(&p->syncLockFlag);
}
static int pluginStateWriteCb(void *handle, const char *data, size_t length) {
pluginInstance *p = (pluginInstance *)handle;
size_t written = 0;
while (written < length) {
@ -290,7 +338,7 @@ static int pluginWriteStateCb(void *handle, const char *data, size_t length) {
}
# if DATA_PRODUCT_PARAMETERS_IN_N > 0
static void pluginLoadParameterCb(void *handle, size_t index, float value) {
static void pluginStateSetParameterCb(void *handle, size_t index, float value) {
size_t i = index;
# ifdef DATA_PARAM_LATENCY_INDEX
if (i >= DATA_PARAM_LATENCY_INDEX)
@ -298,11 +346,10 @@ static void pluginLoadParameterCb(void *handle, size_t index, float value) {
# endif
pluginInstance *p = (pluginInstance *)handle;
size_t ii = parameterInfoToDataIndex[i];
p->parametersIn[ii] = parameterAdjust(parameterInData + ii, value);
plugin_set_parameter(&p->p, index, p->parametersIn[ii]);
p->parametersInSync[ii] = parameterAdjust(parameterInData + ii, value);
p->loaded = 1;
}
# endif
#endif
static Steinberg_tresult pluginQueryInterface(pluginInstance *p, const Steinberg_TUID iid, void ** obj) {
// This seems to violate the way multiple inheritance should work in COM, but hosts like it, so what do I know...
@ -370,18 +417,7 @@ static Steinberg_tresult pluginInitialize(void *thisInterface, struct Steinberg_
/* .handle = */ (void *)p,
/* .format = */ "vst3",
/* .get_bindir = */ getBindirCb,
/* .get_datadir = */ getDatadirCb,
#ifdef DATA_STATE_DSP_CUSTOM
/* .write_state = */ pluginWriteStateCb,
# if DATA_PRODUCT_PARAMETERS_IN_N > 0
/* .load_parameter = */ pluginLoadParameterCb
# else
/* .load_parameter = */ NULL
# endif
#else
/* .write_state = */ NULL,
/* .load_parameter = */ NULL
#endif
/* .get_datadir = */ getDatadirCb
};
plugin_init(&p->p, &cbs);
#if DATA_PRODUCT_PARAMETERS_IN_N > 0
@ -389,6 +425,11 @@ static Steinberg_tresult pluginInitialize(void *thisInterface, struct Steinberg_
p->parametersIn[i] = parameterInData[i].def;
plugin_set_parameter(&p->p, parameterInData[i].index, parameterInData[i].def);
}
for (size_t i = 0; i < DATA_PRODUCT_PARAMETERS_IN_N; i++)
p->parametersInSync[i] = p->parametersIn[i];
atomic_flag_clear(&p->syncLockFlag);
p->synced = 1;
p->loaded = 0;
#endif
#if DATA_PRODUCT_PARAMETERS_OUT_N > 0
for (size_t i = 0; i < DATA_PRODUCT_PARAMETERS_OUT_N; i++)
@ -609,75 +650,98 @@ static Steinberg_tresult pluginSetState(void* thisInterface, struct Steinberg_IB
TRACE("plugin set state\n");
if (state == NULL)
return Steinberg_kResultFalse;
#ifdef DATA_STATE_DSP_CUSTOM
pluginInstance *p = (pluginInstance *)((char *)thisInterface - offsetof(pluginInstance, vtblIComponent));
if (state->lpVtbl->seek(state, 0, Steinberg_IBStream_IStreamSeekMode_kIBSeekEnd, NULL) != Steinberg_kResultOk)
return Steinberg_kResultFalse;
char *data;
Steinberg_int64 length;
if (state->lpVtbl->tell(state, &length) != Steinberg_kResultOk)
if (stateRead(state, &data, &length) != 0)
return Steinberg_kResultFalse;
if (state->lpVtbl->seek(state, 0, Steinberg_IBStream_IStreamSeekMode_kIBSeekSet, NULL) != Steinberg_kResultOk)
return Steinberg_kResultFalse;
char *data = malloc(length);
if (data == NULL)
return Steinberg_kResultFalse;
Steinberg_int64 read = 0;
while (read < length) {
Steinberg_int32 r = (length - read) <= 0x7fffffff ? length - read : 0x7fffffff;
Steinberg_int32 n;
state->lpVtbl->read(state, data + read, length, &n);
if (n != r) {
free(data);
return Steinberg_kResultFalse;
}
read += n;
}
plugin_state_load(&p->p, data, length);
free(data);
#else
# if DATA_PRODUCT_PARAMETERS_IN_N > 0
pluginInstance *p = (pluginInstance *)((char *)thisInterface - offsetof(pluginInstance, vtblIComponent));
#ifdef DATA_STATE_DSP_CUSTOM
plugin_state_callbacks cbs = {
/* .handle = */ (void *)p,
/* .lock = */ pluginStateLockCb,
/* .unlock = */ pluginStateUnlockCb,
/* .write = */ NULL,
# if DATA_PRODUCT_PARAMETERS_IN_N > 0
/* .set_parameter = */ pluginStateSetParameterCb
# endif
};
int err = plugin_state_load(&cbs, data, length);
#else
int err = 0;
if (length != DATA_PRODUCT_PARAMETERS_IN_N * 4) {
if (data)
free(data);
return Steinberg_kResultFalse;
}
# if DATA_PRODUCT_PARAMETERS_IN_N > 0
for (size_t i = 0; i < DATA_PRODUCT_PARAMETERS_IN_N; i++) {
union { float f; uint32_t u; } v;
Steinberg_int32 n;
state->lpVtbl->read(state, &v, 4, &n);
if (n != 4)
return Steinberg_kResultFalse;
v.u = ((uint32_t *)data)[i];
if (IS_BIG_ENDIAN)
v.u = SWAP_UINT32(v.u);
p->parametersIn[i] = parameterAdjust(parameterInData + i, v.f);
plugin_set_parameter(&p->p, parameterInData[i].index, p->parametersIn[i]);
if (isnan(v.f)) {
free(data);
return Steinberg_kResultFalse;
}
}
pluginStateLockCb(p);
for (size_t i = 0; i < DATA_PRODUCT_PARAMETERS_IN_N; i++) {
union { float f; uint32_t u; } v;
v.u = ((uint32_t *)data)[i];
if (IS_BIG_ENDIAN)
v.u = SWAP_UINT32(v.u);
pluginStateSetParameterCb(p, parameterInData[i].index, v.f);
}
pluginStateUnlockCb(p);
# endif
#endif
return Steinberg_kResultOk;
if (data)
free(data);
return err == 0 ? Steinberg_kResultOk : Steinberg_kResultFalse;
}
static Steinberg_tresult pluginGetState(void* thisInterface, struct Steinberg_IBStream* state) {
TRACE("plugin get state\n");
if (state == NULL)
return Steinberg_kResultFalse;
#ifdef DATA_STATE_DSP_CUSTOM
pluginInstance *p = (pluginInstance *)((char *)thisInterface - offsetof(pluginInstance, vtblIComponent));
p->state = state;
if (plugin_state_save(&p->p) != 0)
return Steinberg_kResultFalse;
#else
#ifdef DATA_STATE_DSP_CUSTOM
plugin_state_callbacks cbs = {
/* .handle = */ (void *)p,
/* .lock = */ pluginStateLockCb,
/* .unlock = */ pluginStateUnlockCb,
/* .write = */ pluginStateWriteCb,
# if DATA_PRODUCT_PARAMETERS_IN_N > 0
pluginInstance *p = (pluginInstance *)((char *)thisInterface - offsetof(pluginInstance, vtblIComponent));
/* .set_parameter = */ NULL
# endif
};
int err = plugin_state_save(&p->p, &cbs);
#else
int err = 0;
# if DATA_PRODUCT_PARAMETERS_IN_N > 0
size_t length = DATA_PRODUCT_PARAMETERS_IN_N * 4;
char *data = malloc(length);
if (data == NULL)
return Steinberg_kResultFalse;
pluginStateLockCb(p);
for (size_t i = 0; i < DATA_PRODUCT_PARAMETERS_IN_N; i++) {
union { float f; uint32_t u; } v;
v.f = p->parametersIn[i];
v.f = p->parametersInSync[i];
if (IS_BIG_ENDIAN)
v.u = SWAP_UINT32(v.u);
Steinberg_int32 n;
state->lpVtbl->write(state, &v, 4, &n);
if (n != 4)
return Steinberg_kResultFalse;
((uint32_t *)data)[i] = v.u;
}
pluginStateUnlockCb(p);
err = pluginStateWriteCb(p, data, length);
# endif
#endif
return Steinberg_kResultOk;
return err == 0 ? Steinberg_kResultOk : Steinberg_kResultFalse;
}
static Steinberg_Vst_IComponentVtbl pluginVtblIComponent = {
@ -1087,6 +1151,29 @@ static void controllerGetParamDataValuePtrs(controller *ctrl, size_t index, Para
}
#endif
#ifdef DATA_STATE_DSP_CUSTOM
static void controllerStateLockCb(void *handle) {
(void)handle;
}
static void controllerStateUnlockCb(void *handle) {
(void)handle;
}
# if DATA_PRODUCT_PARAMETERS_IN_N > 0
static void controllerStateSetParameterCb(void *handle, size_t index, float value) {
size_t i = index;
# ifdef DATA_PARAM_LATENCY_INDEX
if (i >= DATA_PARAM_LATENCY_INDEX)
i--;
# endif
controller *c = (controller *)handle;
size_t ii = parameterInfoToDataIndex[i];
c->parametersIn[ii] = parameterAdjust(parameterInData + ii, value);
}
# endif
#endif
#ifdef DATA_UI
# ifdef __linux__
@ -1354,10 +1441,6 @@ static Steinberg_tresult plugViewAttached(void* thisInterface, void* parent, Ste
/* .set_parameter_begin = */ plugViewSetParameterBeginCb,
/* .set_parameter = */ plugViewSetParameterCb,
/* .set_parameter_end = */ plugViewSetParameterEndCb
# else
/* .set_parameter_begin = */ NULL,
/* .set_parameter = */ NULL,
/* .set_parameter_end = */ NULL
# endif
};
v->ui = plugin_ui_create(1, parent, &cbs);
@ -1696,68 +1779,78 @@ static Steinberg_tresult controllerSetComponentState(void* thisInterface, struct
TRACE("controller set component state %p %p\n", thisInterface, (void *)state);
if (state == NULL)
return Steinberg_kResultFalse;
#if DATA_PRODUCT_PARAMETERS_IN_N > 0
char *data;
Steinberg_int64 length;
if (stateRead(state, &data, &length) != 0)
return Steinberg_kResultFalse;
controller *c = (controller *)((char *)thisInterface - offsetof(controller, vtblIEditController));
#ifdef DATA_STATE_DSP_CUSTOM
plugin_state_callbacks cbs = {
/* .handle = */ (void *)c,
/* .lock = */ controllerStateLockCb,
/* .unlock = */ controllerStateUnlockCb,
/* .write = */ NULL,
# if DATA_PRODUCT_PARAMETERS_IN_N > 0
/* .set_parameter = */ controllerStateSetParameterCb
# endif
};
int err = plugin_state_load(&cbs, data, length);
#else
int err = 0;
# if DATA_PRODUCT_PARAMETERS_IN_N > 0
for (size_t i = 0; i < DATA_PRODUCT_PARAMETERS_IN_N; i++) {
union { float f; uint32_t u; } v;
Steinberg_int32 n;
state->lpVtbl->read(state, &v, 4, &n);
if (n != 4)
v.u = ((uint32_t *)data)[i];
if (IS_BIG_ENDIAN)
v.u = SWAP_UINT32(v.u);
if (isnan(v.f)) {
free(data);
return Steinberg_kResultFalse;
}
}
for (size_t i = 0; i < DATA_PRODUCT_PARAMETERS_IN_N; i++) {
union { float f; uint32_t u; } v;
v.u = ((uint32_t *)data)[i];
if (IS_BIG_ENDIAN)
v.u = SWAP_UINT32(v.u);
c->parametersIn[i] = parameterAdjust(parameterInData + i, v.f);
}
#endif
TRACE(" ok\n");
return Steinberg_kResultTrue;
}
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");
if (state == NULL)
# endif
#endif
if (data)
free(data);
if (err != 0)
return Steinberg_kResultFalse;
#if DATA_PRODUCT_PARAMETERS_IN_N > 0
controller *c = (controller *)((char *)thisInterface - offsetof(controller, vtblIEditController));
for (size_t i = 0; i < DATA_PRODUCT_PARAMETERS_IN_N; i++) {
union { float f; uint32_t u; } v;
Steinberg_int32 n;
state->lpVtbl->read(state, &v, 4, &n);
if (n != 4)
return Steinberg_kResultFalse;
if (IS_BIG_ENDIAN)
v.u = SWAP_UINT32(v.u);
c->parametersIn[i] = parameterAdjust(parameterInData + i, v.f);
# ifdef DATA_UI
#if (DATA_PRODUCT_PARAMETERS_IN_N > 0) && defined(DATA_UI)
if (c->views)
for (size_t i = 0; i < DATA_PRODUCT_PARAMETERS_IN_N; i++)
for (size_t j = 0; j < c->viewsCount; j++)
plugViewUpdateParameterIn(c->views[j], i);
#endif
}
#endif
TRACE(" ok\n");
return Steinberg_kResultOk;
}
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");
if (state == NULL)
return Steinberg_kResultFalse;
#if DATA_PRODUCT_PARAMETERS_IN_N > 0
controller *c = (controller *)((char *)thisInterface - offsetof(controller, vtblIEditController));
for (size_t i = 0; i < DATA_PRODUCT_PARAMETERS_IN_N; i++) {
union { float f; uint32_t u; } v;
v.f = c->parametersIn[i];
if (IS_BIG_ENDIAN)
v.u = SWAP_UINT32(v.u);
Steinberg_int32 n;
state->lpVtbl->write(state, &v, 4, &n);
if (n != 4)
return Steinberg_kResultFalse;
static Steinberg_tresult controllerSetState(void* thisInterface, struct Steinberg_IBStream* state) {
(void)thisInterface;
(void)state;
TRACE("controller set state\n");
return Steinberg_kNotImplemented;
}
#endif
return Steinberg_kResultOk;
static Steinberg_tresult controllerGetState(void* thisInterface, struct Steinberg_IBStream* state) {
(void)thisInterface;
(void)state;
TRACE("controller get state\n");
return Steinberg_kNotImplemented;
}
static Steinberg_int32 controllerGetParameterCount(void* thisInterface) {
@ -1967,9 +2060,9 @@ static Steinberg_tresult controllerSetParamNormalized(void* thisInterface, Stein
for (size_t i = 0; i < c->viewsCount; i++)
if(c->views[i]) {
if (parameterInfo[pi].flags & Steinberg_Vst_ParameterInfo_ParameterFlags_kIsReadOnly)
plugViewUpdateParameterOut(c->views[i], pi);
plugViewUpdateParameterOut(c->views[i], parameterInfoToDataIndex[pi]);
else
plugViewUpdateParameterIn(c->views[i], pi);
plugViewUpdateParameterIn(c->views[i], parameterInfoToDataIndex[pi]);
}
# endif
return Steinberg_kResultTrue;

View File

@ -19,10 +19,9 @@
*/
#include <stdint.h>
#include <math.h>
typedef struct plugin {
plugin_callbacks cbs;
float sample_rate;
size_t delay_line_length;
@ -38,8 +37,9 @@ typedef struct plugin {
float yz1;
} plugin;
static void plugin_init(plugin *instance, plugin_callbacks *cbs) {
instance->cbs = *cbs;
static void plugin_init(plugin *instance, const plugin_callbacks *cbs) {
(void)instance;
(void)cbs;
}
static void plugin_fini(plugin *instance) {
@ -139,32 +139,36 @@ static float parse_float(const uint8_t *data) {
return v.f;
}
static int plugin_state_save(plugin *instance) {
static int plugin_state_save(plugin *instance, const plugin_state_callbacks *cbs) {
uint8_t data[13];
instance->cbs.lock_state(instance->cbs.handle);
cbs->lock(cbs->handle);
const float gain = instance->gain;
const float delay = instance->delay;
const float cutoff = instance->cutoff;
const char bypass = instance->bypass;
instance->cbs.unlock_state(instance->cbs.handle);
cbs->unlock(cbs->handle);
serialize_float(data, gain);
serialize_float(data + 4, delay);
serialize_float(data + 8, cutoff);
data[12] = bypass ? 1 : 0;
return instance->cbs.write_state(instance->cbs.handle, (const char *)data, 13);
return cbs->write(cbs->handle, (const char *)data, 13);
}
static void plugin_state_load(plugin *instance, const char *data, size_t length) {
(void)length;
static int plugin_state_load(const plugin_state_callbacks *cbs, const char *data, size_t length) {
if (length != 13)
return -1;
const uint8_t *d = (const uint8_t *)data;
const float gain = parse_float(d);
const float delay = parse_float(d + 4);
const float cutoff = parse_float(d + 8);
const float bypass = d[12] ? 1.f : 0.f;
instance->cbs.lock_state(instance->cbs.handle);
instance->cbs.load_parameter(instance->cbs.handle, plugin_parameter_gain, gain);
instance->cbs.load_parameter(instance->cbs.handle, plugin_parameter_delay, delay);
instance->cbs.load_parameter(instance->cbs.handle, plugin_parameter_cutoff, cutoff);
instance->cbs.load_parameter(instance->cbs.handle, plugin_parameter_bypass, bypass);
instance->cbs.unlock_state(instance->cbs.handle);
if (isnan(gain) || isnan(delay) || isnan(cutoff))
return -1;
cbs->lock(cbs->handle);
cbs->set_parameter(cbs->handle, plugin_parameter_gain, gain);
cbs->set_parameter(cbs->handle, plugin_parameter_delay, delay);
cbs->set_parameter(cbs->handle, plugin_parameter_cutoff, cutoff);
cbs->set_parameter(cbs->handle, plugin_parameter_bypass, bypass);
cbs->unlock(cbs->handle);
return 0;
}