Skip to main content

maolan_plugin_host/
lv2.rs

1#![cfg(not(target_os = "macos"))]
2
3use std::{
4    collections::{HashMap, HashSet},
5    ffi::{CStr, CString, c_char, c_void},
6    fmt,
7    path::{Path, PathBuf},
8    sync::atomic::{AtomicBool, Ordering},
9    sync::{Arc, Mutex},
10};
11
12use crate::util::AudioPort;
13
14pub struct Lv2ControlPortInfo {
15    pub index: u32,
16    pub name: String,
17    pub min: f32,
18    pub max: f32,
19    pub value: f32,
20}
21
22#[derive(Debug, Clone)]
23pub struct Lv2StatePortValue {
24    pub index: u32,
25    pub value: f32,
26}
27
28#[derive(Debug, Clone)]
29pub struct Lv2StateProperty {
30    pub key_uri: String,
31    pub type_uri: String,
32    pub flags: u32,
33    pub value: Vec<u8>,
34}
35
36#[derive(Debug, Clone)]
37pub struct Lv2PluginState {
38    pub port_values: Vec<Lv2StatePortValue>,
39    pub properties: Vec<Lv2StateProperty>,
40}
41
42use crate::util::MidiEvent;
43use crate::util::SimpleMutex;
44use lilv::{World, instance::ActiveInstance, node::Node, plugin::Plugin};
45use lv2_raw::{
46    LV2_ATOM__DOUBLE, LV2_ATOM__FRAMETIME, LV2_ATOM__INT, LV2_ATOM__LONG, LV2_ATOM__OBJECT,
47    LV2_ATOM__SEQUENCE, LV2_URID__MAP, LV2_URID__UNMAP, LV2Atom, LV2AtomDouble, LV2AtomEvent,
48    LV2AtomLong, LV2AtomObjectBody, LV2AtomPropertyBody, LV2AtomSequence, LV2AtomSequenceBody,
49    LV2Feature, LV2Urid, LV2UridMap, LV2UridMapHandle, lv2_atom_pad_size,
50    lv2_atom_sequence_append_event, lv2_atom_sequence_begin, lv2_atom_sequence_is_end,
51    lv2_atom_sequence_next,
52};
53
54#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
55pub struct Lv2PluginInfo {
56    pub uri: String,
57    pub name: String,
58    pub class_label: String,
59    pub bundle_uri: String,
60    pub required_features: Vec<String>,
61    pub audio_inputs: usize,
62    pub audio_outputs: usize,
63    pub midi_inputs: usize,
64    pub midi_outputs: usize,
65}
66
67pub struct Lv2Host {
68    world: World,
69    sample_rate: f64,
70    loaded_plugins: HashMap<String, LoadedPlugin>,
71}
72
73#[derive(Debug, Clone, Copy, Default)]
74pub struct Lv2TransportInfo {
75    pub transport_sample: usize,
76    pub playing: bool,
77    pub bpm: f64,
78    pub tsig_num: u32,
79    pub tsig_denom: u32,
80}
81
82type Lv2WorkerStatus = u32;
83const LV2_WORKER_SUCCESS: Lv2WorkerStatus = 0;
84const LV2_WORKER_ERR_UNKNOWN: Lv2WorkerStatus = 1;
85const LV2_WORKER__SCHEDULE: &str = "http://lv2plug.in/ns/ext/worker#schedule";
86const LV2_WORKER__INTERFACE: &str = "http://lv2plug.in/ns/ext/worker#interface";
87
88const LV2_MIDNAM__INTERFACE: &str = "http://ardour.org/lv2/midnam#interface";
89
90#[repr(C)]
91struct Lv2MidnamInterface {
92    midnam: Option<unsafe extern "C" fn(instance: Lv2Handle) -> *mut c_char>,
93    model: Option<unsafe extern "C" fn(instance: Lv2Handle) -> *mut c_char>,
94    free: Option<unsafe extern "C" fn(ptr: *mut c_char)>,
95}
96
97#[repr(C)]
98struct Lv2WorkerSchedule {
99    handle: *mut c_void,
100    schedule_work:
101        Option<unsafe extern "C" fn(handle: *mut c_void, size: u32, data: *const c_void) -> u32>,
102}
103
104type Lv2WorkerRespondFunc =
105    Option<unsafe extern "C" fn(handle: *mut c_void, size: u32, data: *const c_void) -> u32>;
106
107#[repr(C)]
108struct Lv2WorkerInterface {
109    work: Option<
110        unsafe extern "C" fn(
111            handle: *mut c_void,
112            respond: Lv2WorkerRespondFunc,
113            respond_handle: *mut c_void,
114            size: u32,
115            data: *const c_void,
116        ) -> u32,
117    >,
118    work_response:
119        Option<unsafe extern "C" fn(handle: *mut c_void, size: u32, data: *const c_void) -> u32>,
120    end_run: Option<unsafe extern "C" fn(handle: *mut c_void)>,
121}
122
123struct WorkerScheduleState {
124    jobs: SimpleMutex<Vec<Vec<u8>>>,
125    responses: SimpleMutex<Vec<Vec<u8>>>,
126}
127
128struct WorkerFeature {
129    _uri: CString,
130    _schedule: Box<Lv2WorkerSchedule>,
131    feature: LV2Feature,
132    state: Box<WorkerScheduleState>,
133}
134
135unsafe impl Send for WorkerFeature {}
136
137#[derive(Clone, Copy)]
138enum PortBinding {
139    AudioInput(usize),
140    AudioOutput(usize),
141    AtomInput(usize),
142    AtomOutput(usize),
143    Scalar(usize),
144}
145
146pub struct Lv2Processor {
147    uri: String,
148    plugin_name: String,
149    sample_rate: f64,
150    instance: Option<ActiveInstance>,
151    _world: World,
152    _urid_feature: UridMapFeature,
153    _state_path_feature: StatePathFeature,
154    _instantiate_features: InstantiateFeatureSet,
155    port_bindings: Vec<PortBinding>,
156    scalar_values: Arc<Mutex<Vec<f32>>>,
157    audio_inputs: Vec<Arc<AudioPort>>,
158    audio_outputs: Vec<Arc<AudioPort>>,
159    main_audio_inputs: usize,
160    main_audio_outputs: usize,
161    atom_inputs: Vec<AtomBuffer>,
162    atom_outputs: Vec<AtomBuffer>,
163    atom_sequence_urid: LV2Urid,
164    atom_object_urid: LV2Urid,
165    atom_long_urid: LV2Urid,
166    atom_double_urid: LV2Urid,
167    atom_frame_time_urid: LV2Urid,
168    midi_event_urid: LV2Urid,
169    time_position_urid: LV2Urid,
170    time_frame_urid: LV2Urid,
171    time_speed_urid: LV2Urid,
172    time_bpm_urid: LV2Urid,
173    time_bar_urid: LV2Urid,
174    time_bar_beat_urid: LV2Urid,
175    time_beats_per_bar_urid: LV2Urid,
176    time_beat_unit_urid: LV2Urid,
177    midi_inputs: usize,
178    midi_outputs: usize,
179    has_worker_interface: bool,
180    control_ports: Vec<ControlPortInfo>,
181    midnam_note_names: SimpleMutex<HashMap<u8, String>>,
182    bypassed: AtomicBool,
183}
184
185struct LoadedPlugin {
186    instance: ActiveInstance,
187    _urid_feature: UridMapFeature,
188    _state_path_feature: StatePathFeature,
189    _instantiate_features: InstantiateFeatureSet,
190}
191
192#[derive(Default)]
193struct UridMapState {
194    next_urid: LV2Urid,
195    by_uri: HashMap<String, LV2Urid>,
196    by_urid: HashMap<LV2Urid, CString>,
197}
198
199struct UridMapFeature {
200    _map_uri: CString,
201    _unmap_uri: CString,
202    map_feature: LV2Feature,
203    unmap_feature: LV2Feature,
204    _map: Box<LV2UridMap>,
205    _unmap: Box<LV2UridUnmap>,
206    _state: Box<Mutex<UridMapState>>,
207}
208
209unsafe impl Send for UridMapFeature {}
210
211#[repr(C)]
212struct LV2UridUnmap {
213    handle: LV2UridMapHandle,
214    unmap: extern "C" fn(handle: LV2UridMapHandle, urid: LV2Urid) -> *const c_char,
215}
216
217struct InstantiateFeatureSet {
218    _feature_uris: Vec<CString>,
219    features: Vec<LV2Feature>,
220    _worker_feature: WorkerFeature,
221    _option_values: Vec<u32>,
222    _options: Vec<LV2OptionsOption>,
223    _flag_feature_data: Box<u8>,
224}
225
226unsafe impl Send for InstantiateFeatureSet {}
227
228#[repr(C)]
229#[derive(Clone, Copy)]
230struct LV2OptionsOption {
231    context: u32,
232    subject: u32,
233    key: u32,
234    size: u32,
235    type_: u32,
236    value: *const c_void,
237}
238
239#[derive(Debug, Clone)]
240struct ControlPortInfo {
241    index: u32,
242    name: String,
243    min: f32,
244    max: f32,
245}
246
247struct AtomBuffer {
248    words: Vec<u64>,
249}
250
251impl AtomBuffer {
252    fn new(len_bytes: usize) -> Self {
253        let words = len_bytes.div_ceil(std::mem::size_of::<u64>()).max(1);
254        Self {
255            words: vec![0; words],
256        }
257    }
258
259    fn bytes_mut(&mut self) -> &mut [u8] {
260        let full_len = self.words.len() * std::mem::size_of::<u64>();
261        let raw = self.words.as_mut_ptr().cast::<u8>();
262        unsafe { std::slice::from_raw_parts_mut(raw, full_len) }
263    }
264
265    fn ptr_mut(&mut self) -> *mut u8 {
266        self.words.as_mut_ptr().cast::<u8>()
267    }
268}
269
270#[derive(Debug)]
271struct RawStateProperty {
272    key: u32,
273    type_: u32,
274    flags: u32,
275    value: Vec<u8>,
276}
277
278struct StateSaveContext {
279    properties: Vec<RawStateProperty>,
280}
281
282struct StateRestoreContext {
283    properties: Vec<RawStateProperty>,
284    by_key: HashMap<u32, usize>,
285}
286
287type Lv2Handle = *mut c_void;
288type Lv2StateHandle = *mut c_void;
289type Lv2StateStatus = u32;
290type Lv2StateStoreFn = Option<
291    unsafe extern "C" fn(
292        handle: Lv2StateHandle,
293        key: u32,
294        value: *const c_void,
295        size: usize,
296        type_: u32,
297        flags: u32,
298    ) -> Lv2StateStatus,
299>;
300type Lv2StateRetrieveFn = Option<
301    unsafe extern "C" fn(
302        handle: Lv2StateHandle,
303        key: u32,
304        size: *mut usize,
305        type_: *mut u32,
306        flags: *mut u32,
307    ) -> *const c_void,
308>;
309const LV2_STATE_STATUS_SUCCESS: Lv2StateStatus = 0;
310const LV2_STATE_STATUS_ERR_NO_PROPERTY: Lv2StateStatus = 5;
311
312#[repr(C)]
313struct Lv2StateInterface {
314    save: Option<
315        unsafe extern "C" fn(
316            instance: Lv2Handle,
317            store: Lv2StateStoreFn,
318            handle: Lv2StateHandle,
319            flags: u32,
320            features: *const *const LV2Feature,
321        ) -> Lv2StateStatus,
322    >,
323    restore: Option<
324        unsafe extern "C" fn(
325            instance: Lv2Handle,
326            retrieve: Lv2StateRetrieveFn,
327            handle: Lv2StateHandle,
328            flags: u32,
329            features: *const *const LV2Feature,
330        ) -> Lv2StateStatus,
331    >,
332}
333
334#[repr(C)]
335struct Lv2StateMapPath {
336    handle: *mut c_void,
337    abstract_path: Option<extern "C" fn(*mut c_void, *const c_char) -> *mut c_char>,
338    absolute_path: Option<extern "C" fn(*mut c_void, *const c_char) -> *mut c_char>,
339}
340
341#[repr(C)]
342struct Lv2StateMakePath {
343    handle: *mut c_void,
344    path: Option<extern "C" fn(*mut c_void, *const c_char) -> *mut c_char>,
345}
346
347#[repr(C)]
348struct Lv2StateFreePath {
349    handle: *mut c_void,
350    free_path: Option<extern "C" fn(*mut c_void, *mut c_char)>,
351}
352
353#[derive(Default)]
354struct StatePathContext {
355    base_dir: PathBuf,
356    copy_counter: u64,
357}
358
359struct StatePathFeature {
360    _map_uri: CString,
361    _make_uri: CString,
362    _free_uri: CString,
363    _map: Box<Lv2StateMapPath>,
364    _make: Box<Lv2StateMakePath>,
365    _free: Box<Lv2StateFreePath>,
366    map_feature: LV2Feature,
367    make_feature: LV2Feature,
368    free_feature: LV2Feature,
369    _context: Box<Mutex<StatePathContext>>,
370}
371
372unsafe impl Send for StatePathFeature {}
373
374const LV2_STATE_INTERFACE_URI: &str = "http://lv2plug.in/ns/ext/state#interface";
375const LV2_STATE_MAP_PATH_URI: &str = "http://lv2plug.in/ns/ext/state#mapPath";
376const LV2_STATE_MAKE_PATH_URI: &str = "http://lv2plug.in/ns/ext/state#makePath";
377const LV2_STATE_FREE_PATH_URI: &str = "http://lv2plug.in/ns/ext/state#freePath";
378const LV2_OPTIONS__OPTIONS: &str = "http://lv2plug.in/ns/ext/options#options";
379const LV2_BUF_SIZE__BOUNDED_BLOCK_LENGTH: &str =
380    "http://lv2plug.in/ns/ext/buf-size#boundedBlockLength";
381const LV2_BUF_SIZE__MIN_BLOCK_LENGTH: &str = "http://lv2plug.in/ns/ext/buf-size#minBlockLength";
382const LV2_BUF_SIZE__MAX_BLOCK_LENGTH: &str = "http://lv2plug.in/ns/ext/buf-size#maxBlockLength";
383const LV2_BUF_SIZE__NOMINAL_BLOCK_LENGTH: &str =
384    "http://lv2plug.in/ns/ext/buf-size#nominalBlockLength";
385const LV2_URID__MAP_URI_TYPO_COMPAT: &str = "http://lv2plug.in/ns//ext/urid#map";
386
387const LV2_ATOM_BUFFER_BYTES: usize = 8192;
388
389impl fmt::Debug for Lv2Processor {
390    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
391        f.debug_struct("Lv2Processor")
392            .field("uri", &self.uri)
393            .field("audio_inputs", &self.audio_inputs.len())
394            .field("audio_outputs", &self.audio_outputs.len())
395            .field("main_audio_inputs", &self.main_audio_inputs)
396            .field("main_audio_outputs", &self.main_audio_outputs)
397            .finish()
398    }
399}
400
401impl Lv2Processor {
402    pub fn new(sample_rate: f64, buffer_size: usize, uri: &str) -> Result<Self, String> {
403        let world = World::new();
404        world.load_all();
405
406        let uri_node = world.new_uri(uri);
407        let plugin = world
408            .plugins()
409            .plugin(&uri_node)
410            .ok_or_else(|| format!("Plugin not found for URI: {uri}"))?;
411        if !plugin.verify() {
412            return Err(format!("Plugin failed verification: {uri}"));
413        }
414
415        let mut urid_feature = UridMapFeature::new()?;
416        let mut state_path_feature = StatePathFeature::new(default_state_base_dir());
417        let (instance, instantiate_features) = instantiate_plugin(
418            &plugin,
419            sample_rate,
420            uri,
421            &mut urid_feature,
422            &mut state_path_feature,
423        )?;
424        let active_instance = unsafe { instance.activate() };
425
426        let input_port = world.new_uri("http://lv2plug.in/ns/lv2core#InputPort");
427        let output_port = world.new_uri("http://lv2plug.in/ns/lv2core#OutputPort");
428        let audio_port = world.new_uri("http://lv2plug.in/ns/lv2core#AudioPort");
429        let control_port = world.new_uri("http://lv2plug.in/ns/lv2core#ControlPort");
430        let toggled_property = world.new_uri("http://lv2plug.in/ns/lv2core#toggled");
431        let integer_property = world.new_uri("http://lv2plug.in/ns/lv2core#integer");
432        let enumeration_property = world.new_uri("http://lv2plug.in/ns/lv2core#enumeration");
433        let port_group_predicate = world.new_uri("http://lv2plug.in/ns/ext/port-groups#group");
434        let main_input_group_predicate =
435            world.new_uri("http://lv2plug.in/ns/ext/port-groups#mainInput");
436        let main_output_group_predicate =
437            world.new_uri("http://lv2plug.in/ns/ext/port-groups#mainOutput");
438        let atom_port = world.new_uri("http://lv2plug.in/ns/ext/atom#AtomPort");
439        let event_port = world.new_uri("http://lv2plug.in/ns/ext/event#EventPort");
440
441        let ports_count = plugin.ports_count();
442        let mut port_bindings = vec![PortBinding::Scalar(0); ports_count];
443        let mut scalar_values = vec![0.0_f32; ports_count.max(1)];
444        let mut audio_inputs: Vec<Arc<AudioPort>> = vec![];
445        let mut audio_outputs: Vec<Arc<AudioPort>> = vec![];
446        let mut atom_inputs: Vec<AtomBuffer> = vec![];
447        let mut atom_outputs: Vec<AtomBuffer> = vec![];
448        let mut midi_inputs = 0_usize;
449        let mut midi_outputs = 0_usize;
450        let mut control_ports = vec![];
451        let has_worker_interface = plugin.has_extension_data(&world.new_uri(LV2_WORKER__INTERFACE));
452        let atom_sequence_urid = urid_feature.map_uri(LV2_ATOM__SEQUENCE);
453        let atom_object_urid = urid_feature.map_uri(LV2_ATOM__OBJECT);
454        let atom_long_urid = urid_feature.map_uri(LV2_ATOM__LONG);
455        let atom_double_urid = urid_feature.map_uri(LV2_ATOM__DOUBLE);
456        let atom_frame_time_urid = urid_feature.map_uri(LV2_ATOM__FRAMETIME);
457        let midi_event_urid = urid_feature.map_uri(lv2_raw::LV2_MIDI__MIDIEVENT);
458        let time_position_urid = urid_feature.map_uri(lv2_raw::LV2_TIME__POSITION);
459        let time_frame_urid = urid_feature.map_uri(lv2_raw::LV2_TIME__FRAME);
460        let time_speed_urid = urid_feature.map_uri(lv2_raw::LV2_TIME__SPEED);
461        let time_bpm_urid = urid_feature.map_uri(lv2_raw::LV2_TIME__BEATSPERMINUTE);
462        let time_bar_urid = urid_feature.map_uri(lv2_raw::LV2_TIME__BAR);
463        let time_bar_beat_urid = urid_feature.map_uri(lv2_raw::LV2_TIME__BARBEAT);
464        let time_beats_per_bar_urid = urid_feature.map_uri(lv2_raw::LV2_TIME__BEATSPERBAR);
465        let time_beat_unit_urid = urid_feature.map_uri(lv2_raw::LV2_TIME__BEATUNIT);
466        let declared_audio_inputs = plugin
467            .iter_ports()
468            .filter(|port| port.is_a(&audio_port) && port.is_a(&input_port))
469            .count();
470        let declared_audio_outputs = plugin
471            .iter_ports()
472            .filter(|port| port.is_a(&audio_port) && port.is_a(&output_port))
473            .count();
474        let main_audio_inputs = count_main_audio_ports(
475            &plugin,
476            &main_input_group_predicate,
477            &port_group_predicate,
478            &audio_port,
479            &input_port,
480        )
481        .unwrap_or(declared_audio_inputs);
482        let main_audio_outputs = count_main_audio_ports(
483            &plugin,
484            &main_output_group_predicate,
485            &port_group_predicate,
486            &audio_port,
487            &output_port,
488        )
489        .unwrap_or(declared_audio_outputs);
490
491        for port in plugin.iter_ports() {
492            let index = port.index();
493            let is_audio = port.is_a(&audio_port);
494            let is_control = port.is_a(&control_port);
495            let is_atom = port.is_a(&atom_port) || port.is_a(&event_port);
496            let is_input = port.is_a(&input_port);
497            let is_output = port.is_a(&output_port);
498
499            if is_audio && is_input {
500                let channel = audio_inputs.len();
501                audio_inputs.push(Arc::new(AudioPort::new(buffer_size)));
502                port_bindings[index] = PortBinding::AudioInput(channel);
503            } else if is_audio && is_output {
504                let channel = audio_outputs.len();
505                audio_outputs.push(Arc::new(AudioPort::new(buffer_size)));
506                port_bindings[index] = PortBinding::AudioOutput(channel);
507            } else if is_atom && is_input {
508                midi_inputs += 1;
509                let atom_idx = atom_inputs.len();
510                let mut buffer = AtomBuffer::new(LV2_ATOM_BUFFER_BYTES);
511                prepare_empty_atom_sequence(
512                    buffer.bytes_mut(),
513                    atom_sequence_urid,
514                    atom_frame_time_urid,
515                );
516                atom_inputs.push(buffer);
517                port_bindings[index] = PortBinding::AtomInput(atom_idx);
518            } else if is_atom && is_output {
519                midi_outputs += 1;
520                let atom_idx = atom_outputs.len();
521                let mut buffer = AtomBuffer::new(LV2_ATOM_BUFFER_BYTES);
522                prepare_output_atom_sequence(
523                    buffer.bytes_mut(),
524                    atom_sequence_urid,
525                    atom_frame_time_urid,
526                );
527                atom_outputs.push(buffer);
528                port_bindings[index] = PortBinding::AtomOutput(atom_idx);
529            } else {
530                let range = port.range();
531                let min = range
532                    .minimum
533                    .as_ref()
534                    .and_then(lv2_node_to_f32)
535                    .unwrap_or(0.0);
536                let max = range
537                    .maximum
538                    .as_ref()
539                    .and_then(lv2_node_to_f32)
540                    .unwrap_or(1.0);
541                let explicit_default = range.default.as_ref().and_then(lv2_node_to_f32);
542                let is_toggled = is_control && is_input && port.has_property(&toggled_property);
543                let is_discrete = is_control
544                    && is_input
545                    && (port.has_property(&integer_property)
546                        || port.has_property(&enumeration_property));
547                let (fallback_default, fallback_reason) =
548                    infer_missing_control_default(min, max, is_toggled, is_discrete);
549                let default_value = explicit_default.unwrap_or(fallback_default);
550                scalar_values[index] = default_value;
551                port_bindings[index] = PortBinding::Scalar(index);
552
553                if explicit_default.is_none()
554                    && is_control
555                    && is_input
556                    && std::env::var_os("MAOLAN_TRACE_LV2_DEFAULTS").is_some()
557                {
558                    let symbol = port
559                        .symbol()
560                        .and_then(|node| node.as_str().map(str::to_string))
561                        .unwrap_or_else(|| format!("port_{index}"));
562                    let _ = (uri, index, symbol, min, max, default_value, fallback_reason);
563                }
564
565                if is_control && is_input {
566                    let mut min = min;
567                    let mut max = max;
568                    if !matches!(min.partial_cmp(&max), Some(std::cmp::Ordering::Less)) {
569                        min = default_value - 1.0;
570                        max = default_value + 1.0;
571                    }
572                    let fallback_name = format!("Port {}", index);
573                    let name = port
574                        .name()
575                        .and_then(|node| node.as_str().map(str::to_string))
576                        .or_else(|| {
577                            port.symbol()
578                                .and_then(|node| node.as_str().map(str::to_string))
579                        })
580                        .unwrap_or(fallback_name);
581                    control_ports.push(ControlPortInfo {
582                        index: index as u32,
583                        name,
584                        min,
585                        max,
586                    });
587                }
588            }
589        }
590
591        let mut processor = Self {
592            uri: uri.to_string(),
593            plugin_name: plugin
594                .name()
595                .as_str()
596                .map(str::to_string)
597                .unwrap_or_else(|| uri.to_string()),
598            sample_rate,
599            instance: Some(active_instance),
600            _world: world,
601            _urid_feature: urid_feature,
602            _state_path_feature: state_path_feature,
603            _instantiate_features: instantiate_features,
604            port_bindings,
605            scalar_values: Arc::new(Mutex::new(scalar_values)),
606            audio_inputs,
607            audio_outputs,
608            main_audio_inputs,
609            main_audio_outputs,
610            atom_inputs,
611            atom_outputs,
612            atom_sequence_urid,
613            atom_object_urid,
614            atom_long_urid,
615            atom_double_urid,
616            atom_frame_time_urid,
617            midi_event_urid,
618            time_position_urid,
619            time_frame_urid,
620            time_speed_urid,
621            time_bpm_urid,
622            time_bar_urid,
623            time_bar_beat_urid,
624            time_beats_per_bar_urid,
625            time_beat_unit_urid,
626            midi_inputs,
627            midi_outputs,
628            has_worker_interface,
629            control_ports,
630            midnam_note_names: SimpleMutex::new(HashMap::new()),
631            bypassed: AtomicBool::new(false),
632        };
633        processor.validate_required_runtime_callbacks()?;
634        processor.connect_ports();
635        if std::env::var_os("MAOLAN_LV2_ENABLE_MIDNAM").is_some() {
636            processor.query_midnam();
637        }
638        Ok(processor)
639    }
640
641    pub fn uri(&self) -> &str {
642        &self.uri
643    }
644
645    pub fn name(&self) -> &str {
646        &self.plugin_name
647    }
648
649    pub fn audio_inputs(&self) -> &[Arc<AudioPort>] {
650        &self.audio_inputs
651    }
652
653    pub fn audio_outputs(&self) -> &[Arc<AudioPort>] {
654        &self.audio_outputs
655    }
656
657    pub fn audio_input_count(&self) -> usize {
658        self.audio_inputs.len()
659    }
660
661    pub fn audio_output_count(&self) -> usize {
662        self.audio_outputs.len()
663    }
664
665    pub fn main_audio_input_count(&self) -> usize {
666        self.main_audio_inputs
667    }
668
669    pub fn main_audio_output_count(&self) -> usize {
670        self.main_audio_outputs
671    }
672
673    pub fn midi_input_count(&self) -> usize {
674        self.midi_inputs
675    }
676
677    pub fn midi_output_count(&self) -> usize {
678        self.midi_outputs
679    }
680
681    pub fn setup_audio_ports(&self) {
682        for io in &self.audio_inputs {
683            io.setup();
684        }
685        for io in &self.audio_outputs {
686            io.setup();
687        }
688    }
689
690    pub fn process(&mut self, input_channels: &[Vec<f32>], frames: usize) -> Vec<Vec<f32>> {
691        if let Ok(mut values) = self.scalar_values.lock()
692            && values.is_empty()
693        {
694            values.push(0.0);
695        }
696
697        for (channel, io) in self.audio_inputs.iter_mut().enumerate() {
698            let buffer = io.buffer.lock();
699            buffer.fill(0.0);
700            if let Some(input) = input_channels.get(channel) {
701                let copy_len = input.len().min(frames);
702                buffer[..copy_len].copy_from_slice(&input[..copy_len]);
703            }
704        }
705        for io in &self.audio_outputs {
706            let buffer = io.buffer.lock();
707            buffer.fill(0.0);
708        }
709        for buffer in &mut self.atom_inputs {
710            prepare_empty_atom_sequence(
711                buffer.bytes_mut(),
712                self.atom_sequence_urid,
713                self.atom_frame_time_urid,
714            );
715        }
716        for buffer in &mut self.atom_outputs {
717            prepare_output_atom_sequence(
718                buffer.bytes_mut(),
719                self.atom_sequence_urid,
720                self.atom_frame_time_urid,
721            );
722        }
723
724        self.connect_ports();
725        if let Some(instance) = self.instance.as_mut() {
726            unsafe {
727                instance.run(frames);
728            }
729        }
730        self.audio_outputs
731            .iter()
732            .map(|io| io.buffer.lock().to_vec())
733            .collect()
734    }
735
736    pub fn set_bypassed(&self, bypassed: bool) {
737        self.bypassed.store(bypassed, Ordering::Relaxed);
738    }
739
740    pub fn is_bypassed(&self) -> bool {
741        self.bypassed.load(Ordering::Relaxed)
742    }
743
744    fn bypass_copy_inputs_to_outputs(&self) {
745        for (input, output) in self.audio_inputs.iter().zip(self.audio_outputs.iter()) {
746            let src = input.buffer.lock();
747            let dst = output.buffer.lock();
748            dst.fill(0.0);
749            for (d, s) in dst.iter_mut().zip(src.iter()) {
750                *d = *s;
751            }
752            *output.finished.lock() = true;
753        }
754        for output in self.audio_outputs.iter().skip(self.audio_inputs.len()) {
755            output.buffer.lock().fill(0.0);
756            *output.finished.lock() = true;
757        }
758    }
759
760    pub fn process_with_audio_io(
761        &mut self,
762        frames: usize,
763        midi_inputs: &[Vec<MidiEvent>],
764        transport: Lv2TransportInfo,
765    ) -> Vec<Vec<MidiEvent>> {
766        if let Ok(mut values) = self.scalar_values.lock()
767            && values.is_empty()
768        {
769            values.push(0.0);
770        }
771
772        for io in &self.audio_outputs {
773            let buffer = io.buffer.lock();
774            buffer.fill(0.0);
775            *io.finished.lock() = false;
776        }
777
778        if self.bypassed.load(Ordering::Relaxed) {
779            self.bypass_copy_inputs_to_outputs();
780            return vec![Vec::new(); self.midi_outputs];
781        }
782
783        for buffer in &mut self.atom_inputs {
784            prepare_empty_atom_sequence(
785                buffer.bytes_mut(),
786                self.atom_sequence_urid,
787                self.atom_frame_time_urid,
788            );
789        }
790        for buffer in &mut self.atom_outputs {
791            prepare_output_atom_sequence(
792                buffer.bytes_mut(),
793                self.atom_sequence_urid,
794                self.atom_frame_time_urid,
795            );
796        }
797        for port in 0..self.atom_inputs.len() {
798            self.write_transport_event(port, transport);
799        }
800        for (port, events) in midi_inputs.iter().enumerate() {
801            self.write_midi_input_events(port, events);
802        }
803
804        self.connect_ports();
805        if let Some(instance) = self.instance.as_mut() {
806            unsafe {
807                instance.run(frames);
808            }
809        }
810        self.run_worker_cycle();
811
812        for io in &self.audio_outputs {
813            *io.finished.lock() = true;
814        }
815        let mut midi_outputs = vec![];
816        for port in 0..self.midi_outputs {
817            midi_outputs.push(self.read_midi_output_events(port));
818        }
819        midi_outputs
820    }
821
822    fn run_worker_cycle(&mut self) {
823        if !self.has_worker_interface {
824            return;
825        }
826        let Some(worker_iface) = self.worker_interface() else {
827            return;
828        };
829        let (work_fn, work_response_fn, end_run_fn) = (
830            worker_iface.work,
831            worker_iface.work_response,
832            worker_iface.end_run,
833        );
834        let Some(work_fn) = work_fn else {
835            return;
836        };
837        let instance_handle = self.instance_handle();
838        if instance_handle.is_null() {
839            return;
840        }
841
842        let worker_state = &self._instantiate_features._worker_feature.state;
843        let mut jobs = std::mem::take(worker_state.jobs.lock());
844        for job in jobs.drain(..) {
845            if job.len() > (u32::MAX as usize) {
846                continue;
847            }
848            unsafe {
849                work_fn(
850                    instance_handle,
851                    Some(lv2_worker_respond_callback),
852                    &**worker_state as *const WorkerScheduleState as *mut c_void,
853                    job.len() as u32,
854                    job.as_ptr().cast::<c_void>(),
855                );
856            }
857        }
858        *worker_state.jobs.lock() = jobs;
859
860        if let Some(work_response_fn) = work_response_fn {
861            let mut responses = std::mem::take(worker_state.responses.lock());
862            for response in responses.drain(..) {
863                if response.len() > (u32::MAX as usize) {
864                    continue;
865                }
866                unsafe {
867                    work_response_fn(
868                        instance_handle,
869                        response.len() as u32,
870                        response.as_ptr().cast::<c_void>(),
871                    );
872                }
873            }
874            *worker_state.responses.lock() = responses;
875        }
876
877        if let Some(end_run_fn) = end_run_fn {
878            unsafe {
879                end_run_fn(instance_handle);
880            }
881        }
882    }
883
884    fn connect_ports(&mut self) {
885        for (port_index, binding) in self.port_bindings.iter().enumerate() {
886            match binding {
887                PortBinding::AudioInput(channel) => {
888                    let ptr = self.audio_inputs[*channel].buffer.lock().as_mut_ptr();
889                    if let Some(instance) = self.instance.as_mut() {
890                        unsafe {
891                            instance.instance_mut().connect_port_mut(port_index, ptr);
892                        }
893                    }
894                }
895                PortBinding::AudioOutput(channel) => {
896                    let ptr = self.audio_outputs[*channel].buffer.lock().as_mut_ptr();
897                    if let Some(instance) = self.instance.as_mut() {
898                        unsafe {
899                            instance.instance_mut().connect_port_mut(port_index, ptr);
900                        }
901                    }
902                }
903                PortBinding::AtomInput(atom_index) => {
904                    let ptr = self.atom_inputs[*atom_index].ptr_mut();
905                    if let Some(instance) = self.instance.as_mut() {
906                        unsafe {
907                            instance.instance_mut().connect_port_mut(port_index, ptr);
908                        }
909                    }
910                }
911                PortBinding::AtomOutput(atom_index) => {
912                    let ptr = self.atom_outputs[*atom_index].ptr_mut();
913                    if let Some(instance) = self.instance.as_mut() {
914                        unsafe {
915                            instance.instance_mut().connect_port_mut(port_index, ptr);
916                        }
917                    }
918                }
919                PortBinding::Scalar(index) => {
920                    if let Ok(mut values) = self.scalar_values.lock()
921                        && *index < values.len()
922                    {
923                        let ptr = (&mut values[*index]) as *mut f32;
924                        if let Some(instance) = self.instance.as_mut() {
925                            unsafe {
926                                instance.instance_mut().connect_port_mut(port_index, ptr);
927                            }
928                        }
929                    }
930                }
931            }
932        }
933    }
934
935    fn validate_required_runtime_callbacks(&self) -> Result<(), String> {
936        let Some(instance) = self.instance.as_ref() else {
937            return Err(format!("LV2 plugin '{}' has no active instance", self.uri));
938        };
939        let Some(descriptor) = instance.instance().descriptor() else {
940            return Err(format!("LV2 plugin '{}' has no descriptor", self.uri));
941        };
942        let connect_ptr = (descriptor.connect_port as *const ()) as usize;
943        if connect_ptr == 0 {
944            return Err(format!(
945                "LV2 plugin '{}' has null connect_port callback",
946                self.uri
947            ));
948        }
949        let run_ptr = (descriptor.run as *const ()) as usize;
950        if run_ptr == 0 {
951            return Err(format!("LV2 plugin '{}' has null run callback", self.uri));
952        }
953        Ok(())
954    }
955
956    pub fn snapshot_state(&self) -> Lv2PluginState {
957        let mut state = Lv2PluginState {
958            port_values: self.control_port_values(),
959            properties: vec![],
960        };
961        let Some(interface) = self.state_interface() else {
962            return state;
963        };
964        let Some(save_fn) = interface.save else {
965            return state;
966        };
967
968        let mut ctx = StateSaveContext { properties: vec![] };
969        let features = self.state_feature_ptrs();
970        let status = unsafe {
971            save_fn(
972                self.instance_handle(),
973                Some(lv2_state_store_callback),
974                (&mut ctx as *mut StateSaveContext).cast::<c_void>(),
975                0,
976                features.as_ptr(),
977            )
978        };
979        if status != LV2_STATE_STATUS_SUCCESS {
980            return state;
981        }
982
983        state.properties = ctx
984            .properties
985            .into_iter()
986            .filter_map(|p| {
987                let key_uri = self._urid_feature.unmap_urid(p.key)?;
988                let type_uri = self._urid_feature.unmap_urid(p.type_)?;
989                Some(Lv2StateProperty {
990                    key_uri,
991                    type_uri,
992                    flags: p.flags,
993                    value: p.value,
994                })
995            })
996            .collect();
997        state
998    }
999
1000    pub fn snapshot_port_state(&self) -> Lv2PluginState {
1001        Lv2PluginState {
1002            port_values: self.control_port_values(),
1003            properties: vec![],
1004        }
1005    }
1006
1007    pub fn restore_state(&mut self, state: &Lv2PluginState) -> Result<(), String> {
1008        self.set_control_port_values(&state.port_values);
1009        if state.properties.is_empty() {
1010            return Ok(());
1011        }
1012        let Some(interface) = self.state_interface() else {
1013            return Ok(());
1014        };
1015        let Some(restore_fn) = interface.restore else {
1016            return Ok(());
1017        };
1018
1019        let mut properties: Vec<RawStateProperty> = vec![];
1020        let mut by_key: HashMap<u32, usize> = HashMap::new();
1021        for prop in &state.properties {
1022            let key = self._urid_feature.map_uri(prop.key_uri.as_bytes());
1023            let type_ = self._urid_feature.map_uri(prop.type_uri.as_bytes());
1024            if key == 0 || type_ == 0 {
1025                continue;
1026            }
1027            let idx = properties.len();
1028            properties.push(RawStateProperty {
1029                key,
1030                type_,
1031                flags: prop.flags,
1032                value: prop.value.clone(),
1033            });
1034            by_key.insert(key, idx);
1035        }
1036        let mut ctx = StateRestoreContext { properties, by_key };
1037        let features = self.state_feature_ptrs();
1038
1039        let status = unsafe {
1040            restore_fn(
1041                self.instance_handle(),
1042                Some(lv2_state_retrieve_callback),
1043                (&mut ctx as *mut StateRestoreContext).cast::<c_void>(),
1044                0,
1045                features.as_ptr(),
1046            )
1047        };
1048        if status == LV2_STATE_STATUS_SUCCESS {
1049            Ok(())
1050        } else {
1051            Err(format!(
1052                "LV2 state restore failed for '{}': status {}",
1053                self.uri, status
1054            ))
1055        }
1056    }
1057
1058    fn state_interface(&self) -> Option<&Lv2StateInterface> {
1059        let instance = self.instance.as_ref()?;
1060        if !self.has_extension_data_callback(instance.instance()) {
1061            return None;
1062        }
1063        let ptr = unsafe {
1064            instance
1065                .instance()
1066                .extension_data::<Lv2StateInterface>(LV2_STATE_INTERFACE_URI)?
1067        };
1068        Some(unsafe { ptr.as_ref() })
1069    }
1070
1071    fn worker_interface(&self) -> Option<&Lv2WorkerInterface> {
1072        let instance = self.instance.as_ref()?;
1073        if !self.has_extension_data_callback(instance.instance()) {
1074            return None;
1075        }
1076        let ptr = unsafe {
1077            instance
1078                .instance()
1079                .extension_data::<Lv2WorkerInterface>(LV2_WORKER__INTERFACE)?
1080        };
1081        Some(unsafe { ptr.as_ref() })
1082    }
1083
1084    fn has_extension_data_callback(&self, instance: &lilv::instance::Instance) -> bool {
1085        let Some(descriptor) = instance.descriptor() else {
1086            return false;
1087        };
1088        (descriptor.extension_data as *const ()) as usize != 0
1089    }
1090
1091    fn instance_handle(&self) -> Lv2Handle {
1092        self.instance
1093            .as_ref()
1094            .map(|i| i.instance().handle() as Lv2Handle)
1095            .unwrap_or(std::ptr::null_mut())
1096    }
1097
1098    pub fn instance_access_handle(&self) -> Option<usize> {
1099        let handle = self.instance_handle();
1100        if handle.is_null() {
1101            None
1102        } else {
1103            Some(handle as usize)
1104        }
1105    }
1106
1107    fn state_feature_ptrs(&self) -> [*const LV2Feature; 6] {
1108        let sp = self._state_path_feature.feature_ptrs();
1109        [
1110            self._urid_feature.map_feature() as *const LV2Feature,
1111            self._urid_feature.unmap_feature() as *const LV2Feature,
1112            sp[0],
1113            sp[1],
1114            sp[2],
1115            std::ptr::null(),
1116        ]
1117    }
1118
1119    fn control_port_values(&self) -> Vec<Lv2StatePortValue> {
1120        let Ok(values) = self.scalar_values.lock() else {
1121            return vec![];
1122        };
1123        self.control_ports
1124            .iter()
1125            .filter_map(|port| {
1126                values.get(port.index as usize).map(|v| Lv2StatePortValue {
1127                    index: port.index,
1128                    value: *v,
1129                })
1130            })
1131            .collect()
1132    }
1133
1134    fn set_control_port_values(&mut self, port_values: &[Lv2StatePortValue]) {
1135        let Ok(mut values) = self.scalar_values.lock() else {
1136            return;
1137        };
1138        for port in port_values {
1139            if let Some(slot) = values.get_mut(port.index as usize) {
1140                *slot = port.value;
1141            }
1142        }
1143    }
1144
1145    pub fn set_state_base_dir(&mut self, base_dir: PathBuf) {
1146        self._state_path_feature.set_base_dir(base_dir);
1147    }
1148
1149    pub fn control_ports_with_values(&self) -> Vec<Lv2ControlPortInfo> {
1150        let Ok(values) = self.scalar_values.lock() else {
1151            return Vec::new();
1152        };
1153        self.control_ports
1154            .iter()
1155            .map(|port| Lv2ControlPortInfo {
1156                index: port.index,
1157                name: port.name.clone(),
1158                min: port.min,
1159                max: port.max,
1160                value: values.get(port.index as usize).copied().unwrap_or(0.0),
1161            })
1162            .collect()
1163    }
1164
1165    pub fn set_control_value(&mut self, index: u32, value: f32) -> Result<(), String> {
1166        let Some(port) = self.control_ports.iter().find(|port| port.index == index) else {
1167            return Err(format!("Unknown LV2 control port index: {index}"));
1168        };
1169        let clamped = value.clamp(port.min, port.max);
1170        let Ok(mut values) = self.scalar_values.lock() else {
1171            return Err("Failed to lock LV2 control values".to_string());
1172        };
1173        let Some(slot) = values.get_mut(index as usize) else {
1174            return Err(format!("LV2 control port index out of range: {index}"));
1175        };
1176        *slot = clamped;
1177        Ok(())
1178    }
1179
1180    pub fn midnam_note_names(&self) -> HashMap<u8, String> {
1181        self.midnam_note_names.lock().clone()
1182    }
1183
1184    fn query_midnam(&mut self) {
1185        let Some(instance) = &self.instance else {
1186            return;
1187        };
1188
1189        if !self.has_extension_data_callback(instance.instance()) {
1190            return;
1191        }
1192
1193        let interface_ptr = unsafe {
1194            instance
1195                .instance()
1196                .extension_data::<Lv2MidnamInterface>(LV2_MIDNAM__INTERFACE)
1197        };
1198        let Some(interface_ptr) = interface_ptr else {
1199            return;
1200        };
1201        let interface = unsafe { interface_ptr.as_ref() };
1202
1203        let Some(midnam_fn) = interface.midnam else {
1204            return;
1205        };
1206        let Some(free_fn) = interface.free else {
1207            return;
1208        };
1209
1210        let xml_ptr = unsafe { midnam_fn(instance.instance().handle()) };
1211        if xml_ptr.is_null() {
1212            return;
1213        }
1214
1215        let xml_cstr = unsafe { CStr::from_ptr(xml_ptr) };
1216        let Ok(xml_str) = xml_cstr.to_str() else {
1217            unsafe { free_fn(xml_ptr) };
1218            return;
1219        };
1220
1221        let note_names = self.parse_midnam_xml(xml_str);
1222        *self.midnam_note_names.lock() = note_names;
1223
1224        unsafe { free_fn(xml_ptr) };
1225    }
1226
1227    fn parse_midnam_xml(&self, xml: &str) -> HashMap<u8, String> {
1228        let mut note_names = HashMap::new();
1229
1230        for line in xml.lines() {
1231            let line = line.trim();
1232            if !line.starts_with("<Note ") {
1233                continue;
1234            }
1235
1236            let number = if let Some(start) = line.find("Number=\"") {
1237                let start = start + 8;
1238                if let Some(end) = line[start..].find('"') {
1239                    line[start..start + end].parse::<u8>().ok()
1240                } else {
1241                    None
1242                }
1243            } else {
1244                None
1245            };
1246
1247            let name = if let Some(start) = line.find("Name=\"") {
1248                let start = start + 6;
1249                line[start..]
1250                    .find('"')
1251                    .map(|end| line[start..start + end].to_string())
1252            } else {
1253                None
1254            };
1255
1256            if let (Some(num), Some(name_str)) = (number, name) {
1257                note_names.insert(num, name_str);
1258            }
1259        }
1260
1261        note_names
1262    }
1263
1264    fn write_midi_input_events(&mut self, port: usize, events: &[MidiEvent]) {
1265        let Some(buffer) = self.atom_inputs.get_mut(port) else {
1266            return;
1267        };
1268        let bytes = buffer.bytes_mut();
1269        if bytes.len() < std::mem::size_of::<LV2AtomSequence>() {
1270            return;
1271        }
1272        let seq = bytes.as_mut_ptr() as *mut LV2AtomSequence;
1273        let capacity = bytes
1274            .len()
1275            .saturating_sub(std::mem::size_of::<lv2_raw::LV2Atom>()) as u32;
1276        for event in events {
1277            if event.data.is_empty() {
1278                continue;
1279            }
1280            let mut raw =
1281                vec![0_u8; std::mem::size_of::<lv2_raw::LV2AtomEvent>() + event.data.len()];
1282            let raw_event = raw.as_mut_ptr() as *mut lv2_raw::LV2AtomEvent;
1283            unsafe {
1284                (*raw_event).time_in_frames = event.frame as i64;
1285                (*raw_event).body.mytype = self.midi_event_urid;
1286                (*raw_event).body.size = event.data.len() as u32;
1287                let data_ptr =
1288                    (raw_event as *mut u8).add(std::mem::size_of::<lv2_raw::LV2AtomEvent>());
1289                std::ptr::copy_nonoverlapping(event.data.as_ptr(), data_ptr, event.data.len());
1290                if lv2_atom_sequence_append_event(seq, capacity, raw_event).is_null() {
1291                    break;
1292                }
1293            }
1294        }
1295    }
1296
1297    fn write_transport_event(&mut self, port: usize, transport: Lv2TransportInfo) {
1298        let Some(buffer) = self.atom_inputs.get_mut(port) else {
1299            return;
1300        };
1301        let bytes = buffer.bytes_mut();
1302        if bytes.len() < std::mem::size_of::<LV2AtomSequence>() {
1303            return;
1304        }
1305        let seq = bytes.as_mut_ptr() as *mut LV2AtomSequence;
1306        let capacity = bytes
1307            .len()
1308            .saturating_sub(std::mem::size_of::<lv2_raw::LV2Atom>()) as u32;
1309
1310        let beats_per_bar = if transport.tsig_num == 0 {
1311            4.0
1312        } else {
1313            transport.tsig_num as f64
1314        };
1315        let beat_unit = if transport.tsig_denom == 0 {
1316            4_i64
1317        } else {
1318            transport.tsig_denom as i64
1319        };
1320        let bpm = if transport.bpm > 0.0 {
1321            transport.bpm
1322        } else {
1323            120.0
1324        };
1325        let speed = if transport.playing { 1.0 } else { 0.0 };
1326        let sample = transport.transport_sample as i64;
1327        let seconds = (transport.transport_sample as f64) / self.sample_rate.max(1.0);
1328        let absolute_beats = seconds * bpm / 60.0;
1329        let bar = (absolute_beats / beats_per_bar).floor().max(0.0) as i64;
1330        let bar_beat = absolute_beats - (bar as f64 * beats_per_bar);
1331
1332        let mut payload =
1333            Vec::<u8>::with_capacity(std::mem::size_of::<LV2AtomObjectBody>() + (7 * 32));
1334        let object_body = LV2AtomObjectBody {
1335            id: 0,
1336            otype: self.time_position_urid,
1337        };
1338        let object_body_bytes = unsafe {
1339            std::slice::from_raw_parts(
1340                (&object_body as *const LV2AtomObjectBody).cast::<u8>(),
1341                std::mem::size_of::<LV2AtomObjectBody>(),
1342            )
1343        };
1344        payload.extend_from_slice(object_body_bytes);
1345
1346        append_object_long_property(
1347            &mut payload,
1348            self.time_frame_urid,
1349            self.atom_long_urid,
1350            sample,
1351        );
1352        append_object_double_property(
1353            &mut payload,
1354            self.time_speed_urid,
1355            self.atom_double_urid,
1356            speed,
1357        );
1358        append_object_double_property(&mut payload, self.time_bpm_urid, self.atom_double_urid, bpm);
1359        append_object_long_property(&mut payload, self.time_bar_urid, self.atom_long_urid, bar);
1360        append_object_double_property(
1361            &mut payload,
1362            self.time_bar_beat_urid,
1363            self.atom_double_urid,
1364            bar_beat,
1365        );
1366        append_object_double_property(
1367            &mut payload,
1368            self.time_beats_per_bar_urid,
1369            self.atom_double_urid,
1370            beats_per_bar,
1371        );
1372        append_object_long_property(
1373            &mut payload,
1374            self.time_beat_unit_urid,
1375            self.atom_long_urid,
1376            beat_unit,
1377        );
1378
1379        if payload.len() > (u32::MAX as usize) {
1380            return;
1381        }
1382
1383        let mut raw = vec![0_u8; std::mem::size_of::<LV2AtomEvent>() + payload.len()];
1384        let raw_event = raw.as_mut_ptr() as *mut LV2AtomEvent;
1385        unsafe {
1386            (*raw_event).time_in_frames = 0;
1387            (*raw_event).body.mytype = self.atom_object_urid;
1388            (*raw_event).body.size = payload.len() as u32;
1389            let data_ptr = (raw_event as *mut u8).add(std::mem::size_of::<LV2AtomEvent>());
1390            std::ptr::copy_nonoverlapping(payload.as_ptr(), data_ptr, payload.len());
1391            let _ = lv2_atom_sequence_append_event(seq, capacity, raw_event);
1392        }
1393    }
1394
1395    fn read_midi_output_events(&mut self, port: usize) -> Vec<MidiEvent> {
1396        let Some(buffer) = self.atom_outputs.get_mut(port) else {
1397            return vec![];
1398        };
1399        let bytes = buffer.bytes_mut();
1400        if bytes.len() < std::mem::size_of::<LV2AtomSequence>() {
1401            return vec![];
1402        }
1403
1404        let mut result = Vec::new();
1405        let seq = bytes.as_mut_ptr() as *mut LV2AtomSequence;
1406        unsafe {
1407            let body = &(*seq).body as *const LV2AtomSequenceBody;
1408            let size = (*seq).atom.size;
1409            let mut it = lv2_atom_sequence_begin(body);
1410            while !lv2_atom_sequence_is_end(body, size, it) {
1411                let event = &*it;
1412                if event.body.mytype == self.midi_event_urid && event.body.size > 0 {
1413                    let data_ptr =
1414                        (it as *const u8).add(std::mem::size_of::<lv2_raw::LV2AtomEvent>());
1415                    let data_len = event.body.size as usize;
1416                    let data = std::slice::from_raw_parts(data_ptr, data_len).to_vec();
1417                    result.push(MidiEvent::new(event.time_in_frames.max(0) as u32, data));
1418                }
1419                it = lv2_atom_sequence_next(it);
1420            }
1421        }
1422        result
1423    }
1424}
1425
1426impl Drop for Lv2Processor {
1427    fn drop(&mut self) {
1428        let Some(instance) = self.instance.take() else {
1429            return;
1430        };
1431        drop(instance);
1432    }
1433}
1434
1435impl Lv2Host {
1436    pub fn new(sample_rate: f64) -> Self {
1437        let world = World::new();
1438        world.load_all();
1439        Self {
1440            world,
1441            sample_rate,
1442            loaded_plugins: HashMap::new(),
1443        }
1444    }
1445
1446    pub fn list_plugins(&self) -> Vec<Lv2PluginInfo> {
1447        let input_port = self.world.new_uri("http://lv2plug.in/ns/lv2core#InputPort");
1448        let output_port = self
1449            .world
1450            .new_uri("http://lv2plug.in/ns/lv2core#OutputPort");
1451        let audio_port = self.world.new_uri("http://lv2plug.in/ns/lv2core#AudioPort");
1452        let atom_port = self.world.new_uri("http://lv2plug.in/ns/ext/atom#AtomPort");
1453        let event_port = self
1454            .world
1455            .new_uri("http://lv2plug.in/ns/ext/event#EventPort");
1456        let midi_event = self
1457            .world
1458            .new_uri("http://lv2plug.in/ns/ext/midi#MidiEvent");
1459
1460        let mut plugins = self
1461            .world
1462            .plugins()
1463            .iter()
1464            .filter(|plugin| plugin.verify())
1465            .filter_map(|plugin| {
1466                let uri = plugin.uri().as_uri()?.to_string();
1467                let name = plugin.name().as_str().unwrap_or(&uri).to_string();
1468                let class_label = plugin
1469                    .class()
1470                    .label()
1471                    .as_str()
1472                    .unwrap_or("Unknown")
1473                    .to_string();
1474                let bundle_uri = plugin.bundle_uri().as_uri().unwrap_or("").to_string();
1475                let required_features = plugin_feature_uris(&plugin);
1476                let (audio_inputs, audio_outputs, midi_inputs, midi_outputs) = plugin_port_counts(
1477                    &plugin,
1478                    &input_port,
1479                    &output_port,
1480                    &audio_port,
1481                    &atom_port,
1482                    &event_port,
1483                    &midi_event,
1484                );
1485
1486                Some(Lv2PluginInfo {
1487                    uri,
1488                    name,
1489                    class_label,
1490                    bundle_uri,
1491                    required_features,
1492                    audio_inputs,
1493                    audio_outputs,
1494                    midi_inputs,
1495                    midi_outputs,
1496                })
1497            })
1498            .collect::<Vec<_>>();
1499
1500        plugins.sort_by(|left, right| left.name.cmp(&right.name));
1501        plugins
1502    }
1503
1504    pub fn load_plugin(&mut self, uri: &str) -> Result<(), String> {
1505        if self.loaded_plugins.contains_key(uri) {
1506            return Err(format!("Plugin is already loaded: {uri}"));
1507        }
1508
1509        let plugin = self
1510            .plugin_by_uri(uri)
1511            .ok_or_else(|| format!("Plugin not found for URI: {uri}"))?;
1512
1513        let mut urid_feature = UridMapFeature::new()?;
1514        let mut state_path_feature = StatePathFeature::new(default_state_base_dir());
1515        let (instance, instantiate_features) = instantiate_plugin(
1516            &plugin,
1517            self.sample_rate,
1518            uri,
1519            &mut urid_feature,
1520            &mut state_path_feature,
1521        )?;
1522        let active_instance = unsafe { instance.activate() };
1523        self.loaded_plugins.insert(
1524            uri.to_string(),
1525            LoadedPlugin {
1526                instance: active_instance,
1527                _urid_feature: urid_feature,
1528                _state_path_feature: state_path_feature,
1529                _instantiate_features: instantiate_features,
1530            },
1531        );
1532        Ok(())
1533    }
1534
1535    pub fn unload_plugin(&mut self, uri: &str) -> Result<(), String> {
1536        let loaded_plugin = self
1537            .loaded_plugins
1538            .remove(uri)
1539            .ok_or_else(|| format!("Plugin is not currently loaded: {uri}"))?;
1540        let _ = unsafe { loaded_plugin.instance.deactivate() };
1541        Ok(())
1542    }
1543
1544    pub fn unload_all(&mut self) {
1545        let uris = self.loaded_plugins();
1546        for uri in uris {
1547            let _ = self.unload_plugin(&uri);
1548        }
1549    }
1550
1551    pub fn loaded_plugins(&self) -> Vec<String> {
1552        let mut uris = self.loaded_plugins.keys().cloned().collect::<Vec<_>>();
1553        uris.sort();
1554        uris
1555    }
1556
1557    pub fn loaded_count(&self) -> usize {
1558        self.loaded_plugins.len()
1559    }
1560
1561    fn plugin_by_uri(&self, uri: &str) -> Option<Plugin> {
1562        let uri_node = self.world.new_uri(uri);
1563        self.world.plugins().plugin(&uri_node)
1564    }
1565}
1566
1567impl Drop for Lv2Host {
1568    fn drop(&mut self) {
1569        self.unload_all();
1570    }
1571}
1572
1573fn plugin_feature_uris(plugin: &Plugin) -> Vec<String> {
1574    plugin
1575        .required_features()
1576        .iter()
1577        .filter_map(|feature| {
1578            feature
1579                .as_uri()
1580                .map(str::to_string)
1581                .or_else(|| feature.as_str().map(str::to_string))
1582        })
1583        .collect()
1584}
1585
1586fn instantiate_plugin(
1587    plugin: &Plugin,
1588    sample_rate: f64,
1589    uri: &str,
1590    urid_feature: &mut UridMapFeature,
1591    state_path_feature: &mut StatePathFeature,
1592) -> Result<(lilv::instance::Instance, InstantiateFeatureSet), String> {
1593    let required_features = plugin_feature_uris(plugin);
1594    let feature_set =
1595        build_instantiate_features(&required_features, urid_feature, state_path_feature)?;
1596    let feature_refs: Vec<&LV2Feature> = feature_set.features.iter().collect();
1597    let instance = unsafe { plugin.instantiate(sample_rate, feature_refs) }.ok_or_else(|| {
1598        if required_features.is_empty() {
1599            format!(
1600                "Failed to instantiate '{uri}'. It likely requires LV2 host features that are not wired yet."
1601            )
1602        } else {
1603            format!(
1604                "Failed to instantiate '{uri}'. Required features: {}",
1605                required_features.join(", ")
1606            )
1607        }
1608    })?;
1609    Ok((instance, feature_set))
1610}
1611
1612fn build_instantiate_features(
1613    required_features: &[String],
1614    urid_feature: &UridMapFeature,
1615    state_path_feature: &StatePathFeature,
1616) -> Result<InstantiateFeatureSet, String> {
1617    let mut seen = HashSet::<String>::new();
1618    let mut feature_uris = Vec::<CString>::new();
1619    let mut features = Vec::<LV2Feature>::new();
1620
1621    let mut push_feature =
1622        |uri: &str, data: *mut c_void, allow_duplicate: bool| -> Result<(), String> {
1623            if !allow_duplicate && !seen.insert(uri.to_string()) {
1624                return Ok(());
1625            }
1626            let c_uri = CString::new(uri)
1627                .map_err(|e| format!("Invalid LV2 feature URI '{uri}' for instantiate: {e}"))?;
1628            let feature = LV2Feature {
1629                uri: c_uri.as_ptr(),
1630                data,
1631            };
1632            feature_uris.push(c_uri);
1633            features.push(feature);
1634            Ok(())
1635        };
1636
1637    push_feature(LV2_URID__MAP, urid_feature.map_feature().data, false)?;
1638    push_feature(LV2_URID__UNMAP, urid_feature.unmap_feature().data, false)?;
1639    let worker_feature = WorkerFeature::new()?;
1640    push_feature(LV2_WORKER__SCHEDULE, worker_feature.feature.data, false)?;
1641
1642    let state_features = state_path_feature.feature_ptrs();
1643    for feature_ptr in state_features {
1644        if feature_ptr.is_null() {
1645            continue;
1646        }
1647        let feature = unsafe { &*feature_ptr };
1648        let uri = unsafe { CStr::from_ptr(feature.uri) }
1649            .to_str()
1650            .map_err(|e| format!("Invalid LV2 feature URI from state path feature: {e}"))?;
1651        push_feature(uri, feature.data, false)?;
1652    }
1653
1654    let option_values = vec![1_u32, 8192_u32, 1024_u32];
1655    let int_type = urid_feature.map_uri(LV2_ATOM__INT);
1656    let min_key = urid_feature.map_uri(LV2_BUF_SIZE__MIN_BLOCK_LENGTH.as_bytes());
1657    let max_key = urid_feature.map_uri(LV2_BUF_SIZE__MAX_BLOCK_LENGTH.as_bytes());
1658    let nominal_key = urid_feature.map_uri(LV2_BUF_SIZE__NOMINAL_BLOCK_LENGTH.as_bytes());
1659    let mut options = vec![
1660        LV2OptionsOption {
1661            context: 0,
1662            subject: 0,
1663            key: min_key,
1664            size: std::mem::size_of::<u32>() as u32,
1665            type_: int_type,
1666            value: (&option_values[0] as *const u32).cast::<c_void>(),
1667        },
1668        LV2OptionsOption {
1669            context: 0,
1670            subject: 0,
1671            key: max_key,
1672            size: std::mem::size_of::<u32>() as u32,
1673            type_: int_type,
1674            value: (&option_values[1] as *const u32).cast::<c_void>(),
1675        },
1676        LV2OptionsOption {
1677            context: 0,
1678            subject: 0,
1679            key: nominal_key,
1680            size: std::mem::size_of::<u32>() as u32,
1681            type_: int_type,
1682            value: (&option_values[2] as *const u32).cast::<c_void>(),
1683        },
1684        LV2OptionsOption {
1685            context: 0,
1686            subject: 0,
1687            key: 0,
1688            size: 0,
1689            type_: 0,
1690            value: std::ptr::null(),
1691        },
1692    ];
1693    push_feature(
1694        LV2_OPTIONS__OPTIONS,
1695        options.as_mut_ptr().cast::<c_void>(),
1696        false,
1697    )?;
1698
1699    let flag_feature_data = Box::new(0_u8);
1700
1701    for required in required_features {
1702        let data = match required.as_str() {
1703            LV2_OPTIONS__OPTIONS => options.as_mut_ptr().cast::<c_void>(),
1704            LV2_BUF_SIZE__BOUNDED_BLOCK_LENGTH => (&*flag_feature_data as *const u8)
1705                .cast_mut()
1706                .cast::<c_void>(),
1707            LV2_URID__MAP_URI_TYPO_COMPAT => urid_feature.map_feature().data,
1708            LV2_WORKER__SCHEDULE => worker_feature.feature.data,
1709            _ => (&*flag_feature_data as *const u8)
1710                .cast_mut()
1711                .cast::<c_void>(),
1712        };
1713        push_feature(required, data, false)?;
1714    }
1715
1716    Ok(InstantiateFeatureSet {
1717        _feature_uris: feature_uris,
1718        features,
1719        _worker_feature: worker_feature,
1720        _option_values: option_values,
1721        _options: options,
1722        _flag_feature_data: flag_feature_data,
1723    })
1724}
1725
1726impl WorkerFeature {
1727    fn new() -> Result<Self, String> {
1728        let mut schedule = Box::new(Lv2WorkerSchedule {
1729            handle: std::ptr::null_mut(),
1730            schedule_work: Some(lv2_worker_schedule_work_callback),
1731        });
1732        let state = Box::new(WorkerScheduleState {
1733            jobs: SimpleMutex::new(vec![]),
1734            responses: SimpleMutex::new(vec![]),
1735        });
1736        schedule.handle = &*state as *const WorkerScheduleState as *mut c_void;
1737        let uri =
1738            CString::new(LV2_WORKER__SCHEDULE).map_err(|e| format!("Invalid worker URI: {e}"))?;
1739        let feature = LV2Feature {
1740            uri: uri.as_ptr(),
1741            data: (&mut *schedule as *mut Lv2WorkerSchedule).cast::<c_void>(),
1742        };
1743        Ok(Self {
1744            _uri: uri,
1745            _schedule: schedule,
1746            feature,
1747            state,
1748        })
1749    }
1750}
1751
1752unsafe extern "C" fn lv2_worker_schedule_work_callback(
1753    handle: *mut c_void,
1754    size: u32,
1755    data: *const c_void,
1756) -> u32 {
1757    if handle.is_null() || (size > 0 && data.is_null()) {
1758        return LV2_WORKER_ERR_UNKNOWN;
1759    }
1760    let state = unsafe { &*(handle as *const WorkerScheduleState) };
1761    let bytes = if size == 0 {
1762        vec![]
1763    } else {
1764        unsafe { std::slice::from_raw_parts(data.cast::<u8>(), size as usize).to_vec() }
1765    };
1766    state.jobs.lock().push(bytes);
1767    LV2_WORKER_SUCCESS
1768}
1769
1770unsafe extern "C" fn lv2_worker_respond_callback(
1771    handle: *mut c_void,
1772    size: u32,
1773    data: *const c_void,
1774) -> u32 {
1775    if handle.is_null() || (size > 0 && data.is_null()) {
1776        return LV2_WORKER_ERR_UNKNOWN;
1777    }
1778    let state = unsafe { &*(handle as *const WorkerScheduleState) };
1779    let bytes = if size == 0 {
1780        vec![]
1781    } else {
1782        unsafe { std::slice::from_raw_parts(data.cast::<u8>(), size as usize).to_vec() }
1783    };
1784    state.responses.lock().push(bytes);
1785    LV2_WORKER_SUCCESS
1786}
1787
1788fn append_object_long_property(buffer: &mut Vec<u8>, key: LV2Urid, atom_type: LV2Urid, value: i64) {
1789    let prop = LV2AtomPropertyBody {
1790        key,
1791        context: 0,
1792        value: LV2Atom {
1793            size: std::mem::size_of::<i64>() as u32,
1794            mytype: atom_type,
1795        },
1796    };
1797    let prop_size = std::mem::size_of::<LV2AtomPropertyBody>();
1798    let prop_bytes = unsafe {
1799        std::slice::from_raw_parts(
1800            (&prop as *const LV2AtomPropertyBody).cast::<u8>(),
1801            prop_size,
1802        )
1803    };
1804    buffer.extend_from_slice(prop_bytes);
1805    let atom = LV2AtomLong {
1806        atom: LV2Atom {
1807            size: std::mem::size_of::<i64>() as u32,
1808            mytype: atom_type,
1809        },
1810        body: value,
1811    };
1812    let value_bytes = unsafe {
1813        std::slice::from_raw_parts(
1814            (&atom.body as *const i64).cast::<u8>(),
1815            std::mem::size_of::<i64>(),
1816        )
1817    };
1818    buffer.extend_from_slice(value_bytes);
1819    let written = (prop_size + std::mem::size_of::<i64>()) as u32;
1820    let padded = lv2_atom_pad_size(written) as usize;
1821    if padded > (prop_size + std::mem::size_of::<i64>()) {
1822        buffer.resize(
1823            buffer.len() + (padded - prop_size - std::mem::size_of::<i64>()),
1824            0,
1825        );
1826    }
1827}
1828
1829fn append_object_double_property(
1830    buffer: &mut Vec<u8>,
1831    key: LV2Urid,
1832    atom_type: LV2Urid,
1833    value: f64,
1834) {
1835    let prop = LV2AtomPropertyBody {
1836        key,
1837        context: 0,
1838        value: LV2Atom {
1839            size: std::mem::size_of::<f64>() as u32,
1840            mytype: atom_type,
1841        },
1842    };
1843    let prop_size = std::mem::size_of::<LV2AtomPropertyBody>();
1844    let prop_bytes = unsafe {
1845        std::slice::from_raw_parts(
1846            (&prop as *const LV2AtomPropertyBody).cast::<u8>(),
1847            prop_size,
1848        )
1849    };
1850    buffer.extend_from_slice(prop_bytes);
1851    let atom = LV2AtomDouble {
1852        atom: LV2Atom {
1853            size: std::mem::size_of::<f64>() as u32,
1854            mytype: atom_type,
1855        },
1856        body: value,
1857    };
1858    let value_bytes = unsafe {
1859        std::slice::from_raw_parts(
1860            (&atom.body as *const f64).cast::<u8>(),
1861            std::mem::size_of::<f64>(),
1862        )
1863    };
1864    buffer.extend_from_slice(value_bytes);
1865    let written = (prop_size + std::mem::size_of::<f64>()) as u32;
1866    let padded = lv2_atom_pad_size(written) as usize;
1867    if padded > (prop_size + std::mem::size_of::<f64>()) {
1868        buffer.resize(
1869            buffer.len() + (padded - prop_size - std::mem::size_of::<f64>()),
1870            0,
1871        );
1872    }
1873}
1874
1875impl UridMapFeature {
1876    fn new() -> Result<Self, String> {
1877        let mut map = Box::new(LV2UridMap {
1878            handle: std::ptr::null_mut(),
1879            map: urid_map_callback,
1880        });
1881        let mut unmap = Box::new(LV2UridUnmap {
1882            handle: std::ptr::null_mut(),
1883            unmap: urid_unmap_callback,
1884        });
1885        let state = Box::new(Mutex::new(UridMapState {
1886            next_urid: 1,
1887            by_uri: HashMap::new(),
1888            by_urid: HashMap::new(),
1889        }));
1890        map.handle = (&*state as *const Mutex<UridMapState>) as *mut c_void;
1891        unmap.handle = (&*state as *const Mutex<UridMapState>) as *mut c_void;
1892
1893        let map_uri =
1894            CString::new(LV2_URID__MAP).map_err(|e| format!("Invalid URID feature URI: {e}"))?;
1895        let map_feature = LV2Feature {
1896            uri: map_uri.as_ptr(),
1897            data: (&mut *map as *mut LV2UridMap).cast::<c_void>(),
1898        };
1899        let unmap_uri =
1900            CString::new(LV2_URID__UNMAP).map_err(|e| format!("Invalid URID feature URI: {e}"))?;
1901        let unmap_feature = LV2Feature {
1902            uri: unmap_uri.as_ptr(),
1903            data: (&mut *unmap as *mut LV2UridUnmap).cast::<c_void>(),
1904        };
1905
1906        Ok(Self {
1907            _map_uri: map_uri,
1908            _unmap_uri: unmap_uri,
1909            map_feature,
1910            unmap_feature,
1911            _map: map,
1912            _unmap: unmap,
1913            _state: state,
1914        })
1915    }
1916
1917    fn map_feature(&self) -> &LV2Feature {
1918        &self.map_feature
1919    }
1920
1921    fn unmap_feature(&self) -> &LV2Feature {
1922        &self.unmap_feature
1923    }
1924
1925    fn map_uri(&self, uri: &[u8]) -> LV2Urid {
1926        let Ok(uri_str) = std::str::from_utf8(uri) else {
1927            return 0;
1928        };
1929        let uri_str = uri_str.trim_end_matches('\0');
1930        let Ok(mut state) = self._state.lock() else {
1931            return 0;
1932        };
1933        if let Some(existing) = state.by_uri.get(uri_str).copied() {
1934            return existing;
1935        }
1936        let mapped = state.next_urid;
1937        state.next_urid = state.next_urid.saturating_add(1);
1938        state.by_uri.insert(uri_str.to_string(), mapped);
1939        if let Ok(uri_c) = CString::new(uri_str) {
1940            state.by_urid.insert(mapped, uri_c);
1941        }
1942        mapped
1943    }
1944
1945    fn unmap_urid(&self, urid: LV2Urid) -> Option<String> {
1946        let Ok(state) = self._state.lock() else {
1947            return None;
1948        };
1949        state
1950            .by_urid
1951            .get(&urid)
1952            .and_then(|uri| uri.to_str().ok().map(str::to_string))
1953    }
1954}
1955
1956fn default_state_base_dir() -> PathBuf {
1957    std::env::temp_dir().join("maolan-lv2-state")
1958}
1959
1960impl StatePathFeature {
1961    fn new(base_dir: PathBuf) -> Self {
1962        let context = Box::new(Mutex::new(StatePathContext {
1963            base_dir,
1964            copy_counter: 0,
1965        }));
1966        let handle = (&*context as *const Mutex<StatePathContext>) as *mut c_void;
1967
1968        let map = Box::new(Lv2StateMapPath {
1969            handle,
1970            abstract_path: Some(lv2_state_abstract_path_callback),
1971            absolute_path: Some(lv2_state_absolute_path_callback),
1972        });
1973        let make = Box::new(Lv2StateMakePath {
1974            handle,
1975            path: Some(lv2_state_make_path_callback),
1976        });
1977        let free = Box::new(Lv2StateFreePath {
1978            handle,
1979            free_path: Some(lv2_state_free_path_callback),
1980        });
1981
1982        let map_uri = CString::new(LV2_STATE_MAP_PATH_URI).expect("valid LV2 state mapPath URI");
1983        let make_uri = CString::new(LV2_STATE_MAKE_PATH_URI).expect("valid LV2 state makePath URI");
1984        let free_uri = CString::new(LV2_STATE_FREE_PATH_URI).expect("valid LV2 state freePath URI");
1985
1986        let map_feature = LV2Feature {
1987            uri: map_uri.as_ptr(),
1988            data: (&*map as *const Lv2StateMapPath)
1989                .cast_mut()
1990                .cast::<c_void>(),
1991        };
1992        let make_feature = LV2Feature {
1993            uri: make_uri.as_ptr(),
1994            data: (&*make as *const Lv2StateMakePath)
1995                .cast_mut()
1996                .cast::<c_void>(),
1997        };
1998        let free_feature = LV2Feature {
1999            uri: free_uri.as_ptr(),
2000            data: (&*free as *const Lv2StateFreePath)
2001                .cast_mut()
2002                .cast::<c_void>(),
2003        };
2004
2005        let instance = Self {
2006            _map_uri: map_uri,
2007            _make_uri: make_uri,
2008            _free_uri: free_uri,
2009            _map: map,
2010            _make: make,
2011            _free: free,
2012            map_feature,
2013            make_feature,
2014            free_feature,
2015            _context: context,
2016        };
2017        instance.ensure_base_dir();
2018        instance
2019    }
2020
2021    fn ensure_base_dir(&self) {
2022        if let Ok(ctx) = self._context.lock() {
2023            let _ = std::fs::create_dir_all(&ctx.base_dir);
2024        }
2025    }
2026
2027    fn set_base_dir(&self, base_dir: PathBuf) {
2028        if let Ok(mut ctx) = self._context.lock() {
2029            ctx.base_dir = base_dir;
2030            let _ = std::fs::create_dir_all(&ctx.base_dir);
2031        }
2032    }
2033
2034    fn feature_ptrs(&self) -> [*const LV2Feature; 3] {
2035        [
2036            &self.map_feature as *const LV2Feature,
2037            &self.make_feature as *const LV2Feature,
2038            &self.free_feature as *const LV2Feature,
2039        ]
2040    }
2041}
2042
2043extern "C" fn lv2_state_free_path_callback(_handle: *mut c_void, path: *mut c_char) {
2044    if path.is_null() {
2045        return;
2046    }
2047    unsafe {
2048        let _ = CString::from_raw(path);
2049    }
2050}
2051
2052fn state_ctx_from_handle(handle: *mut c_void) -> Option<&'static Mutex<StatePathContext>> {
2053    if handle.is_null() {
2054        return None;
2055    }
2056    Some(unsafe { &*(handle as *const Mutex<StatePathContext>) })
2057}
2058
2059fn copy_into_state_assets(ctx: &mut StatePathContext, src: &Path) -> Option<String> {
2060    let file_name = src.file_name()?.to_str()?.to_string();
2061    let assets_dir = ctx.base_dir.join("assets");
2062    let _ = std::fs::create_dir_all(&assets_dir);
2063    ctx.copy_counter = ctx.copy_counter.saturating_add(1);
2064    let dst_name = format!("{}-{}", ctx.copy_counter, file_name);
2065    let dst = assets_dir.join(&dst_name);
2066    std::fs::copy(src, &dst).ok()?;
2067    Some(format!("assets/{dst_name}"))
2068}
2069
2070extern "C" fn lv2_state_abstract_path_callback(
2071    handle: *mut c_void,
2072    absolute_path: *const c_char,
2073) -> *mut c_char {
2074    let Some(ctx_lock) = state_ctx_from_handle(handle) else {
2075        return std::ptr::null_mut();
2076    };
2077    if absolute_path.is_null() {
2078        return std::ptr::null_mut();
2079    }
2080    let Some(path_str) = (unsafe { CStr::from_ptr(absolute_path) }).to_str().ok() else {
2081        return std::ptr::null_mut();
2082    };
2083    let path = PathBuf::from(path_str);
2084    let mut mapped = None;
2085    if let Ok(mut ctx) = ctx_lock.lock() {
2086        if let Ok(rel) = path.strip_prefix(&ctx.base_dir) {
2087            mapped = Some(rel.to_string_lossy().to_string());
2088        } else if path.exists() {
2089            mapped = copy_into_state_assets(&mut ctx, &path);
2090        }
2091    }
2092    let out = mapped.unwrap_or_else(|| path_str.to_string());
2093    CString::new(out)
2094        .ok()
2095        .map(CString::into_raw)
2096        .unwrap_or(std::ptr::null_mut())
2097}
2098
2099extern "C" fn lv2_state_absolute_path_callback(
2100    handle: *mut c_void,
2101    abstract_path: *const c_char,
2102) -> *mut c_char {
2103    let Some(ctx_lock) = state_ctx_from_handle(handle) else {
2104        return std::ptr::null_mut();
2105    };
2106    if abstract_path.is_null() {
2107        return std::ptr::null_mut();
2108    }
2109    let Some(path_str) = (unsafe { CStr::from_ptr(abstract_path) }).to_str().ok() else {
2110        return std::ptr::null_mut();
2111    };
2112    let output = if Path::new(path_str).is_absolute() {
2113        path_str.to_string()
2114    } else if let Ok(ctx) = ctx_lock.lock() {
2115        ctx.base_dir.join(path_str).to_string_lossy().to_string()
2116    } else {
2117        path_str.to_string()
2118    };
2119    CString::new(output)
2120        .ok()
2121        .map(CString::into_raw)
2122        .unwrap_or(std::ptr::null_mut())
2123}
2124
2125extern "C" fn lv2_state_make_path_callback(
2126    handle: *mut c_void,
2127    requested: *const c_char,
2128) -> *mut c_char {
2129    let Some(ctx_lock) = state_ctx_from_handle(handle) else {
2130        return std::ptr::null_mut();
2131    };
2132
2133    let requested_name = if requested.is_null() {
2134        "state.bin".to_string()
2135    } else {
2136        (unsafe { CStr::from_ptr(requested) })
2137            .to_str()
2138            .ok()
2139            .filter(|s| !s.is_empty())
2140            .map(|s| s.replace("..", "_"))
2141            .unwrap_or_else(|| "state.bin".to_string())
2142    };
2143
2144    let output = if let Ok(mut ctx) = ctx_lock.lock() {
2145        ctx.copy_counter = ctx.copy_counter.saturating_add(1);
2146        let file_name = format!("generated-{}-{}", ctx.copy_counter, requested_name);
2147        let path = ctx.base_dir.join("generated").join(file_name);
2148        if let Some(parent) = path.parent() {
2149            let _ = std::fs::create_dir_all(parent);
2150        }
2151        path.to_string_lossy().to_string()
2152    } else {
2153        requested_name
2154    };
2155
2156    CString::new(output)
2157        .ok()
2158        .map(CString::into_raw)
2159        .unwrap_or(std::ptr::null_mut())
2160}
2161
2162extern "C" fn lv2_state_store_callback(
2163    handle: Lv2StateHandle,
2164    key: u32,
2165    value: *const c_void,
2166    size: usize,
2167    type_: u32,
2168    flags: u32,
2169) -> Lv2StateStatus {
2170    if handle.is_null() || value.is_null() || size == 0 {
2171        return LV2_STATE_STATUS_ERR_NO_PROPERTY;
2172    }
2173    let ctx = unsafe { &mut *(handle as *mut StateSaveContext) };
2174    let bytes = unsafe { std::slice::from_raw_parts(value.cast::<u8>(), size) };
2175    ctx.properties.push(RawStateProperty {
2176        key,
2177        type_,
2178        flags,
2179        value: bytes.to_vec(),
2180    });
2181    LV2_STATE_STATUS_SUCCESS
2182}
2183
2184extern "C" fn lv2_state_retrieve_callback(
2185    handle: Lv2StateHandle,
2186    key: u32,
2187    size: *mut usize,
2188    type_: *mut u32,
2189    flags: *mut u32,
2190) -> *const c_void {
2191    if handle.is_null() {
2192        return std::ptr::null();
2193    }
2194    let ctx = unsafe { &mut *(handle as *mut StateRestoreContext) };
2195    let Some(idx) = ctx.by_key.get(&key).copied() else {
2196        return std::ptr::null();
2197    };
2198    let Some(prop) = ctx.properties.get(idx) else {
2199        return std::ptr::null();
2200    };
2201    if !size.is_null() {
2202        unsafe {
2203            *size = prop.value.len();
2204        }
2205    }
2206    if !type_.is_null() {
2207        unsafe {
2208            *type_ = prop.type_;
2209        }
2210    }
2211    if !flags.is_null() {
2212        unsafe {
2213            *flags = prop.flags;
2214        }
2215    }
2216    prop.value.as_ptr().cast::<c_void>()
2217}
2218
2219fn prepare_empty_atom_sequence(
2220    buffer: &mut [u8],
2221    sequence_urid: LV2Urid,
2222    frame_time_urid: LV2Urid,
2223) {
2224    buffer.fill(0);
2225    if buffer.len() < std::mem::size_of::<LV2AtomSequence>() {
2226        return;
2227    }
2228    let seq = buffer.as_mut_ptr() as *mut LV2AtomSequence;
2229    unsafe {
2230        (*seq).atom.mytype = sequence_urid;
2231        (*seq).atom.size = std::mem::size_of::<LV2AtomSequenceBody>() as u32;
2232        (*seq).body.unit = frame_time_urid;
2233        (*seq).body.pad = 0;
2234    }
2235}
2236
2237fn prepare_output_atom_sequence(
2238    buffer: &mut [u8],
2239    sequence_urid: LV2Urid,
2240    frame_time_urid: LV2Urid,
2241) {
2242    buffer.fill(0);
2243    if buffer.len() < std::mem::size_of::<LV2AtomSequence>() {
2244        return;
2245    }
2246    let seq = buffer.as_mut_ptr() as *mut LV2AtomSequence;
2247    let body_capacity = buffer
2248        .len()
2249        .saturating_sub(std::mem::size_of::<lv2_raw::LV2Atom>()) as u32;
2250    unsafe {
2251        (*seq).atom.mytype = sequence_urid;
2252        (*seq).atom.size = body_capacity;
2253        (*seq).body.unit = frame_time_urid;
2254        (*seq).body.pad = 0;
2255    }
2256}
2257
2258extern "C" fn urid_map_callback(handle: LV2UridMapHandle, uri: *const c_char) -> LV2Urid {
2259    if handle.is_null() || uri.is_null() {
2260        return 0;
2261    }
2262    let Some(uri_str) = unsafe { CStr::from_ptr(uri) }.to_str().ok() else {
2263        return 0;
2264    };
2265
2266    let state_mutex = unsafe { &*(handle as *const Mutex<UridMapState>) };
2267    let Ok(mut state) = state_mutex.lock() else {
2268        return 0;
2269    };
2270
2271    if let Some(existing) = state.by_uri.get(uri_str).copied() {
2272        return existing;
2273    }
2274
2275    let mapped = state.next_urid;
2276    state.next_urid = state.next_urid.saturating_add(1);
2277    state.by_uri.insert(uri_str.to_string(), mapped);
2278    if let Ok(uri_c) = CString::new(uri_str) {
2279        state.by_urid.insert(mapped, uri_c);
2280    }
2281    mapped
2282}
2283
2284extern "C" fn urid_unmap_callback(handle: LV2UridMapHandle, urid: LV2Urid) -> *const c_char {
2285    if handle.is_null() || urid == 0 {
2286        return std::ptr::null();
2287    }
2288    let state_mutex = unsafe { &*(handle as *const Mutex<UridMapState>) };
2289    let Ok(state) = state_mutex.lock() else {
2290        return std::ptr::null();
2291    };
2292    state
2293        .by_urid
2294        .get(&urid)
2295        .map(|uri| uri.as_ptr())
2296        .unwrap_or(std::ptr::null())
2297}
2298
2299fn plugin_port_counts(
2300    plugin: &Plugin,
2301    input_port: &Node,
2302    output_port: &Node,
2303    audio_port: &Node,
2304    atom_port: &Node,
2305    event_port: &Node,
2306    midi_event: &Node,
2307) -> (usize, usize, usize, usize) {
2308    let mut audio_inputs = 0;
2309    let mut audio_outputs = 0;
2310    let mut midi_inputs = 0;
2311    let mut midi_outputs = 0;
2312
2313    for port in plugin.iter_ports() {
2314        let is_input = port.is_a(input_port);
2315        let is_output = port.is_a(output_port);
2316
2317        if port.is_a(audio_port) {
2318            if is_input {
2319                audio_inputs += 1;
2320            }
2321            if is_output {
2322                audio_outputs += 1;
2323            }
2324        }
2325
2326        let is_event_or_atom = port.is_a(atom_port) || port.is_a(event_port);
2327        let is_midi = is_event_or_atom && port.supports_event(midi_event);
2328        if is_midi {
2329            if is_input {
2330                midi_inputs += 1;
2331            }
2332            if is_output {
2333                midi_outputs += 1;
2334            }
2335        }
2336    }
2337
2338    (audio_inputs, audio_outputs, midi_inputs, midi_outputs)
2339}
2340
2341fn count_main_audio_ports(
2342    plugin: &Plugin,
2343    main_group_predicate: &Node,
2344    port_group_predicate: &Node,
2345    audio_port: &Node,
2346    direction_port: &Node,
2347) -> Option<usize> {
2348    let main_groups: Vec<Node> = plugin.value(main_group_predicate).iter().collect();
2349    if main_groups.is_empty() {
2350        return None;
2351    }
2352
2353    let count = plugin
2354        .iter_ports()
2355        .filter(|port| port.is_a(audio_port) && port.is_a(direction_port))
2356        .filter(|port| {
2357            port.get(port_group_predicate)
2358                .is_some_and(|group| main_groups.contains(&group))
2359        })
2360        .count();
2361
2362    Some(count)
2363}
2364
2365fn infer_missing_control_default(
2366    min: f32,
2367    max: f32,
2368    is_toggled: bool,
2369    is_discrete: bool,
2370) -> (f32, &'static str) {
2371    if !min.is_finite() || !max.is_finite() {
2372        return (0.0, "non-finite-range->0");
2373    }
2374    if is_toggled {
2375        let off = if min <= 0.0 && max >= 0.0 { 0.0 } else { min };
2376        return (off.clamp(min, max), "toggled->off");
2377    }
2378    if is_discrete {
2379        if min < 0.0 && max > 0.0 {
2380            return (0.0, "discrete-bipolar->0");
2381        }
2382        return (min, "discrete->min");
2383    }
2384    if min < 0.0 && max > 0.0 {
2385        return (0.0, "bipolar->0");
2386    }
2387    if min <= 1.0 && max >= 1.0 {
2388        return (1.0, "range-has-unity->1");
2389    }
2390    (min + (max - min) * 0.5, "midpoint")
2391}
2392
2393fn lv2_node_to_f32(node: &Node) -> Option<f32> {
2394    if let Some(v) = node.as_float() {
2395        return Some(v);
2396    }
2397    if let Some(v) = node.as_int() {
2398        return Some(v as f32);
2399    }
2400    node.as_bool().map(|v| if v { 1.0 } else { 0.0 })
2401}