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
59const 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 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 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 for line in xml.lines() {
1150 let line = line.trim();
1151 if !line.starts_with("<Note ") {
1152 continue;
1153 }
1154
1155 let number = if let Some(start) = line.find("Number=\"") {
1157 let start = start + 8; 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 let name = if let Some(start) = line.find("Name=\"") {
1169 let start = start + 6; 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}