//! 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, /// 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, /// 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 /// (). 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 /// (). 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, /// 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`. () 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 { 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 { 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`. () pub const fn const_default() -> Self { Self { layout: None, main_input: None, main_output: None, aux_inputs: &[], aux_outputs: &[], } } }