Skip to main content

Module audio

Module audio 

Source
Expand description

Audio as data: sounds are session resources, and playing one is a command the frame driver drains and applies to the device.

Register a sound’s bytes with Core::add_sound (a SoundId), then play it one of three ways: NodeSpec::click_sound / hover_sound on a node; Ui::audio with an AudioSpec, a playback that runs for as long as the view declares the node; or Core::play / Ui::play with PlayOptions, plus stop, set_volume, pause, resume and set_master_volume for a host that holds the core. The windowed runner drains the resulting AudioCommands; a host driving its own loop drains Core::take_audio_commands. The core never touches a device, so a headless test asserts on the queue.

use kui_core::{AudioCommand, AudioSpec, Core, NodeSpec, PlayOptions, Size};

let mut core = Core::new();
let chime = core.add_sound(b"RIFF....WAVE".to_vec()); // the file's bytes

// Imperative: start it now at half volume and ask for an `ended` event.
let playback = core.play(chime, PlayOptions::default().volume(0.5).tag("chime"));

// Declarative, in a view: a click sound, and a loop that plays while declared.
let mut ui = core.frame(Size::new(400.0, 300.0), 1.0);
ui.leaf_keyed("go", NodeSpec::row().size(80.0, 24.0).on_click("go").click_sound(chime));
ui.audio_keyed("music", AudioSpec::new(chime).looped().volume(0.3));
ui.finish();

// What a driver does with the queue.
for cmd in core.take_audio_commands() {
    match cmd {
        AudioCommand::Play { playback, sound, looped, .. } => {
            println!("play {sound:?} as {playback:?} (loop: {looped})");
        }
        AudioCommand::Stop { playback, .. } => println!("stop {playback:?}"),
        other => println!("{}", other.kind_name()),
    }
}
core.stop(playback, 0.0);

A playback started with a tag comes back as {kind:"sound", phase:"ended", playback, tag} on the origin that started it once the driver reports it finished (Core::audio_ended), not when something stopped it. A play the device refused comes back as phase:"refused" (Core::audio_refused), so nothing waits on an ended that cannot come. A stop that cut a one-shot off mid-sound is reported as the crate::diag::TRUNCATED_PLAYBACK warning (Core::audio_truncated); AudioSpec::finish is the usual answer.

Structs§

AudioSpec
What an audio node declares each frame (Core::audio_node).
AudioStore
Playback bookkeeping on the session: the command queue, the tagged playbacks awaiting their ended event, and the audio nodes’ retained playbacks. The queue and the ids are the session’s, because the process has one device; the mounts are keyed by window as well as by node, because each is reconciled against one window’s frame (finish_frame hands in the window it finished, and only that window’s slice is diffed).
PlayOptions
How to start a playback (Core::play).
PlaybackId
One playback instance. Allocated by the core when the play command is queued, so callers get it synchronously without a driver round trip; 0 is never issued.

Enums§

AudioCommand
An audio intent for the frame driver. Durations are ms; volumes are linear amplitude. Drivers ignore playbacks they no longer hold.
Why
Why a one-shot playback was cut off — what diag::TRUNCATED_PLAYBACK reports once the driver confirms the sound was still running.