refined state api and implemented in lv2 and vst3
This commit is contained in:
parent
440b49c14d
commit
5663b4389c
@ -26,16 +26,20 @@ typedef struct {
|
|||||||
const char * format;
|
const char * format;
|
||||||
const char * (*get_bindir)(void *handle);
|
const char * (*get_bindir)(void *handle);
|
||||||
const char * (*get_datadir)(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;
|
} 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 {
|
typedef struct {
|
||||||
void * handle;
|
void * handle;
|
||||||
const char * format;
|
const char * format;
|
||||||
|
@ -127,6 +127,7 @@ typedef struct {
|
|||||||
#ifdef DATA_STATE_DSP_CUSTOM
|
#ifdef DATA_STATE_DSP_CUSTOM
|
||||||
LV2_URID uri_atom_Chunk;
|
LV2_URID uri_atom_Chunk;
|
||||||
LV2_URID uri_state_data;
|
LV2_URID uri_state_data;
|
||||||
|
plugin_state_callbacks state_cbs;
|
||||||
LV2_State_Store_Function state_store;
|
LV2_State_Store_Function state_store;
|
||||||
LV2_State_Handle state_handle;
|
LV2_State_Handle state_handle;
|
||||||
#endif
|
#endif
|
||||||
@ -138,13 +139,25 @@ static const char * get_bundle_path_cb(void *handle) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#ifdef DATA_STATE_DSP_CUSTOM
|
#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;
|
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);
|
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
|
# 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;
|
plugin_instance * i = (plugin_instance *)handle;
|
||||||
size_t idx = index_to_param[index] - CONTROL_INPUT_INDEX_OFFSET;
|
size_t idx = index_to_param[index] - CONTROL_INPUT_INDEX_OFFSET;
|
||||||
value = adjust_param(idx, value);
|
value = adjust_param(idx, value);
|
||||||
@ -152,18 +165,6 @@ static void load_parameter_cb(void *handle, size_t index, float value) {
|
|||||||
i->loaded = 1;
|
i->loaded = 1;
|
||||||
}
|
}
|
||||||
# endif
|
# 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
|
#endif
|
||||||
|
|
||||||
static LV2_Handle instantiate(const struct LV2_Descriptor * descriptor, double sample_rate, const char * bundle_path, const LV2_Feature * const * features) {
|
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,
|
/* .handle = */ (void *)instance,
|
||||||
/* .format = */ "lv2",
|
/* .format = */ "lv2",
|
||||||
/* .get_bindir = */ get_bundle_path_cb,
|
/* .get_bindir = */ get_bundle_path_cb,
|
||||||
/* .get_datadir = */ 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
|
|
||||||
};
|
};
|
||||||
plugin_init(&instance->p, &cbs);
|
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_store = store;
|
||||||
i->state_handle = handle;
|
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) {
|
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");
|
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;
|
return LV2_STATE_ERR_NO_PROPERTY;
|
||||||
}
|
}
|
||||||
plugin_state_load(&i->p, data, length);
|
plugin_state_callbacks cbs = {
|
||||||
return LV2_STATE_SUCCESS;
|
/* .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) {
|
static const void * extension_data(const char * uri) {
|
||||||
|
@ -53,6 +53,17 @@
|
|||||||
#include <pmmintrin.h>
|
#include <pmmintrin.h>
|
||||||
#endif
|
#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:
|
// COM in C doc:
|
||||||
// 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
|
||||||
@ -229,6 +240,30 @@ static double parameterUnmapByIndex(size_t index, double v) {
|
|||||||
}
|
}
|
||||||
#endif
|
#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 {
|
typedef struct pluginInstance {
|
||||||
Steinberg_Vst_IComponentVtbl * vtblIComponent;
|
Steinberg_Vst_IComponentVtbl * vtblIComponent;
|
||||||
Steinberg_Vst_IAudioProcessorVtbl * vtblIAudioProcessor;
|
Steinberg_Vst_IAudioProcessorVtbl * vtblIAudioProcessor;
|
||||||
@ -239,6 +274,10 @@ typedef struct pluginInstance {
|
|||||||
float sampleRate;
|
float sampleRate;
|
||||||
#if DATA_PRODUCT_PARAMETERS_IN_N > 0
|
#if DATA_PRODUCT_PARAMETERS_IN_N > 0
|
||||||
float parametersIn[DATA_PRODUCT_PARAMETERS_IN_N];
|
float parametersIn[DATA_PRODUCT_PARAMETERS_IN_N];
|
||||||
|
float parametersInSync[DATA_PRODUCT_PARAMETERS_IN_N];
|
||||||
|
atomic_flag syncLockFlag;
|
||||||
|
char synced;
|
||||||
|
char loaded;
|
||||||
#endif
|
#endif
|
||||||
#if DATA_PRODUCT_PARAMETERS_OUT_N > 0
|
#if DATA_PRODUCT_PARAMETERS_OUT_N > 0
|
||||||
float parametersOut[DATA_PRODUCT_PARAMETERS_OUT_N];
|
float parametersOut[DATA_PRODUCT_PARAMETERS_OUT_N];
|
||||||
@ -266,16 +305,25 @@ 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 DATA_STATE_DSP_CUSTOM
|
|
||||||
struct Steinberg_IBStream * state;
|
struct Steinberg_IBStream * state;
|
||||||
#endif
|
|
||||||
} pluginInstance;
|
} pluginInstance;
|
||||||
|
|
||||||
static Steinberg_Vst_IComponentVtbl pluginVtblIComponent;
|
static Steinberg_Vst_IComponentVtbl pluginVtblIComponent;
|
||||||
static Steinberg_Vst_IAudioProcessorVtbl pluginVtblIAudioProcessor;
|
static Steinberg_Vst_IAudioProcessorVtbl pluginVtblIAudioProcessor;
|
||||||
|
|
||||||
#ifdef DATA_STATE_DSP_CUSTOM
|
static void pluginStateLockCb(void *handle) {
|
||||||
static int pluginWriteStateCb(void *handle, const char *data, size_t length) {
|
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;
|
pluginInstance *p = (pluginInstance *)handle;
|
||||||
size_t written = 0;
|
size_t written = 0;
|
||||||
while (written < length) {
|
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
|
# 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;
|
size_t i = index;
|
||||||
# ifdef DATA_PARAM_LATENCY_INDEX
|
# ifdef DATA_PARAM_LATENCY_INDEX
|
||||||
if (i >= 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
|
# endif
|
||||||
pluginInstance *p = (pluginInstance *)handle;
|
pluginInstance *p = (pluginInstance *)handle;
|
||||||
size_t ii = parameterInfoToDataIndex[i];
|
size_t ii = parameterInfoToDataIndex[i];
|
||||||
p->parametersIn[ii] = parameterAdjust(parameterInData + ii, value);
|
p->parametersInSync[ii] = parameterAdjust(parameterInData + ii, value);
|
||||||
plugin_set_parameter(&p->p, index, p->parametersIn[ii]);
|
p->loaded = 1;
|
||||||
}
|
}
|
||||||
# endif
|
# endif
|
||||||
#endif
|
|
||||||
|
|
||||||
static Steinberg_tresult pluginQueryInterface(pluginInstance *p, const Steinberg_TUID iid, void ** obj) {
|
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...
|
// 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,
|
/* .handle = */ (void *)p,
|
||||||
/* .format = */ "vst3",
|
/* .format = */ "vst3",
|
||||||
/* .get_bindir = */ getBindirCb,
|
/* .get_bindir = */ getBindirCb,
|
||||||
/* .get_datadir = */ getDatadirCb,
|
/* .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
|
|
||||||
};
|
};
|
||||||
plugin_init(&p->p, &cbs);
|
plugin_init(&p->p, &cbs);
|
||||||
#if DATA_PRODUCT_PARAMETERS_IN_N > 0
|
#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;
|
p->parametersIn[i] = parameterInData[i].def;
|
||||||
plugin_set_parameter(&p->p, parameterInData[i].index, 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
|
#endif
|
||||||
#if DATA_PRODUCT_PARAMETERS_OUT_N > 0
|
#if DATA_PRODUCT_PARAMETERS_OUT_N > 0
|
||||||
for (size_t i = 0; i < DATA_PRODUCT_PARAMETERS_OUT_N; i++)
|
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");
|
TRACE("plugin set state\n");
|
||||||
if (state == NULL)
|
if (state == NULL)
|
||||||
return Steinberg_kResultFalse;
|
return Steinberg_kResultFalse;
|
||||||
#ifdef DATA_STATE_DSP_CUSTOM
|
|
||||||
pluginInstance *p = (pluginInstance *)((char *)thisInterface - offsetof(pluginInstance, vtblIComponent));
|
char *data;
|
||||||
if (state->lpVtbl->seek(state, 0, Steinberg_IBStream_IStreamSeekMode_kIBSeekEnd, NULL) != Steinberg_kResultOk)
|
|
||||||
return Steinberg_kResultFalse;
|
|
||||||
Steinberg_int64 length;
|
Steinberg_int64 length;
|
||||||
if (state->lpVtbl->tell(state, &length) != Steinberg_kResultOk)
|
if (stateRead(state, &data, &length) != 0)
|
||||||
return Steinberg_kResultFalse;
|
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));
|
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++) {
|
for (size_t i = 0; i < DATA_PRODUCT_PARAMETERS_IN_N; i++) {
|
||||||
union { float f; uint32_t u; } v;
|
union { float f; uint32_t u; } v;
|
||||||
Steinberg_int32 n;
|
v.u = ((uint32_t *)data)[i];
|
||||||
state->lpVtbl->read(state, &v, 4, &n);
|
|
||||||
if (n != 4)
|
|
||||||
return Steinberg_kResultFalse;
|
|
||||||
if (IS_BIG_ENDIAN)
|
if (IS_BIG_ENDIAN)
|
||||||
v.u = SWAP_UINT32(v.u);
|
v.u = SWAP_UINT32(v.u);
|
||||||
p->parametersIn[i] = parameterAdjust(parameterInData + i, v.f);
|
if (isnan(v.f)) {
|
||||||
plugin_set_parameter(&p->p, parameterInData[i].index, p->parametersIn[i]);
|
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
|
||||||
#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) {
|
static Steinberg_tresult pluginGetState(void* thisInterface, struct Steinberg_IBStream* state) {
|
||||||
TRACE("plugin get state\n");
|
TRACE("plugin get state\n");
|
||||||
if (state == NULL)
|
if (state == NULL)
|
||||||
return Steinberg_kResultFalse;
|
return Steinberg_kResultFalse;
|
||||||
#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)
|
#ifdef DATA_STATE_DSP_CUSTOM
|
||||||
return Steinberg_kResultFalse;
|
plugin_state_callbacks cbs = {
|
||||||
#else
|
/* .handle = */ (void *)p,
|
||||||
|
/* .lock = */ pluginStateLockCb,
|
||||||
|
/* .unlock = */ pluginStateUnlockCb,
|
||||||
|
/* .write = */ pluginStateWriteCb,
|
||||||
# if DATA_PRODUCT_PARAMETERS_IN_N > 0
|
# 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++) {
|
for (size_t i = 0; i < DATA_PRODUCT_PARAMETERS_IN_N; i++) {
|
||||||
union { float f; uint32_t u; } v;
|
union { float f; uint32_t u; } v;
|
||||||
v.f = p->parametersIn[i];
|
v.f = p->parametersInSync[i];
|
||||||
if (IS_BIG_ENDIAN)
|
if (IS_BIG_ENDIAN)
|
||||||
v.u = SWAP_UINT32(v.u);
|
v.u = SWAP_UINT32(v.u);
|
||||||
Steinberg_int32 n;
|
((uint32_t *)data)[i] = v.u;
|
||||||
state->lpVtbl->write(state, &v, 4, &n);
|
|
||||||
if (n != 4)
|
|
||||||
return Steinberg_kResultFalse;
|
|
||||||
}
|
}
|
||||||
|
pluginStateUnlockCb(p);
|
||||||
|
err = pluginStateWriteCb(p, data, length);
|
||||||
# endif
|
# endif
|
||||||
#endif
|
#endif
|
||||||
return Steinberg_kResultOk;
|
return err == 0 ? Steinberg_kResultOk : Steinberg_kResultFalse;
|
||||||
}
|
}
|
||||||
|
|
||||||
static Steinberg_Vst_IComponentVtbl pluginVtblIComponent = {
|
static Steinberg_Vst_IComponentVtbl pluginVtblIComponent = {
|
||||||
@ -1087,6 +1151,29 @@ static void controllerGetParamDataValuePtrs(controller *ctrl, size_t index, Para
|
|||||||
}
|
}
|
||||||
#endif
|
#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 DATA_UI
|
||||||
|
|
||||||
# ifdef __linux__
|
# ifdef __linux__
|
||||||
@ -1354,10 +1441,6 @@ static Steinberg_tresult plugViewAttached(void* thisInterface, void* parent, Ste
|
|||||||
/* .set_parameter_begin = */ plugViewSetParameterBeginCb,
|
/* .set_parameter_begin = */ plugViewSetParameterBeginCb,
|
||||||
/* .set_parameter = */ plugViewSetParameterCb,
|
/* .set_parameter = */ plugViewSetParameterCb,
|
||||||
/* .set_parameter_end = */ plugViewSetParameterEndCb
|
/* .set_parameter_end = */ plugViewSetParameterEndCb
|
||||||
# else
|
|
||||||
/* .set_parameter_begin = */ NULL,
|
|
||||||
/* .set_parameter = */ NULL,
|
|
||||||
/* .set_parameter_end = */ NULL
|
|
||||||
# endif
|
# endif
|
||||||
};
|
};
|
||||||
v->ui = plugin_ui_create(1, parent, &cbs);
|
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);
|
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_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));
|
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++) {
|
for (size_t i = 0; i < DATA_PRODUCT_PARAMETERS_IN_N; i++) {
|
||||||
union { float f; uint32_t u; } v;
|
union { float f; uint32_t u; } v;
|
||||||
Steinberg_int32 n;
|
v.u = ((uint32_t *)data)[i];
|
||||||
state->lpVtbl->read(state, &v, 4, &n);
|
if (IS_BIG_ENDIAN)
|
||||||
if (n != 4)
|
v.u = SWAP_UINT32(v.u);
|
||||||
|
if (isnan(v.f)) {
|
||||||
|
free(data);
|
||||||
return Steinberg_kResultFalse;
|
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)
|
if (IS_BIG_ENDIAN)
|
||||||
v.u = SWAP_UINT32(v.u);
|
v.u = SWAP_UINT32(v.u);
|
||||||
c->parametersIn[i] = parameterAdjust(parameterInData + i, v.f);
|
c->parametersIn[i] = parameterAdjust(parameterInData + i, v.f);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
if (data)
|
||||||
|
free(data);
|
||||||
|
if (err != 0)
|
||||||
|
return Steinberg_kResultFalse;
|
||||||
|
|
||||||
|
#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
|
||||||
|
|
||||||
TRACE(" ok\n");
|
TRACE(" ok\n");
|
||||||
return Steinberg_kResultTrue;
|
return Steinberg_kResultOk;
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
(void)thisInterface;
|
||||||
|
(void)state;
|
||||||
|
|
||||||
TRACE("controller set state\n");
|
TRACE("controller set state\n");
|
||||||
if (state == NULL)
|
return Steinberg_kNotImplemented;
|
||||||
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 (c->views)
|
|
||||||
for (size_t j = 0; j < c->viewsCount; j++)
|
|
||||||
plugViewUpdateParameterIn(c->views[j], i);
|
|
||||||
# endif
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
return Steinberg_kResultOk;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
(void)thisInterface;
|
||||||
|
(void)state;
|
||||||
|
|
||||||
TRACE("controller get state\n");
|
TRACE("controller get state\n");
|
||||||
if (state == NULL)
|
return Steinberg_kNotImplemented;
|
||||||
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;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
return Steinberg_kResultOk;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static Steinberg_int32 controllerGetParameterCount(void* thisInterface) {
|
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++)
|
for (size_t i = 0; i < c->viewsCount; i++)
|
||||||
if(c->views[i]) {
|
if(c->views[i]) {
|
||||||
if (parameterInfo[pi].flags & Steinberg_Vst_ParameterInfo_ParameterFlags_kIsReadOnly)
|
if (parameterInfo[pi].flags & Steinberg_Vst_ParameterInfo_ParameterFlags_kIsReadOnly)
|
||||||
plugViewUpdateParameterOut(c->views[i], pi);
|
plugViewUpdateParameterOut(c->views[i], parameterInfoToDataIndex[pi]);
|
||||||
else
|
else
|
||||||
plugViewUpdateParameterIn(c->views[i], pi);
|
plugViewUpdateParameterIn(c->views[i], parameterInfoToDataIndex[pi]);
|
||||||
}
|
}
|
||||||
# endif
|
# endif
|
||||||
return Steinberg_kResultTrue;
|
return Steinberg_kResultTrue;
|
||||||
|
@ -19,10 +19,9 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
typedef struct plugin {
|
typedef struct plugin {
|
||||||
plugin_callbacks cbs;
|
|
||||||
|
|
||||||
float sample_rate;
|
float sample_rate;
|
||||||
size_t delay_line_length;
|
size_t delay_line_length;
|
||||||
|
|
||||||
@ -38,8 +37,9 @@ typedef struct plugin {
|
|||||||
float yz1;
|
float yz1;
|
||||||
} plugin;
|
} plugin;
|
||||||
|
|
||||||
static void plugin_init(plugin *instance, plugin_callbacks *cbs) {
|
static void plugin_init(plugin *instance, const plugin_callbacks *cbs) {
|
||||||
instance->cbs = *cbs;
|
(void)instance;
|
||||||
|
(void)cbs;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void plugin_fini(plugin *instance) {
|
static void plugin_fini(plugin *instance) {
|
||||||
@ -139,32 +139,36 @@ static float parse_float(const uint8_t *data) {
|
|||||||
return v.f;
|
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];
|
uint8_t data[13];
|
||||||
instance->cbs.lock_state(instance->cbs.handle);
|
cbs->lock(cbs->handle);
|
||||||
const float gain = instance->gain;
|
const float gain = instance->gain;
|
||||||
const float delay = instance->delay;
|
const float delay = instance->delay;
|
||||||
const float cutoff = instance->cutoff;
|
const float cutoff = instance->cutoff;
|
||||||
const char bypass = instance->bypass;
|
const char bypass = instance->bypass;
|
||||||
instance->cbs.unlock_state(instance->cbs.handle);
|
cbs->unlock(cbs->handle);
|
||||||
serialize_float(data, gain);
|
serialize_float(data, gain);
|
||||||
serialize_float(data + 4, delay);
|
serialize_float(data + 4, delay);
|
||||||
serialize_float(data + 8, cutoff);
|
serialize_float(data + 8, cutoff);
|
||||||
data[12] = bypass ? 1 : 0;
|
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) {
|
static int plugin_state_load(const plugin_state_callbacks *cbs, const char *data, size_t length) {
|
||||||
(void)length;
|
if (length != 13)
|
||||||
|
return -1;
|
||||||
const uint8_t *d = (const uint8_t *)data;
|
const uint8_t *d = (const uint8_t *)data;
|
||||||
const float gain = parse_float(d);
|
const float gain = parse_float(d);
|
||||||
const float delay = parse_float(d + 4);
|
const float delay = parse_float(d + 4);
|
||||||
const float cutoff = parse_float(d + 8);
|
const float cutoff = parse_float(d + 8);
|
||||||
const float bypass = d[12] ? 1.f : 0.f;
|
const float bypass = d[12] ? 1.f : 0.f;
|
||||||
instance->cbs.lock_state(instance->cbs.handle);
|
if (isnan(gain) || isnan(delay) || isnan(cutoff))
|
||||||
instance->cbs.load_parameter(instance->cbs.handle, plugin_parameter_gain, gain);
|
return -1;
|
||||||
instance->cbs.load_parameter(instance->cbs.handle, plugin_parameter_delay, delay);
|
cbs->lock(cbs->handle);
|
||||||
instance->cbs.load_parameter(instance->cbs.handle, plugin_parameter_cutoff, cutoff);
|
cbs->set_parameter(cbs->handle, plugin_parameter_gain, gain);
|
||||||
instance->cbs.load_parameter(instance->cbs.handle, plugin_parameter_bypass, bypass);
|
cbs->set_parameter(cbs->handle, plugin_parameter_delay, delay);
|
||||||
instance->cbs.unlock_state(instance->cbs.handle);
|
cbs->set_parameter(cbs->handle, plugin_parameter_cutoff, cutoff);
|
||||||
|
cbs->set_parameter(cbs->handle, plugin_parameter_bypass, bypass);
|
||||||
|
cbs->unlock(cbs->handle);
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user