use std::ffi::{CString, c_char, c_void};
use truce_core::export::PluginExport;
use truce_core::state::{deserialize_state, serialize_state};
use truce_params::Params;
use crate::Lv2Instance;
use crate::urid::Urid;
pub(crate) const LV2_STATE__INTERFACE_URI: &str = "http://lv2plug.in/ns/ext/state#interface";
const TRUCE_STATE_KEY_URI: &str = "urn:truce:state-blob";
const LV2_STATE_SUCCESS: u32 = 0;
const _LV2_STATE_ERR_UNKNOWN: u32 = 1;
const LV2_STATE_IS_POD: u32 = 1 << 0;
const LV2_STATE_IS_PORTABLE: u32 = 1 << 1;
type StoreFn = unsafe extern "C" fn(
handle: *mut c_void,
key: Urid,
value: *const c_void,
size: usize,
type_: Urid,
flags: u32,
) -> u32;
type RetrieveFn = unsafe extern "C" fn(
handle: *mut c_void,
key: Urid,
size: *mut usize,
type_: *mut Urid,
flags: *mut u32,
) -> *const c_void;
#[repr(C)]
pub struct Lv2StateInterface {
pub save: unsafe extern "C" fn(
instance: *mut c_void,
store: StoreFn,
handle: *mut c_void,
flags: u32,
features: *const *const crate::types::LV2Feature,
) -> u32,
pub restore: unsafe extern "C" fn(
instance: *mut c_void,
retrieve: RetrieveFn,
handle: *mut c_void,
flags: u32,
features: *const *const crate::types::LV2Feature,
) -> u32,
}
pub(crate) fn state_interface<P: PluginExport>() -> &'static Lv2StateInterface {
struct Holder<P>(std::marker::PhantomData<P>);
impl<P: PluginExport> Holder<P> {
const IFACE: Lv2StateInterface = Lv2StateInterface {
save: save_cb::<P>,
restore: restore_cb::<P>,
};
}
&<Holder<P>>::IFACE
}
unsafe extern "C" fn save_cb<P: PluginExport>(
instance: *mut c_void,
store: StoreFn,
handle: *mut c_void,
_flags: u32,
_features: *const *const crate::types::LV2Feature,
) -> u32 {
unsafe {
if instance.is_null() {
return 0;
}
let inst = &mut *instance.cast::<Lv2Instance<P>>();
let (ids, values) = inst.plugin.params().collect_values();
let extra = inst.plugin.save_state();
let blob = serialize_state(inst.plugin_id_hash, &ids, &values, &extra);
let key = inst.urid_map.intern(TRUCE_STATE_KEY_URI);
let chunk_urid = inst.urid_map.atom_chunk;
if key == 0 || chunk_urid == 0 {
return 0;
}
let flags = LV2_STATE_IS_POD | LV2_STATE_IS_PORTABLE;
let _ = store(
handle,
key,
blob.as_ptr().cast::<c_void>(),
blob.len(),
chunk_urid,
flags,
);
LV2_STATE_SUCCESS
}
}
unsafe extern "C" fn restore_cb<P: PluginExport>(
instance: *mut c_void,
retrieve: RetrieveFn,
handle: *mut c_void,
_flags: u32,
_features: *const *const crate::types::LV2Feature,
) -> u32 {
unsafe {
if instance.is_null() {
return 0;
}
let inst = &mut *instance.cast::<Lv2Instance<P>>();
let key = inst.urid_map.intern(TRUCE_STATE_KEY_URI);
if key == 0 {
return 0;
}
let mut size = 0usize;
let mut type_: Urid = 0;
let mut state_flags: u32 = 0;
let data = retrieve(
handle,
key,
&raw mut size,
&raw mut type_,
&raw mut state_flags,
);
if data.is_null() || size == 0 {
return 0;
}
let slice = core::slice::from_raw_parts(data.cast::<u8>(), size);
if let Some(state) = deserialize_state(slice, inst.plugin_id_hash) {
inst.plugin.params().restore_values(&state.params);
inst.plugin.params().snap_smoothers();
if let Some(extra) = state.extra
&& let Err(e) = inst.plugin.load_state(&extra)
{
eprintln!("truce: lv2 load_state failed: {e}");
}
}
LV2_STATE_SUCCESS
}
}
const _: Option<CString> = None;
const _: Option<*const c_char> = None;