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