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