/* * Tibia * * Copyright (C) 2024, 2025 Orastron Srl unipersonale * * Tibia is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3 of the License. * * Tibia is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Tibia. If not, see . * * File author: Stefano D'Angelo */ #include #include #include "data.h" #include "plugin_api.h" #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wunused-function" #include "plugin.h" #ifdef DATA_UI # include "plugin_ui.h" #endif #pragma GCC diagnostic pop #include #include #include #include #include #if (DATA_PRODUCT_MIDI_INPUTS_N + DATA_PRODUCT_MIDI_OUTPUTS_N > 0) || defined(DATA_STATE_DSP_CUSTOM) # include # if DATA_PRODUCT_MIDI_INPUTS_N + DATA_PRODUCT_MIDI_OUTPUTS_N > 0 # include # include # endif #endif #ifdef DATA_UI # include #endif #ifdef DATA_STATE_DSP_CUSTOM # include #endif #if DATA_PRODUCT_USE_PARAMETERS # include "lv2/lv2plug.in/ns/ext/patch/patch.h" # include #endif #include #if defined(__i386__) || defined(__x86_64__) # include # 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); } static float adjust_param(size_t index, float value) { if (param_data[index].flags & DATA_PARAM_BYPASS) value = value > 0.f ? 0.f : 1.f; else if (param_data[index].flags & DATA_PARAM_TOGGLED) value = value > 0.f ? 1.f : 0.f; else if (param_data[index].flags & DATA_PARAM_INTEGER) value = (int32_t)(value + (value >= 0.f ? 0.5f : -0.5f)); return clampf(value, param_data[index].min, param_data[index].max); } #endif typedef struct { plugin p; #if DATA_PRODUCT_AUDIO_INPUT_CHANNELS_N > 0 const float * x[DATA_PRODUCT_AUDIO_INPUT_CHANNELS_N]; #endif #if DATA_PRODUCT_AUDIO_OUTPUT_CHANNELS_N > 0 float * y[DATA_PRODUCT_AUDIO_OUTPUT_CHANNELS_N]; #endif #if DATA_PRODUCT_MIDI_INPUTS_N > 0 const LV2_Atom_Sequence * x_midi[DATA_PRODUCT_MIDI_INPUTS_N]; #endif #if DATA_PRODUCT_MIDI_OUTPUTS_N > 0 LV2_Atom_Sequence * y_midi[DATA_PRODUCT_MIDI_OUTPUTS_N]; #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; LV2_Log_Logger logger; LV2_URID_Map * map; #if DATA_PRODUCT_MIDI_INPUTS_N + DATA_PRODUCT_MIDI_OUTPUTS_N > 0 LV2_URID uri_midi_MidiEvent; #endif #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 #if (DATA_PRODUCT_CONTROL_INPUTS_N + DATA_PRODUCT_CONTROL_OUTPUTS_N) > 0 # if DATA_PRODUCT_USE_PARAMETERS LV2_URID uri_atom_Blank; LV2_URID uri_atom_Object; LV2_URID uri_atom_URID; LV2_URID uri_atom_Float; LV2_URID uri_atom_Bool; LV2_URID uri_patch_Set; LV2_URID uri_patch_Get; LV2_URID uri_patch_property; LV2_URID uri_patch_value; LV2_URID uri_parameters[DATA_PRODUCT_CONTROL_INPUTS_N + DATA_PRODUCT_CONTROL_OUTPUTS_N]; // TODO: Fix 0 params case # if DATA_PRODUCT_IPM const LV2_Atom_Sequence *controlIn; # endif # if DATA_PRODUCT_OPM const LV2_Atom_Sequence *controlOut; LV2_Atom_Forge forge; LV2_Atom_Float output_parameter_atoms[DATA_PRODUCT_CONTROL_OUTPUTS_N]; # endif # else float *c[DATA_PRODUCT_CONTROL_INPUTS_N + DATA_PRODUCT_CONTROL_OUTPUTS_N]; # endif #endif } plugin_instance; static const char * get_bundle_path_cb(void *handle) { plugin_instance *instance = (plugin_instance *)handle; return instance->bundle_path; } #ifdef DATA_STATE_DSP_CUSTOM 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 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); i->params_sync[idx] = value; i->loaded = 1; } # endif #endif static LV2_Handle instantiate(const struct LV2_Descriptor * descriptor, double sample_rate, const char * bundle_path, const LV2_Feature * const * features) { (void)descriptor; (void)bundle_path; plugin_instance *instance = malloc(sizeof(plugin_instance)); if (instance == NULL) goto err_instance; instance->bundle_path = strdup(bundle_path); if (instance->bundle_path == NULL) goto err_bundle_path; // from https://lv2plug.in/book const char * missing = lv2_features_query(features, LV2_LOG__log, &instance->logger.log, false, LV2_URID__map, &instance->map, true, NULL); lv2_log_logger_set_map(&instance->logger, instance->map); if (missing) { lv2_log_error(&instance->logger, "Missing feature <%s>\n", missing); #ifdef DATA_PRODUCT_MIDI_REQUIRED goto err_urid; #endif } #if DATA_PRODUCT_MIDI_INPUTS_N + DATA_PRODUCT_MIDI_OUTPUTS_N > 0 if (instance->map) instance->uri_midi_MidiEvent = instance->map->map(instance->map->handle, LV2_MIDI__MidiEvent); #endif #ifdef DATA_STATE_DSP_CUSTOM if (instance->map) { instance->uri_atom_Chunk = instance->map->map(instance->map->handle, LV2_ATOM__Chunk); instance->uri_state_data = instance->map->map(instance->map->handle, DATA_LV2_URI "#state_data"); } #endif plugin_callbacks cbs = { /* .handle = */ (void *)instance, /* .format = */ "lv2", /* .get_bindir = */ get_bundle_path_cb, /* .get_datadir = */ get_bundle_path_cb }; plugin_init(&instance->p, &cbs); plugin_set_sample_rate(&instance->p, sample_rate); size_t req = plugin_mem_req(&instance->p); if (req != 0) { instance->mem = malloc(req); if (instance->mem == NULL) { lv2_log_error(&instance->logger, "Not enough memory\n"); goto err_mem; } plugin_mem_set(&instance->p, instance->mem); } else instance->mem = NULL; #if DATA_PRODUCT_AUDIO_INPUT_CHANNELS_N > 0 for (uint32_t i = 0; i < DATA_PRODUCT_AUDIO_INPUT_CHANNELS_N; i++) instance->x[i] = NULL; #endif #if DATA_PRODUCT_AUDIO_OUTPUT_CHANNELS_N > 0 for (uint32_t i = 0; i < DATA_PRODUCT_AUDIO_OUTPUT_CHANNELS_N; i++) instance->y[i] = NULL; #endif #if DATA_PRODUCT_MIDI_INPUTS_N > 0 for (uint32_t i = 0; i < DATA_PRODUCT_MIDI_INPUTS_N; i++) instance->x_midi[i] = NULL; #endif #if DATA_PRODUCT_MIDI_OUTPUTS_N > 0 for (uint32_t i = 0; i < DATA_PRODUCT_MIDI_OUTPUTS_N; i++) instance->y_midi[i] = NULL; #endif #if (DATA_PRODUCT_CONTROL_INPUTS_N + DATA_PRODUCT_CONTROL_OUTPUTS_N) > 0 # if DATA_PRODUCT_USE_PARAMETERS instance->uri_atom_Blank = instance->map->map (instance->map->handle, LV2_ATOM__Blank); instance->uri_atom_Object = instance->map->map (instance->map->handle, LV2_ATOM__Object); instance->uri_atom_URID = instance->map->map (instance->map->handle, LV2_ATOM__URID); instance->uri_atom_Float = instance->map->map (instance->map->handle, LV2_ATOM__Float); instance->uri_atom_Bool = instance->map->map (instance->map->handle, LV2_ATOM__Bool); instance->uri_patch_Set = instance->map->map (instance->map->handle, LV2_PATCH__Set); instance->uri_patch_Get = instance->map->map (instance->map->handle, LV2_PATCH__Get); instance->uri_patch_property = instance->map->map (instance->map->handle, LV2_PATCH__property); instance->uri_patch_value = instance->map->map (instance->map->handle, LV2_PATCH__value); for (int i = 0; i < DATA_PRODUCT_CONTROL_INPUTS_N + DATA_PRODUCT_CONTROL_OUTPUTS_N; i++) { instance->uri_parameters[i] = instance->map->map (instance->map->handle, param_data[i].id); } lv2_atom_forge_init(&instance->forge, instance->map); for (int aa = 0; aa < DATA_PRODUCT_CONTROL_OUTPUTS_N; aa++) { instance->output_parameter_atoms[aa].atom.size = sizeof(float); instance->output_parameter_atoms[aa].atom.type = instance->uri_atom_Float; instance->output_parameter_atoms[aa].body = param_data[param_out_index[aa]].def; } # else for (uint32_t i = 0; i < DATA_PRODUCT_CONTROL_INPUTS_N + DATA_PRODUCT_CONTROL_OUTPUTS_N; i++) instance->c[i] = NULL; # endif #endif return instance; err_mem: plugin_fini(&instance->p); #ifdef DATA_PRODUCT_MIDI_REQUIRED err_urid: #endif free(instance->bundle_path); err_bundle_path: free(instance); err_instance: return NULL; } static void connect_port(LV2_Handle instance, uint32_t port, void * data_location) { #if DATA_PRODUCT_USE_PARAMETERS const uint32_t port0 = port; #endif plugin_instance * i = (plugin_instance *)instance; #if DATA_PRODUCT_AUDIO_INPUT_CHANNELS_N > 0 if (port < DATA_PRODUCT_AUDIO_INPUT_CHANNELS_N) { i->x[port] = data_location; return; } port -= DATA_PRODUCT_AUDIO_INPUT_CHANNELS_N; #endif #if DATA_PRODUCT_AUDIO_OUTPUT_CHANNELS_N > 0 if (port < DATA_PRODUCT_AUDIO_OUTPUT_CHANNELS_N) { i->y[port] = data_location; return; } port -= DATA_PRODUCT_AUDIO_OUTPUT_CHANNELS_N; #endif #if DATA_PRODUCT_MIDI_INPUTS_N > 0 if (port < DATA_PRODUCT_MIDI_INPUTS_N) { i->x_midi[port] = data_location; return; } port -= DATA_PRODUCT_MIDI_INPUTS_N; #endif #if DATA_PRODUCT_MIDI_OUTPUTS_N > 0 if (port < DATA_PRODUCT_MIDI_OUTPUTS_N) { i->y_midi[port] = data_location; return; } port -= DATA_PRODUCT_MIDI_OUTPUTS_N; #endif #if DATA_PRODUCT_USE_PARAMETERS # if DATA_PRODUCT_IPM if (port0 == DATA_PRODUCT_IPM) { i->controlIn = (const LV2_Atom_Sequence*) data_location; return; } # endif # if DATA_PRODUCT_OPM if (port0 == DATA_PRODUCT_OPM) { i->controlOut = (const LV2_Atom_Sequence*) data_location; return; } # endif #else # if (DATA_PRODUCT_CONTROL_INPUTS_N + DATA_PRODUCT_CONTROL_OUTPUTS_N) > 0 i->c[port] = data_location; # endif #endif } static void activate(LV2_Handle instance) { plugin_instance * i = (plugin_instance *)instance; #if DATA_PRODUCT_CONTROL_INPUTS_N > 0 for (uint32_t j = 0; j < DATA_PRODUCT_CONTROL_INPUTS_N; j++) { # if DATA_PRODUCT_USE_PARAMETERS i->params[j] = param_data[j].def; # else i->params[j] = i->c[j] != NULL ? *i->c[j] : param_data[j].def; # endif 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); } #if DATA_PRODUCT_USE_PARAMETERS static inline int getParamIndexByURI(plugin_instance *instance, LV2_URID uri) { for (int i = 0; i < DATA_PRODUCT_CONTROL_INPUTS_N + DATA_PRODUCT_CONTROL_OUTPUTS_N; i++) if (instance->uri_parameters[i] == uri) return i; return -1; } static LV2_State_Status write_param_to_forge(LV2_State_Handle handle, uint32_t key, const void *value, size_t size, uint32_t type) { LV2_Atom_Forge* forge = (LV2_Atom_Forge*)handle; if (!lv2_atom_forge_key(forge, key) || !lv2_atom_forge_atom(forge, size, type) || !lv2_atom_forge_write(forge, value, size)) return LV2_STATE_ERR_UNKNOWN; return LV2_STATE_SUCCESS; } #endif static void run(LV2_Handle instance, uint32_t sample_count) { plugin_instance * i = (plugin_instance *)instance; #if DATA_PRODUCT_USE_PARAMETERS const uint32_t out_capacity = i->controlOut->atom.size; lv2_atom_forge_set_buffer(&i->forge, (uint8_t*)i->controlOut, out_capacity); LV2_Atom_Forge_Frame out_frame; lv2_atom_forge_sequence_head(&i->forge, &out_frame, 0); #endif #if defined(__aarch64__) uint64_t fpcr; __asm__ __volatile__ ("mrs %0, fpcr" : "=r"(fpcr)); __asm__ __volatile__ ("msr fpcr, %0" :: "r"(fpcr | 0x1000000)); // enable FZ #elif defined(__i386__) || defined(__x86_64__) const unsigned int flush_zero_mode = _MM_GET_FLUSH_ZERO_MODE(); const unsigned int denormals_zero_mode = _MM_GET_DENORMALS_ZERO_MODE(); _MM_SET_FLUSH_ZERO_MODE(_MM_FLUSH_ZERO_ON); _MM_SET_DENORMALS_ZERO_MODE(_MM_DENORMALS_ZERO_ON); #endif #if DATA_PRODUCT_CONTROL_INPUTS_N > 0 # ifdef DATA_STATE_DSP_CUSTOM _Bool locked = !atomic_flag_test_and_set(&i->sync_lock_flag); if (locked) { 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; } # endif # if DATA_PRODUCT_USE_PARAMETERS if (i->controlIn && i->map) { LV2_ATOM_SEQUENCE_FOREACH (i->controlIn, ev) { if (ev->body.type != i->uri_atom_Object) { continue; } const LV2_Atom_Object *obj = (LV2_Atom_Object*)&ev->body; if (obj->body.otype == i->uri_patch_Set) { const LV2_Atom* property = NULL; lv2_atom_object_get (obj, i->uri_patch_property, &property, 0); if (!property || property->type != i->uri_atom_URID) continue; const LV2_Atom* val = NULL; lv2_atom_object_get (obj, i->uri_patch_value, &val, 0); if (!val) continue; int index = getParamIndexByURI(i, ((LV2_Atom_URID*)property)->body); if (index < 0) continue; float v = adjust_param(index, *((float*)(val + 1))); if (v != i->params[index]) { i->params[index] = v; # ifdef DATA_STATE_DSP_CUSTOM if (locked) { i->params_sync[index] = i->params[index]; # endif plugin_set_parameter(&i->p, param_data[index].index, v); # ifdef DATA_STATE_DSP_CUSTOM } # endif } } else if (obj->body.otype == i->uri_patch_Get) { const LV2_Atom_URID *property = NULL; lv2_atom_object_get(obj, i->uri_patch_property, (const LV2_Atom**)&property, 0); if (!property || property->atom.type != i->uri_atom_URID) continue; const LV2_URID key = property->body; const int index = getParamIndexByURI(i, key); if (index < 0) continue; int outIndex = -1; for (int p = 0; p < DATA_PRODUCT_CONTROL_OUTPUTS_N; p++) { if (param_out_index[p] == (uint32_t) index) outIndex = p; } if (outIndex == -1) continue; const LV2_Atom* value = &i->output_parameter_atoms[outIndex].atom; LV2_Atom_Forge_Frame frame; lv2_atom_forge_frame_time(&i->forge, ev->time.frames); lv2_atom_forge_object( &i->forge, &frame, 0, i->uri_patch_Set); lv2_atom_forge_key( &i->forge, i->uri_patch_property); lv2_atom_forge_urid( &i->forge, property->body); write_param_to_forge( &i->forge, i->uri_patch_value, value + 1, value->size, value->type); lv2_atom_forge_pop( &i->forge, &frame); } } } # 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; # ifdef DATA_STATE_DSP_CUSTOM if (locked) { i->params_sync[j] = i->params[j]; # endif plugin_set_parameter(&i->p, param_data[j].index, v); # ifdef DATA_STATE_DSP_CUSTOM } # endif } } # endif # ifdef DATA_STATE_DSP_CUSTOM if (locked) atomic_flag_clear(&i->sync_lock_flag); # endif #endif #if DATA_PRODUCT_MIDI_INPUTS_N > 0 // from https://lv2plug.in/book if (i->map) for (size_t j = 0; j < DATA_PRODUCT_MIDI_INPUTS_N; j++) { if (i->x_midi[j] == NULL) continue; LV2_ATOM_SEQUENCE_FOREACH(i->x_midi[j], ev) { if (ev->body.type == i->uri_midi_MidiEvent) { const uint8_t * data = (const uint8_t *)(ev + 1); if ((data[0] & 0xf0) != 0xf0) plugin_midi_msg_in(&i->p, midi_in_index[j], data); } } } #endif #if DATA_PRODUCT_AUDIO_INPUT_CHANNELS_N > 0 const float ** x = i->x; #else const float ** x = NULL; #endif #if DATA_PRODUCT_AUDIO_OUTPUT_CHANNELS_N > 0 float ** y = i-> y; #else float ** y = NULL; #endif plugin_process(&i->p, x, y, sample_count); #if DATA_PRODUCT_CONTROL_OUTPUTS_N > 0 # if DATA_PRODUCT_USE_PARAMETERS for (uint32_t j = 0; j < DATA_PRODUCT_CONTROL_OUTPUTS_N; j++) { float v = plugin_get_parameter(&i->p, param_out_index[j]); i->output_parameter_atoms[j].body = v; lv2_atom_forge_frame_time(&i->forge, 0); LV2_Atom_Forge_Frame frame; lv2_atom_forge_object(&i->forge, &frame, 0, i->uri_patch_Set); lv2_atom_forge_key( &i->forge, i->uri_patch_property); lv2_atom_forge_urid( &i->forge, i->uri_parameters[param_out_index[j]]); lv2_atom_forge_key( &i->forge, i->uri_patch_value); lv2_atom_forge_float( &i->forge, i->output_parameter_atoms[j].body); lv2_atom_forge_pop( &i->forge, &frame); } lv2_atom_forge_pop(&i->forge, &out_frame); # else for (uint32_t j = 0; j < DATA_PRODUCT_CONTROL_OUTPUTS_N; j++) { uint32_t k = param_out_index[j]; if (i->c[k] != NULL) *i->c[k] = plugin_get_parameter(&i->p, k); } # endif #else (void)plugin_get_parameter; #endif #if defined(__aarch64__) __asm__ __volatile__ ("msr fpcr, %0" : : "r"(fpcr)); #elif defined(__i386__) || defined(__x86_64__) _MM_SET_FLUSH_ZERO_MODE(flush_zero_mode); _MM_SET_DENORMALS_ZERO_MODE(denormals_zero_mode); #endif } static void cleanup(LV2_Handle instance) { plugin_instance * i = (plugin_instance *)instance; plugin_fini(&i->p); if (i->mem) free(i->mem); free(i->bundle_path); free(instance); } #ifdef DATA_STATE_DSP_CUSTOM static LV2_State_Status state_save(LV2_Handle instance, LV2_State_Store_Function store, LV2_State_Handle handle, uint32_t flags, const LV2_Feature * const * features) { (void)flags; (void)features; plugin_instance * i = (plugin_instance *)instance; if (!i->map) { lv2_log_error(&i->logger, "Host is trying to store state but didn't provide URID map\n"); return LV2_STATE_ERR_UNKNOWN; // evidently buggy host, we don't have an errcode for it, LV2_STATE_ERR_NO_FEATURE has a different meaning } i->state_store = store; i->state_handle = handle; 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) { (void)flags; (void)features; plugin_instance * i = (plugin_instance *)instance; if (!i->map) { lv2_log_error(&i->logger, "Host is trying to restore state but didn't provide URID map\n"); return LV2_STATE_ERR_UNKNOWN; // evidently buggy host, we don't have an errcode for it, LV2_STATE_ERR_NO_FEATURE has a different meaning } size_t length; uint32_t type, xflags; // jalv 1.6.6 crashes using NULL as per spec, so we have these two const char * data = retrieve(handle, i->uri_state_data, &length, &type, &xflags); if (data == NULL) { 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_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) { static const LV2_State_Interface state = { state_save, state_restore }; if (!strcmp(uri, LV2_STATE__interface)) return &state; return NULL; } #endif static const LV2_Descriptor descriptor = { /* .URI = */ DATA_LV2_URI, /* .instantiate = */ instantiate, /* .connect_port = */ connect_port, /* .activate = */ activate, /* .run = */ run, /* .deactivate = */ NULL, /* .cleanup = */ cleanup, #ifdef DATA_STATE_DSP_CUSTOM /* .extension_data = */ extension_data #else /* .extension_data = */ NULL #endif }; LV2_SYMBOL_EXPORT const LV2_Descriptor * lv2_descriptor(uint32_t index) { return index == 0 ? &descriptor : NULL; } #ifdef DATA_UI typedef struct { plugin_ui * ui; char * bundle_path; # if DATA_PRODUCT_CONTROL_INPUTS_N > 0 LV2UI_Write_Function write; LV2UI_Controller controller; char has_touch; LV2UI_Touch touch; # endif } ui_instance; static const char * ui_get_bundle_path_cb(void *handle) { ui_instance *instance = (ui_instance *)handle; return instance->bundle_path; } # if DATA_PRODUCT_CONTROL_INPUTS_N > 0 static void ui_set_parameter_begin_cb(void *handle, size_t index, float value) { ui_instance *instance = (ui_instance *)handle; if (instance->has_touch) { index = index_to_param[index]; instance->touch.touch(instance->touch.handle, index, true); value = adjust_param(index - CONTROL_INPUT_INDEX_OFFSET, value); instance->write(instance->controller, index, sizeof(float), 0, &value); } } static void ui_set_parameter_cb(void *handle, size_t index, float value) { ui_instance *instance = (ui_instance *)handle; index = index_to_param[index]; value = adjust_param(index - CONTROL_INPUT_INDEX_OFFSET, value); instance->write(instance->controller, index, sizeof(float), 0, &value); } static void ui_set_parameter_end_cb(void *handle, size_t index, float value) { ui_instance *instance = (ui_instance *)handle; if (instance->has_touch) { index = index_to_param[index]; value = adjust_param(index - CONTROL_INPUT_INDEX_OFFSET, value); instance->write(instance->controller, index, sizeof(float), 0, &value); instance->touch.touch(instance->touch.handle, index, false); } } # endif static LV2UI_Handle ui_instantiate(const LV2UI_Descriptor * descriptor, const char * plugin_uri, const char * bundle_path, LV2UI_Write_Function write_function, LV2UI_Controller controller, LV2UI_Widget * widget, const LV2_Feature * const * features) { (void)descriptor; (void)plugin_uri; ui_instance *instance = malloc(sizeof(ui_instance)); if (instance == NULL) goto err_instance; instance->bundle_path = strdup(bundle_path); if (instance->bundle_path == NULL) goto err_bundle_path; char has_parent = 0; void *parent = NULL; # if DATA_PRODUCT_CONTROL_INPUTS_N > 0 instance->has_touch = 0; # endif for (size_t i = 0; features[i] != NULL; i++) { if (!strcmp(features[i]->URI, LV2_UI__parent)) { has_parent = 1; parent = features[i]->data; } # if DATA_PRODUCT_CONTROL_INPUTS_N > 0 else if (!strcmp(features[i]->URI, LV2_UI__touch)) { instance->has_touch = 1; instance->touch = *((LV2UI_Touch *)features[i]->data); } # endif } plugin_ui_callbacks cbs = { /* .handle = */ (void *)instance, /* .format = */ "lv2", /* .get_bindir = */ ui_get_bundle_path_cb, /* .get_datadir = */ ui_get_bundle_path_cb, # if DATA_PRODUCT_CONTROL_INPUTS_N > 0 /* .set_parameter_begin = */ ui_set_parameter_begin_cb, /* .set_parameter = */ ui_set_parameter_cb, /* .set_parameter_end = */ ui_set_parameter_end_cb, # endif }; # if DATA_PRODUCT_CONTROL_INPUTS_N > 0 instance->write = write_function; instance->controller = controller; # else (void)write_function; (void)controller; # endif instance->ui = plugin_ui_create(has_parent, parent, &cbs); if (instance->ui == NULL) goto err_create; *widget = instance->ui->widget; return instance; err_create: free(instance->bundle_path); err_bundle_path: free(instance); err_instance: *widget = NULL; return NULL; } static void ui_cleanup(LV2UI_Handle handle) { ui_instance *instance = (ui_instance *)handle; plugin_ui_free(instance->ui); free(instance->bundle_path); free(instance); } # if DATA_PRODUCT_CONTROL_INPUTS_N + DATA_PRODUCT_CONTROL_OUTPUTS_N > 0 static void ui_port_event(LV2UI_Handle handle, uint32_t port_index, uint32_t buffer_size, uint32_t format, const void * buffer) { (void)buffer_size; (void)format; ui_instance *instance = (ui_instance *)handle; # if DATA_PRODUCT_CONTROL_INPUTS_N > 0 if (port_index < CONTROL_OUTPUT_INDEX_OFFSET) { size_t index = port_index - CONTROL_INPUT_INDEX_OFFSET; plugin_ui_set_parameter(instance->ui, param_data[index].index, adjust_param(index, *((float *)buffer))); } # endif # if DATA_PRODUCT_CONTROL_OUTPUTS_N > 0 # if DATA_PRODUCT_CONTROL_INPUTS_N > 0 else # endif plugin_ui_set_parameter(instance->ui, param_out_index[port_index - CONTROL_OUTPUT_INDEX_OFFSET], *((float *)buffer)); # endif } # endif static int ui_idle(LV2UI_Handle handle) { ui_instance *instance = (ui_instance *)handle; plugin_ui_idle(instance->ui); return 0; } static const void * ui_extension_data(const char * uri) { static const LV2UI_Idle_Interface idle = { ui_idle }; if (!strcmp(uri, LV2_UI__idleInterface)) return &idle; return NULL; } static const LV2UI_Descriptor ui_descriptor = { /* .URI = */ DATA_LV2_UI_URI, /* .instantiate = */ ui_instantiate, /* .cleanup = */ ui_cleanup, # if DATA_PRODUCT_CONTROL_INPUTS_N + DATA_PRODUCT_CONTROL_OUTPUTS_N > 0 /* .port_event = */ ui_port_event, # else /* .port_event = */ NULL, # endif /* .extension_data = */ ui_extension_data }; LV2_SYMBOL_EXPORT const LV2UI_Descriptor * lv2ui_descriptor(uint32_t index) { return index == 0 ? &ui_descriptor : NULL; } #endif