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}