Skip to main content

au_host_test/
au_host_test.rs

1//! Manual verification example for the AUv2 host: scans installed Audio
2//! Units, loads one real effect, and pushes a block of audio through it.
3//!
4//! Run with: cargo run --example au_host_test --features au
5
6use mkaudiolibrary::host::{PluginFormat, load, scan_au};
7use mkaudiolibrary::processor::AudioIO;
8
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}