Skip to main content

Module mixer

Module mixer 

Source
Expand description

A real-time mixer for games. Requires the mixer feature.

Mixer::new returns two halves. The Mixer goes to the audio callback, which calls Mixer::render for every buffer. The MixerHandle stays with the game, which starts voices, changes gains and pans, and stops them.

use audiobox::mixer::{Fade, Loop, Mixer, MixerConfig, Play};
use audiobox::Audio;
use std::sync::Arc;

let (mut mixer, mut handle) = Mixer::new(MixerConfig {
    buses: vec!["music".into(), "effects".into()],
    ..MixerConfig::default()
})?;
let music = handle.bus("music").unwrap();
let effects = handle.bus("effects").unwrap();

let beep = Arc::new(Audio::new(vec![vec![0.25; 4800]], 48_000)?);
handle.play(beep.clone(), &Play { bus: effects, pan: -0.5, ..Play::default() })?;
handle.play(beep, &Play { bus: music, looping: Loop::Whole, fade_in: Fade::linear(0.5), ..Play::default() })?;
handle.set_bus_gain(music, 0.5, Fade::linear(1.0))?;

// In the audio callback: interleaved stereo at 48 kHz.
let mut buffer = [0.0f32; 2 * 512];
mixer.render(&mut buffer);

§Sounds

A voice plays a Sound: decoded Audio shared through an Arc, so that many voices play one sound without copying it, or a Stream that produces frames as it plays, such as a Decoder of compressed music or a Synth rendering a score. Sounds are mono or stereo.

Each voice has a gain, a pan, a bus, a priority, an optional fade-in, and a loop: off, the whole sound, or a region between two frames. Gain, pan and bus gain changes, and stops, can fade over a duration with a FadeCurve.

A voice also has a playback rate, which changes speed and pitch together, and a pitch offset in semitones on top of it. Both can change while the voice plays, with a ramp. Variation draws a random pitch and gain for each start of a repeated sound, such as a footstep, from a generator seeded by MixerConfig::seed.

A voice can start partway through its sound (Play::start), and be paused and resumed, optionally with a fade; it keeps its position, its fades and its loop. Pausing a bus pauses every voice routed through it and no others, so a pause menu can stop the game’s sounds and keep its own. MixerHandle::voice_status reads a voice’s state and position as of the last render, without blocking the audio side. A loop of a stream seeks it back, so a stream that loops must support seeking to the loop start.

§Voices and buses

By default at most MixerConfig::max_voices voices play at once. When a voice starts while that many play, it takes the place of the playing voice with the lowest priority, the oldest among equals, if that priority is not higher than its own. The voice it replaces fades out over 5 ms and reports MixerEvent::Stolen. Otherwise the new voice does not play and reports MixerEvent::Rejected. Give music a high priority so that effects never take its place.

Opt into MixerConfig::virtual_voices to retain up to 4096 logical sources while selecting only max_voices physical voices. Quiet, distant and lower-ranked voices advance their clocks as VoiceState::Virtual. Memory sources skip PCM and rebuild history on promotion; stateful streams still consume/discard PCM. Selection uses priority, estimated source-peak level and start order at fixed 32-frame boundaries and command frames. Virtual/paused voices count against logical and instance capacity. Physical stop fades have eight extra budget slots.

A sound can also limit its own voices with Instances: at most so many at once, with a policy for one more, and a minimum gap between starts. A refused start reports MixerEvent::Limited.

A voice can have a 2D position relative to the listener (Play::position, MixerHandle::set_position), which sets its pan and a distance gain through the mixer’s Space2d.

Every voice plays through a bus: the master bus, or one of the named buses of MixerConfig::buses, which by default play through the master. BusRouting and MixerHandle::set_routing opt into nested groups and shared pre/post-fader BusSend routes, with stereo bus balance and explicit mute/solo path rules. A voice’s samples are scaled by its gain, its fades, its pan, its bus’s gain and the master gain. effects adds ordered bus chains, ramped filters, compression/ducking, delay and reverb. Sidechain dependencies order named buses before master processing.

Many voices together can go beyond full scale. MixerConfig::limiter enables a MasterLimiter on the master bus, off by default, which keeps the output at or below its ceiling and passes quieter signals unchanged. Its look-ahead, 0 by default, delays the output by MixerHandle::latency frames.

§Rendering

Mixer::render fills an interleaved buffer of the output’s channels (1/2/4/6/8) at the output’s sample rate. It writes out.len() / channels whole frames and sets any remaining samples to 0; a buffer shorter than a frame renders nothing, but still applies the changes that are due.

Sources at another sample rate, and voices whose rate or pitch differs from 1, are resampled with a 16-tap windowed-sinc filter (Blackman window, 256 interpolated phases, cutoff at 0.9 of the lower Nyquist frequency). It is designed for ratios up to 2:1, such as 44.1 kHz to 48 kHz; larger downsampling ratios alias more. A source at the output rate is copied sample for sample.

