Skip to main content

maolan_engine/plugins/vst3/
processor.rs

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/// Transport info for VST3 processing.
22#[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    /// Create a new VST3 processor with explicit sample rate
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(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    /// Process audio with MIDI events
329    #[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    /// Snapshot the current plugin state for saving
563    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    /// Restore plugin state from a snapshot
593    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}