Skip to main content

AudioIO

Struct AudioIO 

Source
pub struct AudioIO<'a> {
    pub input: Option<&'a [&'a [f32]]>,
    pub output: &'a mut [&'a mut [f32]],
    pub sidechain_in: Option<&'a [&'a [f32]]>,
    pub sidechain_out: Option<&'a mut [&'a mut [f32]]>,
}
Expand description

Audio I/O view for buffer-based processing.

A thin, non-owning wrapper over per-channel sample slices - input and sidechain-input channels are borrowed immutably, output and sidechain-output channels mutably. AudioIO doesn’t allocate or own any sample storage itself: the caller (typically a host driving Processor::run once per audio callback) owns the actual buffers for the life of the stream and constructs a new AudioIO borrowing into them for each block, matching how real audio APIs (VST3’s AudioBusBuffers, CoreAudio’s AudioBufferList) hand a plugin raw pointers into host-owned memory rather than copying into a buffer the plugin owns.

input, sidechain_in, and sidechain_out are Option: a generator plugin (synth, noise source, …) may have no audio input at all, and sidechain busses are commonly absent. output is always present - every processor produces some output, even if it’s silence.

§Example

fn process(audio: &mut AudioIO)
{
    let Some(input) = audio.input else { return };
    for ch in 0..input.len().min(audio.output.len())
    {
        let (input, output) = (input[ch], &mut audio.output[ch]);
        for i in 0..input.len().min(output.len())
        {
            output[i] = input[i];
        }
    }
}

// Building one for a call, from caller-owned storage:
let input_storage = vec![vec![0.0f32; 512]; 2];
let mut output_storage = vec![vec![0.0f32; 512]; 2];
let input: Vec<&[f32]> = input_storage.iter().map(Vec::as_slice).collect();
let mut output: Vec<&mut [f32]> = output_storage.iter_mut().map(Vec::as_mut_slice).collect();
let mut audio = AudioIO::new(Some(&input), &mut output, None, None);
process(&mut audio);

Fields§

§input: Option<&'a [&'a [f32]]>

Input audio channels, borrowed from caller-owned storage. None for a plugin with no audio input (e.g. an instrument/generator).

§output: &'a mut [&'a mut [f32]]

Output audio channels, borrowed mutably from caller-owned storage.

§sidechain_in: Option<&'a [&'a [f32]]>

Sidechain input channels. None when no sidechain bus is connected.

§sidechain_out: Option<&'a mut [&'a mut [f32]]>

Sidechain output channels. None when no sidechain bus is connected.

Implementations§

Source§

impl<'a> AudioIO<'a>

Source

pub fn new( input: Option<&'a [&'a [f32]]>, output: &'a mut [&'a mut [f32]], sidechain_in: Option<&'a [&'a [f32]]>, sidechain_out: Option<&'a mut [&'a mut [f32]]>, ) -> Self

Wrap existing per-channel sample slices - no allocation.

