lv2 state and fixes

This commit is contained in:
Stefano D'Angelo 2025-01-18 18:16:00 +01:00
parent 355cc690fb
commit 6e9bdb0ad9
4 changed files with 112 additions and 15 deletions

View File

@ -20,13 +20,17 @@
] ; ] ;
lv2:minorVersion {{=/^([0-9]+)\./.exec(it.lv2.version)[1]}} ; lv2:minorVersion {{=/^([0-9]+)\./.exec(it.lv2.version)[1]}} ;
lv2:microVersion {{=/^[0-9]+\.([0-9]+)/.exec(it.lv2.version)[1]}} ; lv2:microVersion {{=/^[0-9]+\.([0-9]+)/.exec(it.lv2.version)[1]}} ;
{{?it.tibia.lv2.ports.find(p => p.type == "midi")}} {{?(it.tibia.lv2.ports.find(p => p.type == "midi") || (it.product.state && it.product.state.dspCustom))}}
lv2:requiredFeature urid:map ; lv2:requiredFeature urid:map ;
lv2:optionalFeature log:log ; lv2:optionalFeature log:log ;
{{?}} {{?}}
lv2:optionalFeature lv2:hardRTCapable ; lv2:optionalFeature lv2:hardRTCapable ;
{{?it.product.ui}} {{?it.product.ui}}
ui:ui plugin:ui ; ui:ui plugin:ui ;
lv2:extensionData ui:idleInterface ;
{{?}}
{{?(it.product.state && it.product.state.dspCustom)}}
lv2:extensionData state:interface ;
{{?}} {{?}}
lv2:port [ lv2:port [
{{~it.tibia.lv2.ports :p:i}} {{~it.tibia.lv2.ports :p:i}}

View File

@ -1,7 +1,7 @@
/* /*
* Tibia * Tibia
* *
* Copyright (C) 2024 Orastron Srl unipersonale * Copyright (C) 2024, 2025 Orastron Srl unipersonale
* *
* Tibia is free software: you can redistribute it and/or modify * Tibia is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@ -69,10 +69,16 @@ static uint32_t param_out_index[DATA_PRODUCT_CONTROL_OUTPUTS_N] = {
#define DATA_UI #define DATA_UI
#define DATA_LV2_UI_URI "{{=it.tibia.CGetUTF8StringLiteral(it.tibia.lv2.expandURI(it.lv2.uri + '#ui'))}}" #define DATA_LV2_UI_URI "{{=it.tibia.CGetUTF8StringLiteral(it.tibia.lv2.expandURI(it.lv2.uri + '#ui'))}}"
#define DATA_UI_USER_RESIZABLE {{=it.product.ui.userResizable ? 1 : 0}} #define DATA_UI_USER_RESIZABLE {{=it.product.ui.userResizable ? 1 : 0}}
{{?}}
{{?(it.product.ui || (it.product.state && it.product.state.dspCustom))}}
#if DATA_PRODUCT_CONTROL_INPUTS_N > 0 #if DATA_PRODUCT_CONTROL_INPUTS_N > 0
static uint32_t index_to_param[DATA_PRODUCT_CONTROL_INPUTS_N + DATA_PRODUCT_CONTROL_OUTPUTS_N] = { static uint32_t index_to_param[DATA_PRODUCT_CONTROL_INPUTS_N + DATA_PRODUCT_CONTROL_OUTPUTS_N] = {
{{~it.tibia.lv2.ports.filter(x => x.type == "control").map((e, i) => ({ i: i, pi: e.paramIndex })).sort((a, b) => a.pi - b.pi) :p}}{{=p.i + it.tibia.lv2.ports.filter(x => x.type != "control").length}}, {{~}} {{~it.tibia.lv2.ports.filter(x => x.type == "control").map((e, i) => ({ i: i, pi: e.paramIndex })).sort((a, b) => a.pi - b.pi) :p}}{{=p.i + it.tibia.lv2.ports.filter(x => x.type != "control").length}}, {{~}}
}; };
#endif #endif
{{?}} {{?}}
{{?it.product.state && it.product.state.dspCustom}}
#define DATA_STATE_DSP_CUSTOM
{{?}}

View File

@ -1,7 +1,7 @@
/* /*
* Tibia * Tibia
* *
* Copyright (C) 2024 Orastron Srl unipersonale * Copyright (C) 2024, 2025 Orastron Srl unipersonale
* *
* Tibia is free software: you can redistribute it and/or modify * Tibia is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@ -32,18 +32,23 @@
#pragma GCC diagnostic pop #pragma GCC diagnostic pop
#include "lv2/core/lv2.h" #include "lv2/core/lv2.h"
#include "lv2/core/lv2_util.h" #if (DATA_PRODUCT_MIDI_INPUTS_N + DATA_PRODUCT_MIDI_OUTPUTS_N > 0) || defined(DATA_STATE_DSP_CUSTOM)
#include "lv2/log/log.h" # include "lv2/core/lv2_util.h"
#include "lv2/log/logger.h" # include "lv2/log/log.h"
#include "lv2/urid/urid.h" # include "lv2/log/logger.h"
#if DATA_PRODUCT_MIDI_INPUTS_N + DATA_PRODUCT_MIDI_OUTPUTS_N > 0 # include "lv2/urid/urid.h"
# include "lv2/atom/util.h"
# include "lv2/atom/atom.h" # include "lv2/atom/atom.h"
# include "lv2/midi/midi.h" # if DATA_PRODUCT_MIDI_INPUTS_N + DATA_PRODUCT_MIDI_OUTPUTS_N > 0
# include "lv2/atom/util.h"
# include "lv2/midi/midi.h"
# endif
#endif #endif
#ifdef DATA_UI #ifdef DATA_UI
# include "lv2/ui/ui.h" # include "lv2/ui/ui.h"
#endif #endif
#ifdef DATA_STATE_DSP_CUSTOM
# include "lv2/state/state.h"
#endif
#include <string.h> #include <string.h>
@ -52,6 +57,7 @@
# include <pmmintrin.h> # include <pmmintrin.h>
#endif #endif
#if DATA_PRODUCT_CONTROL_INPUTS_N > 0
static inline float clampf(float x, float m, float M) { static inline float clampf(float x, float m, float M) {
return x < m ? m : (x > M ? M : x); return x < m ? m : (x > M ? M : x);
} }
@ -65,6 +71,7 @@ static float adjust_param(size_t index, float value) {
value = (int32_t)(value + (value >= 0.f ? 0.5f : -0.5f)); value = (int32_t)(value + (value >= 0.f ? 0.5f : -0.5f));
return clampf(value, param_data[index].min, param_data[index].max); return clampf(value, param_data[index].min, param_data[index].max);
} }
#endif
typedef struct { typedef struct {
plugin p; plugin p;
@ -88,10 +95,18 @@ typedef struct {
#endif #endif
void * mem; void * mem;
char * bundle_path; char * bundle_path;
#if (DATA_PRODUCT_MIDI_INPUTS_N + DATA_PRODUCT_MIDI_OUTPUTS_N > 0) || defined(DATA_STATE_DSP_CUSTOM)
LV2_Log_Logger logger; LV2_Log_Logger logger;
LV2_URID_Map * map; LV2_URID_Map * map;
#if DATA_PRODUCT_MIDI_INPUTS_N + DATA_PRODUCT_MIDI_OUTPUTS_N > 0 # if DATA_PRODUCT_MIDI_INPUTS_N + DATA_PRODUCT_MIDI_OUTPUTS_N > 0
LV2_URID uri_midi_MidiEvent; LV2_URID uri_midi_MidiEvent;
# endif
# ifdef DATA_STATE_DSP_CUSTOM
LV2_URID uri_atom_Chunk;
LV2_URID uri_state_data;
LV2_State_Store_Function state_store;
LV2_State_Handle state_handle;
# endif
#endif #endif
} plugin_instance; } plugin_instance;
@ -100,6 +115,23 @@ static const char * get_bundle_path_cb(void *handle) {
return instance->bundle_path; return instance->bundle_path;
} }
#ifdef DATA_STATE_DSP_CUSTOM
static int write_state_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) {
plugin_instance * i = (plugin_instance *)handle;
size_t idx = index_to_param[index];
value = adjust_param(idx, value);
i->params[idx] = value;
plugin_set_parameter(&i->p, index, value);
}
# 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) {
(void)descriptor; (void)descriptor;
(void)bundle_path; (void)bundle_path;
@ -112,6 +144,7 @@ static LV2_Handle instantiate(const struct LV2_Descriptor * descriptor, double s
if (instance->bundle_path == NULL) if (instance->bundle_path == NULL)
goto err_bundle_path; goto err_bundle_path;
#if (DATA_PRODUCT_MIDI_INPUTS_N + DATA_PRODUCT_MIDI_OUTPUTS_N > 0) || defined(DATA_STATE_DSP_CUSTOM)
// from https://lv2plug.in/book // from https://lv2plug.in/book
const char* missing = lv2_features_query(features, const char* missing = lv2_features_query(features,
LV2_LOG__log, &instance->logger.log, false, LV2_LOG__log, &instance->logger.log, false,
@ -124,15 +157,31 @@ static LV2_Handle instantiate(const struct LV2_Descriptor * descriptor, double s
goto err_urid; goto err_urid;
} }
#if DATA_PRODUCT_MIDI_INPUTS_N + DATA_PRODUCT_MIDI_OUTPUTS_N > 0 # if DATA_PRODUCT_MIDI_INPUTS_N + DATA_PRODUCT_MIDI_OUTPUTS_N > 0
instance->uri_midi_MidiEvent = instance->map->map(instance->map->handle, LV2_MIDI__MidiEvent); instance->uri_midi_MidiEvent = instance->map->map(instance->map->handle, LV2_MIDI__MidiEvent);
# endif
# ifdef DATA_STATE_DSP_CUSTOM
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
#endif #endif
plugin_callbacks cbs = { plugin_callbacks cbs = {
/* .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
# else
/* .load_parameter = */ NULL
# endif
# else
/* .write_state = */ NULL,
/* .load_parameter = */ NULL
# endif
}; };
plugin_init(&instance->p, &cbs); plugin_init(&instance->p, &cbs);
@ -254,8 +303,8 @@ static void run(LV2_Handle instance, uint32_t sample_count) {
} }
#endif #endif
// from https://lv2plug.in/book
#if DATA_PRODUCT_MIDI_INPUTS_N > 0 #if DATA_PRODUCT_MIDI_INPUTS_N > 0
// from https://lv2plug.in/book
for (size_t j = 0; j < DATA_PRODUCT_MIDI_INPUTS_N; j++) { for (size_t j = 0; j < DATA_PRODUCT_MIDI_INPUTS_N; j++) {
if (i->x_midi[j] == NULL) if (i->x_midi[j] == NULL)
continue; continue;
@ -308,6 +357,39 @@ static void cleanup(LV2_Handle instance) {
free(instance); 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;
i->state_store = store;
i->state_handle = handle;
return plugin_state_save(&i->p) == 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;
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)
return LV2_STATE_ERR_NO_PROPERTY;
plugin_state_load(&i->p, data, length);
return LV2_STATE_SUCCESS;
}
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 = { static const LV2_Descriptor descriptor = {
/* .URI = */ DATA_LV2_URI, /* .URI = */ DATA_LV2_URI,
/* .instantiate = */ instantiate, /* .instantiate = */ instantiate,
@ -316,7 +398,11 @@ static const LV2_Descriptor descriptor = {
/* .run = */ run, /* .run = */ run,
/* .deactivate = */ NULL, /* .deactivate = */ NULL,
/* .cleanup = */ cleanup, /* .cleanup = */ cleanup,
#ifdef DATA_STATE_DSP_CUSTOM
/* .extension_data = */ extension_data
#else
/* .extension_data = */ NULL /* .extension_data = */ NULL
#endif
}; };
LV2_SYMBOL_EXPORT const LV2_Descriptor * lv2_descriptor(uint32_t index) { LV2_SYMBOL_EXPORT const LV2_Descriptor * lv2_descriptor(uint32_t index) {

View File

@ -1,7 +1,7 @@
/* /*
* Tibia * Tibia
* *
* Copyright (C) 2024 Orastron Srl unipersonale * Copyright (C) 2024, 2025 Orastron Srl unipersonale
* *
* Tibia is free software: you can redistribute it and/or modify * Tibia is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@ -34,6 +34,7 @@ module.exports = function (data, api, outputCommon, outputData) {
{ id: "pprops", uri: "http://lv2plug.in/ns/ext/port-props#" }, { id: "pprops", uri: "http://lv2plug.in/ns/ext/port-props#" },
{ id: "rdf", uri: "http://www.w3.org/1999/02/22-rdf-syntax-ns#" }, { id: "rdf", uri: "http://www.w3.org/1999/02/22-rdf-syntax-ns#" },
{ id: "rdfs", uri: "http://www.w3.org/2000/01/rdf-schema#" }, { id: "rdfs", uri: "http://www.w3.org/2000/01/rdf-schema#" },
{ id: "state", uri: "http://lv2plug.in/ns/extensions/state#" },
{ id: "ui", uri: "http://lv2plug.in/ns/extensions/ui#" }, { id: "ui", uri: "http://lv2plug.in/ns/extensions/ui#" },
{ id: "units", uri: "http://lv2plug.in/ns/extensions/units#" }, { id: "units", uri: "http://lv2plug.in/ns/extensions/units#" },
{ id: "urid", uri: "http://lv2plug.in/ns/ext/urid#" } { id: "urid", uri: "http://lv2plug.in/ns/ext/urid#" }