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