Examples found in repository?
examples/vst3_host_test.rs (line 62)
11fn main() {
12    let dir = Path::new("/Library/Audio/Plug-Ins/VST3");
13    let found = scan_vst3(dir);
14    println!("Found {} VST3 classes in {}", found.len(), dir.display());
15    for d in found.iter().take(8) {
16        println!("  [{}] {} - {}", d.format, d.vendor, d.name);
17    }
18
19    let descriptor = found
20        .first()
21        .expect("no VST3 plugins found to test against");
22    println!("\nLoading: {} - {}", descriptor.vendor, descriptor.name);
23
24    let mut plugin = load(descriptor).expect("failed to load VST3 plugin");
25    println!(
26        "Loaded '{}' by '{}': {} in / {} out, {} parameters",
27        plugin.name(),
28        plugin.vendor(),
29        plugin.num_inputs(),
30        plugin.num_outputs(),
31        plugin.num_parameters()
32    );
33
34    plugin.prepare(48000, 512).expect("prepare failed");
35    plugin.set_active(true).expect("set_active failed");
36
37    for i in 0..plugin.num_parameters().min(5) {
38        println!(
39            "  param[{}] '{}' = {:.4}",
40            i,
41            plugin.parameter_name(i),
42            plugin.get_parameter(i)
43        );
44    }
45
46    let in_channels = plugin.num_inputs().max(1);
47    let out_channels = plugin.num_outputs().max(1);
48
49    // AudioIO borrows into caller-owned storage rather than allocating its
50    // own - own the actual sample memory here, for the life of this block.
51    let mut input_storage = vec![vec![0.0f32; 512]; in_channels];
52    let mut output_storage = vec![vec![0.0f32; 512]; out_channels];
53
54    for channel in &mut input_storage {
55        for (i, sample) in channel.iter_mut().enumerate() {
56            *sample = (i as f32 / 48000.0 * 440.0 * std::f32::consts::TAU).sin() * 0.25;
57        }
58    }
59
60    let input: Vec<&[f32]> = input_storage.iter().map(Vec::as_slice).collect();
61    let mut output: Vec<&mut [f32]> = output_storage.iter_mut().map(Vec::as_mut_slice).collect();
62    let mut audio = AudioIO::new(Some(&input), &mut output, None, None);
63
64    plugin.process(&mut audio);
65
66    let mut peak = 0.0f32;
67    let mut nonzero = false;
68    for channel in &output_storage {
69        for &s in channel.iter() {
70            if s != 0.0 {
71                nonzero = true;
72            }
73            peak = peak.max(s.abs());
74        }
75    }
76
77    println!("\nOutput peak: {:.6}, nonzero: {}", peak, nonzero);
78    println!(
79        "OK: VST3 host rendered a real block of audio through '{}'.",
80        plugin.name()
81    );
82}
More examples
Hide additional examples
examples/au_host_test.rs (line 56)
9fn main() {
10    let found = scan_au();
11    println!("Found {} Audio Units", found.len());
12    for d in found.iter().take(5) {
13        println!(
14            "  [{}] {} - {} ({})",
15            d.format, d.vendor, d.name, d.category
16        );
17    }
18
19    let effect = found
20        .iter()
21        .find(|d| d.category == "Effect")
22        .expect("no Effect-type Audio Unit found to test against");
23    println!("\nLoading: {} - {}", effect.vendor, effect.name);
24    assert_eq!(effect.format, PluginFormat::Au);
25
26    let mut plugin = load(effect).expect("failed to load AU");
27    println!(
28        "Loaded '{}' by '{}': {} in / {} out, {} parameters",
29        plugin.name(),
30        plugin.vendor(),
31        plugin.num_inputs(),
32        plugin.num_outputs(),
33        plugin.num_parameters()
34    );
35
36    plugin.prepare(48000, 512).expect("prepare failed");
37    plugin.set_active(true).expect("set_active failed");
38
39    let in_channels = plugin.num_inputs();
40    let out_channels = plugin.num_outputs().max(1);
41
42    // AudioIO borrows into caller-owned storage rather than allocating its
43    // own - own the actual sample memory here, for the life of this block.
44    let mut input_storage = vec![vec![0.0f32; 512]; in_channels];
45    let mut output_storage = vec![vec![0.0f32; 512]; out_channels];
46
47    // Feed a simple 440Hz sine into every input channel.
48    for channel in &mut input_storage {
49        for (i, sample) in channel.iter_mut().enumerate() {
50            *sample = (i as f32 / 48000.0 * 440.0 * std::f32::consts::TAU).sin() * 0.25;
51        }
52    }
53
54    let input: Vec<&[f32]> = input_storage.iter().map(Vec::as_slice).collect();
55    let mut output: Vec<&mut [f32]> = output_storage.iter_mut().map(Vec::as_mut_slice).collect();
56    let mut audio = AudioIO::new(Some(&input), &mut output, None, None);
57
58    plugin.process(&mut audio);
59
60    let mut peak = 0.0f32;
61    let mut nonzero = false;
62    for channel in &output_storage {
63        for &s in channel.iter() {
64            if s != 0.0 {
65                nonzero = true;
66            }
67            peak = peak.max(s.abs());
68        }
69    }
70
71    println!("Output peak: {:.6}, nonzero: {}", peak, nonzero);
72    println!(
73        "OK: AU host rendered a real block of audio through '{}'.",
74        plugin.name()
75    );
76
77    // Also verify parameter enumeration/get/set against whichever scanned
78    // Effect actually exposes AU parameters (Doomsday above may not).
79    println!("\nSearching for an Effect with exposed parameters...");
80    for candidate in found.iter().filter(|d| d.category == "Effect").take(40) {
81        let Ok(mut p) = load(candidate) else {
82            continue;
83        };
84        if p.prepare(48000, 512).is_err() {
85            continue;
86        }
87        if p.num_parameters() == 0 {
88            continue;
89        }
90
91        println!(
92            "  {} - {}: {} parameters",
93            candidate.vendor,
94            candidate.name,
95            p.num_parameters()
96        );
97        for i in 0..p.num_parameters().min(3) {
98            let before = p.get_parameter(i);
99            p.set_parameter(i, 0.75);
100            let after = p.get_parameter(i);
101            println!(
102                "    [{}] '{}': {:.4} -> set 0.75 -> {:.4}",
103                i,
104                p.parameter_name(i),
105                before,
106                after
107            );
108        }
109        break;
110    }
111}

Trait Implementations§

Source§

impl Default for AudioIO<'_>

Source§

fn default() -> Self

An empty view (no channels). Mainly useful as a placeholder before the first real block is available.

Auto Trait Implementations§

§

impl<'a> !UnwindSafe for AudioIO<'a>

§

impl<'a> Freeze for AudioIO<'a>

§

impl<'a> RefUnwindSafe for AudioIO<'a>

§

impl<'a> Send for AudioIO<'a>

§

impl<'a> Sync for AudioIO<'a>

§

impl<'a> Unpin for AudioIO<'a>

§

impl<'a> UnsafeUnpin for AudioIO<'a>

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.