beamer_core/plugin.rs
1//! Core plugin trait definitions.
2//!
3//! This module defines the two-phase plugin lifecycle:
4//!
5//! - **[`Plugin`]** (unprepared state): Holds parameters, created before audio config is known.
6//! Transforms into a processor via [`Plugin::prepare()`] when configuration arrives.
7//!
8//! - **[`AudioProcessor`]** (prepared state): Ready for audio processing with real sample rate
9//! and buffer configuration. Created by [`Plugin::prepare()`], can return to unprepared
10//! state via [`AudioProcessor::unprepare()`] for sample rate changes.
11//!
12//! This design eliminates placeholder values by making it impossible to process audio
13//! until proper configuration is available.
14
15use crate::buffer::{AuxiliaryBuffers, Buffer};
16use crate::error::PluginResult;
17use crate::midi::{
18 KeyswitchInfo, Midi2Controller, MidiBuffer, MidiEvent, MpeInputDeviceSettings,
19 NoteExpressionTypeInfo, PhysicalUIMap,
20};
21use crate::midi_cc_config::MidiCcConfig;
22use crate::parameter_groups::ParameterGroups;
23use crate::parameter_store::ParameterStore;
24use crate::process_context::ProcessContext;
25
26// =============================================================================
27// HasParameters Trait (Shared Parameter Access)
28// =============================================================================
29
30/// Trait for types that hold parameters.
31///
32/// This trait provides a common interface for parameter access, shared between
33/// [`Plugin`] (unprepared state) and [`AudioProcessor`] (prepared state).
34/// Both traits require `HasParameters` as a supertrait.
35///
36/// # Derive Macro
37///
38/// Use `#[derive(HasParameters)]` to automatically implement this trait for structs
39/// with a `#[parameters]` field annotation:
40///
41/// ```ignore
42/// #[derive(Default, HasParameters)]
43/// pub struct GainPlugin {
44/// #[parameters]
45/// parameters: GainParameters,
46/// }
47///
48/// #[derive(HasParameters)]
49/// pub struct GainProcessor {
50/// #[parameters]
51/// parameters: GainParameters,
52/// }
53/// ```
54///
55/// This eliminates the boilerplate of implementing `parameters()` and `parameters_mut()`
56/// on both your Plugin and Processor types.
57pub trait HasParameters: Send + 'static {
58 /// The parameter collection type.
59 type Parameters: ParameterStore + ParameterGroups + crate::parameter_types::Parameters;
60
61 /// Returns a reference to the parameters.
62 fn parameters(&self) -> &Self::Parameters;
63
64 /// Returns a mutable reference to the parameters.
65 fn parameters_mut(&mut self) -> &mut Self::Parameters;
66}
67
68// =============================================================================
69// Processor Configuration Types
70// =============================================================================
71
72/// Marker trait for processor configuration types.
73///
74/// Plugins declare their configuration requirements via the associated
75/// [`Plugin::Config`] type. The framework provides these standard configs:
76///
77/// - [`NoConfig`]: For plugins that don't need sample rate (e.g., simple gain)
78/// - [`AudioSetup`]: For plugins that need sample rate and max buffer size
79/// - [`FullAudioSetup`]: For plugins that also need bus layout information
80///
81/// Plugins can also define custom config types by implementing this trait.
82pub trait ProcessorConfig: Clone + Send + 'static {}
83
84/// Configuration for plugins that don't need audio setup information.
85///
86/// Use this for stateless plugins like simple gain, pan, or polarity flip
87/// that don't have any sample-rate-dependent state.
88///
89/// # Example
90///
91/// ```ignore
92/// impl Plugin for GainPlugin {
93/// type Config = NoConfig;
94/// // ...
95/// fn prepare(self, _: NoConfig) -> GainProcessor {
96/// GainProcessor { parameters: self.parameters }
97/// }
98/// }
99/// ```
100#[derive(Clone, Copy, Debug, Default, PartialEq)]
101pub struct NoConfig;
102impl ProcessorConfig for NoConfig {}
103
104/// Standard audio setup configuration with sample rate and max buffer size.
105///
106/// Use this for most plugins that have sample-rate-dependent state,
107/// such as delays, filters, compressors, or any plugin with smoothing.
108///
109/// # Example
110///
111/// ```ignore
112/// impl Plugin for DelayPlugin {
113/// type Config = AudioSetup;
114/// // ...
115/// fn prepare(self, config: AudioSetup) -> DelayProcessor {
116/// let buffer_size = (MAX_DELAY_SECONDS * config.sample_rate) as usize;
117/// DelayProcessor {
118/// parameters: self.parameters,
119/// sample_rate: config.sample_rate, // Real value from start!
120/// buffer: vec![0.0; buffer_size], // Correct allocation!
121/// }
122/// }
123/// }
124/// ```
125#[derive(Clone, Debug, PartialEq)]
126pub struct AudioSetup {
127 /// Sample rate in Hz (e.g., 44100.0, 48000.0, 96000.0)
128 pub sample_rate: f64,
129 /// Maximum number of samples per process() call
130 pub max_buffer_size: usize,
131}
132impl ProcessorConfig for AudioSetup {}
133
134/// Full audio setup including bus layout information.
135///
136/// Use this for plugins that need to know the channel configuration,
137/// such as surround processors or plugins with channel-specific processing.
138///
139/// # Example
140///
141/// ```ignore
142/// impl Plugin for SurroundPlugin {
143/// type Config = FullAudioSetup;
144/// // ...
145/// fn prepare(self, config: FullAudioSetup) -> SurroundProcessor {
146/// let channel_count = config.layout.main_output_channels();
147/// SurroundProcessor {
148/// parameters: self.parameters,
149/// sample_rate: config.sample_rate,
150/// per_channel_state: vec![ChannelState::new(); channel_count],
151/// }
152/// }
153/// }
154/// ```
155#[derive(Clone, Debug, PartialEq)]
156pub struct FullAudioSetup {
157 /// Sample rate in Hz
158 pub sample_rate: f64,
159 /// Maximum number of samples per process() call
160 pub max_buffer_size: usize,
161 /// Bus layout information
162 pub layout: BusLayout,
163}
164impl ProcessorConfig for FullAudioSetup {}
165
166/// Bus layout information for plugins that need channel configuration.
167#[derive(Clone, Debug, Default, PartialEq)]
168pub struct BusLayout {
169 /// Number of channels on the main input bus
170 pub main_input_channels: u32,
171 /// Number of channels on the main output bus
172 pub main_output_channels: u32,
173 /// Number of auxiliary input buses
174 pub aux_input_count: usize,
175 /// Number of auxiliary output buses
176 pub aux_output_count: usize,
177}
178
179impl BusLayout {
180 /// Create a stereo (2 in, 2 out) layout with no aux buses.
181 pub const fn stereo() -> Self {
182 Self {
183 main_input_channels: 2,
184 main_output_channels: 2,
185 aux_input_count: 0,
186 aux_output_count: 0,
187 }
188 }
189
190 /// Create a layout from a plugin's bus configuration.
191 pub fn from_plugin<P: Plugin>(plugin: &P) -> Self {
192 Self {
193 main_input_channels: plugin
194 .input_bus_info(0)
195 .map(|b| b.channel_count)
196 .unwrap_or(2),
197 main_output_channels: plugin
198 .output_bus_info(0)
199 .map(|b| b.channel_count)
200 .unwrap_or(2),
201 aux_input_count: plugin.input_bus_count().saturating_sub(1),
202 aux_output_count: plugin.output_bus_count().saturating_sub(1),
203 }
204 }
205}
206
207// =============================================================================
208// Bus Configuration
209// =============================================================================
210
211/// Audio bus type.
212#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
213pub enum BusType {
214 /// Main audio bus (e.g., primary stereo input/output).
215 #[default]
216 Main,
217 /// Auxiliary bus (e.g., sidechain input).
218 Aux,
219}
220
221/// Information about an audio bus.
222#[derive(Debug, Clone)]
223pub struct BusInfo {
224 /// Display name for the bus (e.g., "Input", "Sidechain").
225 pub name: &'static str,
226 /// Bus type (main or auxiliary).
227 pub bus_type: BusType,
228 /// Number of channels in this bus.
229 pub channel_count: u32,
230 /// Whether the bus is active by default.
231 pub is_default_active: bool,
232}
233
234impl Default for BusInfo {
235 fn default() -> Self {
236 Self {
237 name: "Main",
238 bus_type: BusType::Main,
239 channel_count: 2,
240 is_default_active: true,
241 }
242 }
243}
244
245impl BusInfo {
246 /// Create a stereo main bus.
247 pub const fn stereo(name: &'static str) -> Self {
248 Self {
249 name,
250 bus_type: BusType::Main,
251 channel_count: 2,
252 is_default_active: true,
253 }
254 }
255
256 /// Create a mono main bus.
257 pub const fn mono(name: &'static str) -> Self {
258 Self {
259 name,
260 bus_type: BusType::Main,
261 channel_count: 1,
262 is_default_active: true,
263 }
264 }
265
266 /// Create an auxiliary bus (e.g., sidechain).
267 pub const fn aux(name: &'static str, channel_count: u32) -> Self {
268 Self {
269 name,
270 bus_type: BusType::Aux,
271 channel_count,
272 is_default_active: false,
273 }
274 }
275}
276
277// =============================================================================
278// AudioProcessor Trait
279// =============================================================================
280
281/// The prepared processor - ready for audio processing.
282///
283/// This trait defines the DSP (Digital Signal Processing) interface that
284/// plugin implementations must provide. It is designed to be format-agnostic,
285/// meaning the same implementation can be wrapped for VST3, CLAP, or other
286/// plugin formats.
287///
288/// An `AudioProcessor` is created by calling [`Plugin::prepare()`] with the
289/// audio configuration. Unlike the old design where `setup()` was called
290/// after construction, here the processor is created with valid configuration
291/// from the start - no placeholder values.
292///
293/// # Lifecycle
294///
295/// ```text
296/// Plugin::default() -> Plugin (unprepared, holds parameters)
297/// |
298/// v Plugin::prepare(config)
299/// |
300/// v
301/// AudioProcessor (prepared, ready for audio)
302/// |
303/// v AudioProcessor::unprepare()
304/// |
305/// v
306/// Plugin (unprepared, parameters preserved)
307/// ```
308///
309/// # Thread Safety
310///
311/// Implementors must be `Send` because the plugin may be moved between threads.
312/// The `process` method is called on the audio thread and must be real-time safe:
313/// - No allocations
314/// - No locks (use lock-free structures)
315/// - No syscalls
316/// - No unbounded loops
317///
318/// # Note on HasParameters
319///
320/// The `AudioProcessor` trait requires [`HasParameters`] as a supertrait, which provides
321/// the `parameters()` and `parameters_mut()` methods. Use `#[derive(HasParameters)]` with a
322/// `#[parameters]` field annotation to implement this automatically.
323pub trait AudioProcessor: HasParameters {
324 /// The unprepared plugin type that created this processor.
325 ///
326 /// Used by [`AudioProcessor::unprepare()`] to return to the unprepared state.
327 /// The Parameters type must match the plugin's Parameters type.
328 type Plugin: Plugin<Processor = Self, Parameters = Self::Parameters>;
329
330 /// Process an audio buffer with transport context.
331 ///
332 /// This is the main DSP entry point, called on the audio thread for each
333 /// block of audio. The buffer provides input samples and mutable output
334 /// buffers for the main bus.
335 ///
336 /// # Arguments
337 ///
338 /// * `buffer` - Main audio bus (stereo/surround input and output)
339 /// * `aux` - Auxiliary buses (sidechain, aux sends) - ignore if not needed
340 /// * `context` - Processing context with sample rate, buffer size, and transport info
341 ///
342 /// # Real-Time Safety
343 ///
344 /// This method must be real-time safe. Do not allocate, lock mutexes,
345 /// or perform any operation with unbounded execution time.
346 ///
347 /// # Example: Simple Gain
348 ///
349 /// ```ignore
350 /// fn process(&mut self, buffer: &mut Buffer, _aux: &mut AuxiliaryBuffers, _context: &ProcessContext) {
351 /// let gain = self.parameters.gain();
352 /// for (input, output) in buffer.zip_channels() {
353 /// for (i, o) in input.iter().zip(output.iter_mut()) {
354 /// *o = *i * gain;
355 /// }
356 /// }
357 /// }
358 /// ```
359 ///
360 /// # Example: Tempo-Synced LFO
361 ///
362 /// ```ignore
363 /// fn process(&mut self, buffer: &mut Buffer, _aux: &mut AuxiliaryBuffers, context: &ProcessContext) {
364 /// // Calculate LFO rate synced to host tempo
365 /// let lfo_hz = context.transport.tempo
366 /// .map(|tempo| tempo / 60.0 / 4.0) // 1 cycle per 4 beats
367 /// .unwrap_or(2.0); // Fallback: 2 Hz
368 ///
369 /// let increment = (lfo_hz * 2.0 * std::f32::consts::PI) / context.sample_rate as f32;
370 ///
371 /// for (input, output) in buffer.zip_channels() {
372 /// for (i, o) in input.iter().zip(output.iter_mut()) {
373 /// let lfo = self.phase.sin();
374 /// *o = *i * (1.0 + lfo * 0.5);
375 /// self.phase += increment;
376 /// }
377 /// }
378 /// }
379 /// ```
380 ///
381 /// # Example: Sidechain Ducker
382 ///
383 /// ```ignore
384 /// fn process(&mut self, buffer: &mut Buffer, aux: &mut AuxiliaryBuffers, _context: &ProcessContext) {
385 /// let duck = aux.sidechain()
386 /// .map(|sc| (sc.rms(0) * 4.0).min(1.0))
387 /// .unwrap_or(0.0);
388 ///
389 /// buffer.copy_to_output();
390 /// buffer.apply_output_gain(1.0 - duck * 0.8);
391 /// }
392 /// ```
393 fn process(&mut self, buffer: &mut Buffer, aux: &mut AuxiliaryBuffers, context: &ProcessContext);
394
395 /// Return to the unprepared plugin state.
396 ///
397 /// This is used when sample rate or buffer configuration changes.
398 /// The processor is consumed and returns the original plugin with
399 /// parameters preserved. The wrapper can then call `prepare()` again
400 /// with the new configuration.
401 ///
402 /// # Example
403 ///
404 /// ```ignore
405 /// impl AudioProcessor for DelayProcessor {
406 /// type Plugin = DelayPlugin;
407 ///
408 /// fn unprepare(self) -> DelayPlugin {
409 /// DelayPlugin {
410 /// parameters: self.parameters,
411 /// // DSP state (delay_lines, etc.) is discarded
412 /// }
413 /// }
414 /// }
415 /// ```
416 fn unprepare(self) -> Self::Plugin
417 where
418 Self: Sized;
419
420 // Note: `parameters()` and `parameters_mut()` are provided by the `HasParameters` supertrait.
421 // Use `#[derive(HasParameters)]` with a `#[parameters]` field annotation to implement them.
422
423 // =========================================================================
424 // Activation State
425 // =========================================================================
426
427 /// Called when the plugin is activated or deactivated.
428 ///
429 /// Activation typically happens when the user inserts the plugin into a
430 /// track or opens a project. Deactivation happens when removed or project
431 /// is closed.
432 ///
433 /// **Important:** When `active == true`, you should reset your DSP state
434 /// (clear delay lines, reset filter histories, zero envelopes, etc.).
435 /// Hosts call `setActive(false)` followed by `setActive(true)` to request
436 /// a full state reset.
437 ///
438 /// # Example
439 ///
440 /// ```ignore
441 /// fn set_active(&mut self, active: bool) {
442 /// if active {
443 /// // Reset DSP state on activation
444 /// self.delay_line.clear();
445 /// self.envelope.reset();
446 /// self.filter_state = FilterState::default();
447 /// }
448 /// }
449 /// ```
450 ///
451 /// Default implementation does nothing.
452 fn set_active(&mut self, _active: bool) {}
453
454 /// Get the tail length in samples.
455 ///
456 /// This indicates how many samples of audio "tail" the plugin produces
457 /// after input stops (e.g., reverb decay). Return 0 for no tail, or
458 /// `u32::MAX` for infinite tail.
459 ///
460 /// Default returns 0 (no tail).
461 fn tail_samples(&self) -> u32 {
462 0
463 }
464
465 /// Get the latency in samples.
466 ///
467 /// If the plugin introduces processing latency (e.g., lookahead limiters),
468 /// return the latency in samples here. The host can use this for delay
469 /// compensation.
470 ///
471 /// Default returns 0 (no latency).
472 fn latency_samples(&self) -> u32 {
473 0
474 }
475
476 /// Get the bypass ramp length in samples.
477 ///
478 /// When bypass is engaged or disengaged, this defines the crossfade
479 /// duration to avoid clicks. The host uses this value (combined with
480 /// `tail_samples()`) to determine how long to continue calling `process()`
481 /// after input stops.
482 ///
483 /// Return 0 for instant bypass (no crossfade), or a sample count for
484 /// smooth crossfading. Typical values:
485 /// - 64 samples (~1.3ms at 48kHz) - fast, suitable for most effects
486 /// - 256 samples (~5.3ms at 48kHz) - smoother, for sensitive material
487 /// - 512+ samples - very smooth, for reverbs/delays with long tails
488 ///
489 /// Default returns 64 samples.
490 ///
491 /// # Example
492 ///
493 /// ```ignore
494 /// fn bypass_ramp_samples(&self) -> u32 {
495 /// // Use 10ms crossfade based on current sample rate
496 /// (self.sample_rate * 0.01) as u32
497 /// }
498 /// ```
499 fn bypass_ramp_samples(&self) -> u32 {
500 64
501 }
502
503 // =========================================================================
504 // 64-bit Processing Support
505 // =========================================================================
506
507 /// Returns true if the plugin supports native 64-bit (double precision) processing.
508 ///
509 /// Override this to return `true` if your plugin implements `process_f64()` natively.
510 /// When false (default), the framework will automatically convert 64-bit host buffers
511 /// to 32-bit, call `process()`, and convert back.
512 ///
513 /// # Performance Considerations
514 ///
515 /// - For most plugins, f32 is sufficient and the default conversion is fine
516 /// - Implement native f64 only if your DSP algorithm benefits from double precision
517 /// (e.g., IIR filters with long decay, precision-sensitive synthesis)
518 /// - The conversion overhead is minimal (~few microseconds per buffer)
519 ///
520 /// Default returns `false`.
521 fn supports_double_precision(&self) -> bool {
522 false
523 }
524
525 /// Process an audio buffer at 64-bit (double) precision.
526 ///
527 /// This is the f64 equivalent of `process()`. Override this method AND
528 /// return `true` from `supports_double_precision()` to enable native
529 /// 64-bit processing.
530 ///
531 /// If `supports_double_precision()` returns `false`, this method is never
532 /// called - the framework converts to f32 and calls `process()` instead.
533 ///
534 /// # Default Implementation
535 ///
536 /// The default implementation converts f64→f32, calls `process()`, then
537 /// converts f32→f64. This allows any plugin to work in a 64-bit host
538 /// without modification.
539 ///
540 /// # Example: Native f64 Plugin
541 ///
542 /// ```ignore
543 /// fn supports_double_precision(&self) -> bool {
544 /// true
545 /// }
546 ///
547 /// fn process_f64(
548 /// &mut self,
549 /// buffer: &mut Buffer<f64>,
550 /// aux: &mut AuxiliaryBuffers<f64>,
551 /// context: &ProcessContext,
552 /// ) {
553 /// let gain = self.parameters.gain_linear() as f64;
554 /// for (input, output) in buffer.zip_channels() {
555 /// for (i, o) in input.iter().zip(output.iter_mut()) {
556 /// *o = *i * gain;
557 /// }
558 /// }
559 /// }
560 /// ```
561 fn process_f64(
562 &mut self,
563 buffer: &mut Buffer<f64>,
564 _aux: &mut AuxiliaryBuffers<f64>,
565 context: &ProcessContext,
566 ) {
567 // Default implementation: convert f64 → f32, process, convert back
568 //
569 // NOTE: This is a fallback implementation that allocates memory.
570 // In practice, this method is rarely called because:
571 // - The VST3 wrapper handles conversion with pre-allocated buffers
572 // (see `process_audio_f64_converted` in beamer-vst3/src/processor.rs)
573 // - Future format wrappers (CLAP, etc.) should also pre-allocate
574 //
575 // If you're implementing a custom wrapper, ensure you handle
576 // f64→f32 conversion with pre-allocated buffers for real-time safety.
577
578 let num_samples = buffer.num_samples();
579 let num_input_channels = buffer.num_input_channels();
580 let num_output_channels = buffer.num_output_channels();
581
582 // Allocate conversion buffers (VST3 wrapper uses pre-allocated buffers,
583 // this is only for the fallback default implementation)
584 let input_f32: Vec<Vec<f32>> = (0..num_input_channels)
585 .map(|ch| buffer.input(ch).iter().map(|&s| s as f32).collect())
586 .collect();
587 let mut output_f32: Vec<Vec<f32>> = (0..num_output_channels)
588 .map(|_| vec![0.0f32; num_samples])
589 .collect();
590
591 // Build f32 buffer slices
592 let input_slices: Vec<&[f32]> = input_f32.iter().map(|v| v.as_slice()).collect();
593 let output_slices: Vec<&mut [f32]> = output_f32
594 .iter_mut()
595 .map(|v| v.as_mut_slice())
596 .collect();
597
598 let mut buffer_f32 = Buffer::new(input_slices, output_slices, num_samples);
599
600 // For aux buffers, we use empty for now (full aux conversion is complex)
601 // The VST3 wrapper handles proper aux conversion
602 let mut aux_f32: AuxiliaryBuffers<f32> = AuxiliaryBuffers::empty();
603
604 // Process at f32
605 self.process(&mut buffer_f32, &mut aux_f32, context);
606
607 // Convert output back to f64
608 for (ch, output_samples) in output_f32.iter().enumerate().take(num_output_channels) {
609 let output_ch = buffer.output(ch);
610 for (i, sample) in output_samples.iter().enumerate() {
611 output_ch[i] = *sample as f64;
612 }
613 }
614 }
615
616 /// Save the plugin state to bytes.
617 ///
618 /// This is called when the DAW saves a project or preset. The returned
619 /// bytes should contain all state needed to restore the plugin to its
620 /// current configuration.
621 ///
622 /// Default returns an empty vector.
623 fn save_state(&self) -> PluginResult<Vec<u8>> {
624 Ok(Vec::new())
625 }
626
627 /// Load the plugin state from bytes.
628 ///
629 /// This is called when the DAW loads a project or preset. The data is
630 /// the same bytes returned from a previous `save_state` call.
631 ///
632 /// Default does nothing.
633 fn load_state(&mut self, _data: &[u8]) -> PluginResult<()> {
634 Ok(())
635 }
636
637 // =========================================================================
638 // MIDI Processing
639 // =========================================================================
640
641 /// Process MIDI events.
642 ///
643 /// Called during processing with any incoming MIDI events. Plugins can
644 /// transform events and add them to the output buffer, pass them through
645 /// unchanged, or consume them entirely.
646 ///
647 /// # Arguments
648 /// * `input` - Slice of incoming MIDI events (sorted by sample_offset)
649 /// * `output` - Buffer to write output MIDI events to
650 ///
651 /// # Real-Time Safety
652 ///
653 /// This method must be real-time safe. Do not allocate, lock mutexes,
654 /// or perform any operation with unbounded execution time.
655 ///
656 /// **Note:** Cloning a `SysEx` event allocates due to `Box<SysEx>`. SysEx
657 /// events are rare in typical use cases. If strict real-time safety is
658 /// required, override this method to handle SysEx specially.
659 ///
660 /// # Default Implementation
661 ///
662 /// The default implementation passes all events through unchanged.
663 fn process_midi(&mut self, input: &[MidiEvent], output: &mut MidiBuffer) {
664 for event in input {
665 output.push(event.clone());
666 }
667 }
668
669 /// Returns whether this plugin processes MIDI events.
670 ///
671 /// Override to return `true` if your plugin needs MIDI input/output.
672 /// This is used by the host to determine event bus configuration.
673 ///
674 /// Default returns `false`.
675 fn wants_midi(&self) -> bool {
676 false
677 }
678
679}
680
681// =============================================================================
682// Plugin Trait
683// =============================================================================
684
685/// The unprepared plugin - holds parameters before audio config is known.
686///
687/// This is the primary trait that plugin authors implement to create a complete
688/// audio plugin. It holds parameters and configuration that doesn't depend on
689/// sample rate, and transforms into an [`AudioProcessor`] via [`Plugin::prepare()`]
690/// when audio configuration becomes available.
691///
692/// # Two-Phase Lifecycle
693///
694/// ```text
695/// Plugin::default() -> Plugin (unprepared, holds parameters)
696/// |
697/// v Plugin::prepare(config)
698/// |
699/// v
700/// AudioProcessor (prepared, ready for audio)
701/// |
702/// v AudioProcessor::unprepare()
703/// |
704/// v
705/// Plugin (unprepared, parameters preserved)
706/// ```
707///
708/// # Example: Simple Gain (NoConfig)
709///
710/// ```ignore
711/// #[derive(Default, HasParameters)]
712/// pub struct GainPlugin {
713/// #[parameters]
714/// parameters: GainParameters,
715/// }
716///
717/// impl Plugin for GainPlugin {
718/// type Config = NoConfig;
719/// type Processor = GainProcessor;
720///
721/// fn prepare(self, _: NoConfig) -> GainProcessor {
722/// GainProcessor { parameters: self.parameters }
723/// }
724/// }
725///
726/// #[derive(HasParameters)]
727/// pub struct GainProcessor {
728/// #[parameters]
729/// parameters: GainParameters,
730/// }
731///
732/// impl AudioProcessor for GainProcessor {
733/// type Plugin = GainPlugin;
734///
735/// fn process(&mut self, buffer: &mut Buffer, _aux: &mut AuxiliaryBuffers, _context: &ProcessContext) {
736/// let gain = self.parameters.gain_linear();
737/// for (input, output) in buffer.zip_channels() {
738/// for (i, o) in input.iter().zip(output.iter_mut()) {
739/// *o = *i * gain;
740/// }
741/// }
742/// }
743///
744/// fn unprepare(self) -> GainPlugin {
745/// GainPlugin { parameters: self.parameters }
746/// }
747/// }
748/// ```
749///
750/// # Example: Delay (AudioSetup)
751///
752/// ```ignore
753/// #[derive(Default, HasParameters)]
754/// pub struct DelayPlugin {
755/// #[parameters]
756/// parameters: DelayParameters,
757/// }
758///
759/// impl Plugin for DelayPlugin {
760/// type Config = AudioSetup;
761/// type Processor = DelayProcessor;
762///
763/// fn prepare(self, config: AudioSetup) -> DelayProcessor {
764/// let buffer_size = (MAX_DELAY_SECONDS * config.sample_rate) as usize;
765/// DelayProcessor {
766/// parameters: self.parameters,
767/// sample_rate: config.sample_rate, // Real value from start!
768/// buffer: vec![0.0; buffer_size], // Correct allocation!
769/// }
770/// }
771/// }
772/// ```
773///
774/// # Note on HasParameters
775///
776/// The `Plugin` trait requires [`HasParameters`] as a supertrait, which provides the
777/// `parameters()` and `parameters_mut()` methods. Use `#[derive(HasParameters)]` with a
778/// `#[parameters]` field annotation to implement this automatically.
779pub trait Plugin: HasParameters + Default {
780 /// The configuration type this plugin needs to prepare.
781 ///
782 /// - [`NoConfig`]: For plugins that don't need sample rate (simple gain)
783 /// - [`AudioSetup`]: For plugins that need sample rate and max buffer size
784 /// - [`FullAudioSetup`]: For plugins that also need bus layout information
785 type Config: ProcessorConfig;
786
787 /// The prepared processor type created by [`Plugin::prepare()`].
788 type Processor: AudioProcessor<Plugin = Self, Parameters = Self::Parameters>;
789
790 /// Transform this plugin into a prepared processor.
791 ///
792 /// This is called when audio configuration becomes available (in VST3,
793 /// during `setupProcessing()`). The plugin is consumed and transformed
794 /// into a processor with valid configuration - no placeholder values.
795 ///
796 /// # Arguments
797 ///
798 /// * `config` - The audio configuration (sample rate, buffer size, layout)
799 ///
800 /// # Returns
801 ///
802 /// A prepared processor ready for audio processing.
803 fn prepare(self, config: Self::Config) -> Self::Processor;
804
805 // =========================================================================
806 // Bus Configuration (static, known before prepare)
807 // =========================================================================
808
809 /// Returns the number of audio input buses.
810 ///
811 /// Default returns 1 (single stereo input).
812 fn input_bus_count(&self) -> usize {
813 1
814 }
815
816 /// Returns the number of audio output buses.
817 ///
818 /// Default returns 1 (single stereo output).
819 fn output_bus_count(&self) -> usize {
820 1
821 }
822
823 /// Returns information about an input bus.
824 ///
825 /// Default returns a stereo main bus for index 0.
826 fn input_bus_info(&self, index: usize) -> Option<BusInfo> {
827 if index == 0 {
828 Some(BusInfo::stereo("Input"))
829 } else {
830 None
831 }
832 }
833
834 /// Returns information about an output bus.
835 ///
836 /// Default returns a stereo main bus for index 0.
837 fn output_bus_info(&self, index: usize) -> Option<BusInfo> {
838 if index == 0 {
839 Some(BusInfo::stereo("Output"))
840 } else {
841 None
842 }
843 }
844
845 /// Returns whether this plugin processes MIDI events.
846 ///
847 /// Override to return `true` if your plugin needs MIDI input/output.
848 /// This is used by the host to determine event bus configuration.
849 ///
850 /// **Note:** This method is also on [`AudioProcessor`], but the Plugin
851 /// version is queried during bus configuration (before prepare).
852 /// Both should return the same value.
853 ///
854 /// Default returns `false`.
855 fn wants_midi(&self) -> bool {
856 false
857 }
858
859 // =========================================================================
860 // MIDI Mapping (IMidiMapping)
861 // =========================================================================
862
863 /// Get the parameter ID mapped to a MIDI CC.
864 ///
865 /// Override this to enable DAW MIDI learn for your parameters. When the
866 /// DAW queries which parameter is assigned to a MIDI CC, this method is
867 /// called.
868 ///
869 /// # Arguments
870 /// * `bus_index` - MIDI bus index (usually 0)
871 /// * `channel` - MIDI channel (0-15), or -1 to query all channels
872 /// * `cc` - MIDI CC number (0-127)
873 ///
874 /// # Returns
875 /// `Some(parameter_id)` if this CC is mapped to a parameter, `None` otherwise.
876 ///
877 /// # Example
878 /// ```ignore
879 /// fn midi_cc_to_parameter(&self, _bus: i32, _channel: i16, cc: u8) -> Option<u32> {
880 /// match cc {
881 /// cc::MOD_WHEEL => Some(PARAM_VIBRATO_DEPTH),
882 /// cc::EXPRESSION => Some(PARAM_VOLUME),
883 /// _ => None,
884 /// }
885 /// }
886 /// ```
887 fn midi_cc_to_parameter(&self, bus_index: i32, channel: i16, cc: u8) -> Option<u32> {
888 let _ = (bus_index, channel, cc);
889 None
890 }
891
892 // =========================================================================
893 // MIDI CC Configuration (VST3 IMidiMapping hidden parameters)
894 // =========================================================================
895
896 /// Returns MIDI CC configuration for automatic host mapping.
897 ///
898 /// Override to enable MIDI CC/pitch bend/aftertouch reception via IMidiMapping.
899 /// The framework will:
900 /// 1. Create hidden parameters for each enabled controller
901 /// 2. Handle IMidiMapping queries from the DAW
902 /// 3. Convert parameter changes to MidiEvents in process_midi()
903 /// 4. Provide direct CC value access via ProcessContext::midi_cc()
904 ///
905 /// This solves the VST3 MIDI input problem where most DAWs don't send
906 /// `kLegacyMIDICCOutEvent` for input. Instead, they use the `IMidiMapping`
907 /// interface to map MIDI controllers to parameters.
908 ///
909 /// # Example
910 ///
911 /// ```ignore
912 /// impl Plugin for MySynth {
913 /// fn midi_cc_config(&self) -> Option<MidiCcConfig> {
914 /// Some(MidiCcConfig::new()
915 /// .with_pitch_bend()
916 /// .with_mod_wheel()
917 /// .with_aftertouch()
918 /// .with_ccs(&[7, 10, 11, 64])) // Volume, Pan, Expression, Sustain
919 /// }
920 /// }
921 ///
922 /// // Access CC values during processing:
923 /// fn process(&mut self, buffer: &mut Buffer, _aux: &mut AuxiliaryBuffers, context: &ProcessContext) {
924 /// if let Some(cc) = context.midi_cc() {
925 /// let pitch_bend = cc.pitch_bend(); // -1.0 to 1.0
926 /// let mod_wheel = cc.mod_wheel(); // 0.0 to 1.0
927 /// }
928 /// }
929 /// ```
930 fn midi_cc_config(&self) -> Option<MidiCcConfig> {
931 None
932 }
933
934 // =========================================================================
935 // MIDI Learn (IMidiLearn)
936 // =========================================================================
937
938 /// Called by DAW when live MIDI CC input is received during learn mode.
939 ///
940 /// Override this to implement MIDI learn in your plugin UI. When the user
941 /// enables "MIDI Learn" mode and moves a MIDI CC knob, the DAW calls this
942 /// method so the plugin can map that CC to a parameter.
943 ///
944 /// # Arguments
945 /// * `bus_index` - MIDI bus index (usually 0)
946 /// * `channel` - MIDI channel (0-15)
947 /// * `cc` - MIDI CC number that was moved
948 ///
949 /// # Returns
950 /// `true` if the input was handled (learned), `false` otherwise.
951 fn on_midi_learn(&mut self, bus_index: i32, channel: i16, cc: u8) -> bool {
952 let _ = (bus_index, channel, cc);
953 false
954 }
955
956 // =========================================================================
957 // MIDI 2.0 Mapping (IMidiMapping2)
958 // =========================================================================
959
960 /// Get all MIDI 1.0 CC assignments for bulk query.
961 ///
962 /// Override to provide mappings for DAW queries. This is more efficient
963 /// than individual `midi_cc_to_parameter` queries when there are many mappings.
964 ///
965 /// Default returns empty slice (no mappings).
966 fn midi1_assignments(&self) -> &[Midi1Assignment] {
967 &[]
968 }
969
970 /// Get all MIDI 2.0 controller assignments for bulk query.
971 ///
972 /// Override to provide MIDI 2.0 Registered/Assignable controller mappings.
973 ///
974 /// Default returns empty slice (no mappings).
975 fn midi2_assignments(&self) -> &[Midi2Assignment] {
976 &[]
977 }
978
979 // =========================================================================
980 // MIDI 2.0 Learn (IMidiLearn2)
981 // =========================================================================
982
983 /// Called when MIDI 1.0 CC input is received during learn mode.
984 ///
985 /// This is the MIDI 2.0 version of `on_midi_learn` with separate methods
986 /// for MIDI 1.0 and MIDI 2.0 controllers.
987 ///
988 /// Default returns `false` (not handled).
989 fn on_midi1_learn(&mut self, bus_index: i32, channel: u8, cc: u8) -> bool {
990 let _ = (bus_index, channel, cc);
991 false
992 }
993
994 /// Called when MIDI 2.0 controller input is received during learn mode.
995 ///
996 /// Override to implement MIDI 2.0 controller learning.
997 ///
998 /// Default returns `false` (not handled).
999 fn on_midi2_learn(&mut self, bus_index: i32, channel: u8, controller: Midi2Controller) -> bool {
1000 let _ = (bus_index, channel, controller);
1001 false
1002 }
1003
1004 // =========================================================================
1005 // Note Expression Controller (INoteExpressionController - VST3 SDK 3.5.0)
1006 // =========================================================================
1007
1008 /// Returns the number of supported note expression types.
1009 ///
1010 /// Override to advertise which note expressions your plugin supports
1011 /// (e.g., volume, pan, tuning for MPE instruments).
1012 ///
1013 /// Default returns 0 (no note expressions).
1014 fn note_expression_count(&self, bus_index: i32, channel: i16) -> usize {
1015 let _ = (bus_index, channel);
1016 0
1017 }
1018
1019 /// Returns information about a note expression type by index.
1020 ///
1021 /// Override to provide details about each supported expression type.
1022 ///
1023 /// Default returns None.
1024 fn note_expression_info(
1025 &self,
1026 bus_index: i32,
1027 channel: i16,
1028 index: usize,
1029 ) -> Option<NoteExpressionTypeInfo> {
1030 let _ = (bus_index, channel, index);
1031 None
1032 }
1033
1034 /// Converts a normalized note expression value to a display string.
1035 ///
1036 /// Override to provide custom formatting (e.g., "2.5 semitones" for tuning).
1037 ///
1038 /// Default returns the value as a percentage.
1039 fn note_expression_value_to_string(
1040 &self,
1041 bus_index: i32,
1042 channel: i16,
1043 type_id: u32,
1044 value: f64,
1045 ) -> String {
1046 let _ = (bus_index, channel, type_id);
1047 format!("{:.1}%", value * 100.0)
1048 }
1049
1050 /// Parses a string to a normalized note expression value.
1051 ///
1052 /// Override to support custom parsing.
1053 ///
1054 /// Default returns None (parsing not supported).
1055 fn note_expression_string_to_value(
1056 &self,
1057 bus_index: i32,
1058 channel: i16,
1059 type_id: u32,
1060 string: &str,
1061 ) -> Option<f64> {
1062 let _ = (bus_index, channel, type_id, string);
1063 None
1064 }
1065
1066 // =========================================================================
1067 // Keyswitch Controller (IKeyswitchController - VST3 SDK 3.5.0)
1068 // =========================================================================
1069
1070 /// Returns the number of keyswitches (articulations).
1071 ///
1072 /// Override for sample libraries and orchestral instruments that
1073 /// support keyswitching between articulations.
1074 ///
1075 /// Default returns 0 (no keyswitches).
1076 fn keyswitch_count(&self, bus_index: i32, channel: i16) -> usize {
1077 let _ = (bus_index, channel);
1078 0
1079 }
1080
1081 /// Returns information about a keyswitch by index.
1082 ///
1083 /// Override to provide keyswitch details for DAW expression maps.
1084 ///
1085 /// Default returns None.
1086 fn keyswitch_info(&self, bus_index: i32, channel: i16, index: usize) -> Option<KeyswitchInfo> {
1087 let _ = (bus_index, channel, index);
1088 None
1089 }
1090
1091 // =========================================================================
1092 // Physical UI Mapping (INoteExpressionPhysicalUIMapping - VST3 SDK 3.6.11)
1093 // =========================================================================
1094
1095 /// Returns mappings from physical UI controllers to note expressions.
1096 ///
1097 /// Override to define how MPE controllers (X-axis, Y-axis, Pressure)
1098 /// map to your plugin's note expression types.
1099 ///
1100 /// # Example
1101 /// ```ignore
1102 /// fn physical_ui_mappings(&self, _bus: i32, _channel: i16) -> &[PhysicalUIMap] {
1103 /// &[
1104 /// PhysicalUIMap::y_axis(note_expression::BRIGHTNESS),
1105 /// PhysicalUIMap::pressure(note_expression::EXPRESSION),
1106 /// ]
1107 /// }
1108 /// ```
1109 ///
1110 /// Default returns empty slice (no mappings).
1111 fn physical_ui_mappings(&self, bus_index: i32, channel: i16) -> &[PhysicalUIMap] {
1112 let _ = (bus_index, channel);
1113 &[]
1114 }
1115
1116 // =========================================================================
1117 // MPE Wrapper Support (IVst3WrapperMPESupport - VST3 SDK 3.6.12)
1118 // =========================================================================
1119
1120 /// Called to enable or disable MPE input processing.
1121 ///
1122 /// Override to handle MPE enable/disable notifications from wrappers.
1123 ///
1124 /// Default does nothing and returns true.
1125 fn enable_mpe_input_processing(&mut self, enabled: bool) -> bool {
1126 let _ = enabled;
1127 true
1128 }
1129
1130 /// Called when the MPE input device settings change.
1131 ///
1132 /// Override to receive MPE zone configuration from wrappers.
1133 ///
1134 /// Default does nothing and returns true.
1135 fn set_mpe_input_device_settings(&mut self, settings: MpeInputDeviceSettings) -> bool {
1136 let _ = settings;
1137 true
1138 }
1139}
1140
1141// =============================================================================
1142// MIDI Mapping Types
1143// =============================================================================
1144
1145/// Base assignment info for MIDI controller → parameter mapping.
1146#[derive(Debug, Clone, Copy)]
1147pub struct MidiControllerAssignment {
1148 /// Parameter ID this controller maps to.
1149 pub parameter_id: u32,
1150 /// MIDI bus index.
1151 pub bus_index: i32,
1152 /// MIDI channel (0-15).
1153 pub channel: u8,
1154}
1155
1156/// MIDI 1.0 CC assignment.
1157///
1158/// Maps a MIDI 1.0 Control Change to a parameter.
1159#[derive(Debug, Clone, Copy)]
1160pub struct Midi1Assignment {
1161 /// Base assignment info (parameter_id, bus, channel).
1162 pub assignment: MidiControllerAssignment,
1163 /// CC number (0-127).
1164 pub controller: u8,
1165}
1166
1167impl Midi1Assignment {
1168 /// Create a new MIDI 1.0 CC assignment.
1169 pub const fn new(parameter_id: u32, bus_index: i32, channel: u8, controller: u8) -> Self {
1170 Self {
1171 assignment: MidiControllerAssignment {
1172 parameter_id,
1173 bus_index,
1174 channel,
1175 },
1176 controller,
1177 }
1178 }
1179
1180 /// Create an assignment for the default bus and all channels.
1181 pub const fn simple(parameter_id: u32, controller: u8) -> Self {
1182 Self::new(parameter_id, 0, 0, controller)
1183 }
1184}
1185
1186/// MIDI 2.0 controller assignment.
1187///
1188/// Maps a MIDI 2.0 Registered/Assignable Controller to a parameter.
1189#[derive(Debug, Clone, Copy)]
1190pub struct Midi2Assignment {
1191 /// Base assignment info (parameter_id, bus, channel).
1192 pub assignment: MidiControllerAssignment,
1193 /// MIDI 2.0 controller identifier.
1194 pub controller: Midi2Controller,
1195}
1196
1197impl Midi2Assignment {
1198 /// Create a new MIDI 2.0 controller assignment.
1199 pub const fn new(
1200 parameter_id: u32,
1201 bus_index: i32,
1202 channel: u8,
1203 controller: Midi2Controller,
1204 ) -> Self {
1205 Self {
1206 assignment: MidiControllerAssignment {
1207 parameter_id,
1208 bus_index,
1209 channel,
1210 },
1211 controller,
1212 }
1213 }
1214
1215 /// Create an assignment for the default bus and all channels.
1216 pub const fn simple(parameter_id: u32, controller: Midi2Controller) -> Self {
1217 Self::new(parameter_id, 0, 0, controller)
1218 }
1219}