1use std::ffi::c_void;
7use std::path::Path;
8use std::sync::atomic::{AtomicU32, Ordering};
9use std::sync::{Mutex, OnceLock};
10use std::time::{Duration, Instant};
11use vst3::Steinberg::Vst::ProcessModes_::kRealtime;
12use vst3::Steinberg::Vst::SymbolicSampleSizes_::kSample32;
13use vst3::Steinberg::Vst::*;
14use vst3::Steinberg::*;
15use vst3::{ComPtr, Interface};
16
17static HOST_RUN_LOOP_STATE: OnceLock<Mutex<HostRunLoopState>> = OnceLock::new();
18
19struct HostRunLoopState {
20 event_handlers: Vec<RunLoopEventHandler>,
21 timer_handlers: Vec<RunLoopTimerHandler>,
22}
23
24struct RunLoopEventHandler {
25 handler: usize,
26 fd: i32,
27}
28
29struct RunLoopTimerHandler {
30 handler: usize,
31 interval: Duration,
32 next_tick: Instant,
33}
34
35fn run_loop_state() -> &'static Mutex<HostRunLoopState> {
36 HOST_RUN_LOOP_STATE.get_or_init(|| {
37 Mutex::new(HostRunLoopState {
38 event_handlers: Vec::new(),
39 timer_handlers: Vec::new(),
40 })
41 })
42}
43
44pub fn pump_host_run_loop() {
45 let (event_calls, timer_calls): (Vec<(usize, i32)>, Vec<usize>) = {
48 let mut state = run_loop_state().lock().expect("run loop mutex poisoned");
49 let now = Instant::now();
50 let event_calls = state
51 .event_handlers
52 .iter()
53 .map(|h| (h.handler, h.fd))
54 .collect::<Vec<_>>();
55 let mut timer_calls = Vec::new();
56 for timer in &mut state.timer_handlers {
57 if now >= timer.next_tick {
58 timer_calls.push(timer.handler);
59 timer.next_tick = now + timer.interval;
60 }
61 }
62 (event_calls, timer_calls)
63 };
64
65 for (handler, fd) in event_calls {
66 let handler_ptr = handler as *mut Linux::IEventHandler;
67 if handler_ptr.is_null() {
68 continue;
69 }
70 unsafe {
71 ((*(*handler_ptr).vtbl).onFDIsSet)(handler_ptr, fd);
72 }
73 }
74 for handler in timer_calls {
75 let handler_ptr = handler as *mut Linux::ITimerHandler;
76 if handler_ptr.is_null() {
77 continue;
78 }
79 unsafe {
80 ((*(*handler_ptr).vtbl).onTimer)(handler_ptr);
81 }
82 }
83}
84
85pub struct PluginFactory {
87 factory: ComPtr<IPluginFactory>,
89 module: libloading::Library,
90 module_inited: bool,
91}
92
93impl std::fmt::Debug for PluginFactory {
94 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
95 f.debug_struct("PluginFactory")
96 .field("factory", &"<COM ptr>")
97 .field("module", &"<library>")
98 .finish()
99 }
100}
101
102impl PluginFactory {
103 pub fn from_module(bundle_path: &Path) -> Result<Self, String> {
105 let module_path = get_module_path(bundle_path)?;
107
108 let library = unsafe {
110 libloading::Library::new(&module_path)
111 .map_err(|e| format!("Failed to load VST3 module {:?}: {}", module_path, e))?
112 };
113
114 let module_inited = unsafe {
117 match library.get::<unsafe extern "system" fn() -> bool>(b"InitDll") {
118 Ok(init_dll) => init_dll(),
119 Err(_) => false,
120 }
121 };
122
123 let get_factory: libloading::Symbol<unsafe extern "system" fn() -> *mut c_void> = unsafe {
126 library
127 .get(b"GetPluginFactory")
128 .map_err(|e| format!("Failed to find GetPluginFactory: {}", e))?
129 };
130
131 let factory_ptr = unsafe { get_factory() };
133 if factory_ptr.is_null() {
134 return Err("GetPluginFactory returned null".to_string());
135 }
136
137 let factory = unsafe { ComPtr::from_raw(factory_ptr as *mut IPluginFactory) }
139 .ok_or("Failed to create ComPtr for IPluginFactory")?;
140
141 Ok(Self {
142 factory,
143 module: library,
144 module_inited,
145 })
146 }
147
148 pub fn get_class_info(&self, index: i32) -> Option<ClassInfo> {
150 use vst3::Steinberg::IPluginFactoryTrait;
151
152 let mut info = PClassInfo {
153 cid: [0; 16],
154 cardinality: 0,
155 category: [0; 32],
156 name: [0; 64],
157 };
158
159 let result = unsafe { self.factory.getClassInfo(index, &mut info) };
160
161 if result == kResultOk {
162 Some(ClassInfo {
163 name: extract_cstring(&info.name),
164 category: extract_cstring(&info.category),
165 cid: info.cid,
166 })
167 } else {
168 None
169 }
170 }
171
172 pub fn count_classes(&self) -> i32 {
174 use vst3::Steinberg::IPluginFactoryTrait;
175 unsafe { self.factory.countClasses() }
176 }
177
178 pub fn create_instance(&self, class_id: &[i8; 16]) -> Result<PluginInstance, String> {
180 use vst3::Steinberg::IPluginFactoryTrait;
181
182 let mut instance_ptr: *mut c_void = std::ptr::null_mut();
183
184 let result = unsafe {
185 self.factory.createInstance(
186 class_id.as_ptr(),
187 IComponent::IID.as_ptr() as *const i8,
188 &mut instance_ptr,
189 )
190 };
191
192 if result != kResultOk || instance_ptr.is_null() {
193 return Err(format!(
194 "Failed to create plugin instance (result: {})",
195 result
196 ));
197 }
198
199 let component = unsafe { ComPtr::from_raw(instance_ptr as *mut IComponent) }
200 .ok_or("Failed to create ComPtr for IComponent")?;
201
202 Ok(PluginInstance::new(component))
203 }
204
205 pub fn create_edit_controller(
206 &self,
207 class_id: &[i8; 16],
208 ) -> Result<ComPtr<IEditController>, String> {
209 use vst3::Steinberg::IPluginFactoryTrait;
210
211 let mut instance_ptr: *mut c_void = std::ptr::null_mut();
212
213 let result = unsafe {
214 self.factory.createInstance(
215 class_id.as_ptr(),
216 IEditController::IID.as_ptr() as *const i8,
217 &mut instance_ptr,
218 )
219 };
220
221 if result != kResultOk || instance_ptr.is_null() {
222 return Err(format!(
223 "Failed to create edit controller instance (result: {})",
224 result
225 ));
226 }
227
228 unsafe { ComPtr::from_raw(instance_ptr as *mut IEditController) }
229 .ok_or("Failed to create ComPtr for IEditController".to_string())
230 }
231}
232
233impl Drop for PluginFactory {
234 fn drop(&mut self) {
235 if !self.module_inited {
236 return;
237 }
238
239 unsafe {
240 if let Ok(exit_dll) = self
241 .module
242 .get::<unsafe extern "system" fn() -> bool>(b"ExitDll")
243 {
244 let _ = exit_dll();
245 }
246 }
247 }
248}
249
250pub struct ClassInfo {
252 pub name: String,
253 pub category: String,
254 pub cid: [i8; 16],
255}
256
257pub struct PluginInstance {
259 pub component: ComPtr<IComponent>,
260 pub audio_processor: Option<ComPtr<IAudioProcessor>>,
261 pub edit_controller: Option<ComPtr<IEditController>>,
262 host_context: Box<HostApplicationContext>,
263 component_handler: Box<HostComponentHandlerContext>,
264}
265
266impl std::fmt::Debug for PluginInstance {
267 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
268 f.debug_struct("PluginInstance")
269 .field("component", &"<COM ptr>")
270 .field("audio_processor", &self.audio_processor.is_some())
271 .field("edit_controller", &self.edit_controller.is_some())
272 .finish()
273 }
274}
275
276impl PluginInstance {
277 fn new(component: ComPtr<IComponent>) -> Self {
278 Self {
279 component,
280 audio_processor: None,
281 edit_controller: None,
282 host_context: Box::new(HostApplicationContext::new()),
283 component_handler: Box::new(HostComponentHandlerContext::new()),
284 }
285 }
286
287 pub fn audio_bus_counts(&self) -> (usize, usize) {
288 use vst3::Steinberg::Vst::{BusDirections_, IComponentTrait, MediaTypes_};
289
290 let input_count = unsafe {
291 self.component
292 .getBusCount(MediaTypes_::kAudio as i32, BusDirections_::kInput as i32)
293 }
294 .max(0) as usize;
295 let output_count = unsafe {
296 self.component
297 .getBusCount(MediaTypes_::kAudio as i32, BusDirections_::kOutput as i32)
298 }
299 .max(0) as usize;
300 (input_count, output_count)
301 }
302
303 pub fn event_bus_counts(&self) -> (usize, usize) {
304 use vst3::Steinberg::Vst::{BusDirections_, IComponentTrait, MediaTypes_};
305
306 let input_count = unsafe {
307 self.component
308 .getBusCount(MediaTypes_::kEvent as i32, BusDirections_::kInput as i32)
309 }
310 .max(0) as usize;
311 let output_count = unsafe {
312 self.component
313 .getBusCount(MediaTypes_::kEvent as i32, BusDirections_::kOutput as i32)
314 }
315 .max(0) as usize;
316 (input_count, output_count)
317 }
318
319 pub fn main_audio_channel_counts(&self) -> (usize, usize) {
320 use vst3::Steinberg::Vst::{BusDirections_, BusTypes_, IComponentTrait, MediaTypes_};
321
322 let main_channels_for_direction = |direction: i32| -> usize {
323 let bus_count = unsafe {
324 self.component
325 .getBusCount(MediaTypes_::kAudio as i32, direction)
326 }
327 .max(0) as usize;
328 if bus_count == 0 {
329 return 0;
330 }
331
332 let mut first_nonzero = 0usize;
333 for idx in 0..bus_count {
334 let mut info: vst3::Steinberg::Vst::BusInfo = unsafe { std::mem::zeroed() };
335 let result = unsafe {
336 self.component.getBusInfo(
337 MediaTypes_::kAudio as i32,
338 direction,
339 idx as i32,
340 &mut info,
341 )
342 };
343 if result != kResultOk {
344 continue;
345 }
346 let channels = info.channelCount.max(0) as usize;
347 if channels > 0 && first_nonzero == 0 {
348 first_nonzero = channels;
349 }
350 if info.busType == BusTypes_::kMain as i32 {
351 return channels.max(1);
352 }
353 }
354
355 first_nonzero.max(1)
356 };
357
358 (
359 main_channels_for_direction(BusDirections_::kInput as i32),
360 main_channels_for_direction(BusDirections_::kOutput as i32),
361 )
362 }
363
364 pub fn initialize(&mut self, factory: &PluginFactory) -> Result<(), String> {
366 use vst3::Steinberg::IPluginBaseTrait;
367 use vst3::Steinberg::Vst::{IComponentTrait, IEditControllerTrait};
368
369 let context = &mut self.host_context.host as *mut IHostApplication as *mut FUnknown;
371 let result = unsafe { self.component.initialize(context) };
372
373 if result != kResultOk {
374 return Err(format!(
375 "Failed to initialize component (result: {})",
376 result
377 ));
378 }
379
380 let mut processor_ptr: *mut c_void = std::ptr::null_mut();
382 let result = unsafe {
383 let component_raw = self.component.as_ptr();
385 let vtbl = (*component_raw).vtbl;
386 let query_interface = (*vtbl).base.base.queryInterface;
387 let iid = std::mem::transmute::<&[u8; 16], &[i8; 16]>(&IAudioProcessor::IID);
389 query_interface(component_raw as *mut _, iid, &mut processor_ptr)
390 };
391
392 if result == kResultOk && !processor_ptr.is_null() {
393 self.audio_processor =
394 unsafe { ComPtr::from_raw(processor_ptr as *mut IAudioProcessor) };
395 }
396
397 let mut controller_ptr: *mut c_void = std::ptr::null_mut();
399 let query_result = unsafe {
400 let component_raw = self.component.as_ptr();
401 let vtbl = (*component_raw).vtbl;
402 let query_interface = (*vtbl).base.base.queryInterface;
403 let iid = std::mem::transmute::<&[u8; 16], &[i8; 16]>(&IEditController::IID);
404 query_interface(component_raw as *mut _, iid, &mut controller_ptr)
405 };
406 if query_result == kResultOk && !controller_ptr.is_null() {
407 self.edit_controller =
408 unsafe { ComPtr::from_raw(controller_ptr as *mut IEditController) };
409 }
410
411 if self.edit_controller.is_none() {
413 let mut controller_cid: TUID = [0; 16];
414 let cid_result = unsafe { self.component.getControllerClassId(&mut controller_cid) };
415 if cid_result == kResultOk {
416 let mut maybe_controller = factory.create_edit_controller(&controller_cid).ok();
417 if let Some(controller) = maybe_controller.as_mut() {
418 let controller_context =
419 &mut self.host_context.host as *mut IHostApplication as *mut FUnknown;
420 let init_result = unsafe { controller.initialize(controller_context) };
421 if init_result != kResultOk {
422 maybe_controller = None;
423 }
424 }
425 self.edit_controller = maybe_controller;
426 }
427 }
428
429 if let Some(controller) = self.edit_controller.as_ref() {
430 let handler =
431 &mut self.component_handler.handler as *mut IComponentHandler as *mut FUnknown;
432 let _ = unsafe { controller.setComponentHandler(handler as *mut IComponentHandler) };
433 }
434
435 Ok(())
436 }
437
438 pub fn set_active(&mut self, active: bool) -> Result<(), String> {
440 let result = unsafe { self.component.setActive(if active { 1 } else { 0 }) };
441
442 if result != kResultOk {
443 return Err(format!("Failed to set active state (result: {})", result));
444 }
445
446 Ok(())
447 }
448
449 pub fn setup_processing(
451 &mut self,
452 sample_rate: f64,
453 max_samples: i32,
454 input_channels: i32,
455 output_channels: i32,
456 ) -> Result<(), String> {
457 use vst3::Steinberg::Vst::{
458 BusDirections_, BusInfo, BusInfo_::BusFlags_ as BusFlags, BusTypes_,
459 IAudioProcessorTrait, IComponentTrait, MediaTypes_, SpeakerArr,
460 };
461
462 let processor = self
463 .audio_processor
464 .as_ref()
465 .ok_or("No audio processor available")?;
466
467 let sample_size_result = unsafe { processor.canProcessSampleSize(kSample32 as i32) };
468 if sample_size_result != kResultOk {
469 return Err(format!(
470 "Plugin does not support 32-bit sample size (result: {})",
471 sample_size_result
472 ));
473 }
474
475 let configure_audio_buses = |direction: i32, requested_channels: i32| {
476 let bus_count = unsafe {
477 self.component
478 .getBusCount(MediaTypes_::kAudio as i32, direction)
479 }
480 .max(0) as usize;
481 if bus_count == 0 {
482 return Vec::new();
483 }
484
485 let mut infos: Vec<BusInfo> = Vec::with_capacity(bus_count);
486 for idx in 0..bus_count {
487 let mut info: BusInfo = unsafe { std::mem::zeroed() };
488 let r = unsafe {
489 self.component.getBusInfo(
490 MediaTypes_::kAudio as i32,
491 direction,
492 idx as i32,
493 &mut info,
494 )
495 };
496 if r != kResultOk {
497 info.channelCount = if idx == 0 { 2 } else { 0 };
498 info.busType = if idx == 0 {
499 BusTypes_::kMain as i32
500 } else {
501 BusTypes_::kAux as i32
502 };
503 info.flags = if idx == 0 {
504 BusFlags::kDefaultActive as u32
505 } else {
506 0
507 };
508 }
509 infos.push(info);
510 }
511
512 let mut remaining = requested_channels.max(0);
513 let mut active = vec![false; bus_count];
514 let mut arrangements = vec![SpeakerArr::kEmpty; bus_count];
515
516 let mut ordered: Vec<usize> = (0..bus_count)
517 .filter(|&idx| infos[idx].busType == BusTypes_::kMain as i32)
518 .collect();
519 ordered.extend(
520 (0..bus_count).filter(|&idx| infos[idx].busType != BusTypes_::kMain as i32),
521 );
522
523 for idx in ordered {
524 if remaining <= 0 {
525 break;
526 }
527 let bus_channels = infos[idx].channelCount.max(1);
528 let allocate = remaining.min(bus_channels);
529 if allocate > 0 {
530 active[idx] = true;
531 arrangements[idx] = if allocate > 1 {
532 SpeakerArr::kStereo
533 } else {
534 SpeakerArr::kMono
535 };
536 remaining -= allocate;
537 }
538 }
539
540 if requested_channels > 0 && !active.iter().any(|v| *v) {
541 active[0] = true;
542 arrangements[0] = if requested_channels > 1 {
543 SpeakerArr::kStereo
544 } else {
545 SpeakerArr::kMono
546 };
547 }
548
549 for idx in 0..bus_count {
550 let _ = unsafe {
551 self.component.activateBus(
552 MediaTypes_::kAudio as i32,
553 direction,
554 idx as i32,
555 if active[idx] { 1 } else { 0 },
556 )
557 };
558 }
559
560 arrangements
561 };
562
563 let mut input_arrangements =
564 configure_audio_buses(BusDirections_::kInput as i32, input_channels);
565 let mut output_arrangements =
566 configure_audio_buses(BusDirections_::kOutput as i32, output_channels);
567 if !input_arrangements.is_empty() || !output_arrangements.is_empty() {
568 let _ = unsafe {
569 processor.setBusArrangements(
570 if input_arrangements.is_empty() {
571 std::ptr::null_mut()
572 } else {
573 input_arrangements.as_mut_ptr()
574 },
575 input_arrangements.len() as i32,
576 if output_arrangements.is_empty() {
577 std::ptr::null_mut()
578 } else {
579 output_arrangements.as_mut_ptr()
580 },
581 output_arrangements.len() as i32,
582 )
583 };
584 }
585
586 let event_in_buses = unsafe {
587 self.component
588 .getBusCount(MediaTypes_::kEvent as i32, BusDirections_::kInput as i32)
589 }
590 .max(0) as usize;
591 for idx in 0..event_in_buses {
592 let _ = unsafe {
593 self.component.activateBus(
594 MediaTypes_::kEvent as i32,
595 BusDirections_::kInput as i32,
596 idx as i32,
597 1,
598 )
599 };
600 }
601 let event_out_buses = unsafe {
602 self.component
603 .getBusCount(MediaTypes_::kEvent as i32, BusDirections_::kOutput as i32)
604 }
605 .max(0) as usize;
606 for idx in 0..event_out_buses {
607 let _ = unsafe {
608 self.component.activateBus(
609 MediaTypes_::kEvent as i32,
610 BusDirections_::kOutput as i32,
611 idx as i32,
612 1,
613 )
614 };
615 }
616
617 let mut setup = ProcessSetup {
618 processMode: kRealtime as i32,
619 symbolicSampleSize: kSample32 as i32,
620 maxSamplesPerBlock: max_samples,
621 sampleRate: sample_rate,
622 };
623
624 let result = unsafe { processor.setupProcessing(&mut setup) };
625
626 if result != kResultOk {
627 return Err(format!("Failed to setup processing (result: {})", result));
628 }
629
630 Ok(())
631 }
632
633 pub fn start_processing(&mut self) -> Result<(), String> {
634 use vst3::Steinberg::Vst::IAudioProcessorTrait;
635
636 let Some(processor) = &self.audio_processor else {
637 return Ok(());
638 };
639 let result = unsafe { processor.setProcessing(1) };
640 if result != kResultOk {
641 return Err(format!(
642 "Failed to enable processing state (result: {})",
643 result
644 ));
645 }
646 Ok(())
647 }
648
649 pub fn stop_processing(&mut self) {
650 use vst3::Steinberg::Vst::IAudioProcessorTrait;
651
652 if let Some(processor) = &self.audio_processor {
653 unsafe {
654 let _ = processor.setProcessing(0);
655 }
656 }
657 }
658
659 pub fn terminate(&mut self) -> Result<(), String> {
661 use vst3::Steinberg::IPluginBaseTrait;
662
663 let result = unsafe { self.component.terminate() };
664
665 if result != kResultOk {
666 return Err(format!(
667 "Failed to terminate component (result: {})",
668 result
669 ));
670 }
671
672 Ok(())
673 }
674}
675
676#[repr(C)]
677struct HostApplicationContext {
678 host: IHostApplication,
679 run_loop: HostRunLoopContext,
680 ref_count: AtomicU32,
681}
682
683impl HostApplicationContext {
684 fn new() -> Self {
685 Self {
686 host: IHostApplication {
687 vtbl: &HOST_APPLICATION_VTBL,
688 },
689 run_loop: HostRunLoopContext::new(),
690 ref_count: AtomicU32::new(1),
691 }
692 }
693}
694
695#[repr(C)]
696struct HostRunLoopContext {
697 iface: Linux::IRunLoop,
698 ref_count: AtomicU32,
699}
700
701impl HostRunLoopContext {
702 fn new() -> Self {
703 Self {
704 iface: Linux::IRunLoop {
705 vtbl: &HOST_RUN_LOOP_VTBL,
706 },
707 ref_count: AtomicU32::new(1),
708 }
709 }
710}
711
712#[repr(C)]
713struct HostComponentHandlerContext {
714 handler: IComponentHandler,
715 ref_count: AtomicU32,
716}
717
718impl HostComponentHandlerContext {
719 fn new() -> Self {
720 Self {
721 handler: IComponentHandler {
722 vtbl: &HOST_COMPONENT_HANDLER_VTBL,
723 },
724 ref_count: AtomicU32::new(1),
725 }
726 }
727}
728
729unsafe extern "system" fn component_handler_query_interface(
730 this: *mut FUnknown,
731 iid: *const TUID,
732 obj: *mut *mut c_void,
733) -> tresult {
734 if this.is_null() || iid.is_null() {
735 if !obj.is_null() {
736 unsafe { *obj = std::ptr::null_mut() };
737 }
738 return kNoInterface;
739 }
740
741 let iid_bytes = unsafe { &*iid };
742 let requested_handler = iid_bytes
743 .iter()
744 .zip(IComponentHandler::IID.iter())
745 .all(|(a, b)| (*a as u8) == *b);
746 let requested_unknown = iid_bytes
747 .iter()
748 .zip(FUnknown::IID.iter())
749 .all(|(a, b)| (*a as u8) == *b);
750 if !(requested_handler || requested_unknown) {
751 if !obj.is_null() {
752 unsafe { *obj = std::ptr::null_mut() };
753 }
754 return kNoInterface;
755 }
756
757 let ctx = this as *mut HostComponentHandlerContext;
758 unsafe {
759 (*ctx).ref_count.fetch_add(1, Ordering::Relaxed);
760 if !obj.is_null() {
761 *obj = this.cast::<c_void>();
762 }
763 }
764 kResultOk
765}
766
767unsafe extern "system" fn component_handler_add_ref(this: *mut FUnknown) -> uint32 {
768 if this.is_null() {
769 return 0;
770 }
771 let ctx = this as *mut HostComponentHandlerContext;
772 unsafe { (*ctx).ref_count.fetch_add(1, Ordering::Relaxed) + 1 }
773}
774
775unsafe extern "system" fn component_handler_release(this: *mut FUnknown) -> uint32 {
776 if this.is_null() {
777 return 0;
778 }
779 let ctx = this as *mut HostComponentHandlerContext;
780 unsafe { (*ctx).ref_count.fetch_sub(1, Ordering::Relaxed) - 1 }
781}
782
783unsafe extern "system" fn component_handler_begin_edit(
784 _this: *mut IComponentHandler,
785 _id: ParamID,
786) -> tresult {
787 kResultOk
788}
789
790unsafe extern "system" fn component_handler_perform_edit(
791 _this: *mut IComponentHandler,
792 _id: ParamID,
793 _value_normalized: ParamValue,
794) -> tresult {
795 kResultOk
796}
797
798unsafe extern "system" fn component_handler_end_edit(
799 _this: *mut IComponentHandler,
800 _id: ParamID,
801) -> tresult {
802 kResultOk
803}
804
805unsafe extern "system" fn component_handler_restart_component(
806 _this: *mut IComponentHandler,
807 _flags: i32,
808) -> tresult {
809 kResultOk
810}
811
812static HOST_COMPONENT_HANDLER_VTBL: IComponentHandlerVtbl = IComponentHandlerVtbl {
813 base: FUnknownVtbl {
814 queryInterface: component_handler_query_interface,
815 addRef: component_handler_add_ref,
816 release: component_handler_release,
817 },
818 beginEdit: component_handler_begin_edit,
819 performEdit: component_handler_perform_edit,
820 endEdit: component_handler_end_edit,
821 restartComponent: component_handler_restart_component,
822};
823
824unsafe extern "system" fn host_query_interface(
825 this: *mut FUnknown,
826 iid: *const TUID,
827 obj: *mut *mut c_void,
828) -> tresult {
829 if this.is_null() || iid.is_null() {
830 if !obj.is_null() {
831 unsafe {
833 *obj = std::ptr::null_mut();
834 }
835 }
836 return kNoInterface;
837 }
838
839 let iid_bytes = unsafe { &*iid };
840 let requested_host = iid_bytes
841 .iter()
842 .zip(IHostApplication::IID.iter())
843 .all(|(a, b)| (*a as u8) == *b);
844 let requested_unknown = iid_bytes
845 .iter()
846 .zip(FUnknown::IID.iter())
847 .all(|(a, b)| (*a as u8) == *b);
848 #[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "openbsd"))]
849 let requested_run_loop = iid_bytes
850 .iter()
851 .zip(Linux::IRunLoop::IID.iter())
852 .all(|(a, b)| (*a as u8) == *b);
853 #[cfg(not(any(target_os = "linux", target_os = "freebsd", target_os = "openbsd")))]
854 let requested_run_loop = false;
855 if !(requested_host || requested_unknown || requested_run_loop) {
856 if !obj.is_null() {
857 unsafe {
859 *obj = std::ptr::null_mut();
860 }
861 }
862 return kNoInterface;
863 }
864
865 let ctx = this as *mut HostApplicationContext;
866 if !ctx.is_null() {
867 unsafe {
868 if requested_run_loop {
869 (*ctx).run_loop.ref_count.fetch_add(1, Ordering::Relaxed);
870 } else {
871 (*ctx).ref_count.fetch_add(1, Ordering::Relaxed);
873 }
874 }
875 }
876
877 if !obj.is_null() {
878 unsafe {
879 if requested_run_loop {
880 *obj = (&mut (*ctx).run_loop.iface as *mut Linux::IRunLoop).cast::<c_void>();
881 } else {
882 *obj = this.cast::<c_void>();
884 }
885 }
886 }
887 kResultOk
888}
889
890unsafe extern "system" fn host_add_ref(this: *mut FUnknown) -> uint32 {
891 if this.is_null() {
892 return 0;
893 }
894 let ctx = this as *mut HostApplicationContext;
895 unsafe { (*ctx).ref_count.fetch_add(1, Ordering::Relaxed) + 1 }
897}
898
899unsafe extern "system" fn host_release(this: *mut FUnknown) -> uint32 {
900 if this.is_null() {
901 return 0;
902 }
903 let ctx = this as *mut HostApplicationContext;
904 unsafe { (*ctx).ref_count.fetch_sub(1, Ordering::Relaxed) - 1 }
906}
907
908unsafe extern "system" fn host_get_name(
909 _this: *mut IHostApplication,
910 name: *mut String128,
911) -> tresult {
912 if name.is_null() {
913 return kNoInterface;
914 }
915 let encoded: Vec<u16> = "Maolan".encode_utf16().collect();
916 unsafe {
918 (*name).fill(0);
919 for (idx, ch) in encoded
920 .iter()
921 .take((*name).len().saturating_sub(1))
922 .enumerate()
923 {
924 (*name)[idx] = *ch;
925 }
926 }
927 kResultOk
928}
929
930unsafe extern "system" fn host_create_instance(
931 _this: *mut IHostApplication,
932 cid: *mut TUID,
933 iid: *mut TUID,
934 obj: *mut *mut c_void,
935) -> tresult {
936 if obj.is_null() {
937 return kInvalidArgument;
938 }
939 unsafe {
941 *obj = std::ptr::null_mut();
942 }
943
944 let wants_message =
945 iid_ptr_matches(cid, &IMessage::IID) || iid_ptr_matches(iid, &IMessage::IID);
946 let wants_attributes =
947 iid_ptr_matches(cid, &IAttributeList::IID) || iid_ptr_matches(iid, &IAttributeList::IID);
948 if wants_message {
949 let message = Box::new(HostMessage::new());
950 let raw = Box::into_raw(message);
951 unsafe {
953 *obj = (&mut (*raw).iface as *mut IMessage).cast::<c_void>();
954 }
955 return kResultOk;
956 }
957
958 if wants_attributes {
959 let attrs = Box::new(HostAttributeList::new());
960 let raw = Box::into_raw(attrs);
961 unsafe {
963 *obj = (&mut (*raw).iface as *mut IAttributeList).cast::<c_void>();
964 }
965 return kResultOk;
966 }
967
968 kNotImplemented
969}
970
971static HOST_APPLICATION_VTBL: IHostApplicationVtbl = IHostApplicationVtbl {
972 base: FUnknownVtbl {
973 queryInterface: host_query_interface,
974 addRef: host_add_ref,
975 release: host_release,
976 },
977 getName: host_get_name,
978 createInstance: host_create_instance,
979};
980
981unsafe extern "system" fn run_loop_query_interface(
982 this: *mut FUnknown,
983 iid: *const TUID,
984 obj: *mut *mut c_void,
985) -> tresult {
986 if this.is_null() || iid.is_null() || obj.is_null() {
987 return kInvalidArgument;
988 }
989 let requested_run_loop = iid_ptr_matches(iid, &Linux::IRunLoop::IID);
990 let requested_unknown = iid_ptr_matches(iid, &FUnknown::IID);
991 if !(requested_run_loop || requested_unknown) {
992 unsafe { *obj = std::ptr::null_mut() };
993 return kNoInterface;
994 }
995 let ctx = this as *mut HostRunLoopContext;
996 unsafe {
997 (*ctx).ref_count.fetch_add(1, Ordering::Relaxed);
998 *obj = this.cast::<c_void>();
999 }
1000 kResultOk
1001}
1002
1003unsafe extern "system" fn run_loop_add_ref(this: *mut FUnknown) -> uint32 {
1004 if this.is_null() {
1005 return 0;
1006 }
1007 let ctx = this as *mut HostRunLoopContext;
1008 unsafe { (*ctx).ref_count.fetch_add(1, Ordering::Relaxed) + 1 }
1009}
1010
1011unsafe extern "system" fn run_loop_release(this: *mut FUnknown) -> uint32 {
1012 if this.is_null() {
1013 return 0;
1014 }
1015 let ctx = this as *mut HostRunLoopContext;
1016 unsafe { (*ctx).ref_count.fetch_sub(1, Ordering::Relaxed) - 1 }
1017}
1018
1019unsafe extern "system" fn run_loop_register_event_handler(
1020 _this: *mut Linux::IRunLoop,
1021 handler: *mut Linux::IEventHandler,
1022 fd: i32,
1023) -> tresult {
1024 if handler.is_null() {
1025 return kInvalidArgument;
1026 }
1027 let unknown = handler as *mut FUnknown;
1028 unsafe {
1029 let _ = ((*(*unknown).vtbl).addRef)(unknown);
1030 }
1031 let mut state = run_loop_state().lock().expect("run loop mutex poisoned");
1032 state.event_handlers.push(RunLoopEventHandler {
1033 handler: handler as usize,
1034 fd,
1035 });
1036 kResultOk
1037}
1038
1039unsafe extern "system" fn run_loop_unregister_event_handler(
1040 _this: *mut Linux::IRunLoop,
1041 handler: *mut Linux::IEventHandler,
1042) -> tresult {
1043 if handler.is_null() {
1044 return kInvalidArgument;
1045 }
1046 let mut state = run_loop_state().lock().expect("run loop mutex poisoned");
1047 state.event_handlers.retain(|h| {
1048 if h.handler == handler as usize {
1049 let unknown = handler as *mut FUnknown;
1050 unsafe {
1051 let _ = ((*(*unknown).vtbl).release)(unknown);
1052 }
1053 false
1054 } else {
1055 true
1056 }
1057 });
1058 kResultOk
1059}
1060
1061unsafe extern "system" fn run_loop_register_timer(
1062 _this: *mut Linux::IRunLoop,
1063 handler: *mut Linux::ITimerHandler,
1064 milliseconds: u64,
1065) -> tresult {
1066 if handler.is_null() {
1067 return kInvalidArgument;
1068 }
1069 let unknown = handler as *mut FUnknown;
1070 unsafe {
1071 let _ = ((*(*unknown).vtbl).addRef)(unknown);
1072 }
1073 let interval = Duration::from_millis(milliseconds.max(1));
1074 let mut state = run_loop_state().lock().expect("run loop mutex poisoned");
1075 state.timer_handlers.push(RunLoopTimerHandler {
1076 handler: handler as usize,
1077 interval,
1078 next_tick: Instant::now() + interval,
1079 });
1080 unsafe {
1081 ((*(*handler).vtbl).onTimer)(handler);
1083 }
1084 kResultOk
1085}
1086
1087unsafe extern "system" fn run_loop_unregister_timer(
1088 _this: *mut Linux::IRunLoop,
1089 handler: *mut Linux::ITimerHandler,
1090) -> tresult {
1091 if handler.is_null() {
1092 return kInvalidArgument;
1093 }
1094 let mut state = run_loop_state().lock().expect("run loop mutex poisoned");
1095 state.timer_handlers.retain(|t| {
1096 if t.handler == handler as usize {
1097 let unknown = handler as *mut FUnknown;
1098 unsafe {
1099 let _ = ((*(*unknown).vtbl).release)(unknown);
1100 }
1101 false
1102 } else {
1103 true
1104 }
1105 });
1106 kResultOk
1107}
1108
1109static HOST_RUN_LOOP_VTBL: Linux::IRunLoopVtbl = Linux::IRunLoopVtbl {
1110 base: FUnknownVtbl {
1111 queryInterface: run_loop_query_interface,
1112 addRef: run_loop_add_ref,
1113 release: run_loop_release,
1114 },
1115 registerEventHandler: run_loop_register_event_handler,
1116 unregisterEventHandler: run_loop_unregister_event_handler,
1117 registerTimer: run_loop_register_timer,
1118 unregisterTimer: run_loop_unregister_timer,
1119};
1120
1121#[repr(C)]
1122struct HostMessage {
1123 iface: IMessage,
1124 ref_count: AtomicU32,
1125 message_id: FIDString,
1126 attributes: *mut IAttributeList,
1127}
1128
1129impl HostMessage {
1130 fn new() -> Self {
1131 let attrs = Box::new(HostAttributeList::new());
1132 let attrs_raw = Box::into_raw(attrs);
1133 Self {
1134 iface: IMessage {
1135 vtbl: &HOST_MESSAGE_VTBL,
1136 },
1137 ref_count: AtomicU32::new(1),
1138 message_id: c"".as_ptr(),
1139 attributes: unsafe { &mut (*attrs_raw).iface as *mut IAttributeList },
1141 }
1142 }
1143}
1144
1145#[repr(C)]
1146struct HostAttributeList {
1147 iface: IAttributeList,
1148 ref_count: AtomicU32,
1149}
1150
1151impl HostAttributeList {
1152 fn new() -> Self {
1153 Self {
1154 iface: IAttributeList {
1155 vtbl: &HOST_ATTRIBUTE_LIST_VTBL,
1156 },
1157 ref_count: AtomicU32::new(1),
1158 }
1159 }
1160}
1161
1162fn iid_ptr_matches(iid_ptr: *const TUID, guid: &[u8; 16]) -> bool {
1163 if iid_ptr.is_null() {
1164 return false;
1165 }
1166 let iid = unsafe { &*iid_ptr };
1168 iid.iter()
1169 .zip(guid.iter())
1170 .all(|(lhs, rhs)| (*lhs as u8) == *rhs)
1171}
1172
1173unsafe extern "system" fn host_message_query_interface(
1174 this: *mut FUnknown,
1175 iid: *const TUID,
1176 obj: *mut *mut c_void,
1177) -> tresult {
1178 if this.is_null() || iid.is_null() || obj.is_null() {
1179 return kInvalidArgument;
1180 }
1181 let requested_message = iid_ptr_matches(iid, &IMessage::IID);
1182 let requested_unknown = iid_ptr_matches(iid, &FUnknown::IID);
1183 if !(requested_message || requested_unknown) {
1184 unsafe { *obj = std::ptr::null_mut() };
1186 return kNoInterface;
1187 }
1188 let msg = this as *mut HostMessage;
1189 unsafe {
1191 (*msg).ref_count.fetch_add(1, Ordering::Relaxed);
1192 *obj = this.cast::<c_void>();
1193 }
1194 kResultOk
1195}
1196
1197unsafe extern "system" fn host_message_add_ref(this: *mut FUnknown) -> uint32 {
1198 if this.is_null() {
1199 return 0;
1200 }
1201 let msg = this as *mut HostMessage;
1202 unsafe { (*msg).ref_count.fetch_add(1, Ordering::Relaxed) + 1 }
1204}
1205
1206unsafe extern "system" fn host_message_release(this: *mut FUnknown) -> uint32 {
1207 if this.is_null() {
1208 return 0;
1209 }
1210 let msg = this as *mut HostMessage;
1211 let remaining = unsafe { (*msg).ref_count.fetch_sub(1, Ordering::AcqRel) - 1 };
1213 if remaining == 0 {
1214 unsafe {
1216 if !(*msg).attributes.is_null() {
1217 let attrs_unknown = (*msg).attributes.cast::<FUnknown>();
1218 let _ = host_attr_release(attrs_unknown);
1219 (*msg).attributes = std::ptr::null_mut();
1220 }
1221 let _ = Box::from_raw(msg);
1222 }
1223 }
1224 remaining
1225}
1226
1227unsafe extern "system" fn host_message_get_id(this: *mut IMessage) -> FIDString {
1228 if this.is_null() {
1229 return c"".as_ptr();
1230 }
1231 let msg = this as *mut HostMessage;
1232 unsafe { (*msg).message_id }
1234}
1235
1236unsafe extern "system" fn host_message_set_id(this: *mut IMessage, id: FIDString) {
1237 if this.is_null() {
1238 return;
1239 }
1240 let msg = this as *mut HostMessage;
1241 unsafe {
1243 (*msg).message_id = if id.is_null() { c"".as_ptr() } else { id };
1244 }
1245}
1246
1247unsafe extern "system" fn host_message_get_attributes(this: *mut IMessage) -> *mut IAttributeList {
1248 if this.is_null() {
1249 return std::ptr::null_mut();
1250 }
1251 let msg = this as *mut HostMessage;
1252 unsafe {
1254 if !(*msg).attributes.is_null() {
1255 let attrs_unknown = (*msg).attributes.cast::<FUnknown>();
1256 let _ = host_attr_add_ref(attrs_unknown);
1257 }
1258 (*msg).attributes
1259 }
1260}
1261
1262static HOST_MESSAGE_VTBL: IMessageVtbl = IMessageVtbl {
1263 base: FUnknownVtbl {
1264 queryInterface: host_message_query_interface,
1265 addRef: host_message_add_ref,
1266 release: host_message_release,
1267 },
1268 getMessageID: host_message_get_id,
1269 setMessageID: host_message_set_id,
1270 getAttributes: host_message_get_attributes,
1271};
1272
1273unsafe extern "system" fn host_attr_query_interface(
1274 this: *mut FUnknown,
1275 iid: *const TUID,
1276 obj: *mut *mut c_void,
1277) -> tresult {
1278 if this.is_null() || iid.is_null() || obj.is_null() {
1279 return kInvalidArgument;
1280 }
1281 let requested_attr = iid_ptr_matches(iid, &IAttributeList::IID);
1282 let requested_unknown = iid_ptr_matches(iid, &FUnknown::IID);
1283 if !(requested_attr || requested_unknown) {
1284 unsafe { *obj = std::ptr::null_mut() };
1286 return kNoInterface;
1287 }
1288 let attrs = this as *mut HostAttributeList;
1289 unsafe {
1291 (*attrs).ref_count.fetch_add(1, Ordering::Relaxed);
1292 *obj = this.cast::<c_void>();
1293 }
1294 kResultOk
1295}
1296
1297unsafe extern "system" fn host_attr_add_ref(this: *mut FUnknown) -> uint32 {
1298 if this.is_null() {
1299 return 0;
1300 }
1301 let attrs = this as *mut HostAttributeList;
1302 unsafe { (*attrs).ref_count.fetch_add(1, Ordering::Relaxed) + 1 }
1304}
1305
1306unsafe extern "system" fn host_attr_release(this: *mut FUnknown) -> uint32 {
1307 if this.is_null() {
1308 return 0;
1309 }
1310 let attrs = this as *mut HostAttributeList;
1311 let remaining = unsafe { (*attrs).ref_count.fetch_sub(1, Ordering::AcqRel) - 1 };
1313 if remaining == 0 {
1314 unsafe {
1316 let _ = Box::from_raw(attrs);
1317 }
1318 }
1319 remaining
1320}
1321
1322unsafe extern "system" fn host_attr_set_int(
1323 _this: *mut IAttributeList,
1324 _id: IAttrID,
1325 _value: i64,
1326) -> tresult {
1327 kResultOk
1328}
1329
1330unsafe extern "system" fn host_attr_get_int(
1331 _this: *mut IAttributeList,
1332 _id: IAttrID,
1333 value: *mut i64,
1334) -> tresult {
1335 if value.is_null() {
1336 return kInvalidArgument;
1337 }
1338 unsafe { *value = 0 };
1340 kResultFalse
1341}
1342
1343unsafe extern "system" fn host_attr_set_float(
1344 _this: *mut IAttributeList,
1345 _id: IAttrID,
1346 _value: f64,
1347) -> tresult {
1348 kResultOk
1349}
1350
1351unsafe extern "system" fn host_attr_get_float(
1352 _this: *mut IAttributeList,
1353 _id: IAttrID,
1354 value: *mut f64,
1355) -> tresult {
1356 if value.is_null() {
1357 return kInvalidArgument;
1358 }
1359 unsafe { *value = 0.0 };
1361 kResultFalse
1362}
1363
1364unsafe extern "system" fn host_attr_set_string(
1365 _this: *mut IAttributeList,
1366 _id: IAttrID,
1367 _string: *const TChar,
1368) -> tresult {
1369 kResultOk
1370}
1371
1372unsafe extern "system" fn host_attr_get_string(
1373 _this: *mut IAttributeList,
1374 _id: IAttrID,
1375 string: *mut TChar,
1376 size_in_bytes: u32,
1377) -> tresult {
1378 if string.is_null() || size_in_bytes < std::mem::size_of::<TChar>() as u32 {
1379 return kInvalidArgument;
1380 }
1381 unsafe { *string = 0 };
1383 kResultFalse
1384}
1385
1386unsafe extern "system" fn host_attr_set_binary(
1387 _this: *mut IAttributeList,
1388 _id: IAttrID,
1389 _data: *const c_void,
1390 _size_in_bytes: u32,
1391) -> tresult {
1392 kResultOk
1393}
1394
1395unsafe extern "system" fn host_attr_get_binary(
1396 _this: *mut IAttributeList,
1397 _id: IAttrID,
1398 data: *mut *const c_void,
1399 size_in_bytes: *mut u32,
1400) -> tresult {
1401 if data.is_null() || size_in_bytes.is_null() {
1402 return kInvalidArgument;
1403 }
1404 unsafe {
1406 *data = std::ptr::null();
1407 *size_in_bytes = 0;
1408 }
1409 kResultFalse
1410}
1411
1412static HOST_ATTRIBUTE_LIST_VTBL: IAttributeListVtbl = IAttributeListVtbl {
1413 base: FUnknownVtbl {
1414 queryInterface: host_attr_query_interface,
1415 addRef: host_attr_add_ref,
1416 release: host_attr_release,
1417 },
1418 setInt: host_attr_set_int,
1419 getInt: host_attr_get_int,
1420 setFloat: host_attr_set_float,
1421 getFloat: host_attr_get_float,
1422 setString: host_attr_set_string,
1423 getString: host_attr_get_string,
1424 setBinary: host_attr_set_binary,
1425 getBinary: host_attr_get_binary,
1426};
1427
1428fn get_module_path(bundle_path: &Path) -> Result<std::path::PathBuf, String> {
1430 #[cfg(target_os = "macos")]
1431 {
1432 let module = bundle_path.join("Contents").join("MacOS").join(
1434 bundle_path
1435 .file_stem()
1436 .and_then(|s| s.to_str())
1437 .unwrap_or("plugin"),
1438 );
1439 if module.exists() {
1440 Ok(module)
1441 } else {
1442 Err(format!("VST3 module not found at {:?}", module))
1443 }
1444 }
1445
1446 #[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "openbsd"))]
1447 {
1448 let module = bundle_path
1450 .join("Contents")
1451 .join("x86_64-linux")
1452 .join(format!(
1453 "{}.so",
1454 bundle_path
1455 .file_stem()
1456 .and_then(|s| s.to_str())
1457 .unwrap_or("plugin")
1458 ));
1459 if module.exists() {
1460 Ok(module)
1461 } else {
1462 Err(format!("VST3 module not found at {:?}", module))
1463 }
1464 }
1465
1466 #[cfg(not(any(
1467 target_os = "macos",
1468 target_os = "linux",
1469 target_os = "freebsd",
1470 target_os = "openbsd"
1471 )))]
1472 {
1473 Err("Unsupported platform".to_string())
1474 }
1475}
1476
1477fn extract_cstring(bytes: &[i8]) -> String {
1478 let len = bytes.iter().position(|&c| c == 0).unwrap_or(bytes.len());
1479 let u8_bytes: Vec<u8> = bytes[..len].iter().map(|&b| b as u8).collect();
1480 String::from_utf8_lossy(&u8_bytes).to_string()
1481}
1482
1483#[cfg(test)]
1484mod tests {
1485 use super::*;
1486 use std::fs::{self, File};
1487 use std::path::PathBuf;
1488 use std::time::{SystemTime, UNIX_EPOCH};
1489
1490 fn unique_temp_dir(name: &str) -> PathBuf {
1491 let nanos = SystemTime::now()
1492 .duration_since(UNIX_EPOCH)
1493 .expect("clock before epoch")
1494 .as_nanos();
1495 std::env::temp_dir().join(format!(
1496 "maolan-engine-{name}-{}-{nanos}",
1497 std::process::id()
1498 ))
1499 }
1500
1501 #[test]
1502 fn iid_ptr_matches_rejects_null_and_non_matching_guids() {
1503 let iid: TUID = [1; 16];
1504 let matching_guid = [1_u8; 16];
1505 let different_guid = [2_u8; 16];
1506
1507 assert!(!iid_ptr_matches(std::ptr::null(), &matching_guid));
1508 assert!(iid_ptr_matches(&iid, &matching_guid));
1509 assert!(!iid_ptr_matches(&iid, &different_guid));
1510 }
1511
1512 #[test]
1513 fn extract_cstring_stops_at_nul_and_uses_lossy_utf8() {
1514 let bytes = [b'A' as i8, b'B' as i8, -1, 0, b'Z' as i8];
1515
1516 assert_eq!(extract_cstring(&bytes), "AB\u{FFFD}");
1517 }
1518
1519 #[test]
1520 fn extract_cstring_uses_full_slice_when_not_nul_terminated() {
1521 let bytes = [b'X' as i8, b'Y' as i8, b'Z' as i8];
1522
1523 assert_eq!(extract_cstring(&bytes), "XYZ");
1524 }
1525
1526 #[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "openbsd"))]
1527 #[test]
1528 fn get_module_path_returns_unix_shared_object_path() {
1529 let bundle_path = unique_temp_dir("vst3-module").join("Example.vst3");
1530 let module_path = bundle_path
1531 .join("Contents")
1532 .join("x86_64-linux")
1533 .join("Example.so");
1534 fs::create_dir_all(module_path.parent().expect("module parent"))
1535 .expect("create module directory");
1536 File::create(&module_path).expect("create module file");
1537
1538 let resolved = get_module_path(&bundle_path).expect("resolve module path");
1539
1540 assert_eq!(resolved, module_path);
1541
1542 let _ = fs::remove_dir_all(bundle_path.parent().expect("bundle parent").to_path_buf());
1543 }
1544
1545 #[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "openbsd"))]
1546 #[test]
1547 fn get_module_path_errors_when_unix_module_is_missing() {
1548 let bundle_path = unique_temp_dir("missing-vst3").join("Missing.vst3");
1549 fs::create_dir_all(&bundle_path).expect("create bundle directory");
1550
1551 let err = get_module_path(&bundle_path).expect_err("missing module should error");
1552
1553 assert!(err.contains("Missing.so"));
1554
1555 let _ = fs::remove_dir_all(bundle_path.parent().expect("bundle parent").to_path_buf());
1556 }
1557}