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