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