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.connect_ports();
604        processor.query_midnam();
605        Ok(processor)
606    }
607
608    pub fn uri(&self) -> &str {
609        &self.uri
610    }
611
612    pub fn name(&self) -> &str {
613        &self.plugin_name
614    }
615
616    pub fn audio_inputs(&self) -> &[Arc<AudioIO>] {
617        &self.audio_inputs
618    }
619
620    pub fn audio_outputs(&self) -> &[Arc<AudioIO>] {
621        &self.audio_outputs
622    }
623
624    pub fn audio_input_count(&self) -> usize {
625        self.audio_inputs.len()
626    }
627
628    pub fn audio_output_count(&self) -> usize {
629        self.audio_outputs.len()
630    }
631
632    pub fn main_audio_input_count(&self) -> usize {
633        self.main_audio_inputs
634    }
635
636    pub fn main_audio_output_count(&self) -> usize {
637        self.main_audio_outputs
638    }
639
640    pub fn midi_input_count(&self) -> usize {
641        self.midi_inputs
642    }
643
644    pub fn midi_output_count(&self) -> usize {
645        self.midi_outputs
646    }
647
648    pub fn setup_audio_ports(&self) {
649        for io in &self.audio_inputs {
650            io.setup();
651        }
652        for io in &self.audio_outputs {
653            io.setup();
654        }
655    }
656
657    pub fn process(&mut self, input_channels: &[Vec<f32>], frames: usize) -> Vec<Vec<f32>> {
658        if let Ok(mut values) = self.scalar_values.lock()
659            && values.is_empty()
660        {
661            values.push(0.0);
662        }
663
664        for (channel, io) in self.audio_inputs.iter_mut().enumerate() {
665            let buffer = io.buffer.lock();
666            buffer.fill(0.0);
667            if let Some(input) = input_channels.get(channel) {
668                let copy_len = input.len().min(frames);
669                buffer[..copy_len].copy_from_slice(&input[..copy_len]);
670            }
671        }
672        for io in &self.audio_outputs {
673            let buffer = io.buffer.lock();
674            buffer.fill(0.0);
675        }
676        for buffer in &mut self.atom_inputs {
677            prepare_empty_atom_sequence(
678                buffer.bytes_mut(),
679                self.atom_sequence_urid,
680                self.atom_frame_time_urid,
681            );
682        }
683        for buffer in &mut self.atom_outputs {
684            prepare_output_atom_sequence(
685                buffer.bytes_mut(),
686                self.atom_sequence_urid,
687                self.atom_frame_time_urid,
688            );
689        }
690
691        self.connect_ports();
692        if let Some(instance) = self.instance.as_mut() {
693            unsafe {
694                instance.run(frames);
695            }
696        }
697        self.audio_outputs
698            .iter()
699            .map(|io| io.buffer.lock().as_ref().to_vec())
700            .collect()
701    }
702
703    pub fn process_with_audio_io(
704        &mut self,
705        frames: usize,
706        midi_inputs: &[Vec<MidiEvent>],
707        transport: Lv2TransportInfo,
708    ) -> Vec<Vec<MidiEvent>> {
709        if let Ok(mut values) = self.scalar_values.lock()
710            && values.is_empty()
711        {
712            values.push(0.0);
713        }
714
715        for io in &self.audio_outputs {
716            let buffer = io.buffer.lock();
717            buffer.fill(0.0);
718            *io.finished.lock() = false;
719        }
720        for buffer in &mut self.atom_inputs {
721            prepare_empty_atom_sequence(
722                buffer.bytes_mut(),
723                self.atom_sequence_urid,
724                self.atom_frame_time_urid,
725            );
726        }
727        for buffer in &mut self.atom_outputs {
728            prepare_output_atom_sequence(
729                buffer.bytes_mut(),
730                self.atom_sequence_urid,
731                self.atom_frame_time_urid,
732            );
733        }
734        for port in 0..self.atom_inputs.len() {
735            self.write_transport_event(port, transport);
736        }
737        for (port, events) in midi_inputs.iter().enumerate() {
738            self.write_midi_input_events(port, events);
739        }
740
741        self.connect_ports();
742        if let Some(instance) = self.instance.as_mut() {
743            unsafe {
744                instance.run(frames);
745            }
746        }
747        self.run_worker_cycle();
748
749        for io in &self.audio_outputs {
750            *io.finished.lock() = true;
751        }
752        let mut midi_outputs = vec![];
753        for port in 0..self.midi_outputs {
754            midi_outputs.push(self.read_midi_output_events(port));
755        }
756        midi_outputs
757    }
758
759    fn run_worker_cycle(&mut self) {
760        if !self.has_worker_interface {
761            return;
762        }
763        let Some(worker_iface) = self.worker_interface() else {
764            return;
765        };
766        let (work_fn, work_response_fn, end_run_fn) = (
767            worker_iface.work,
768            worker_iface.work_response,
769            worker_iface.end_run,
770        );
771        let Some(work_fn) = work_fn else {
772            return;
773        };
774        let instance_handle = self.instance_handle();
775        if instance_handle.is_null() {
776            return;
777        }
778
779        let worker_state = &self._instantiate_features._worker_feature.state;
780        let mut jobs = std::mem::take(worker_state.jobs.lock());
781        for job in jobs.drain(..) {
782            if job.len() > (u32::MAX as usize) {
783                continue;
784            }
785            unsafe {
786                work_fn(
787                    instance_handle,
788                    Some(lv2_worker_respond_callback),
789                    &**worker_state as *const WorkerScheduleState as *mut c_void,
790                    job.len() as u32,
791                    job.as_ptr().cast::<c_void>(),
792                );
793            }
794        }
795        *worker_state.jobs.lock() = jobs;
796
797        if let Some(work_response_fn) = work_response_fn {
798            let mut responses = std::mem::take(worker_state.responses.lock());
799            for response in responses.drain(..) {
800                if response.len() > (u32::MAX as usize) {
801                    continue;
802                }
803                unsafe {
804                    work_response_fn(
805                        instance_handle,
806                        response.len() as u32,
807                        response.as_ptr().cast::<c_void>(),
808                    );
809                }
810            }
811            *worker_state.responses.lock() = responses;
812        }
813
814        if let Some(end_run_fn) = end_run_fn {
815            unsafe {
816                end_run_fn(instance_handle);
817            }
818        }
819    }
820
821    fn connect_ports(&mut self) {
822        for (port_index, binding) in self.port_bindings.iter().enumerate() {
823            match binding {
824                PortBinding::AudioInput(channel) => {
825                    let ptr = self.audio_inputs[*channel].buffer.lock().as_mut_ptr();
826                    if let Some(instance) = self.instance.as_mut() {
827                        unsafe {
828                            instance.instance_mut().connect_port_mut(port_index, ptr);
829                        }
830                    }
831                }
832                PortBinding::AudioOutput(channel) => {
833                    let ptr = self.audio_outputs[*channel].buffer.lock().as_mut_ptr();
834                    if let Some(instance) = self.instance.as_mut() {
835                        unsafe {
836                            instance.instance_mut().connect_port_mut(port_index, ptr);
837                        }
838                    }
839                }
840                PortBinding::AtomInput(atom_index) => {
841                    let ptr = self.atom_inputs[*atom_index].ptr_mut();
842                    if let Some(instance) = self.instance.as_mut() {
843                        unsafe {
844                            instance.instance_mut().connect_port_mut(port_index, ptr);
845                        }
846                    }
847                }
848                PortBinding::AtomOutput(atom_index) => {
849                    let ptr = self.atom_outputs[*atom_index].ptr_mut();
850                    if let Some(instance) = self.instance.as_mut() {
851                        unsafe {
852                            instance.instance_mut().connect_port_mut(port_index, ptr);
853                        }
854                    }
855                }
856                PortBinding::Scalar(index) => {
857                    if let Ok(mut values) = self.scalar_values.lock()
858                        && *index < values.len()
859                    {
860                        let ptr = (&mut values[*index]) as *mut f32;
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                }
868            }
869        }
870    }
871
872    pub fn snapshot_state(&self) -> Lv2PluginState {
873        let mut state = Lv2PluginState {
874            port_values: self.control_port_values(),
875            properties: vec![],
876        };
877        let Some(interface) = self.state_interface() else {
878            return state;
879        };
880        let Some(save_fn) = interface.save else {
881            return state;
882        };
883
884        let mut ctx = StateSaveContext { properties: vec![] };
885        let features = self.state_feature_ptrs();
886        let status = unsafe {
887            save_fn(
888                self.instance_handle(),
889                Some(lv2_state_store_callback),
890                (&mut ctx as *mut StateSaveContext).cast::<c_void>(),
891                0,
892                features.as_ptr(),
893            )
894        };
895        if status != LV2_STATE_STATUS_SUCCESS {
896            return state;
897        }
898
899        state.properties = ctx
900            .properties
901            .into_iter()
902            .filter_map(|p| {
903                let key_uri = self._urid_feature.unmap_urid(p.key)?;
904                let type_uri = self._urid_feature.unmap_urid(p.type_)?;
905                Some(Lv2StateProperty {
906                    key_uri,
907                    type_uri,
908                    flags: p.flags,
909                    value: p.value,
910                })
911            })
912            .collect();
913        state
914    }
915
916    pub fn snapshot_port_state(&self) -> Lv2PluginState {
917        Lv2PluginState {
918            port_values: self.control_port_values(),
919            properties: vec![],
920        }
921    }
922
923    pub fn restore_state(&mut self, state: &Lv2PluginState) -> Result<(), String> {
924        self.set_control_port_values(&state.port_values);
925        if state.properties.is_empty() {
926            return Ok(());
927        }
928        let Some(interface) = self.state_interface() else {
929            return Ok(());
930        };
931        let Some(restore_fn) = interface.restore else {
932            return Ok(());
933        };
934
935        let mut properties: Vec<RawStateProperty> = vec![];
936        let mut by_key: HashMap<u32, usize> = HashMap::new();
937        for prop in &state.properties {
938            let key = self._urid_feature.map_uri(prop.key_uri.as_bytes());
939            let type_ = self._urid_feature.map_uri(prop.type_uri.as_bytes());
940            if key == 0 || type_ == 0 {
941                continue;
942            }
943            let idx = properties.len();
944            properties.push(RawStateProperty {
945                key,
946                type_,
947                flags: prop.flags,
948                value: prop.value.clone(),
949            });
950            by_key.insert(key, idx);
951        }
952        let mut ctx = StateRestoreContext { properties, by_key };
953        let features = self.state_feature_ptrs();
954
955        let status = unsafe {
956            restore_fn(
957                self.instance_handle(),
958                Some(lv2_state_retrieve_callback),
959                (&mut ctx as *mut StateRestoreContext).cast::<c_void>(),
960                0,
961                features.as_ptr(),
962            )
963        };
964        if status == LV2_STATE_STATUS_SUCCESS {
965            Ok(())
966        } else {
967            Err(format!(
968                "LV2 state restore failed for '{}': status {}",
969                self.uri, status
970            ))
971        }
972    }
973
974    fn state_interface(&self) -> Option<&Lv2StateInterface> {
975        let instance = self.instance.as_ref()?;
976        if !self.has_extension_data_callback(instance.instance()) {
977            return None;
978        }
979        let ptr = unsafe {
980            instance
981                .instance()
982                .extension_data::<Lv2StateInterface>(LV2_STATE_INTERFACE_URI)?
983        };
984        Some(unsafe { ptr.as_ref() })
985    }
986
987    fn worker_interface(&self) -> Option<&Lv2WorkerInterface> {
988        let instance = self.instance.as_ref()?;
989        if !self.has_extension_data_callback(instance.instance()) {
990            return None;
991        }
992        let ptr = unsafe {
993            instance
994                .instance()
995                .extension_data::<Lv2WorkerInterface>(LV2_WORKER__INTERFACE)?
996        };
997        Some(unsafe { ptr.as_ref() })
998    }
999
1000    fn has_extension_data_callback(&self, instance: &lilv::instance::Instance) -> bool {
1001        let Some(descriptor) = instance.descriptor() else {
1002            return false;
1003        };
1004        (descriptor.extension_data as *const ()) as usize != 0
1005    }
1006
1007    fn instance_handle(&self) -> Lv2Handle {
1008        self.instance
1009            .as_ref()
1010            .map(|i| i.instance().handle() as Lv2Handle)
1011            .unwrap_or(std::ptr::null_mut())
1012    }
1013
1014    pub fn instance_access_handle(&self) -> Option<usize> {
1015        let handle = self.instance_handle();
1016        if handle.is_null() {
1017            None
1018        } else {
1019            Some(handle as usize)
1020        }
1021    }
1022
1023    fn state_feature_ptrs(&self) -> [*const LV2Feature; 6] {
1024        let sp = self._state_path_feature.feature_ptrs();
1025        [
1026            self._urid_feature.map_feature() as *const LV2Feature,
1027            self._urid_feature.unmap_feature() as *const LV2Feature,
1028            sp[0],
1029            sp[1],
1030            sp[2],
1031            std::ptr::null(),
1032        ]
1033    }
1034
1035    fn control_port_values(&self) -> Vec<Lv2StatePortValue> {
1036        let Ok(values) = self.scalar_values.lock() else {
1037            return vec![];
1038        };
1039        self.control_ports
1040            .iter()
1041            .filter_map(|port| {
1042                values.get(port.index as usize).map(|v| Lv2StatePortValue {
1043                    index: port.index,
1044                    value: *v,
1045                })
1046            })
1047            .collect()
1048    }
1049
1050    fn set_control_port_values(&mut self, port_values: &[Lv2StatePortValue]) {
1051        let Ok(mut values) = self.scalar_values.lock() else {
1052            return;
1053        };
1054        for port in port_values {
1055            if let Some(slot) = values.get_mut(port.index as usize) {
1056                *slot = port.value;
1057            }
1058        }
1059    }
1060
1061    pub fn set_state_base_dir(&mut self, base_dir: PathBuf) {
1062        self._state_path_feature.set_base_dir(base_dir);
1063    }
1064
1065    pub fn control_ports_with_values(&self) -> Vec<Lv2ControlPortInfo> {
1066        let Ok(values) = self.scalar_values.lock() else {
1067            return Vec::new();
1068        };
1069        self.control_ports
1070            .iter()
1071            .map(|port| Lv2ControlPortInfo {
1072                index: port.index,
1073                name: port.name.clone(),
1074                min: port.min,
1075                max: port.max,
1076                value: values.get(port.index as usize).copied().unwrap_or(0.0),
1077            })
1078            .collect()
1079    }
1080
1081    pub fn set_control_value(&mut self, index: u32, value: f32) -> Result<(), String> {
1082        let Some(port) = self.control_ports.iter().find(|port| port.index == index) else {
1083            return Err(format!("Unknown LV2 control port index: {index}"));
1084        };
1085        let clamped = value.clamp(port.min, port.max);
1086        let Ok(mut values) = self.scalar_values.lock() else {
1087            return Err("Failed to lock LV2 control values".to_string());
1088        };
1089        let Some(slot) = values.get_mut(index as usize) else {
1090            return Err(format!("LV2 control port index out of range: {index}"));
1091        };
1092        *slot = clamped;
1093        Ok(())
1094    }
1095
1096    pub fn midnam_note_names(&self) -> HashMap<u8, String> {
1097        self.midnam_note_names.lock().clone()
1098    }
1099
1100    fn query_midnam(&mut self) {
1101        let Some(instance) = &self.instance else {
1102            return;
1103        };
1104
1105        if !self.has_extension_data_callback(instance.instance()) {
1106            return;
1107        }
1108
1109        let interface_ptr = unsafe {
1110            instance
1111                .instance()
1112                .extension_data::<Lv2MidnamInterface>(LV2_MIDNAM__INTERFACE)
1113        };
1114        let Some(interface_ptr) = interface_ptr else {
1115            return;
1116        };
1117        let interface = unsafe { interface_ptr.as_ref() };
1118
1119        // Query the midnam XML
1120        let Some(midnam_fn) = interface.midnam else {
1121            return;
1122        };
1123        let Some(free_fn) = interface.free else {
1124            return;
1125        };
1126
1127        let xml_ptr = unsafe { midnam_fn(instance.instance().handle()) };
1128        if xml_ptr.is_null() {
1129            return;
1130        }
1131
1132        let xml_cstr = unsafe { CStr::from_ptr(xml_ptr) };
1133        let Ok(xml_str) = xml_cstr.to_str() else {
1134            unsafe { free_fn(xml_ptr) };
1135            return;
1136        };
1137
1138        // Parse the midnam XML to extract note names
1139        let note_names = self.parse_midnam_xml(xml_str);
1140        *self.midnam_note_names.lock() = note_names;
1141
1142        unsafe { free_fn(xml_ptr) };
1143    }
1144
1145    fn parse_midnam_xml(&self, xml: &str) -> HashMap<u8, String> {
1146        let mut note_names = HashMap::new();
1147
1148        // Simple XML parsing for <Note Number="X" Name="Y"/> elements
1149        for line in xml.lines() {
1150            let line = line.trim();
1151            if !line.starts_with("<Note ") {
1152                continue;
1153            }
1154
1155            // Extract Number attribute
1156            let number = if let Some(start) = line.find("Number=\"") {
1157                let start = start + 8; // len("Number=\"")
1158                if let Some(end) = line[start..].find('"') {
1159                    line[start..start + end].parse::<u8>().ok()
1160                } else {
1161                    None
1162                }
1163            } else {
1164                None
1165            };
1166
1167            // Extract Name attribute
1168            let name = if let Some(start) = line.find("Name=\"") {
1169                let start = start + 6; // len("Name=\"")
1170                line[start..]
1171                    .find('"')
1172                    .map(|end| line[start..start + end].to_string())
1173            } else {
1174                None
1175            };
1176
1177            if let (Some(num), Some(name_str)) = (number, name) {
1178                note_names.insert(num, name_str);
1179            }
1180        }
1181
1182        note_names
1183    }
1184
1185    fn write_midi_input_events(&mut self, port: usize, events: &[MidiEvent]) {
1186        let Some(buffer) = self.atom_inputs.get_mut(port) else {
1187            return;
1188        };
1189        let bytes = buffer.bytes_mut();
1190        if bytes.len() < std::mem::size_of::<LV2AtomSequence>() {
1191            return;
1192        }
1193        let seq = bytes.as_mut_ptr() as *mut LV2AtomSequence;
1194        let capacity = bytes
1195            .len()
1196            .saturating_sub(std::mem::size_of::<lv2_raw::LV2Atom>()) as u32;
1197        for event in events {
1198            if event.data.is_empty() {
1199                continue;
1200            }
1201            let mut raw =
1202                vec![0_u8; std::mem::size_of::<lv2_raw::LV2AtomEvent>() + event.data.len()];
1203            let raw_event = raw.as_mut_ptr() as *mut lv2_raw::LV2AtomEvent;
1204            unsafe {
1205                (*raw_event).time_in_frames = event.frame as i64;
1206                (*raw_event).body.mytype = self.midi_event_urid;
1207                (*raw_event).body.size = event.data.len() as u32;
1208                let data_ptr =
1209                    (raw_event as *mut u8).add(std::mem::size_of::<lv2_raw::LV2AtomEvent>());
1210                std::ptr::copy_nonoverlapping(event.data.as_ptr(), data_ptr, event.data.len());
1211                if lv2_atom_sequence_append_event(seq, capacity, raw_event).is_null() {
1212                    break;
1213                }
1214            }
1215        }
1216    }
1217
1218    fn write_transport_event(&mut self, port: usize, transport: Lv2TransportInfo) {
1219        let Some(buffer) = self.atom_inputs.get_mut(port) else {
1220            return;
1221        };
1222        let bytes = buffer.bytes_mut();
1223        if bytes.len() < std::mem::size_of::<LV2AtomSequence>() {
1224            return;
1225        }
1226        let seq = bytes.as_mut_ptr() as *mut LV2AtomSequence;
1227        let capacity = bytes
1228            .len()
1229            .saturating_sub(std::mem::size_of::<lv2_raw::LV2Atom>()) as u32;
1230
1231        let beats_per_bar = if transport.tsig_num == 0 {
1232            4.0
1233        } else {
1234            transport.tsig_num as f64
1235        };
1236        let beat_unit = if transport.tsig_denom == 0 {
1237            4_i64
1238        } else {
1239            transport.tsig_denom as i64
1240        };
1241        let bpm = if transport.bpm > 0.0 {
1242            transport.bpm
1243        } else {
1244            120.0
1245        };
1246        let speed = if transport.playing { 1.0 } else { 0.0 };
1247        let sample = transport.transport_sample as i64;
1248        let seconds = (transport.transport_sample as f64) / self.sample_rate.max(1.0);
1249        let absolute_beats = seconds * bpm / 60.0;
1250        let bar = (absolute_beats / beats_per_bar).floor().max(0.0) as i64;
1251        let bar_beat = absolute_beats - (bar as f64 * beats_per_bar);
1252
1253        let mut payload =
1254            Vec::<u8>::with_capacity(std::mem::size_of::<LV2AtomObjectBody>() + (7 * 32));
1255        let object_body = LV2AtomObjectBody {
1256            id: 0,
1257            otype: self.time_position_urid,
1258        };
1259        let object_body_bytes = unsafe {
1260            std::slice::from_raw_parts(
1261                (&object_body as *const LV2AtomObjectBody).cast::<u8>(),
1262                std::mem::size_of::<LV2AtomObjectBody>(),
1263            )
1264        };
1265        payload.extend_from_slice(object_body_bytes);
1266
1267        append_object_long_property(
1268            &mut payload,
1269            self.time_frame_urid,
1270            self.atom_long_urid,
1271            sample,
1272        );
1273        append_object_double_property(
1274            &mut payload,
1275            self.time_speed_urid,
1276            self.atom_double_urid,
1277            speed,
1278        );
1279        append_object_double_property(&mut payload, self.time_bpm_urid, self.atom_double_urid, bpm);
1280        append_object_long_property(&mut payload, self.time_bar_urid, self.atom_long_urid, bar);
1281        append_object_double_property(
1282            &mut payload,
1283            self.time_bar_beat_urid,
1284            self.atom_double_urid,
1285            bar_beat,
1286        );
1287        append_object_double_property(
1288            &mut payload,
1289            self.time_beats_per_bar_urid,
1290            self.atom_double_urid,
1291            beats_per_bar,
1292        );
1293        append_object_long_property(
1294            &mut payload,
1295            self.time_beat_unit_urid,
1296            self.atom_long_urid,
1297            beat_unit,
1298        );
1299
1300        if payload.len() > (u32::MAX as usize) {
1301            return;
1302        }
1303
1304        let mut raw = vec![0_u8; std::mem::size_of::<LV2AtomEvent>() + payload.len()];
1305        let raw_event = raw.as_mut_ptr() as *mut LV2AtomEvent;
1306        unsafe {
1307            (*raw_event).time_in_frames = 0;
1308            (*raw_event).body.mytype = self.atom_object_urid;
1309            (*raw_event).body.size = payload.len() as u32;
1310            let data_ptr = (raw_event as *mut u8).add(std::mem::size_of::<LV2AtomEvent>());
1311            std::ptr::copy_nonoverlapping(payload.as_ptr(), data_ptr, payload.len());
1312            let _ = lv2_atom_sequence_append_event(seq, capacity, raw_event);
1313        }
1314    }
1315
1316    fn read_midi_output_events(&mut self, port: usize) -> Vec<MidiEvent> {
1317        let Some(buffer) = self.atom_outputs.get_mut(port) else {
1318            return vec![];
1319        };
1320        let bytes = buffer.bytes_mut();
1321        if bytes.len() < std::mem::size_of::<LV2AtomSequence>() {
1322            return vec![];
1323        }
1324
1325        let mut result = Vec::new();
1326        let seq = bytes.as_mut_ptr() as *mut LV2AtomSequence;
1327        unsafe {
1328            let body = &(*seq).body as *const LV2AtomSequenceBody;
1329            let size = (*seq).atom.size;
1330            let mut it = lv2_atom_sequence_begin(body);
1331            while !lv2_atom_sequence_is_end(body, size, it) {
1332                let event = &*it;
1333                if event.body.mytype == self.midi_event_urid && event.body.size > 0 {
1334                    let data_ptr =
1335                        (it as *const u8).add(std::mem::size_of::<lv2_raw::LV2AtomEvent>());
1336                    let data_len = event.body.size as usize;
1337                    let data = std::slice::from_raw_parts(data_ptr, data_len).to_vec();
1338                    result.push(MidiEvent::new(event.time_in_frames.max(0) as u32, data));
1339                }
1340                it = lv2_atom_sequence_next(it);
1341            }
1342        }
1343        result
1344    }
1345}
1346
1347impl Drop for Lv2Processor {
1348    fn drop(&mut self) {
1349        let Some(instance) = self.instance.take() else {
1350            return;
1351        };
1352        drop(instance);
1353    }
1354}
1355
1356impl Lv2Host {
1357    pub fn new(sample_rate: f64) -> Self {
1358        let world = World::new();
1359        world.load_all();
1360        Self {
1361            world,
1362            sample_rate,
1363            loaded_plugins: HashMap::new(),
1364        }
1365    }
1366
1367    pub fn list_plugins(&self) -> Vec<Lv2PluginInfo> {
1368        let input_port = self.world.new_uri("http://lv2plug.in/ns/lv2core#InputPort");
1369        let output_port = self
1370            .world
1371            .new_uri("http://lv2plug.in/ns/lv2core#OutputPort");
1372        let audio_port = self.world.new_uri("http://lv2plug.in/ns/lv2core#AudioPort");
1373        let atom_port = self.world.new_uri("http://lv2plug.in/ns/ext/atom#AtomPort");
1374        let event_port = self
1375            .world
1376            .new_uri("http://lv2plug.in/ns/ext/event#EventPort");
1377        let midi_event = self
1378            .world
1379            .new_uri("http://lv2plug.in/ns/ext/midi#MidiEvent");
1380
1381        let mut plugins = self
1382            .world
1383            .plugins()
1384            .iter()
1385            .filter(|plugin| plugin.verify())
1386            .filter_map(|plugin| {
1387                let uri = plugin.uri().as_uri()?.to_string();
1388                let name = plugin.name().as_str().unwrap_or(&uri).to_string();
1389                let class_label = plugin
1390                    .class()
1391                    .label()
1392                    .as_str()
1393                    .unwrap_or("Unknown")
1394                    .to_string();
1395                let bundle_uri = plugin.bundle_uri().as_uri().unwrap_or("").to_string();
1396                let required_features = plugin_feature_uris(&plugin);
1397                let (audio_inputs, audio_outputs, midi_inputs, midi_outputs) = plugin_port_counts(
1398                    &plugin,
1399                    &input_port,
1400                    &output_port,
1401                    &audio_port,
1402                    &atom_port,
1403                    &event_port,
1404                    &midi_event,
1405                );
1406
1407                Some(Lv2PluginInfo {
1408                    uri,
1409                    name,
1410                    class_label,
1411                    bundle_uri,
1412                    required_features,
1413                    audio_inputs,
1414                    audio_outputs,
1415                    midi_inputs,
1416                    midi_outputs,
1417                })
1418            })
1419            .collect::<Vec<_>>();
1420
1421        plugins.sort_by(|left, right| left.name.cmp(&right.name));
1422        plugins
1423    }
1424
1425    pub fn load_plugin(&mut self, uri: &str) -> Result<(), String> {
1426        if self.loaded_plugins.contains_key(uri) {
1427            return Err(format!("Plugin is already loaded: {uri}"));
1428        }
1429
1430        let plugin = self
1431            .plugin_by_uri(uri)
1432            .ok_or_else(|| format!("Plugin not found for URI: {uri}"))?;
1433
1434        let mut urid_feature = UridMapFeature::new()?;
1435        let mut state_path_feature = StatePathFeature::new(default_state_base_dir());
1436        let (instance, instantiate_features) = instantiate_plugin(
1437            &plugin,
1438            self.sample_rate,
1439            uri,
1440            &mut urid_feature,
1441            &mut state_path_feature,
1442        )?;
1443        let active_instance = unsafe { instance.activate() };
1444        self.loaded_plugins.insert(
1445            uri.to_string(),
1446            LoadedPlugin {
1447                instance: active_instance,
1448                _urid_feature: urid_feature,
1449                _state_path_feature: state_path_feature,
1450                _instantiate_features: instantiate_features,
1451            },
1452        );
1453        Ok(())
1454    }
1455
1456    pub fn unload_plugin(&mut self, uri: &str) -> Result<(), String> {
1457        let loaded_plugin = self
1458            .loaded_plugins
1459            .remove(uri)
1460            .ok_or_else(|| format!("Plugin is not currently loaded: {uri}"))?;
1461        let _ = unsafe { loaded_plugin.instance.deactivate() };
1462        Ok(())
1463    }
1464
1465    pub fn unload_all(&mut self) {
1466        let uris = self.loaded_plugins();
1467        for uri in uris {
1468            let _ = self.unload_plugin(&uri);
1469        }
1470    }
1471
1472    pub fn loaded_plugins(&self) -> Vec<String> {
1473        let mut uris = self.loaded_plugins.keys().cloned().collect::<Vec<_>>();
1474        uris.sort();
1475        uris
1476    }
1477
1478    pub fn loaded_count(&self) -> usize {
1479        self.loaded_plugins.len()
1480    }
1481
1482    fn plugin_by_uri(&self, uri: &str) -> Option<Plugin> {
1483        let uri_node = self.world.new_uri(uri);
1484        self.world.plugins().plugin(&uri_node)
1485    }
1486}
1487
1488impl Drop for Lv2Host {
1489    fn drop(&mut self) {
1490        self.unload_all();
1491    }
1492}
1493
1494fn plugin_feature_uris(plugin: &Plugin) -> Vec<String> {
1495    plugin
1496        .required_features()
1497        .iter()
1498        .filter_map(|feature| {
1499            feature
1500                .as_uri()
1501                .map(str::to_string)
1502                .or_else(|| feature.as_str().map(str::to_string))
1503        })
1504        .collect()
1505}
1506
1507fn instantiate_plugin(
1508    plugin: &Plugin,
1509    sample_rate: f64,
1510    uri: &str,
1511    urid_feature: &mut UridMapFeature,
1512    state_path_feature: &mut StatePathFeature,
1513) -> Result<(lilv::instance::Instance, InstantiateFeatureSet), String> {
1514    let required_features = plugin_feature_uris(plugin);
1515    let feature_set =
1516        build_instantiate_features(&required_features, urid_feature, state_path_feature)?;
1517    let feature_refs: Vec<&LV2Feature> = feature_set.features.iter().collect();
1518    let instance = unsafe { plugin.instantiate(sample_rate, feature_refs) }.ok_or_else(|| {
1519        if required_features.is_empty() {
1520            format!(
1521                "Failed to instantiate '{uri}'. It likely requires LV2 host features that are not wired yet."
1522            )
1523        } else {
1524            format!(
1525                "Failed to instantiate '{uri}'. Required features: {}",
1526                required_features.join(", ")
1527            )
1528        }
1529    })?;
1530    Ok((instance, feature_set))
1531}
1532
1533fn build_instantiate_features(
1534    required_features: &[String],
1535    urid_feature: &UridMapFeature,
1536    state_path_feature: &StatePathFeature,
1537) -> Result<InstantiateFeatureSet, String> {
1538    let mut seen = HashSet::<String>::new();
1539    let mut feature_uris = Vec::<CString>::new();
1540    let mut features = Vec::<LV2Feature>::new();
1541
1542    let mut push_feature =
1543        |uri: &str, data: *mut c_void, allow_duplicate: bool| -> Result<(), String> {
1544            if !allow_duplicate && !seen.insert(uri.to_string()) {
1545                return Ok(());
1546            }
1547            let c_uri = CString::new(uri)
1548                .map_err(|e| format!("Invalid LV2 feature URI '{uri}' for instantiate: {e}"))?;
1549            let feature = LV2Feature {
1550                uri: c_uri.as_ptr(),
1551                data,
1552            };
1553            feature_uris.push(c_uri);
1554            features.push(feature);
1555            Ok(())
1556        };
1557
1558    push_feature(LV2_URID__MAP, urid_feature.map_feature().data, false)?;
1559    push_feature(LV2_URID__UNMAP, urid_feature.unmap_feature().data, false)?;
1560    let worker_feature = WorkerFeature::new()?;
1561    push_feature(LV2_WORKER__SCHEDULE, worker_feature.feature.data, false)?;
1562
1563    let state_features = state_path_feature.feature_ptrs();
1564    for feature_ptr in state_features {
1565        if feature_ptr.is_null() {
1566            continue;
1567        }
1568        let feature = unsafe { &*feature_ptr };
1569        let uri = unsafe { CStr::from_ptr(feature.uri) }
1570            .to_str()
1571            .map_err(|e| format!("Invalid LV2 feature URI from state path feature: {e}"))?;
1572        push_feature(uri, feature.data, false)?;
1573    }
1574
1575    let option_values = vec![1_u32, 8192_u32, 1024_u32];
1576    let int_type = urid_feature.map_uri(LV2_ATOM__INT);
1577    let min_key = urid_feature.map_uri(LV2_BUF_SIZE__MIN_BLOCK_LENGTH.as_bytes());
1578    let max_key = urid_feature.map_uri(LV2_BUF_SIZE__MAX_BLOCK_LENGTH.as_bytes());
1579    let nominal_key = urid_feature.map_uri(LV2_BUF_SIZE__NOMINAL_BLOCK_LENGTH.as_bytes());
1580    let mut options = vec![
1581        LV2OptionsOption {
1582            context: 0,
1583            subject: 0,
1584            key: min_key,
1585            size: std::mem::size_of::<u32>() as u32,
1586            type_: int_type,
1587            value: (&option_values[0] as *const u32).cast::<c_void>(),
1588        },
1589        LV2OptionsOption {
1590            context: 0,
1591            subject: 0,
1592            key: max_key,
1593            size: std::mem::size_of::<u32>() as u32,
1594            type_: int_type,
1595            value: (&option_values[1] as *const u32).cast::<c_void>(),
1596        },
1597        LV2OptionsOption {
1598            context: 0,
1599            subject: 0,
1600            key: nominal_key,
1601            size: std::mem::size_of::<u32>() as u32,
1602            type_: int_type,
1603            value: (&option_values[2] as *const u32).cast::<c_void>(),
1604        },
1605        LV2OptionsOption {
1606            context: 0,
1607            subject: 0,
1608            key: 0,
1609            size: 0,
1610            type_: 0,
1611            value: std::ptr::null(),
1612        },
1613    ];
1614    push_feature(
1615        LV2_OPTIONS__OPTIONS,
1616        options.as_mut_ptr().cast::<c_void>(),
1617        false,
1618    )?;
1619
1620    let flag_feature_data = Box::new(0_u8);
1621
1622    for required in required_features {
1623        let data = match required.as_str() {
1624            LV2_OPTIONS__OPTIONS => options.as_mut_ptr().cast::<c_void>(),
1625            LV2_BUF_SIZE__BOUNDED_BLOCK_LENGTH => (&*flag_feature_data as *const u8)
1626                .cast_mut()
1627                .cast::<c_void>(),
1628            LV2_URID__MAP_URI_TYPO_COMPAT => urid_feature.map_feature().data,
1629            LV2_WORKER__SCHEDULE => worker_feature.feature.data,
1630            _ => (&*flag_feature_data as *const u8)
1631                .cast_mut()
1632                .cast::<c_void>(),
1633        };
1634        push_feature(required, data, false)?;
1635    }
1636
1637    Ok(InstantiateFeatureSet {
1638        _feature_uris: feature_uris,
1639        features,
1640        _worker_feature: worker_feature,
1641        _option_values: option_values,
1642        _options: options,
1643        _flag_feature_data: flag_feature_data,
1644    })
1645}
1646
1647impl WorkerFeature {
1648    fn new() -> Result<Self, String> {
1649        let mut schedule = Box::new(Lv2WorkerSchedule {
1650            handle: std::ptr::null_mut(),
1651            schedule_work: Some(lv2_worker_schedule_work_callback),
1652        });
1653        let state = Box::new(WorkerScheduleState {
1654            jobs: UnsafeMutex::new(vec![]),
1655            responses: UnsafeMutex::new(vec![]),
1656        });
1657        schedule.handle = &*state as *const WorkerScheduleState as *mut c_void;
1658        let uri =
1659            CString::new(LV2_WORKER__SCHEDULE).map_err(|e| format!("Invalid worker URI: {e}"))?;
1660        let feature = LV2Feature {
1661            uri: uri.as_ptr(),
1662            data: (&mut *schedule as *mut Lv2WorkerSchedule).cast::<c_void>(),
1663        };
1664        Ok(Self {
1665            _uri: uri,
1666            _schedule: schedule,
1667            feature,
1668            state,
1669        })
1670    }
1671}
1672
1673unsafe extern "C" fn lv2_worker_schedule_work_callback(
1674    handle: *mut c_void,
1675    size: u32,
1676    data: *const c_void,
1677) -> u32 {
1678    if handle.is_null() || (size > 0 && data.is_null()) {
1679        return LV2_WORKER_ERR_UNKNOWN;
1680    }
1681    let state = unsafe { &*(handle as *const WorkerScheduleState) };
1682    let bytes = if size == 0 {
1683        vec![]
1684    } else {
1685        unsafe { std::slice::from_raw_parts(data.cast::<u8>(), size as usize).to_vec() }
1686    };
1687    state.jobs.lock().push(bytes);
1688    LV2_WORKER_SUCCESS
1689}
1690
1691unsafe extern "C" fn lv2_worker_respond_callback(
1692    handle: *mut c_void,
1693    size: u32,
1694    data: *const c_void,
1695) -> u32 {
1696    if handle.is_null() || (size > 0 && data.is_null()) {
1697        return LV2_WORKER_ERR_UNKNOWN;
1698    }
1699    let state = unsafe { &*(handle as *const WorkerScheduleState) };
1700    let bytes = if size == 0 {
1701        vec![]
1702    } else {
1703        unsafe { std::slice::from_raw_parts(data.cast::<u8>(), size as usize).to_vec() }
1704    };
1705    state.responses.lock().push(bytes);
1706    LV2_WORKER_SUCCESS
1707}
1708
1709fn append_object_long_property(buffer: &mut Vec<u8>, key: LV2Urid, atom_type: LV2Urid, value: i64) {
1710    let prop = LV2AtomPropertyBody {
1711        key,
1712        context: 0,
1713        value: LV2Atom {
1714            size: std::mem::size_of::<i64>() as u32,
1715            mytype: atom_type,
1716        },
1717    };
1718    let prop_size = std::mem::size_of::<LV2AtomPropertyBody>();
1719    let prop_bytes = unsafe {
1720        std::slice::from_raw_parts(
1721            (&prop as *const LV2AtomPropertyBody).cast::<u8>(),
1722            prop_size,
1723        )
1724    };
1725    buffer.extend_from_slice(prop_bytes);
1726    let atom = LV2AtomLong {
1727        atom: LV2Atom {
1728            size: std::mem::size_of::<i64>() as u32,
1729            mytype: atom_type,
1730        },
1731        body: value,
1732    };
1733    let value_bytes = unsafe {
1734        std::slice::from_raw_parts(
1735            (&atom.body as *const i64).cast::<u8>(),
1736            std::mem::size_of::<i64>(),
1737        )
1738    };
1739    buffer.extend_from_slice(value_bytes);
1740    let written = (prop_size + std::mem::size_of::<i64>()) as u32;
1741    let padded = lv2_atom_pad_size(written) as usize;
1742    if padded > (prop_size + std::mem::size_of::<i64>()) {
1743        buffer.resize(
1744            buffer.len() + (padded - prop_size - std::mem::size_of::<i64>()),
1745            0,
1746        );
1747    }
1748}
1749
1750fn append_object_double_property(
1751    buffer: &mut Vec<u8>,
1752    key: LV2Urid,
1753    atom_type: LV2Urid,
1754    value: f64,
1755) {
1756    let prop = LV2AtomPropertyBody {
1757        key,
1758        context: 0,
1759        value: LV2Atom {
1760            size: std::mem::size_of::<f64>() as u32,
1761            mytype: atom_type,
1762        },
1763    };
1764    let prop_size = std::mem::size_of::<LV2AtomPropertyBody>();
1765    let prop_bytes = unsafe {
1766        std::slice::from_raw_parts(
1767            (&prop as *const LV2AtomPropertyBody).cast::<u8>(),
1768            prop_size,
1769        )
1770    };
1771    buffer.extend_from_slice(prop_bytes);
1772    let atom = LV2AtomDouble {
1773        atom: LV2Atom {
1774            size: std::mem::size_of::<f64>() as u32,
1775            mytype: atom_type,
1776        },
1777        body: value,
1778    };
1779    let value_bytes = unsafe {
1780        std::slice::from_raw_parts(
1781            (&atom.body as *const f64).cast::<u8>(),
1782            std::mem::size_of::<f64>(),
1783        )
1784    };
1785    buffer.extend_from_slice(value_bytes);
1786    let written = (prop_size + std::mem::size_of::<f64>()) as u32;
1787    let padded = lv2_atom_pad_size(written) as usize;
1788    if padded > (prop_size + std::mem::size_of::<f64>()) {
1789        buffer.resize(
1790            buffer.len() + (padded - prop_size - std::mem::size_of::<f64>()),
1791            0,
1792        );
1793    }
1794}
1795
1796impl UridMapFeature {
1797    fn new() -> Result<Self, String> {
1798        let mut map = Box::new(LV2UridMap {
1799            handle: std::ptr::null_mut(),
1800            map: urid_map_callback,
1801        });
1802        let mut unmap = Box::new(LV2UridUnmap {
1803            handle: std::ptr::null_mut(),
1804            unmap: urid_unmap_callback,
1805        });
1806        let state = Box::new(Mutex::new(UridMapState {
1807            next_urid: 1,
1808            by_uri: HashMap::new(),
1809            by_urid: HashMap::new(),
1810        }));
1811        map.handle = (&*state as *const Mutex<UridMapState>) as *mut c_void;
1812        unmap.handle = (&*state as *const Mutex<UridMapState>) as *mut c_void;
1813
1814        let map_uri =
1815            CString::new(LV2_URID__MAP).map_err(|e| format!("Invalid URID feature URI: {e}"))?;
1816        let map_feature = LV2Feature {
1817            uri: map_uri.as_ptr(),
1818            data: (&mut *map as *mut LV2UridMap).cast::<c_void>(),
1819        };
1820        let unmap_uri =
1821            CString::new(LV2_URID__UNMAP).map_err(|e| format!("Invalid URID feature URI: {e}"))?;
1822        let unmap_feature = LV2Feature {
1823            uri: unmap_uri.as_ptr(),
1824            data: (&mut *unmap as *mut LV2UridUnmap).cast::<c_void>(),
1825        };
1826
1827        Ok(Self {
1828            _map_uri: map_uri,
1829            _unmap_uri: unmap_uri,
1830            map_feature,
1831            unmap_feature,
1832            _map: map,
1833            _unmap: unmap,
1834            _state: state,
1835        })
1836    }
1837
1838    fn map_feature(&self) -> &LV2Feature {
1839        &self.map_feature
1840    }
1841
1842    fn unmap_feature(&self) -> &LV2Feature {
1843        &self.unmap_feature
1844    }
1845
1846    fn map_uri(&self, uri: &[u8]) -> LV2Urid {
1847        let Ok(uri_str) = std::str::from_utf8(uri) else {
1848            return 0;
1849        };
1850        let uri_str = uri_str.trim_end_matches('\0');
1851        let Ok(mut state) = self._state.lock() else {
1852            return 0;
1853        };
1854        if let Some(existing) = state.by_uri.get(uri_str).copied() {
1855            return existing;
1856        }
1857        let mapped = state.next_urid;
1858        state.next_urid = state.next_urid.saturating_add(1);
1859        state.by_uri.insert(uri_str.to_string(), mapped);
1860        if let Ok(uri_c) = CString::new(uri_str) {
1861            state.by_urid.insert(mapped, uri_c);
1862        }
1863        mapped
1864    }
1865
1866    fn unmap_urid(&self, urid: LV2Urid) -> Option<String> {
1867        let Ok(state) = self._state.lock() else {
1868            return None;
1869        };
1870        state
1871            .by_urid
1872            .get(&urid)
1873            .and_then(|uri| uri.to_str().ok().map(str::to_string))
1874    }
1875}
1876
1877fn default_state_base_dir() -> PathBuf {
1878    std::env::temp_dir().join("maolan-lv2-state")
1879}
1880
1881impl StatePathFeature {
1882    fn new(base_dir: PathBuf) -> Self {
1883        let context = Box::new(Mutex::new(StatePathContext {
1884            base_dir,
1885            copy_counter: 0,
1886        }));
1887        let handle = (&*context as *const Mutex<StatePathContext>) as *mut c_void;
1888
1889        let map = Box::new(Lv2StateMapPath {
1890            handle,
1891            abstract_path: Some(lv2_state_abstract_path_callback),
1892            absolute_path: Some(lv2_state_absolute_path_callback),
1893        });
1894        let make = Box::new(Lv2StateMakePath {
1895            handle,
1896            path: Some(lv2_state_make_path_callback),
1897        });
1898        let free = Box::new(Lv2StateFreePath {
1899            handle,
1900            free_path: Some(lv2_state_free_path_callback),
1901        });
1902
1903        let map_uri = CString::new(LV2_STATE_MAP_PATH_URI).expect("valid LV2 state mapPath URI");
1904        let make_uri = CString::new(LV2_STATE_MAKE_PATH_URI).expect("valid LV2 state makePath URI");
1905        let free_uri = CString::new(LV2_STATE_FREE_PATH_URI).expect("valid LV2 state freePath URI");
1906
1907        let map_feature = LV2Feature {
1908            uri: map_uri.as_ptr(),
1909            data: (&*map as *const Lv2StateMapPath)
1910                .cast_mut()
1911                .cast::<c_void>(),
1912        };
1913        let make_feature = LV2Feature {
1914            uri: make_uri.as_ptr(),
1915            data: (&*make as *const Lv2StateMakePath)
1916                .cast_mut()
1917                .cast::<c_void>(),
1918        };
1919        let free_feature = LV2Feature {
1920            uri: free_uri.as_ptr(),
1921            data: (&*free as *const Lv2StateFreePath)
1922                .cast_mut()
1923                .cast::<c_void>(),
1924        };
1925
1926        let instance = Self {
1927            _map_uri: map_uri,
1928            _make_uri: make_uri,
1929            _free_uri: free_uri,
1930            _map: map,
1931            _make: make,
1932            _free: free,
1933            map_feature,
1934            make_feature,
1935            free_feature,
1936            _context: context,
1937        };
1938        instance.ensure_base_dir();
1939        instance
1940    }
1941
1942    fn ensure_base_dir(&self) {
1943        if let Ok(ctx) = self._context.lock() {
1944            let _ = std::fs::create_dir_all(&ctx.base_dir);
1945        }
1946    }
1947
1948    fn set_base_dir(&self, base_dir: PathBuf) {
1949        if let Ok(mut ctx) = self._context.lock() {
1950            ctx.base_dir = base_dir;
1951            let _ = std::fs::create_dir_all(&ctx.base_dir);
1952        }
1953    }
1954
1955    fn feature_ptrs(&self) -> [*const LV2Feature; 3] {
1956        [
1957            &self.map_feature as *const LV2Feature,
1958            &self.make_feature as *const LV2Feature,
1959            &self.free_feature as *const LV2Feature,
1960        ]
1961    }
1962}
1963
1964extern "C" fn lv2_state_free_path_callback(_handle: *mut c_void, path: *mut c_char) {
1965    if path.is_null() {
1966        return;
1967    }
1968    unsafe {
1969        let _ = CString::from_raw(path);
1970    }
1971}
1972
1973fn state_ctx_from_handle(handle: *mut c_void) -> Option<&'static Mutex<StatePathContext>> {
1974    if handle.is_null() {
1975        return None;
1976    }
1977    Some(unsafe { &*(handle as *const Mutex<StatePathContext>) })
1978}
1979
1980fn copy_into_state_assets(ctx: &mut StatePathContext, src: &Path) -> Option<String> {
1981    let file_name = src.file_name()?.to_str()?.to_string();
1982    let assets_dir = ctx.base_dir.join("assets");
1983    let _ = std::fs::create_dir_all(&assets_dir);
1984    ctx.copy_counter = ctx.copy_counter.saturating_add(1);
1985    let dst_name = format!("{}-{}", ctx.copy_counter, file_name);
1986    let dst = assets_dir.join(&dst_name);
1987    std::fs::copy(src, &dst).ok()?;
1988    Some(format!("assets/{dst_name}"))
1989}
1990
1991extern "C" fn lv2_state_abstract_path_callback(
1992    handle: *mut c_void,
1993    absolute_path: *const c_char,
1994) -> *mut c_char {
1995    let Some(ctx_lock) = state_ctx_from_handle(handle) else {
1996        return std::ptr::null_mut();
1997    };
1998    if absolute_path.is_null() {
1999        return std::ptr::null_mut();
2000    }
2001    let Some(path_str) = (unsafe { CStr::from_ptr(absolute_path) }).to_str().ok() else {
2002        return std::ptr::null_mut();
2003    };
2004    let path = PathBuf::from(path_str);
2005    let mut mapped = None;
2006    if let Ok(mut ctx) = ctx_lock.lock() {
2007        if let Ok(rel) = path.strip_prefix(&ctx.base_dir) {
2008            mapped = Some(rel.to_string_lossy().to_string());
2009        } else if path.exists() {
2010            mapped = copy_into_state_assets(&mut ctx, &path);
2011        }
2012    }
2013    let out = mapped.unwrap_or_else(|| path_str.to_string());
2014    CString::new(out)
2015        .ok()
2016        .map(CString::into_raw)
2017        .unwrap_or(std::ptr::null_mut())
2018}
2019
2020extern "C" fn lv2_state_absolute_path_callback(
2021    handle: *mut c_void,
2022    abstract_path: *const c_char,
2023) -> *mut c_char {
2024    let Some(ctx_lock) = state_ctx_from_handle(handle) else {
2025        return std::ptr::null_mut();
2026    };
2027    if abstract_path.is_null() {
2028        return std::ptr::null_mut();
2029    }
2030    let Some(path_str) = (unsafe { CStr::from_ptr(abstract_path) }).to_str().ok() else {
2031        return std::ptr::null_mut();
2032    };
2033    let output = if Path::new(path_str).is_absolute() {
2034        path_str.to_string()
2035    } else if let Ok(ctx) = ctx_lock.lock() {
2036        ctx.base_dir.join(path_str).to_string_lossy().to_string()
2037    } else {
2038        path_str.to_string()
2039    };
2040    CString::new(output)
2041        .ok()
2042        .map(CString::into_raw)
2043        .unwrap_or(std::ptr::null_mut())
2044}
2045
2046extern "C" fn lv2_state_make_path_callback(
2047    handle: *mut c_void,
2048    requested: *const c_char,
2049) -> *mut c_char {
2050    let Some(ctx_lock) = state_ctx_from_handle(handle) else {
2051        return std::ptr::null_mut();
2052    };
2053
2054    let requested_name = if requested.is_null() {
2055        "state.bin".to_string()
2056    } else {
2057        (unsafe { CStr::from_ptr(requested) })
2058            .to_str()
2059            .ok()
2060            .filter(|s| !s.is_empty())
2061            .map(|s| s.replace("..", "_"))
2062            .unwrap_or_else(|| "state.bin".to_string())
2063    };
2064
2065    let output = if let Ok(mut ctx) = ctx_lock.lock() {
2066        ctx.copy_counter = ctx.copy_counter.saturating_add(1);
2067        let file_name = format!("generated-{}-{}", ctx.copy_counter, requested_name);
2068        let path = ctx.base_dir.join("generated").join(file_name);
2069        if let Some(parent) = path.parent() {
2070            let _ = std::fs::create_dir_all(parent);
2071        }
2072        path.to_string_lossy().to_string()
2073    } else {
2074        requested_name
2075    };
2076
2077    CString::new(output)
2078        .ok()
2079        .map(CString::into_raw)
2080        .unwrap_or(std::ptr::null_mut())
2081}
2082
2083extern "C" fn lv2_state_store_callback(
2084    handle: Lv2StateHandle,
2085    key: u32,
2086    value: *const c_void,
2087    size: usize,
2088    type_: u32,
2089    flags: u32,
2090) -> Lv2StateStatus {
2091    if handle.is_null() || value.is_null() || size == 0 {
2092        return LV2_STATE_STATUS_ERR_NO_PROPERTY;
2093    }
2094    let ctx = unsafe { &mut *(handle as *mut StateSaveContext) };
2095    let bytes = unsafe { std::slice::from_raw_parts(value.cast::<u8>(), size) };
2096    ctx.properties.push(RawStateProperty {
2097        key,
2098        type_,
2099        flags,
2100        value: bytes.to_vec(),
2101    });
2102    LV2_STATE_STATUS_SUCCESS
2103}
2104
2105extern "C" fn lv2_state_retrieve_callback(
2106    handle: Lv2StateHandle,
2107    key: u32,
2108    size: *mut usize,
2109    type_: *mut u32,
2110    flags: *mut u32,
2111) -> *const c_void {
2112    if handle.is_null() {
2113        return std::ptr::null();
2114    }
2115    let ctx = unsafe { &mut *(handle as *mut StateRestoreContext) };
2116    let Some(idx) = ctx.by_key.get(&key).copied() else {
2117        return std::ptr::null();
2118    };
2119    let Some(prop) = ctx.properties.get(idx) else {
2120        return std::ptr::null();
2121    };
2122    if !size.is_null() {
2123        unsafe {
2124            *size = prop.value.len();
2125        }
2126    }
2127    if !type_.is_null() {
2128        unsafe {
2129            *type_ = prop.type_;
2130        }
2131    }
2132    if !flags.is_null() {
2133        unsafe {
2134            *flags = prop.flags;
2135        }
2136    }
2137    prop.value.as_ptr().cast::<c_void>()
2138}
2139
2140fn prepare_empty_atom_sequence(
2141    buffer: &mut [u8],
2142    sequence_urid: LV2Urid,
2143    frame_time_urid: LV2Urid,
2144) {
2145    buffer.fill(0);
2146    if buffer.len() < std::mem::size_of::<LV2AtomSequence>() {
2147        return;
2148    }
2149    let seq = buffer.as_mut_ptr() as *mut LV2AtomSequence;
2150    unsafe {
2151        (*seq).atom.mytype = sequence_urid;
2152        (*seq).atom.size = std::mem::size_of::<LV2AtomSequenceBody>() as u32;
2153        (*seq).body.unit = frame_time_urid;
2154        (*seq).body.pad = 0;
2155    }
2156}
2157
2158fn prepare_output_atom_sequence(
2159    buffer: &mut [u8],
2160    sequence_urid: LV2Urid,
2161    frame_time_urid: LV2Urid,
2162) {
2163    buffer.fill(0);
2164    if buffer.len() < std::mem::size_of::<LV2AtomSequence>() {
2165        return;
2166    }
2167    let seq = buffer.as_mut_ptr() as *mut LV2AtomSequence;
2168    let body_capacity = buffer
2169        .len()
2170        .saturating_sub(std::mem::size_of::<lv2_raw::LV2Atom>()) as u32;
2171    unsafe {
2172        (*seq).atom.mytype = sequence_urid;
2173        (*seq).atom.size = body_capacity;
2174        (*seq).body.unit = frame_time_urid;
2175        (*seq).body.pad = 0;
2176    }
2177}
2178
2179extern "C" fn urid_map_callback(handle: LV2UridMapHandle, uri: *const c_char) -> LV2Urid {
2180    if handle.is_null() || uri.is_null() {
2181        return 0;
2182    }
2183    let Some(uri_str) = unsafe { CStr::from_ptr(uri) }.to_str().ok() else {
2184        return 0;
2185    };
2186
2187    let state_mutex = unsafe { &*(handle as *const Mutex<UridMapState>) };
2188    let Ok(mut state) = state_mutex.lock() else {
2189        return 0;
2190    };
2191
2192    if let Some(existing) = state.by_uri.get(uri_str).copied() {
2193        return existing;
2194    }
2195
2196    let mapped = state.next_urid;
2197    state.next_urid = state.next_urid.saturating_add(1);
2198    state.by_uri.insert(uri_str.to_string(), mapped);
2199    if let Ok(uri_c) = CString::new(uri_str) {
2200        state.by_urid.insert(mapped, uri_c);
2201    }
2202    mapped
2203}
2204
2205extern "C" fn urid_unmap_callback(handle: LV2UridMapHandle, urid: LV2Urid) -> *const c_char {
2206    if handle.is_null() || urid == 0 {
2207        return std::ptr::null();
2208    }
2209    let state_mutex = unsafe { &*(handle as *const Mutex<UridMapState>) };
2210    let Ok(state) = state_mutex.lock() else {
2211        return std::ptr::null();
2212    };
2213    state
2214        .by_urid
2215        .get(&urid)
2216        .map(|uri| uri.as_ptr())
2217        .unwrap_or(std::ptr::null())
2218}
2219
2220fn plugin_port_counts(
2221    plugin: &Plugin,
2222    input_port: &Node,
2223    output_port: &Node,
2224    audio_port: &Node,
2225    atom_port: &Node,
2226    event_port: &Node,
2227    midi_event: &Node,
2228) -> (usize, usize, usize, usize) {
2229    let mut audio_inputs = 0;
2230    let mut audio_outputs = 0;
2231    let mut midi_inputs = 0;
2232    let mut midi_outputs = 0;
2233
2234    for port in plugin.iter_ports() {
2235        let is_input = port.is_a(input_port);
2236        let is_output = port.is_a(output_port);
2237
2238        if port.is_a(audio_port) {
2239            if is_input {
2240                audio_inputs += 1;
2241            }
2242            if is_output {
2243                audio_outputs += 1;
2244            }
2245        }
2246
2247        let is_event_or_atom = port.is_a(atom_port) || port.is_a(event_port);
2248        let is_midi = is_event_or_atom && port.supports_event(midi_event);
2249        if is_midi {
2250            if is_input {
2251                midi_inputs += 1;
2252            }
2253            if is_output {
2254                midi_outputs += 1;
2255            }
2256        }
2257    }
2258
2259    (audio_inputs, audio_outputs, midi_inputs, midi_outputs)
2260}
2261
2262fn count_main_audio_ports(
2263    plugin: &Plugin,
2264    main_group_predicate: &Node,
2265    port_group_predicate: &Node,
2266    audio_port: &Node,
2267    direction_port: &Node,
2268) -> Option<usize> {
2269    let main_groups: Vec<Node> = plugin.value(main_group_predicate).iter().collect();
2270    if main_groups.is_empty() {
2271        return None;
2272    }
2273
2274    let count = plugin
2275        .iter_ports()
2276        .filter(|port| port.is_a(audio_port) && port.is_a(direction_port))
2277        .filter(|port| {
2278            port.get(port_group_predicate)
2279                .is_some_and(|group| main_groups.iter().any(|main| *main == group))
2280        })
2281        .count();
2282
2283    Some(count)
2284}
2285
2286fn infer_missing_control_default(
2287    min: f32,
2288    max: f32,
2289    is_toggled: bool,
2290    is_discrete: bool,
2291) -> (f32, &'static str) {
2292    if !min.is_finite() || !max.is_finite() {
2293        return (0.0, "non-finite-range->0");
2294    }
2295    if is_toggled {
2296        let off = if min <= 0.0 && max >= 0.0 { 0.0 } else { min };
2297        return (off.clamp(min, max), "toggled->off");
2298    }
2299    if is_discrete {
2300        if min < 0.0 && max > 0.0 {
2301            return (0.0, "discrete-bipolar->0");
2302        }
2303        return (min, "discrete->min");
2304    }
2305    if min < 0.0 && max > 0.0 {
2306        return (0.0, "bipolar->0");
2307    }
2308    if min <= 1.0 && max >= 1.0 {
2309        return (1.0, "range-has-unity->1");
2310    }
2311    (min + (max - min) * 0.5, "midpoint")
2312}
2313
2314fn lv2_node_to_f32(node: &Node) -> Option<f32> {
2315    if let Some(v) = node.as_float() {
2316        return Some(v);
2317    }
2318    if let Some(v) = node.as_int() {
2319        return Some(v as f32);
2320    }
2321    node.as_bool().map(|v| if v { 1.0 } else { 0.0 })
2322}