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