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>
impl<'a> AudioIO<'a>
Sourcepub 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
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?
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
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}