Skip to main content

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