maolan_engine/plugins/vst3/
processor.rs1use super::interfaces::PluginInstance;
2use super::midi::{EventBuffer, ParameterChanges};
3use super::port::{BusInfo, ParameterInfo};
4use super::state::{MemoryStream, Vst3PluginState, ibstream_ptr};
5use crate::audio::io::AudioIO;
6use crate::midi::io::MidiEvent;
7use std::fmt;
8use std::path::Path;
9use std::sync::{Arc, Mutex};
10use vst3::ComWrapper;
11use vst3::Steinberg::Vst::ProcessModes_::kRealtime;
12use vst3::Steinberg::Vst::SymbolicSampleSizes_::kSample32;
13
14pub struct Vst3Processor {
15 path: String,
17 name: String,
18 plugin_id: String,
19
20 instance: PluginInstance,
22 _factory: super::interfaces::PluginFactory,
24
25 audio_inputs: Vec<Arc<AudioIO>>,
27 audio_outputs: Vec<Arc<AudioIO>>,
28 midi_input_ports: usize,
29 midi_output_ports: usize,
30 main_audio_inputs: usize,
31 main_audio_outputs: usize,
32 input_buses: Vec<BusInfo>,
33 output_buses: Vec<BusInfo>,
34
35 parameters: Vec<ParameterInfo>,
37 scalar_values: Arc<Mutex<Vec<f32>>>,
38 previous_values: Arc<Mutex<Vec<f32>>>,
39 max_samples_per_block: usize,
40 processing_started: bool,
41}
42
43impl fmt::Debug for Vst3Processor {
44 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
45 f.debug_struct("Vst3Processor")
46 .field("path", &self.path)
47 .field("name", &self.name)
48 .field("plugin_id", &self.plugin_id)
49 .field("audio_inputs", &self.audio_inputs.len())
50 .field("audio_outputs", &self.audio_outputs.len())
51 .field("midi_input_ports", &self.midi_input_ports)
52 .field("midi_output_ports", &self.midi_output_ports)
53 .field("main_audio_inputs", &self.main_audio_inputs)
54 .field("main_audio_outputs", &self.main_audio_outputs)
55 .field("input_buses", &self.input_buses)
56 .field("output_buses", &self.output_buses)
57 .field("parameters", &self.parameters)
58 .field("max_samples_per_block", &self.max_samples_per_block)
59 .field("processing_started", &self.processing_started)
60 .finish()
61 }
62}
63
64impl Vst3Processor {
65 pub fn new_with_sample_rate(
67 sample_rate: f64,
68 buffer_size: usize,
69 plugin_path: &str,
70 audio_inputs: usize,
71 audio_outputs: usize,
72 ) -> Result<Self, String> {
73 let path_buf = Path::new(plugin_path);
74 let name = path_buf
75 .file_stem()
76 .or_else(|| path_buf.file_name())
77 .and_then(|s| s.to_str())
78 .unwrap_or("Unknown VST3")
79 .to_string();
80
81 let factory = super::interfaces::PluginFactory::from_module(path_buf)?;
83
84 let class_count = factory.count_classes();
85 if class_count == 0 {
86 return Err("No plugin classes found".to_string());
87 }
88
89 let class_info = factory
91 .get_class_info(0)
92 .ok_or("Failed to get class info")?;
93
94 let mut instance = factory.create_instance(&class_info.cid)?;
95
96 instance.initialize(&factory)?;
98
99 let (plugin_input_buses, plugin_output_buses) = instance.audio_bus_counts();
100 let (plugin_main_in_channels, plugin_main_out_channels) =
101 instance.main_audio_channel_counts();
102 let (midi_input_ports, midi_output_ports) = instance.event_bus_counts();
103
104 let requested_inputs = if plugin_input_buses > 0 {
105 audio_inputs
106 .max(1)
107 .min(plugin_main_in_channels.max(1))
108 .min(i32::MAX as usize)
109 } else {
110 0
111 };
112 let requested_outputs = if plugin_output_buses > 0 {
113 audio_outputs
114 .max(1)
115 .min(plugin_main_out_channels.max(1))
116 .min(i32::MAX as usize)
117 } else {
118 0
119 };
120 let input_buses = if plugin_input_buses > 0 {
122 vec![BusInfo {
123 index: 0,
124 name: "Input".to_string(),
125 channel_count: requested_inputs.max(1),
126 is_active: true,
127 }]
128 } else {
129 vec![]
130 };
131
132 let output_buses = if plugin_output_buses > 0 {
133 vec![BusInfo {
134 index: 0,
135 name: "Output".to_string(),
136 channel_count: requested_outputs.max(1),
137 is_active: true,
138 }]
139 } else {
140 vec![]
141 };
142
143 let mut audio_input_ios = Vec::new();
145 for _ in 0..requested_inputs {
146 audio_input_ios.push(Arc::new(AudioIO::new(buffer_size)));
147 }
148
149 let mut audio_output_ios = Vec::new();
150 for _ in 0..requested_outputs {
151 audio_output_ios.push(Arc::new(AudioIO::new(buffer_size)));
152 }
153
154 instance.setup_processing(
157 sample_rate,
158 buffer_size as i32,
159 requested_inputs as i32,
160 requested_outputs as i32,
161 )?;
162 instance.set_active(true)?;
163
164 let processing_started = false;
165
166 let parameters = Vec::new();
169 let scalar_values = Arc::new(Mutex::new(Vec::new()));
170 let previous_values = Arc::new(Mutex::new(Vec::new()));
171 let plugin_id = format!("{:02X?}", class_info.cid);
172
173 Ok(Self {
174 path: plugin_path.to_string(),
175 name,
176 plugin_id,
177 instance,
178 _factory: factory,
179 audio_inputs: audio_input_ios,
180 audio_outputs: audio_output_ios,
181 midi_input_ports,
182 midi_output_ports,
183 main_audio_inputs: requested_inputs,
184 main_audio_outputs: requested_outputs,
185 input_buses,
186 output_buses,
187 parameters,
188 scalar_values,
189 previous_values,
190 max_samples_per_block: buffer_size,
191 processing_started,
192 })
193 }
194
195 pub fn path(&self) -> &str {
196 &self.path
197 }
198
199 pub fn name(&self) -> &str {
200 &self.name
201 }
202
203 pub fn plugin_id(&self) -> &str {
204 &self.plugin_id
205 }
206
207 pub fn audio_inputs(&self) -> &[Arc<AudioIO>] {
208 &self.audio_inputs
209 }
210
211 pub fn audio_outputs(&self) -> &[Arc<AudioIO>] {
212 &self.audio_outputs
213 }
214
215 pub fn main_audio_input_count(&self) -> usize {
216 self.main_audio_inputs
217 }
218
219 pub fn main_audio_output_count(&self) -> usize {
220 self.main_audio_outputs
221 }
222
223 pub fn midi_input_count(&self) -> usize {
224 self.midi_input_ports
225 }
226
227 pub fn midi_output_count(&self) -> usize {
228 self.midi_output_ports
229 }
230
231 pub fn setup_audio_ports(&self) {
232 for port in &self.audio_inputs {
233 port.setup();
234 }
235 for port in &self.audio_outputs {
236 port.setup();
237 }
238 }
239
240 pub fn process_with_audio_io(&self, frames: usize) {
241 for input in &self.audio_inputs {
243 input.process();
244 }
245
246 let processor = match &self.instance.audio_processor {
248 Some(proc) => proc,
249 None => {
250 self.process_silence();
251 return;
252 }
253 };
254
255 if self.process_vst3(processor, frames, &[]).is_err() {
257 self.process_silence();
258 }
259 }
260
261 pub fn process_with_midi(&self, frames: usize, input_events: &[MidiEvent]) -> Vec<MidiEvent> {
263 for input in &self.audio_inputs {
265 input.process();
266 }
267
268 let processor = match &self.instance.audio_processor {
270 Some(proc) => proc,
271 None => {
272 self.process_silence();
273 return Vec::new();
274 }
275 };
276
277 match self.process_vst3(processor, frames, input_events) {
279 Ok(output_buffer) => {
280 output_buffer.to_midi_events()
282 }
283 Err(_) => {
284 self.process_silence();
285 Vec::new()
286 }
287 }
288 }
289
290 fn process_vst3(
291 &self,
292 processor: &vst3::ComPtr<vst3::Steinberg::Vst::IAudioProcessor>,
293 frames: usize,
294 input_events: &[MidiEvent],
295 ) -> Result<EventBuffer, String> {
296 use vst3::Steinberg::Vst::IAudioProcessorTrait;
297 use vst3::Steinberg::Vst::*;
298
299 let input_guards: Vec<_> = self
301 .audio_inputs
302 .iter()
303 .map(|io| io.buffer.lock())
304 .collect();
305 let output_guards: Vec<_> = self
306 .audio_outputs
307 .iter()
308 .map(|io| io.buffer.lock())
309 .collect();
310
311 let mut input_channel_ptrs: Vec<*mut f32> = input_guards
312 .iter()
313 .map(|buf| buf.as_ptr() as *mut f32)
314 .collect();
315 let mut output_channel_ptrs: Vec<*mut f32> = output_guards
316 .iter()
317 .map(|buf| buf.as_ptr() as *mut f32)
318 .collect();
319
320 let max_input_frames = input_guards
321 .iter()
322 .map(|buf| buf.len())
323 .min()
324 .unwrap_or(frames);
325 let max_output_frames = output_guards
326 .iter()
327 .map(|buf| buf.len())
328 .min()
329 .unwrap_or(frames);
330 let num_frames = frames.min(max_input_frames).min(max_output_frames);
331 if num_frames == 0 {
332 return Ok(EventBuffer::new());
333 }
334
335 let mut input_buses = Vec::new();
336 if !self.input_buses.is_empty() && !input_channel_ptrs.is_empty() {
337 input_buses.push(AudioBusBuffers {
338 numChannels: input_channel_ptrs.len() as i32,
339 silenceFlags: 0,
340 __field0: AudioBusBuffers__type0 {
341 channelBuffers32: input_channel_ptrs.as_mut_ptr(),
342 },
343 });
344 }
345
346 let mut output_buses = Vec::new();
347 if !self.output_buses.is_empty() && !output_channel_ptrs.is_empty() {
348 output_buses.push(AudioBusBuffers {
349 numChannels: output_channel_ptrs.len() as i32,
350 silenceFlags: 0,
351 __field0: AudioBusBuffers__type0 {
352 channelBuffers32: output_channel_ptrs.as_mut_ptr(),
353 },
354 });
355 }
356
357 let mut process_context: ProcessContext = unsafe { std::mem::zeroed() };
359 let input_event_list = if self.midi_input_ports > 0 {
360 Some(ComWrapper::new(EventBuffer::from_midi_events(
361 input_events,
362 0,
363 )))
364 } else {
365 None
366 };
367 let midi_mapping = self
368 .instance
369 .edit_controller
370 .as_ref()
371 .and_then(|controller| controller.cast::<IMidiMapping>());
372 let input_parameter_changes = midi_mapping.as_ref().and_then(|mapping| {
373 ParameterChanges::from_midi_events(input_events, mapping, 0).map(ComWrapper::new)
374 });
375 let output_event_list = if self.midi_output_ports > 0 {
376 Some(ComWrapper::new(EventBuffer::new()))
377 } else {
378 None
379 };
380 let mut process_data = ProcessData {
381 processMode: kRealtime as i32,
382 symbolicSampleSize: kSample32 as i32,
383 numSamples: num_frames as i32,
384 numInputs: input_buses.len() as i32,
385 numOutputs: output_buses.len() as i32,
386 inputs: if input_buses.is_empty() {
387 std::ptr::null_mut()
388 } else {
389 input_buses.as_mut_ptr()
390 },
391 outputs: if output_buses.is_empty() {
392 std::ptr::null_mut()
393 } else {
394 output_buses.as_mut_ptr()
395 },
396 inputParameterChanges: input_parameter_changes
397 .as_ref()
398 .map(ParameterChanges::changes_ptr)
399 .unwrap_or(std::ptr::null_mut()),
400 outputParameterChanges: std::ptr::null_mut(),
401 inputEvents: input_event_list
402 .as_ref()
403 .map(EventBuffer::event_list_ptr)
404 .unwrap_or(std::ptr::null_mut()),
405 outputEvents: output_event_list
406 .as_ref()
407 .map(EventBuffer::event_list_ptr)
408 .unwrap_or(std::ptr::null_mut()),
409 processContext: &mut process_context,
410 };
411
412 let result = unsafe { processor.process(&mut process_data) };
414
415 if result != vst3::Steinberg::kResultOk {
416 return Err(format!("VST3 process failed with result: {}", result));
417 }
418
419 for output in &self.audio_outputs {
421 *output.finished.lock() = true;
422 }
423
424 Ok(output_event_list
425 .as_ref()
426 .map(|events| EventBuffer::from_midi_events(&events.to_midi_events(), 0))
427 .unwrap_or_default())
428 }
429
430 fn process_silence(&self) {
431 for output in &self.audio_outputs {
432 let out_buf = output.buffer.lock();
433 out_buf.fill(0.0);
434 *output.finished.lock() = true;
435 }
436 }
437
438 pub fn parameters(&self) -> &[ParameterInfo] {
439 &self.parameters
440 }
441
442 pub fn get_parameter_value(&self, param_id: u32) -> Option<f32> {
443 let idx = self.parameters.iter().position(|p| p.id == param_id)?;
444 Some(self.scalar_values.lock().unwrap()[idx])
445 }
446
447 pub fn set_parameter_value(
448 &mut self,
449 param_id: u32,
450 normalized_value: f32,
451 ) -> Result<(), String> {
452 let idx = self
453 .parameters
454 .iter()
455 .position(|p| p.id == param_id)
456 .ok_or("Parameter not found")?;
457
458 self.scalar_values.lock().unwrap()[idx] = normalized_value;
459
460 if let Some(controller) = &self.instance.edit_controller {
462 use vst3::Steinberg::Vst::IEditControllerTrait;
463 unsafe {
464 controller.setParamNormalized(param_id, normalized_value as f64);
465 }
466 }
467
468 Ok(())
469 }
470
471 pub fn snapshot_state(&self) -> Result<Vst3PluginState, String> {
473 use vst3::Steinberg::Vst::{IComponentTrait, IEditControllerTrait};
474
475 let instance = &self.instance;
476
477 let comp_stream = vst3::ComWrapper::new(MemoryStream::new());
479 unsafe {
480 let result = instance
481 .component
482 .getState(ibstream_ptr(&comp_stream) as *mut _);
483 if result != vst3::Steinberg::kResultOk {
484 return Err("Failed to get component state".to_string());
485 }
486 }
487
488 let ctrl_stream = vst3::ComWrapper::new(MemoryStream::new());
490 if let Some(controller) = &instance.edit_controller {
491 unsafe {
492 controller.getState(ibstream_ptr(&ctrl_stream) as *mut _);
493 }
494 }
495
496 Ok(Vst3PluginState {
497 plugin_id: self.plugin_id.clone(),
498 component_state: comp_stream.bytes(),
499 controller_state: ctrl_stream.bytes(),
500 })
501 }
502
503 pub fn restore_state(&mut self, state: &Vst3PluginState) -> Result<(), String> {
505 use vst3::Steinberg::Vst::{IComponentTrait, IEditControllerTrait};
506
507 if state.plugin_id != self.plugin_id {
508 return Err(format!(
509 "Plugin ID mismatch: expected '{}', got '{}'",
510 self.plugin_id, state.plugin_id
511 ));
512 }
513
514 let instance = &self.instance;
515
516 if !state.component_state.is_empty() {
518 let comp_stream =
519 vst3::ComWrapper::new(MemoryStream::from_bytes(&state.component_state));
520 unsafe {
521 let result = instance
522 .component
523 .setState(ibstream_ptr(&comp_stream) as *mut _);
524 if result != vst3::Steinberg::kResultOk {
525 return Err("Failed to set component state".to_string());
526 }
527 }
528 }
529
530 if !state.controller_state.is_empty()
532 && let Some(controller) = &instance.edit_controller
533 {
534 let ctrl_stream =
535 vst3::ComWrapper::new(MemoryStream::from_bytes(&state.controller_state));
536 unsafe {
537 controller.setState(ibstream_ptr(&ctrl_stream) as *mut _);
538 }
539
540 for (idx, param) in self.parameters.iter().enumerate() {
542 let value = unsafe { controller.getParamNormalized(param.id) };
543 self.scalar_values.lock().unwrap()[idx] = value as f32;
544 self.previous_values.lock().unwrap()[idx] = value as f32;
545 }
546 }
547
548 Ok(())
549 }
550}
551
552impl Drop for Vst3Processor {
553 fn drop(&mut self) {
554 if self.processing_started {
555 self.instance.stop_processing();
556 }
557 let _ = self.instance.set_active(false);
558 let _ = self.instance.terminate();
559 }
560}
561
562pub fn list_plugins() -> Vec<super::host::Vst3PluginInfo> {
564 super::host::list_plugins()
565}