Skip to main content

nice_plug_core/
audio_setup.rs

1//! Types and definitions surrounding a plugin's audio IO setup.
2
3use std::num::NonZeroU32;
4
5use crate::buffer::Buffer;
6
7/// A description of a plugin's audio IO configuration. The [`Plugin`][crate::plugin::Plugin]
8/// defines a list of supported audio IO configs, with the first one acting as the default layout.
9/// Depending on the plugin API, the host may pick a different configuration from the list and use
10/// that instead. The final chosen configuration is passed as an argument to the
11/// [`Plugin::initialize()`][crate::plugin::Plugin::initialize] function so the plugin can allocate
12/// its data structures based on the number of audio channels it needs to process.
13#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
14pub struct AudioIOLayout {
15    /// The number of main input channels for the plugin, if it has a main input port. This can be
16    /// set to `None` if the plugin does not have one.
17    pub main_input_channels: Option<NonZeroU32>,
18    /// The number of main output channels for the plugin, if it has a main output port. This can be
19    /// set to `None` if the plugin does not have one.
20    pub main_output_channels: Option<NonZeroU32>,
21    /// The plugin's additional sidechain inputs, if it has any. Use the [`new_nonzero_u32()`]
22    /// function to construct these values until const `Option::unwrap()` gets stabilized
23    /// (<https://github.com/rust-lang/rust/issues/67441>).
24    pub aux_input_ports: &'static [NonZeroU32],
25    /// The plugin's additional outputs, if it has any. Use the [`new_nonzero_u32()`] function to
26    /// construct these values until const `Option::unwrap()` gets stabilized
27    /// (<https://github.com/rust-lang/rust/issues/67441>).
28    pub aux_output_ports: &'static [NonZeroU32],
29
30    /// Optional names for the audio ports. Defining these can be useful for plugins with multiple
31    /// output and input ports.
32    pub names: PortNames,
33}
34
35/// Construct a `NonZeroU32` value at compile time. Equivalent to `NonZeroU32::new(n).unwrap()`.
36pub const fn new_nonzero_u32(n: u32) -> NonZeroU32 {
37    match NonZeroU32::new(n) {
38        Some(n) => n,
39        None => panic!("'new_nonzero_u32()' called with a zero value"),
40    }
41}
42
43/// Contains auxiliary (sidechain) input and output buffers for a process call.
44pub struct AuxiliaryBuffers<'a> {
45    /// Buffers for all auxiliary (sidechain) inputs defined for this plugin. The data in these
46    /// buffers can safely be overwritten. Auxiliary inputs can be defined using the
47    /// [`AudioIOLayout::aux_input_ports`] field.
48    pub inputs: &'a mut [Buffer<'a>],
49    /// Buffers for all auxiliary outputs defined for this plugin. Auxiliary outputs can be defined using the
50    /// [`AudioIOLayout::aux_output_ports`] field.
51    pub outputs: &'a mut [Buffer<'a>],
52}
53
54/// Contains names for the ports defined in an `AudioIOLayout`. Setting these is optional, but it
55/// makes working with multi-output plugins much more convenient.
56///
57/// All of these names should start with a capital letter to be consistent with automatically
58/// generated names.
59#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
60pub struct PortNames {
61    /// The name for the audio IO layout as a whole. Useful when a plugin has multiple distinct
62    /// layouts. Will be generated if not set.
63    pub layout: Option<&'static str>,
64
65    /// The name for the main input port. Will be generated if not set.
66    pub main_input: Option<&'static str>,
67    /// The name for the main output port. Will be generated if not set.
68    pub main_output: Option<&'static str>,
69    /// Names for auxiliary (sidechain) input ports. Will be generated if not set or if this slice
70    /// does not contain enough names.
71    pub aux_inputs: &'static [&'static str],
72    /// Names for auxiliary output ports. Will be generated if not set or if this slice does not
73    /// contain enough names.
74    pub aux_outputs: &'static [&'static str],
75}
76
77/// Configuration for (the host's) audio buffers.
78#[derive(Debug, Clone, Copy, PartialEq)]
79pub struct BufferConfig {
80    /// The current sample rate.
81    pub sample_rate: f32,
82    /// The minimum buffer size the host will use. This may not be set.
83    pub min_buffer_size: Option<u32>,
84    /// The maximum buffer size the host will use. The plugin should be able to accept variable
85    /// sized buffers up to this size, or between the minimum and the maximum buffer size if both
86    /// are set.
87    pub max_buffer_size: u32,
88    /// The current processing mode. The host will reinitialize the plugin any time this changes.
89    pub process_mode: ProcessMode,
90}
91
92/// The plugin's current processing mode. Exposed through [`BufferConfig::process_mode`]. The host
93/// will reinitialize the plugin whenever this changes.
94#[derive(Debug, Clone, Copy, PartialEq, Eq)]
95pub enum ProcessMode {
96    /// The plugin is processing audio in real time at a fixed rate.
97    Realtime,
98    /// The plugin is processing audio at a real time-like pace, but at irregular intervals. The
99    /// host may do this to process audio ahead of time to loosen realtime constraints and to reduce
100    /// the chance of xruns happening. This is only used by VST3.
101    Buffered,
102    /// The plugin is rendering audio offline, potentially faster than realtime ('freewheeling').
103    /// The host will continuously call the process function back to back until all audio has been
104    /// processed.
105    Offline,
106}
107
108impl AudioIOLayout {
109    /// [`AudioIOLayout::default()`], but as a const function. Used when initializing
110    /// `Plugin::AUDIO_IO_LAYOUTS`. (<https://github.com/rust-lang/rust/issues/67792>)
111    pub const fn const_default() -> Self {
112        Self {
113            main_input_channels: None,
114            main_output_channels: None,
115            aux_input_ports: &[],
116            aux_output_ports: &[],
117            names: PortNames::const_default(),
118        }
119    }
120
121    /// A descriptive name for the layout. This is taken from `PortNames::layout` if set. Otherwise
122    /// it is generated based on the layout.
123    pub fn name(&self) -> String {
124        if let Some(name) = self.names.layout {
125            return name.to_owned();
126        }
127
128        // If the name is not set then we'll try to come up with something descriptive
129        match (
130            self.main_input_channels
131                .map(NonZeroU32::get)
132                .unwrap_or_default(),
133            self.main_output_channels
134                .map(NonZeroU32::get)
135                .unwrap_or_default(),
136            self.aux_input_ports.len(),
137            self.aux_output_ports.len(),
138        ) {
139            (0, 0, 0, 0) => String::from("Empty"),
140            (_, 1, 0, _) | (1, 0, _, _) => String::from("Mono"),
141            (_, 2, 0, _) | (2, 0, _, _) => String::from("Stereo"),
142            (_, 1, _, _) => String::from("Mono with sidechain"),
143            (_, 2, _, _) => String::from("Stereo with sidechain"),
144            // These probably, hopefully won't occur
145            (i, o, 0, 0) => format!("{i} inputs, {o} outputs"),
146            (i, o, _, 0) => format!("{i} inputs, {o} outputs, with sidechain"),
147            // And these don't make much sense, suggestions for something better are welcome
148            (i, o, 0, aux_o) => format!("{i} inputs, {o}*{} outputs", aux_o + 1),
149            (i, o, aux_i, aux_o) => format!("{i}*{} inputs, {o}*{} outputs", aux_i + 1, aux_o + 1),
150        }
151    }
152
153    /// The name for the main input port. Either generated or taken from the `names` field.
154    pub fn main_input_name(&self) -> String {
155        self.names.main_input.unwrap_or("Input").to_owned()
156    }
157
158    /// The name for the main output port. Either generated or taken from the `names` field.
159    pub fn main_output_name(&self) -> String {
160        self.names.main_input.unwrap_or("Output").to_owned()
161    }
162
163    /// The name for the auxiliary input port with the given index. Either generated or taken from
164    /// the `names` field.
165    pub fn aux_input_name(&self, idx: usize) -> Option<String> {
166        if idx >= self.aux_input_ports.len() {
167            None
168        } else {
169            match self.names.aux_inputs.get(idx) {
170                Some(name) => Some(String::from(*name)),
171                None if self.aux_input_ports.len() == 1 => Some(String::from("Sidechain Input")),
172                None => Some(format!("Sidechain Input {}", idx + 1)),
173            }
174        }
175    }
176
177    /// The name for the auxiliary output port with the given index. Either generated or taken from
178    /// the `names` field.
179    pub fn aux_output_name(&self, idx: usize) -> Option<String> {
180        if idx >= self.aux_output_ports.len() {
181            None
182        } else {
183            match self.names.aux_outputs.get(idx) {
184                Some(name) => Some(String::from(*name)),
185                None if self.aux_output_ports.len() == 1 => Some(String::from("Auxiliary Output")),
186                None => Some(format!("Auxiliary Output {}", idx + 1)),
187            }
188        }
189    }
190}
191
192impl PortNames {
193    /// [`PortNames::default()`], but as a const function. Used when initializing
194    /// `Plugin::AUDIO_IO_LAYOUTS`. (<https://github.com/rust-lang/rust/issues/67792>)
195    pub const fn const_default() -> Self {
196        Self {
197            layout: None,
198            main_input: None,
199            main_output: None,
200            aux_inputs: &[],
201            aux_outputs: &[],
202        }
203    }
204}