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