Play::resampling can explicitly select ResamplingQuality::Linear, cheaper two-point interpolation without an anti-alias filter. Sinc remains the default.

Panning a mono source follows a constant-power law: cos θ on the left and sin θ on the right, with θ = (pan + 1)·π/4, so the centre is -3 dB on each side. A stereo source is balanced: the centre leaves it unchanged and the far side follows √2·cos θ or √2·sin θ, limited to 1. A mono output sums mono sources and averages the two channels of stereo ones.

§Real-time safety

Mixer::render allocates nothing, takes no lock that can wait and does not block. Changes reach it through a bounded single-producer, single-consumer queue of the crate’s own, allocated when the mixer is created. The handle and the mixer each own one end and one counter, so a slot is only ever used by one side at a time; each slot is taken with try_lock, which never blocks and, under this protocol, is never contended. A full queue is reported to the handle; neither side ever waits. Voices and their buffers are built by the handle, on the game’s thread. Finished voices go back to the handle through a second such queue and are freed by MixerHandle::poll_event, so the audio thread frees no memory either. Poll events regularly, such as once per game frame.

§Timing and determinism

Every change applies at an output frame. MixerHandle::at schedules changes at an exact frame, counted from the first rendered frame; they apply there whatever the buffer sizes, splitting a buffer if needed. Changes sent without a frame apply at the start of the next render. The same changes at the same frames give the same samples on every platform: resampling, panning and fades use only basic IEEE arithmetic and the crate’s own sine series.

Re-exports§

pub use spatial::Attenuation;
pub use spatial::DistanceModel;
pub use spatial::Emitter;
pub use spatial::Listener;
pub use spatial::OutputLayout;
pub use spatial::Placement;
pub use spatial::SpatialConfig;

Modules§

effects
Prepared bus effect chains. All times and parameter ramps are sample based.
events
Named sound events, parsed and prepared on the control thread (events feature).
music
Music that follows a game: transitions, cues and intensity layers. Requires music.
spatial
Point-source 3D placement, horizontal speaker layouts and an explicit stereo fold. Coordinates are right-handed: +X right, +Y up, default forward -Z. Positions and distances use the same units; velocities and sound speed use units/second. Velocity controls Doppler only: the host supplies updated positions explicitly.

Structs§

BusId
A bus: BusId::MASTER or a named bus from MixerHandle::bus.
BusRouting
One named bus’s complete routing/settings. Omitted buses retain one unity post-fader output to master. The master is a sink, configured through its existing gain/pause/effect APIs, and cannot appear as a source here.
BusSend
Commands
Commands applied at one moment: see MixerHandle::now and MixerHandle::at.
Fade
A change over a duration, with a curve. Zero seconds changes at once.
Instances
Limits for one sound: at most max voices with the same key play at once, and a new one starts no sooner than min_gap seconds after the last, so that ten coins collected together do not sum into distortion.
MasterLimiter
Settings of the master bus’s limiter, enabled with MixerConfig::limiter.
Mixer
The audio thread’s half of the mixer. See the module documentation.
MixerConfig
MixerHandle
The game’s half of the mixer: starts, changes and stops voices.
Play
How a voice starts playing.
RoutingId
A successfully queued edit ID. Poll RoutingApplied/Rejected for its outcome.
Space2d
How a 2D position relative to the listener becomes a pan and a gain.
Variation
Random variation for sounds that repeat, such as footsteps, coins and impacts.
VirtualVoices
Opt-in logical voice pool. MixerConfig::max_voices remains the physical budget.
VoiceId
Identifies a voice in commands and events. Unique for the life of a mixer.
VoiceStatus
A voice’s state and its position in its sound, in source frames. A looping voice’s position wraps back with the loop.

Enums§

Distance
How gain falls with distance in a Space2d.
Loop
Which part of a sound repeats.
MixerEvent
Something that happened to a voice, reported by MixerHandle::poll_event.
OverLimit
What happens when a sound already plays its Instances::max voices.
ResamplingQuality
Per-voice interpolation quality. The default retains the original PCM/filter.
SendPosition
Both taps are after inserts and the bus pause fade. Pre-fader excludes bus gain/pan; post-fader includes them. Mute/solo gate both taps and sidechain keys.
Sound
What a voice plays.
VoiceState
What a voice is doing, as of the mixer’s last render.

Constants§

MAX_BUS_ROUTES
Total main outputs plus sends in one graph, including omitted default outputs.
MAX_LOGICAL_VOICES
Maximum retained logical voices, excluding the existing bounded fade-out slots.
VIRTUAL_VOICE_QUANTUM
Fixed global output-frame cadence for physical selection, plus command frames.

Traits§

Stream
A source of interleaved frames that a voice plays as it goes.