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