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: &[],
        }
    }
}