SUPERB
Examples

metronome_count_in

examples/flows/metronome_count_in.rs, the whole program

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

//! `metronome` is the click, and the bars it plays before a recording.
//!
//! ```text
//! metronome::enable / level / count_in
//! ```
//!
//! ```text
//! metronome::enable(&mut session, true)?;
//! metronome::level(&mut session, -6.0)?;
//! metronome::count_in(&mut session, 1)?;
//! record::start(&mut session, source)?;
//! ```
//!
//! ```text
//! cargo run --example metronome_count_in
//! ```

use superb::primitives::buffers::PlanarBuffer;
use superb::primitives::io::capture::capture_queue;
use superb::primitives::session::TrackKind;
use superb::primitives::transport::TransportState;
use superb::primitives::{Result, SampleRate, TimePosition};
use superb::{metronome, record, session, track, transport};

fn main() -> Result<()> {
    let rate = SampleRate::HZ_48K;
    let block = 256;
    let folder = std::env::temp_dir().join("metronome_count_in.superb");
    let mut session = session::create(&folder, rate, block)?;
    // 120 bpm (a beat is 0.5 s). vox armed.
    session::set_tempo(&mut session, 120.0)?;
    let vox = track::add(&mut session, "vox", TrackKind::Audio)?;
    track::arm(&mut session, vox, true)?;

    // Click at −6 dB. One render per beat, four beats.
    metronome::enable(&mut session, true)?;
    metronome::level(&mut session, -6.0)?;
    transport::start(&mut session)?;
    let blocks_per_beat = rate.hz() as usize / 2 / block;
    for beat in 1..=4 {
        let audio = session::steps::render_blocks(&mut session, blocks_per_beat)?;
        println!("beat {beat}: click peak {:.3}", audio.peak());
    }
    transport::stop(&mut session)?;
    metronome::enable(&mut session, false)?;

    // 1 bar count-in. Seek to 2.0 s. Recording rolls from 0, punches in at 2.0 s.
    metronome::count_in(&mut session, 1)?;
    transport::seek(&mut session, TimePosition::from_seconds(2.0, rate))?;
    session::steps::render_blocks(&mut session, 1)?;
    let (mut mic, blocks) = capture_queue(1, block, 64)?;
    record::start(
        &mut session,
        record::Source::Queue {
            blocks,
            channels: 1,
        },
    )?;
    let recording = session.recording.as_ref().expect("recording");
    println!(
        "rolling from {:.2} s, recording from {:.2} s",
        recording.started_at().to_seconds(rate),
        recording.punch_in().to_seconds(rate)
    );
    let mut sung = PlanarBuffer::new(1, block);
    sung.fill(0.3);
    let mut dropped_in = None;
    let mut count_in_peak = 0.0f32;
    let rendered = 3 * rate.hz() as usize / block;
    for n in 0..rendered {
        mic.push(&sung, (n * block) as u64 * 1_000_000 / rate.hz() as u64);
        let heard = session::steps::render_blocks(&mut session, 1)?;
        if transport::state(&session) == TransportState::Recording {
            dropped_in.get_or_insert(transport::playhead(&session).to_seconds(rate));
        } else {
            count_in_peak = count_in_peak.max(heard.peak());
        }
    }
    println!(
        "count-in click peak {count_in_peak:.3}; dropped into record at {:.2} s",
        dropped_in.expect("dropped into record")
    );
    let clips = record::stop(&mut session)?;
    let clip = session.memory.clip(clips[0])?;
    println!(
        "clip \"{}\" starts at {:.2} s and runs {:.2} s",
        clip.name,
        clip.start().to_seconds(rate),
        clip.length().to_seconds(rate)
    );
    Ok(())
}