Skip to main content

Crate kael_audio

Crate kael_audio 

Source
Expand description

§kael_audio

Cross-platform playback, device discovery, input capture, real-time and offline mixing, DSP, playlists, and lightweight spatial audio for desktop products built with Kael primitives or another UI stack.

The crate has two playback layers. AudioPlayer is a thread-local convenience API for file, credential-free HTTPS, and bounded in-memory media. Mixer and AudioEngine accept caller-defined sample sources and expose a device-frame master clock for A/V synchronization. Native AudioEngineHandle values provide cross-thread control while the host stream remains on its creation thread. A browser handle is deliberately main-thread and weak, so it cannot keep an AudioContext alive after its owning engine is dropped.

§Device-free mixing

use kael_audio::{BufferSource, Mixer};

fn main() -> kael_audio::Result<()> {
    let mut mixer = Mixer::new(48_000, 2);
    mixer.insert_voice(
        1,
        Box::new(BufferSource::new(vec![0.25; 512], 2)),
        1.0,
    )?;

    let mut output = vec![0.0; 512];
    mixer.process(&mut output);
    assert!(output.iter().all(|sample| (*sample - 0.25).abs() < 1e-6));
    Ok(())
}

render_offline and resample_linear return explicit errors for invalid or oversized buffers instead of silently truncating work. The linear resampler is suited to previews and UI audio; use a band-limited resampler for mastering.

§Live output and capture

use kael_audio::{AudioEngine, AudioInputStream, SineSource};

fn main() -> kael_audio::Result<()> {
    let engine = AudioEngine::new()?;
    let voice = engine.play_source(
        Box::new(SineSource::new(440.0, engine.sample_rate(), 0.2)),
        1.0,
    )?;
    engine.set_voice_gain(voice, 0.5)?;

    let _input = AudioInputStream::new(|samples, format| {
        // Copy or enqueue promptly; this slice is valid only for the callback.
        let _ = (samples, format);
    })?;

    if let Some(error) = engine.take_error() {
        eprintln!("audio output needs recovery: {error}");
    }
    Ok(())
}

Use output_devices, input_devices, and the corresponding from_*_device constructors for explicit routing. Enumeration returns a bounded list. Recreate an engine or input stream after the operating system changes or removes its device; asynchronous failures are available through take_error and take_input_error.

SampleSource::fill and input callbacks run on host audio threads. They must not block, perform I/O, or allocate in steady state. The output engine reserves its voice, command, and conversion storage before playback; supports at most AudioEngine::MAX_VOICES live or pending voices; coalesces gain commands; and clips submitted device samples to -1.0..=1.0. Ended source destruction runs on a cleanup thread. A panicking sample source is retired, while a panicking input callback is disabled and reported. With unwind-enabled builds, neither panic is allowed to unwind through the host callback boundary.

§Spatial and application state

SpatialAudioScene wraps mixer sources with inexpensive equal-power stereo panning and inverse-distance attenuation. It is intentionally not an HRTF or room-acoustics renderer. Scene mutation stays off the device callback, and a source retains its last safe gains if it is removed while a wrapper is alive.

AudioSession models category, activity, route, and interruption state inside the application. It does not claim ownership of an operating-system media session. AudioPlayer listeners run synchronously on the calling thread; dropping their Subscription unregisters them, while detach deliberately keeps them registered.

For policy-controlled downloads, fetch remote media with Kael’s networking battery and play a local file or bounded memory source. Direct player URLs are restricted to credential-free HTTPS.

§Browser builds

On wasm32-unknown-unknown, AudioPlayer uses HTMLAudioElement for credential-free HTTPS and bounded in-memory media. Live sample mixing uses the same Rust Mixer/DSP sources as desktop through a bounded, pull-driven AudioWorklet; microphone capture uses getUserMedia and a credit-bounded capture worklet. Device enumeration, graph construction, and permission are explicitly asynchronous:

use kael_audio::{
    AudioEngine, AudioInputStream, BrowserAudioEngineConfig, SineSource,
    input_devices_async, output_devices_async,
};

let (outputs, inputs) = (output_devices_async().await?, input_devices_async().await?);
let engine = AudioEngine::new_async_with_config(
    BrowserAudioEngineConfig::new(2, 256, 4)?,
).await?;
engine.play_source(
    Box::new(SineSource::new(440.0, engine.sample_rate(), 0.2)),
    1.0,
)?;

// Call directly from a click/key activation when browser autoplay policy
// requires it.
engine.resume_async().await?;

