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