diff --git a/templates/api/src/plugin_api.h b/templates/api/src/plugin_api.h index a1ce7a5..327db47 100644 --- a/templates/api/src/plugin_api.h +++ b/templates/api/src/plugin_api.h @@ -28,6 +28,8 @@ typedef struct { const char * (*get_datadir)(void *handle); int (*write_state)(void *handle, const char *data, size_t length); void (*load_parameter)(void *handle, size_t index, float value); + void (*lock_state)(void *handle); + void (*unlock_state)(void *handle); } plugin_callbacks; typedef struct { diff --git a/templates/lv2/src/lv2.c b/templates/lv2/src/lv2.c index 37ebe8c..1355e3e 100644 --- a/templates/lv2/src/lv2.c +++ b/templates/lv2/src/lv2.c @@ -57,6 +57,24 @@ # include #endif +#if (DATA_PRODUCT_CONTROL_INPUTS_N > 0) && defined(DATA_STATE_DSP_CUSTOM) +# include +# if defined(_WIN32) || defined(__CYGWIN__) +# include +# define yield SwitchToThread +# else +# include +# define yield sched_yield +# endif +#endif + +#define CONTROL_INPUT_INDEX_OFFSET ( \ + DATA_PRODUCT_AUDIO_INPUT_CHANNELS_N \ + + DATA_PRODUCT_AUDIO_OUTPUT_CHANNELS_N \ + + DATA_PRODUCT_MIDI_INPUTS_N \ + + DATA_PRODUCT_MIDI_OUTPUTS_N ) +#define CONTROL_OUTPUT_INDEX_OFFSET (CONTROL_INPUT_INDEX_OFFSET + DATA_PRODUCT_CONTROL_INPUTS_N) + #if DATA_PRODUCT_CONTROL_INPUTS_N > 0 static inline float clampf(float x, float m, float M) { return x < m ? m : (x > M ? M : x); @@ -92,6 +110,12 @@ typedef struct { #endif #if DATA_PRODUCT_CONTROL_INPUTS_N > 0 float params[DATA_PRODUCT_CONTROL_INPUTS_N]; +# ifdef DATA_STATE_DSP_CUSTOM + float params_sync[DATA_PRODUCT_CONTROL_INPUTS_N]; + atomic_flag sync_lock_flag; + char synced; + char loaded; +# endif #endif void * mem; char * bundle_path; @@ -122,12 +146,24 @@ static int write_state_cb(void *handle, const char *data, size_t length) { # if DATA_PRODUCT_CONTROL_INPUTS_N > 0 static void load_parameter_cb(void *handle, size_t index, float value) { plugin_instance * i = (plugin_instance *)handle; - size_t idx = index_to_param[index]; + size_t idx = index_to_param[index] - CONTROL_INPUT_INDEX_OFFSET; value = adjust_param(idx, value); - i->params[idx] = value; - plugin_set_parameter(&i->p, index, value); + i->params_sync[idx] = 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) { @@ -175,13 +211,17 @@ static LV2_Handle instantiate(const struct LV2_Descriptor * descriptor, double s # ifdef DATA_STATE_DSP_CUSTOM /* .write_state = */ write_state_cb, # if DATA_PRODUCT_CONTROL_INPUTS_N > 0 - /* .load_parameter = */ load_parameter_cb + /* .load_parameter = */ load_parameter_cb, # else - /* .load_parameter = */ NULL + /* .load_parameter = */ NULL, # endif + /* .lock_state = */ lock_state_cb, + /* .unlock_state = */ unlock_state_cb # else /* .write_state = */ NULL, - /* .load_parameter = */ NULL + /* .load_parameter = */ NULL, + /* .state_lock = */ NULL, + /* .state_unlock = */ NULL # endif }; plugin_init(&instance->p, &cbs); @@ -275,6 +315,15 @@ static void activate(LV2_Handle instance) { i->params[j] = i->c[j] != NULL ? *i->c[j] : param_data[j].def; plugin_set_parameter(&i->p, param_data[j].index, i->params[j]); } +# ifdef DATA_STATE_DSP_CUSTOM + for (uint32_t j = 0; j < DATA_PRODUCT_CONTROL_INPUTS_N; j++) + i->params_sync[j] = i->params[j]; + // why is this not correct? + // i->sync_lock_flag = ATOMIC_FLAG_INIT; + atomic_flag_clear(&i->sync_lock_flag); + i->synced = 1; + i->loaded = 0; +# endif #endif plugin_reset(&i->p); } @@ -295,15 +344,47 @@ static void run(LV2_Handle instance, uint32_t sample_count) { #endif #if DATA_PRODUCT_CONTROL_INPUTS_N > 0 - for (uint32_t j = 0; j < DATA_PRODUCT_CONTROL_INPUTS_N; j++) { - if (i->c[j] == NULL) - continue; - float v = adjust_param(j, *i->c[j]); - if (v != i->params[j]) { - i->params[j] = v; - plugin_set_parameter(&i->p, param_data[j].index, v); +# ifdef DATA_STATE_DSP_CUSTOM + if (!atomic_flag_test_and_set(&i->sync_lock_flag)) { + if (!i->synced) { + if (i->loaded) { + for (uint32_t j = 0; j < DATA_PRODUCT_CONTROL_INPUTS_N; j++) { + i->params[j] = i->params_sync[j]; + plugin_set_parameter(&i->p, param_data[j].index, i->params[j]); + } + } else { + for (uint32_t j = 0; j < DATA_PRODUCT_CONTROL_INPUTS_N; j++) + if (i->params[j] != i->params_sync[j]) { + i->params_sync[j] = i->params[j]; + plugin_set_parameter(&i->p, param_data[j].index, i->params[j]); + } + } + } + i->synced = 1; + i->loaded = 0; + atomic_flag_clear(&i->sync_lock_flag); +# endif + + for (uint32_t j = 0; j < DATA_PRODUCT_CONTROL_INPUTS_N; j++) { + if (i->c[j] == NULL) + continue; + float v = adjust_param(j, *i->c[j]); + if (v != i->params[j]) { + i->params[j] = v; + plugin_set_parameter(&i->p, param_data[j].index, v); + } + } +# ifdef DATA_STATE_DSP_CUSTOM + } else { + for (uint32_t j = 0; j < DATA_PRODUCT_CONTROL_INPUTS_N; j++) { + if (i->c[j] == NULL) + continue; + float v = adjust_param(j, *i->c[j]); + if (v != i->params[j]) + i->params[j] = v; } } +# endif #endif #if DATA_PRODUCT_MIDI_INPUTS_N > 0 @@ -429,13 +510,6 @@ typedef struct { # endif } ui_instance; -# define CONTROL_INPUT_INDEX_OFFSET ( \ - DATA_PRODUCT_AUDIO_INPUT_CHANNELS_N \ - + DATA_PRODUCT_AUDIO_OUTPUT_CHANNELS_N \ - + DATA_PRODUCT_MIDI_INPUTS_N \ - + DATA_PRODUCT_MIDI_OUTPUTS_N ) -# define CONTROL_OUTPUT_INDEX_OFFSET (CONTROL_INPUT_INDEX_OFFSET + DATA_PRODUCT_CONTROL_INPUTS_N) - static const char * ui_get_bundle_path_cb(void *handle) { ui_instance *instance = (ui_instance *)handle; return instance->bundle_path; diff --git a/test/plugin.h b/test/plugin.h index d64da73..45b6bde 100644 --- a/test/plugin.h +++ b/test/plugin.h @@ -141,18 +141,30 @@ static float parse_float(const uint8_t *data) { static int plugin_state_save(plugin *instance) { uint8_t data[13]; - serialize_float(data, instance->gain); - serialize_float(data + 4, instance->delay); - serialize_float(data + 8, instance->cutoff); - data[12] = instance->bypass ? 1 : 0; + instance->cbs.lock_state(instance->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); + 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); } static void plugin_state_load(plugin *instance, const char *data, size_t length) { (void)length; const uint8_t *d = (const uint8_t *)data; - instance->cbs.load_parameter(instance->cbs.handle, plugin_parameter_gain, parse_float(d)); - instance->cbs.load_parameter(instance->cbs.handle, plugin_parameter_delay, parse_float(d + 4)); - instance->cbs.load_parameter(instance->cbs.handle, plugin_parameter_cutoff, parse_float(d + 8)); - instance->cbs.load_parameter(instance->cbs.handle, plugin_parameter_bypass, d[12] ? 1.f : 0.f); + 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); }