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