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