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