1use super::interfaces::{HostPlugFrame, PluginInstance, Vst3GuiInfo, protected_call};
2use super::midi::{EventBuffer, ParameterChanges};
3use super::port::{BusInfo, ParameterInfo};
4use super::state::{MemoryStream, Vst3PluginState, ibstream_ptr};
5use crate::audio::io::AudioIO;
6use crate::midi::io::MidiEvent;
7use std::ffi::{CString, c_void};
8use std::fmt;
9use std::path::Path;
10use std::sync::atomic::Ordering;
11use std::sync::{Arc, Mutex};
12use vst3::ComPtr;
13use vst3::ComWrapper;
14use vst3::Steinberg::Vst::ProcessModes_::kRealtime;
15use vst3::Steinberg::Vst::SymbolicSampleSizes_::kSample32;
16use vst3::Steinberg::Vst::{IEditControllerTrait, ViewType};
17use vst3::Steinberg::{FIDString, IPlugFrame, IPlugView, IPlugViewTrait, ViewRect, kResultOk};
18
19pub struct Vst3Processor {
20 path: String,
22 name: String,
23 plugin_id: String,
24
25 instance: PluginInstance,
27 _factory: super::interfaces::PluginFactory,
29
30 audio_inputs: Vec<Arc<AudioIO>>,
32 audio_outputs: Vec<Arc<AudioIO>>,
33 midi_input_ports: usize,
34 midi_output_ports: usize,
35 main_audio_inputs: usize,
36 main_audio_outputs: usize,
37 input_buses: Vec<BusInfo>,
38 output_buses: Vec<BusInfo>,
39
40 parameters: Vec<ParameterInfo>,
42 scalar_values: Arc<Mutex<Vec<f32>>>,
43 previous_values: Arc<Mutex<Vec<f32>>>,
44 max_samples_per_block: usize,
45 processing_started: bool,
46 sample_rate: f64,
47
48 gui_session: Arc<Mutex<Vst3GuiSession>>,
50}
51
52struct Vst3GuiSession {
53 view: Option<ComPtr<IPlugView>>,
54 plug_frame: Option<ComWrapper<HostPlugFrame>>,
55 ui_should_close: bool,
56 platform_type: Option<String>,
57}
58
59impl fmt::Debug for Vst3Processor {
60 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
61 f.debug_struct("Vst3Processor")
62 .field("path", &self.path)
63 .field("name", &self.name)
64 .field("plugin_id", &self.plugin_id)
65 .field("audio_inputs", &self.audio_inputs.len())
66 .field("audio_outputs", &self.audio_outputs.len())
67 .field("midi_input_ports", &self.midi_input_ports)
68 .field("midi_output_ports", &self.midi_output_ports)
69 .field("main_audio_inputs", &self.main_audio_inputs)
70 .field("main_audio_outputs", &self.main_audio_outputs)
71 .field("input_buses", &self.input_buses)
72 .field("output_buses", &self.output_buses)
73 .field("parameters", &self.parameters)
74 .field("max_samples_per_block", &self.max_samples_per_block)
75 .field("processing_started", &self.processing_started)
76 .finish()
77 }
78}
79
80impl Vst3Processor {
81 pub fn new_with_sample_rate(
83 sample_rate: f64,
84 buffer_size: usize,
85 plugin_path: &str,
86 audio_inputs: usize,
87 audio_outputs: usize,
88 ) -> Result<Self, String> {
89 let path_buf = Path::new(plugin_path);
90 let name = path_buf
91 .file_stem()
92 .or_else(|| path_buf.file_name())
93 .and_then(|s| s.to_str())
94 .unwrap_or("Unknown VST3")
95 .to_string();
96
97 let factory = super::interfaces::PluginFactory::from_module(path_buf)?;
99
100 let class_count = factory.count_classes();
101 if class_count == 0 {
102 return Err("No plugin classes found".to_string());
103 }
104
105 let mut class_info = None;
107 for i in 0..class_count {
108 if let Some(info) = factory.get_class_info(i)
109 && info.category.contains("Audio Module")
110 {
111 class_info = Some(info);
112 break;
113 }
114 }
115 let class_info = class_info
117 .or_else(|| factory.get_class_info(0))
118 .ok_or("Failed to get class info")?;
119
120 let mut instance = factory.create_instance(&class_info.cid)?;
121
122 instance.initialize(&factory)?;
124
125 let (plugin_input_buses, plugin_output_buses) = instance.audio_bus_counts();
126 let (plugin_main_in_channels, plugin_main_out_channels) =
127 instance.main_audio_channel_counts();
128 let (midi_input_ports, midi_output_ports) = instance.event_bus_counts();
129
130 let requested_inputs = if plugin_input_buses > 0 {
131 audio_inputs
132 .max(1)
133 .min(plugin_main_in_channels.max(1))
134 .min(i32::MAX as usize)
135 } else {
136 0
137 };
138 let requested_outputs = if plugin_output_buses > 0 {
139 audio_outputs
140 .max(1)
141 .min(plugin_main_out_channels.max(1))
142 .min(i32::MAX as usize)
143 } else {
144 0
145 };
146 let input_buses = if plugin_input_buses > 0 {
148 vec![BusInfo {
149 index: 0,
150 name: "Input".to_string(),
151 channel_count: requested_inputs.max(1),
152 is_active: true,
153 }]
154 } else {
155 vec![]
156 };
157
158 let output_buses = if plugin_output_buses > 0 {
159 vec![BusInfo {
160 index: 0,
161 name: "Output".to_string(),
162 channel_count: requested_outputs.max(1),
163 is_active: true,
164 }]
165 } else {
166 vec![]
167 };
168
169 let mut audio_input_ios = Vec::new();
171 for _ in 0..requested_inputs {
172 audio_input_ios.push(Arc::new(AudioIO::new(buffer_size)));
173 }
174
175 let mut audio_output_ios = Vec::new();
176 for _ in 0..requested_outputs {
177 audio_output_ios.push(Arc::new(AudioIO::new(buffer_size)));
178 }
179
180 instance.setup_processing(
183 sample_rate,
184 buffer_size as i32,
185 requested_inputs as i32,
186 requested_outputs as i32,
187 )?;
188 instance.set_active(true)?;
189
190 let processing_started = false;
191
192 let parameters = protected_call(|| instance.query_parameters()).unwrap_or_default();
195 let scalar_values = Arc::new(Mutex::new(
196 parameters.iter().map(|p| p.default_value as f32).collect(),
197 ));
198 let previous_values = Arc::new(Mutex::new(
199 parameters.iter().map(|p| p.default_value as f32).collect(),
200 ));
201 let plugin_id = format!("{:02X?}", class_info.cid);
202
203 let gui_session = Arc::new(Mutex::new(Vst3GuiSession {
204 view: None,
205 plug_frame: None,
206 ui_should_close: false,
207 platform_type: None,
208 }));
209
210 Ok(Self {
211 path: plugin_path.to_string(),
212 name,
213 plugin_id,
214 instance,
215 _factory: factory,
216 audio_inputs: audio_input_ios,
217 audio_outputs: audio_output_ios,
218 midi_input_ports,
219 midi_output_ports,
220 main_audio_inputs: requested_inputs,
221 main_audio_outputs: requested_outputs,
222 input_buses,
223 output_buses,
224 parameters,
225 scalar_values,
226 previous_values,
227 max_samples_per_block: buffer_size,
228 processing_started,
229 sample_rate,
230 gui_session,
231 })
232 }
233
234 pub fn path(&self) -> &str {
235 &self.path
236 }
237
238 pub fn name(&self) -> &str {
239 &self.name
240 }
241
242 pub fn plugin_id(&self) -> &str {
243 &self.plugin_id
244 }
245
246 pub fn audio_inputs(&self) -> &[Arc<AudioIO>] {
247 &self.audio_inputs
248 }
249
250 pub fn audio_outputs(&self) -> &[Arc<AudioIO>] {
251 &self.audio_outputs
252 }
253
254 pub fn main_audio_input_count(&self) -> usize {
255 self.main_audio_inputs
256 }
257
258 pub fn main_audio_output_count(&self) -> usize {
259 self.main_audio_outputs
260 }
261
262 pub fn midi_input_count(&self) -> usize {
263 self.midi_input_ports
264 }
265
266 pub fn midi_output_count(&self) -> usize {
267 self.midi_output_ports
268 }
269
270 pub fn setup_audio_ports(&self) {
271 for port in &self.audio_inputs {
272 port.setup();
273 }
274 for port in &self.audio_outputs {
275 port.setup();
276 }
277 }
278
279 pub fn process_with_audio_io(&self, frames: usize) {
280 for input in &self.audio_inputs {
282 input.process();
283 }
284
285 let processor = match &self.instance.audio_processor {
287 Some(proc) => proc,
288 None => {
289 self.process_silence();
290 return;
291 }
292 };
293
294 if self.process_vst3(processor, frames, &[]).is_err() {
296 self.process_silence();
297 }
298 }
299
300 pub fn process_with_midi(&self, frames: usize, input_events: &[MidiEvent]) -> Vec<MidiEvent> {
302 for input in &self.audio_inputs {
304 input.process();
305 }
306
307 let processor = match &self.instance.audio_processor {
309 Some(proc) => proc,
310 None => {
311 self.process_silence();
312 return Vec::new();
313 }
314 };
315
316 match self.process_vst3(processor, frames, input_events) {
318 Ok(output_buffer) => {
319 output_buffer.to_midi_events()
321 }
322 Err(_) => {
323 self.process_silence();
324 Vec::new()
325 }
326 }
327 }
328
329 fn process_vst3(
330 &self,
331 processor: &vst3::ComPtr<vst3::Steinberg::Vst::IAudioProcessor>,
332 frames: usize,
333 input_events: &[MidiEvent],
334 ) -> Result<EventBuffer, String> {
335 use vst3::Steinberg::Vst::IAudioProcessorTrait;
336 use vst3::Steinberg::Vst::*;
337
338 let input_guards: Vec<_> = self
340 .audio_inputs
341 .iter()
342 .map(|io| io.buffer.lock())
343 .collect();
344 let output_guards: Vec<_> = self
345 .audio_outputs
346 .iter()
347 .map(|io| io.buffer.lock())
348 .collect();
349
350 let mut input_channel_ptrs: Vec<*mut f32> = input_guards
351 .iter()
352 .map(|buf| buf.as_ptr() as *mut f32)
353 .collect();
354 let mut output_channel_ptrs: Vec<*mut f32> = output_guards
355 .iter()
356 .map(|buf| buf.as_ptr() as *mut f32)
357 .collect();
358
359 let max_input_frames = input_guards
360 .iter()
361 .map(|buf| buf.len())
362 .min()
363 .unwrap_or(frames);
364 let max_output_frames = output_guards
365 .iter()
366 .map(|buf| buf.len())
367 .min()
368 .unwrap_or(frames);
369 let num_frames = frames.min(max_input_frames).min(max_output_frames);
370 if num_frames == 0 {
371 return Ok(EventBuffer::new());
372 }
373
374 let mut input_buses = Vec::new();
375 if !self.input_buses.is_empty() && !input_channel_ptrs.is_empty() {
376 input_buses.push(AudioBusBuffers {
377 numChannels: input_channel_ptrs.len() as i32,
378 silenceFlags: 0,
379 __field0: AudioBusBuffers__type0 {
380 channelBuffers32: input_channel_ptrs.as_mut_ptr(),
381 },
382 });
383 }
384
385 let mut output_buses = Vec::new();
386 if !self.output_buses.is_empty() && !output_channel_ptrs.is_empty() {
387 output_buses.push(AudioBusBuffers {
388 numChannels: output_channel_ptrs.len() as i32,
389 silenceFlags: 0,
390 __field0: AudioBusBuffers__type0 {
391 channelBuffers32: output_channel_ptrs.as_mut_ptr(),
392 },
393 });
394 }
395
396 let mut process_context: ProcessContext = unsafe { std::mem::zeroed() };
398 process_context.sampleRate = self.sample_rate;
399 process_context.tempo = 120.0;
400 process_context.timeSigNumerator = 4;
401 process_context.timeSigDenominator = 4;
402 process_context.state = ProcessContext_::StatesAndFlags_::kPlaying
403 | ProcessContext_::StatesAndFlags_::kTempoValid
404 | ProcessContext_::StatesAndFlags_::kTimeSigValid
405 | ProcessContext_::StatesAndFlags_::kContTimeValid
406 | ProcessContext_::StatesAndFlags_::kSystemTimeValid;
407 let input_event_list = if self.midi_input_ports > 0 {
408 Some(ComWrapper::new(EventBuffer::from_midi_events(
409 input_events,
410 0,
411 )))
412 } else {
413 None
414 };
415 let midi_mapping = self
416 .instance
417 .edit_controller
418 .as_ref()
419 .and_then(|controller| controller.cast::<IMidiMapping>());
420 let input_parameter_changes = midi_mapping.as_ref().and_then(|mapping| {
421 ParameterChanges::from_midi_events(input_events, mapping, 0).map(ComWrapper::new)
422 });
423 let output_event_list = if self.midi_output_ports > 0 {
424 Some(ComWrapper::new(EventBuffer::new()))
425 } else {
426 None
427 };
428 let mut process_data = ProcessData {
429 processMode: kRealtime as i32,
430 symbolicSampleSize: kSample32 as i32,
431 numSamples: num_frames as i32,
432 numInputs: input_buses.len() as i32,
433 numOutputs: output_buses.len() as i32,
434 inputs: if input_buses.is_empty() {
435 std::ptr::null_mut()
436 } else {
437 input_buses.as_mut_ptr()
438 },
439 outputs: if output_buses.is_empty() {
440 std::ptr::null_mut()
441 } else {
442 output_buses.as_mut_ptr()
443 },
444 inputParameterChanges: input_parameter_changes
445 .as_ref()
446 .map(ParameterChanges::changes_ptr)
447 .unwrap_or(std::ptr::null_mut()),
448 outputParameterChanges: std::ptr::null_mut(),
449 inputEvents: input_event_list
450 .as_ref()
451 .map(EventBuffer::event_list_ptr)
452 .unwrap_or(std::ptr::null_mut()),
453 outputEvents: output_event_list
454 .as_ref()
455 .map(EventBuffer::event_list_ptr)
456 .unwrap_or(std::ptr::null_mut()),
457 processContext: &mut process_context,
458 };
459
460 let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| unsafe {
462 processor.process(&mut process_data)
463 }));
464
465 match result {
466 Ok(vst3::Steinberg::kResultOk) => {}
467 Ok(err_code) => {
468 return Err(format!("VST3 process failed with result: {}", err_code));
469 }
470 Err(_) => {
471 return Err("VST3 process panicked".to_string());
472 }
473 }
474
475 for output in &self.audio_outputs {
477 *output.finished.lock() = true;
478 }
479
480 Ok(output_event_list
481 .as_ref()
482 .map(|events| EventBuffer::from_midi_events(&events.to_midi_events(), 0))
483 .unwrap_or_default())
484 }
485
486 fn process_silence(&self) {
487 for output in &self.audio_outputs {
488 let out_buf = output.buffer.lock();
489 out_buf.fill(0.0);
490 *output.finished.lock() = true;
491 }
492 }
493
494 pub fn parameters(&self) -> &[ParameterInfo] {
495 &self.parameters
496 }
497
498 pub fn get_parameter_value(&self, param_id: u32) -> Option<f32> {
499 let idx = self.parameters.iter().position(|p| p.id == param_id)?;
500 Some(self.scalar_values.lock().unwrap()[idx])
501 }
502
503 pub fn set_parameter_value(&self, param_id: u32, normalized_value: f32) -> Result<(), String> {
504 let idx = self
505 .parameters
506 .iter()
507 .position(|p| p.id == param_id)
508 .ok_or("Parameter not found")?;
509
510 self.scalar_values.lock().unwrap()[idx] = normalized_value;
511
512 if let Some(controller) = &self.instance.edit_controller {
514 use vst3::Steinberg::Vst::IEditControllerTrait;
515 unsafe {
516 controller.setParamNormalized(param_id, normalized_value as f64);
517 }
518 }
519
520 Ok(())
521 }
522
523 pub fn snapshot_state(&self) -> Result<Vst3PluginState, String> {
525 use vst3::Steinberg::Vst::{IComponentTrait, IEditControllerTrait};
526
527 let instance = &self.instance;
528
529 let comp_stream = vst3::ComWrapper::new(MemoryStream::new());
531 unsafe {
532 let result = instance
533 .component
534 .getState(ibstream_ptr(&comp_stream) as *mut _);
535 if result != vst3::Steinberg::kResultOk {
536 return Err("Failed to get component state".to_string());
537 }
538 }
539
540 let ctrl_stream = vst3::ComWrapper::new(MemoryStream::new());
542 if let Some(controller) = &instance.edit_controller {
543 unsafe {
544 controller.getState(ibstream_ptr(&ctrl_stream) as *mut _);
545 }
546 }
547
548 Ok(Vst3PluginState {
549 plugin_id: self.plugin_id.clone(),
550 component_state: comp_stream.bytes(),
551 controller_state: ctrl_stream.bytes(),
552 })
553 }
554
555 pub fn restore_state(&self, state: &Vst3PluginState) -> Result<(), String> {
557 use vst3::Steinberg::Vst::{IComponentTrait, IEditControllerTrait};
558
559 if state.plugin_id != self.plugin_id {
560 return Err(format!(
561 "Plugin ID mismatch: expected '{}', got '{}'",
562 self.plugin_id, state.plugin_id
563 ));
564 }
565
566 let instance = &self.instance;
567
568 if !state.component_state.is_empty() {
570 let comp_stream =
571 vst3::ComWrapper::new(MemoryStream::from_bytes(&state.component_state));
572 unsafe {
573 let result = instance
574 .component
575 .setState(ibstream_ptr(&comp_stream) as *mut _);
576 if result != vst3::Steinberg::kResultOk {
577 return Err("Failed to set component state".to_string());
578 }
579 }
580 }
581
582 if !state.controller_state.is_empty()
584 && let Some(controller) = &instance.edit_controller
585 {
586 let ctrl_stream =
587 vst3::ComWrapper::new(MemoryStream::from_bytes(&state.controller_state));
588 unsafe {
589 controller.setState(ibstream_ptr(&ctrl_stream) as *mut _);
590 }
591
592 for (idx, param) in self.parameters.iter().enumerate() {
594 let value = unsafe { controller.getParamNormalized(param.id) };
595 self.scalar_values.lock().unwrap()[idx] = value as f32;
596 self.previous_values.lock().unwrap()[idx] = value as f32;
597 }
598 }
599
600 Ok(())
601 }
602
603 pub fn gui_info(&self) -> Result<Vst3GuiInfo, String> {
608 let controller = self
609 .instance
610 .edit_controller
611 .as_ref()
612 .ok_or("No edit controller available")?;
613 let view = unsafe { controller.createView(ViewType::kEditor) };
614 if view.is_null() {
615 return Ok(Vst3GuiInfo {
616 has_gui: false,
617 size: None,
618 });
619 }
620 unsafe {
622 let _ = ComPtr::<IPlugView>::from_raw(view);
623 }
624 Ok(Vst3GuiInfo {
625 has_gui: true,
626 size: None,
627 })
628 }
629
630 pub fn gui_create(&self, platform_type: &str) -> Result<(), String> {
631 let mut session = self.gui_session.lock().unwrap();
632 if session.view.is_some() {
633 return Ok(());
634 }
635 let controller = self
636 .instance
637 .edit_controller
638 .as_ref()
639 .ok_or("No edit controller available")?;
640 let view = unsafe { controller.createView(ViewType::kEditor) };
641 if view.is_null() {
642 return Err("Plugin does not provide an editor view".to_string());
643 }
644 let view =
645 unsafe { ComPtr::<IPlugView>::from_raw(view) }.ok_or("Failed to wrap IPlugView")?;
646
647 let platform_cstr =
648 CString::new(platform_type).map_err(|e| format!("Invalid platform type: {e}"))?;
649 let supported =
650 unsafe { view.isPlatformTypeSupported(platform_cstr.as_ptr() as FIDString) };
651 if supported != kResultOk {
652 return Err(format!("Platform type '{}' not supported", platform_type));
653 }
654
655 session.view = Some(view);
656 session.platform_type = Some(platform_type.to_string());
657 Ok(())
658 }
659
660 pub fn gui_get_size(&self) -> Result<(i32, i32), String> {
661 let session = self.gui_session.lock().unwrap();
662 let view = session.view.as_ref().ok_or("No GUI view created")?;
663 let mut rect = ViewRect {
664 left: 0,
665 top: 0,
666 right: 0,
667 bottom: 0,
668 };
669 let result = unsafe { view.getSize(&mut rect) };
670 if result != kResultOk {
671 return Err("Failed to get GUI size".to_string());
672 }
673 Ok((rect.right - rect.left, rect.bottom - rect.top))
674 }
675
676 pub fn gui_set_parent(&self, window: usize, platform_type: &str) -> Result<(), String> {
677 let mut session = self.gui_session.lock().unwrap();
678 let view = session.view.as_ref().ok_or("No GUI view created")?;
679
680 let plug_frame = ComWrapper::new(HostPlugFrame::new());
681 if let Some(frame_ptr) = plug_frame.to_com_ptr::<IPlugFrame>() {
682 unsafe {
683 let _ = view.setFrame(frame_ptr.into_raw());
684 }
685 }
686
687 let platform_cstr =
688 CString::new(platform_type).map_err(|e| format!("Invalid platform type: {e}"))?;
689 let result =
690 unsafe { view.attached(window as *mut c_void, platform_cstr.as_ptr() as FIDString) };
691 if result != kResultOk {
692 return Err(format!("Failed to attach GUI view: {:#x}", result));
693 }
694
695 session.plug_frame = Some(plug_frame);
696 Ok(())
697 }
698
699 pub fn gui_on_size(&self, width: i32, height: i32) -> Result<(), String> {
700 let session = self.gui_session.lock().unwrap();
701 let view = session.view.as_ref().ok_or("No GUI view created")?;
702 let mut rect = ViewRect {
703 left: 0,
704 top: 0,
705 right: width,
706 bottom: height,
707 };
708 unsafe {
709 let _ = view.onSize(&mut rect);
710 }
711 Ok(())
712 }
713
714 pub fn gui_show(&self) -> Result<(), String> {
715 let session = self.gui_session.lock().unwrap();
716 let view = session.view.as_ref().ok_or("No GUI view created")?;
717 unsafe {
718 let _ = view.onFocus(1);
719 }
720 Ok(())
721 }
722
723 pub fn gui_hide(&self) {
724 if let Ok(session) = self.gui_session.lock()
725 && let Some(view) = session.view.as_ref()
726 {
727 unsafe {
728 let _ = view.onFocus(0);
729 }
730 }
731 }
732
733 pub fn gui_destroy(&self) {
734 if let Ok(mut session) = self.gui_session.lock() {
735 if let Some(view) = session.view.take() {
736 unsafe {
737 let _ = view.setFrame(std::ptr::null_mut());
738 let _ = view.removed();
739 }
740 }
741 session.plug_frame.take();
742 session.platform_type.take();
743 }
744 }
745
746 pub fn ui_begin_session(&self) {
747 if let Ok(mut session) = self.gui_session.lock() {
748 session.ui_should_close = false;
749 }
750 }
751
752 pub fn ui_end_session(&self) {
753 if let Ok(mut session) = self.gui_session.lock() {
754 session.ui_should_close = false;
755 }
756 }
757
758 pub fn ui_should_close(&self) -> bool {
759 if let Ok(session) = self.gui_session.lock() {
760 return session.ui_should_close;
761 }
762 false
763 }
764
765 pub fn ui_take_param_updates(&self) -> Vec<(u32, f64)> {
766 let mut changes = Vec::new();
767 if let Ok(mut param_changes) = self.instance.parameter_changes.lock() {
768 std::mem::swap(&mut changes, &mut *param_changes);
769 }
770 changes
771 }
772
773 pub fn gui_check_resize(&self) -> Option<(i32, i32)> {
774 if let Ok(session) = self.gui_session.lock()
775 && let Some(ref frame) = session.plug_frame
776 && frame.resize_requested.swap(false, Ordering::Relaxed)
777 && let Ok(size) = frame.requested_size.lock()
778 {
779 return *size;
780 }
781 None
782 }
783
784 pub fn gui_on_main_thread(&self) {
785 super::interfaces::pump_host_run_loop();
786 }
787}
788
789impl Drop for Vst3Processor {
790 fn drop(&mut self) {
791 if self.processing_started {
792 self.instance.stop_processing();
793 }
794 let _ = self.instance.set_active(false);
795 let _ = self.instance.terminate();
796 }
797}
798
799pub fn list_plugins() -> Vec<super::host::Vst3PluginInfo> {
801 super::host::list_plugins()
802}