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