// Request directly from a user activation. The callback is delivered through
// bounded MessagePort credits on the browser main thread.
let input = AudioInputStream::new_async(|samples, format| {
    consume_promptly(samples, format);
}).await?;

The synchronous device, capture, and live-engine constructors still return an explicit browser error; native signatures are unchanged. Browser device labels may remain privacy-hidden until permission is granted. Only the default output route is portable today: selecting another enumerated sink returns OutputRoutingUnsupported rather than silently using the wrong speaker.

The default output window is four 256-frame chunks (1,024 frames, about 21.3 ms at 48 kHz) plus the browser/device latency. Bounds are 1–8 channels, 128–4,096 frames per chunk in 128-frame increments, and 2–32 pending chunks. This caps transferred audio at 1,048,576 f32 samples (4 MiB) per bridge window, with one additional chunk-sized assembly scratch buffer. Live output accepts at most 1,024 voices, lifecycle/pressure events are bounded, and device enumeration retains at most 1,024 descriptors with 1,024 bytes per browser string.

Kael does not require SharedArrayBuffer or cross-origin isolation. The AudioWorklet owns real-time rendering/capture, but Rust source mixing and input callbacks run on browser message turns; a blocked main thread can therefore produce an OutputUnderrun or CaptureOverflow. The bridge is event-driven and has no main-thread polling loop. Products requiring worklet-owned Wasm DSP, sub-10-ms synthesis, HRTF/room processing, or very large game-audio graphs need a specialized isolated audio backend; this adapter does not claim native callback parity for those workloads.

AudioWorklet and microphone capture require a secure context (localhost qualifies). A strict Content Security Policy must permit Kael’s temporary blob: worklet module. Resume and permission prompts should begin inside a transient user activation. Browsers expose no portable way to cancel a pending getUserMedia permission prompt; once it resolves, Kael stops granted tracks on every later setup failure, cancellation, explicit close, or drop.

Browser filesystem paths remain explicitly unsupported. Device-free Mixer, DSP, resampling, playlists, session state, and lightweight equal-power stereo spatial processing remain available. See the browser audio guide for the full boundary and release probe.

The API reference is available on docs.rs. See the Kael repository for workspace architecture and production guidance.

§License

Licensed under the Apache License, Version 2.0. See LICENSE-APACHE.

Re-exports§

pub use capture::AudioInputConfig;
pub use capture::AudioInputStream;
pub use devices::AudioInputDevice;
pub use devices::AudioOutputDevice;
pub use devices::default_input_device;
pub use devices::default_output_device;
pub use devices::input_devices;
pub use devices::output_devices;
pub use dsp::Biquad;
pub use dsp::Compressor;
pub use dsp::OnePole;
pub use dsp::WaveformPeak;
pub use dsp::waveform_peaks;
pub use mixer::AudioClock;
pub use mixer::AudioEngine;
pub use mixer::AudioEngineHandle;
pub use mixer::BufferSource;
pub use mixer::Mixer;
pub use mixer::SampleSource;
pub use mixer::SineSource;
pub use mixer::VoiceId;
pub use mixer::resample_linear;
pub use player::AudioPlayer;
pub use player::AudioSource;
pub use player::PlaybackState;
pub use player::Subscription;
pub use player::Track;
pub use playlist::Playlist;
pub use playlist::RepeatMode;
pub use session::AudioCategory;
pub use session::AudioRoute;
pub use session::AudioSession;
pub use session::Interruption;
pub use spatial::SpatialAudioScene;
pub use spatial::SpatialSourceId;

Modules§

capture
Cross-platform microphone and audio-input capture. Cross-platform microphone and audio-input capture.
devices
Cross-platform audio device discovery. Cross-platform audio device discovery.
dsp
Audio DSP processors: gain, pan, filtering, limiting, fades, metering. Audio DSP processors for the mixer: gain, pan, filtering, limiting, fades.
effects
Shared clamps for playback values. Shared clamps for audio values.
mixer
Real-time mixing graph with a device-sample-counter master clock. Real-time audio mixing graph with a device-sample-counter master clock.
platform
Platform metadata for audio services. Platform metadata for audio services.
player
Audio player and track types. Audio player and track types.
playlist
Playlist management. Playlist management.
session
Audio-session state. Audio-session state.
spatial
Lightweight spatial-audio scene and stereo source processing. Lightweight spatial-audio scene and stereo source processing.

Enums§

AudioPlaybackError
An error that can occur while preparing or controlling audio playback.

Type Aliases§

Result
Result<T, Error>