SUPERB
Examples

decode_bounce

examples/primitives/decode_bounce.rs, the whole program

Run it with cargo run --example decode_bounce (see the examples for the features some need).

//! Two seconds of 330 Hz at amplitude 0.6, fader −3 dB, constant-power
//! centre. The bounce peaks at 0.300: 96000 frames at 48 kHz, then
//! 88200 frames at 44.1 kHz.
//!
//! ```text
//! cargo run --example decode_bounce
//! ```

use superb::primitives::buffers::gain::db_to_linear;
use superb::primitives::graph::nodes::SineNode;
use superb::primitives::io::files::{bounce_wav, decode, resampled_frames};
use superb::primitives::io::wav::WavFormat;
use superb::primitives::io::Engine;
use superb::primitives::mixer::{Mixer, PanLaw};
use superb::primitives::transport::Transport;
use superb::primitives::{Result, SampleRate};

fn main() -> Result<()> {
    let rate = SampleRate::HZ_48K;
    assert_eq!(rate.seconds_to_samples(1.0), 48_000);

    // Stereo 330 Hz at amplitude 0.6 into a track at −3 dB. Centre pan
    // uses constant power, so each side is multiplied by √2/2. The master
    // is balance at centre and does not change the level.
    let mut mixer = Mixer::new(2);
    let track = mixer.add_track("tone");
    let osc_node = SineNode::new(330.0, 0.6, 2);
    assert!((osc_node.frequency().get() - 330.0).abs() < 1e-6);
    assert!((osc_node.amplitude().get() - 0.6).abs() < 1e-6);
    let osc = mixer.graph_mut().add_node(Box::new(osc_node));
    mixer.connect_source(osc, track)?;
    let params = mixer.track_params(track)?;
    params.set_volume_db(-3.0);
    assert!((params.volume_db() + 3.0).abs() < 1e-3);
    assert!(params.pan().abs() < 1e-6);
    assert_eq!(params.pan_law(), PanLaw::ConstantPower);
    let master = mixer.bus_params(mixer.master())?;
    assert_eq!(master.pan_law(), PanLaw::Balance);
    assert!((master.gain() - 1.0).abs() < 1e-6);

    let mut engine = Engine::new(mixer, Transport::new(rate, 120.0));
    engine.transport_mut().play();

    // Two seconds is 96000 frames. The sine reaches full scale, so the
    // peak is 0.6 × −3 dB × √2/2, which prints as 0.300.
    let bounced = "decode_bounce_48k.wav";
    let frames = rate.hz() as usize * 2;
    assert_eq!(frames, 96_000);
    let rendered = bounce_wav(&mut engine, rate, 256, frames, bounced, WavFormat::Float32)?;
    let (side, _) = PanLaw::ConstantPower.gains(0.0);
    let expected = 0.6 * db_to_linear(-3.0) * side;
    assert_eq!(rendered.frames(), 96_000);
    assert_eq!(rendered.channels(), 2);
    assert!((rendered.peak() - expected).abs() < 1e-3);
    assert_eq!(format!("{:.3}", rendered.peak()), "0.300");
    println!(
        "bounced  {bounced}: {} frames, {} ch, peak {:.3}",
        rendered.frames(),
        rendered.channels(),
        rendered.peak()
    );

    // Reading the file back gives the same 96000 frames, 2.00 s, peak 0.300.
    let file = decode(bounced)?;
    assert_eq!(file.frames(), 96_000);
    assert_eq!(file.sample_rate.hz(), 48_000);
    assert_eq!(format!("{:.2}", file.duration_seconds()), "2.00");
    assert_eq!(format!("{:.3}", file.buffer.peak()), "0.300");
    println!(
        "decoded  {bounced}: {} frames @ {} ({:.2} s), peak {:.3}",
        file.frames(),
        file.sample_rate,
        file.duration_seconds(),
        file.buffer.peak()
    );

    // 96000 × 44100 / 48000 = 88200 frames. The float buffer still peaks
    // at 0.300; the file is written as 16-bit PCM.
    let target = SampleRate::CD;
    assert_eq!(target.hz(), 44_100);
    let converted = file.resampled_to(target)?;
    assert_eq!(resampled_frames(96_000, rate, target), 88_200);
    assert_eq!(converted.frames(), 88_200);
    assert_eq!(
        converted.frames(),
        resampled_frames(file.frames(), file.sample_rate, target)
    );
    assert_eq!(format!("{:.3}", converted.buffer.peak()), "0.300");
    let out = "decode_bounce_44k1.wav";
    converted.write_wav(out, WavFormat::Pcm16)?;
    println!(
        "exported {out}: {} frames @ {}, peak {:.3}",
        converted.frames(),
        converted.sample_rate,
        converted.buffer.peak()
    );

    // The 16-bit file reads back as 88200 frames at 44100 Hz.
    let check = decode(out)?;
    assert_eq!(check.frames(), 88_200);
    assert_eq!(check.sample_rate.hz(), 44_100);
    println!(
        "re-read  {out}: {} frames @ {}",
        check.frames(),
        check.sample_rate
    );
    Ok(())
}