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
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    /// Create a new VST3 processor with explicit sample rate
80    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    /// Process audio with MIDI events
317    #[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    /// Snapshot the current plugin state for saving
538    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    /// Restore plugin state from a snapshot
568    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}