Mixer
Mixer groups your audio into buses — like Music, SFX, and Ambience — so you can control whole
categories of sound at once. Route any Source or
SoundFontInstrument to a group, then set that group’s volume, mute, or solo.
Each sound plays at its own volume times its group’s effective gain (the group’s gain times its
ancestors’, gated by mute and solo), applied every frame. Groups nest, so a Master group scales
everything. Introduced by engine PLM-147 (ADR 0046).
Mixer is a static facade; MixerGroup is a handle to one group.
Getting groups
Section titled “Getting groups”Mixer.master
Section titled “Mixer.master”Returns: the MixerGroup for the implicit Master bus (the root every group descends from).
Mixer.group(name)
Section titled “Mixer.group(name)”Get or create a group of that name directly under Master.
Returns: a MixerGroup.
Mixer.groupUnder(name, parent)
Section titled “Mixer.groupUnder(name, parent)”Get or create a group named name nested under an existing parent MixerGroup.
Returns: a MixerGroup.
import "engine" for Mixer
var music = Mixer.group("Music")var sfx = Mixer.group("SFX")var footsteps = Mixer.groupUnder("Footsteps", sfx) // SFX/FootstepsRouting sounds to a group
Section titled “Routing sounds to a group”group.addSource(source)
Section titled “group.addSource(source)”Route a Source (from Audio.newSource / Audio.newStreamingSource) to this group.
group.addInstrument(instrument)
Section titled “group.addInstrument(instrument)”Route a SoundFontInstrument to this group.
var shot = Audio.newSource("sfx/shot.wav")sfx.addSource(shot)
var piano = SoundFontInstrument.new("soundfonts/piano.sf2", 48000, 2)music.addInstrument(piano)A sound that is never routed plays under Master (unity gain), so adding the mixer to a project changes nothing until you route sounds and adjust groups.
Controlling a group
Section titled “Controlling a group”group.name
Section titled “group.name”Returns: String — the group’s name.
group.volume / group.volume = (v)
Section titled “group.volume / group.volume = (v)”The group’s linear gain (1 = unity). Setting it scales every sound routed to the group (and its child
groups). Clamped to >= 0.
group.muted / group.muted = (b)
Section titled “group.muted / group.muted = (b)”Mute or unmute the group. A muted group (and everything under it) is silent, regardless of volume.
group.soloed / group.soloed = (b)
Section titled “group.soloed / group.soloed = (b)”Solo the group. While any group is soloed, only soloed groups and their subtrees are audible — everything else is silenced. (Mute still wins inside a soloed subtree.)
music.volume = 0.6 // duck the music bussfx.muted = true // silence all SFXmusic.soloed = true // hear only Music (and its children)Ducking
Section titled “Ducking”A group can duck — automatically drop in level — while another group is making sound. The classic use is music ducking under dialogue or SFX. Introduced by PLM-148 (ADR 0047).
group.duckUnder(triggerGroup, amountDb, attack, release)
Section titled “group.duckUnder(triggerGroup, amountDb, attack, release)”Make this group duck while triggerGroup’s subtree has a playing source.
Parameters:
triggerGroup(MixerGroup) — when any source routed to this group (or its children) is playing, the duck fires.amountDb(Num) — the reduction at full duck, in decibels (e.g.-12;0or positive = no duck).attack(Num) — seconds to ramp down to the ducked level.release(Num) — seconds to ramp back to full when the trigger goes quiet.
group.clearDuck()
Section titled “group.clearDuck()”Remove the duck and release the group back to full level.
group.duckGain
Section titled “group.duckGain”Returns: Num — the live duck envelope (1 = not ducked, down to the target while ducking). Useful for a
meter.
var music = Mixer.group("Music")var voice = Mixer.group("Voice")// Music drops 12 dB whenever anything on the Voice bus is playing.music.duckUnder(voice, -12, 0.05, 0.4) // 50 ms attack, 400 ms releaseDucking is activity-driven: it fires whenever a source in the trigger’s subtree is playing (not keyed to its loudness — a level-metered sidechain compressor is a later, per-group-DSP capability). The duck composes with the group’s own volume, and mute/solo still win.
Effects
Section titled “Effects”A group can host audio effects via OpenAL EFX — put a whole bus in a reverb space, or run its sound
through an echo, chorus, distortion, and more. Everything routed to the group takes on the effect,
and a group can hold several effects in order (a serial chain). Introduced by PLM-153 (reverb, ADR 0050);
the full suite by PLM-154 (ADR 0051). Effects need a device with ALC_EXT_EFX; without it these calls are safe
no-ops and audio stays dry.
group.addEffect(type)
Section titled “group.addEffect(type)”Add an effect by type name. Returns a MixerEffect handle, or null for an unknown type.
Call it more than once on a group to build an ordered chain.
Types (case-insensitive): reverb, chorus, echo, flanger, distortion, equalizer, frequencyshifter,
vocalmorpher, pitchshifter, ringmodulator, autowah, compressor. Each starts with OpenAL’s sensible
defaults, so addEffect("echo") is already a working echo — tune it with setParam.
group.addReverbPreset(name)
Section titled “group.addReverbPreset(name)”Shortcut for addEffect("reverb") with a named EAX preset applied. Returns a MixerEffect handle.
Presets: Generic, Room, Bathroom, LivingRoom, StoneRoom, Auditorium, ConcertHall, Cave,
Arena, Hangar, Alley, Forest, City, Mountains, Plain, ParkingLot, Underwater, Drugged,
Dizzy (case-insensitive). Presets are reverb-only; other effect types are tuned purely with setParam.
group.clearEffects()
Section titled “group.clearEffects()”Remove all effects on the group (back to dry). This invalidates any MixerEffect handles into the group.
group.effectCount
Section titled “group.effectCount”Returns: Num — how many effects the group has.
group.setEffectMix(mix)
Section titled “group.setEffectMix(mix)”Set the group’s wet/dry mix — how much the effect is heard. An effect is a parallel wet mixed on top of
the dry signal, and OpenAL caps the wet at unity, so on a full-volume bus an added effect can be too quiet to
notice. setEffectMix fixes that by ducking the dry so the wet stands out:
mix = 0(default) — dry unchanged; a subtle parallel wet (the original behavior).mix → 1— dry fully ducked; you hear mostly the effect (insert-style, e.g. for distortion).- e.g.
0.5— dry −6 dB, so a reverb or echo is clearly audible.
Raising the mix lowers the group’s overall level (the usual wet/dry trade) — turn the group’s volume up to
make it up. Only bites when the group has effects.
var music = Mixer.group("Music")music.addReverbPreset("Cave")music.setEffectMix(0.5) // now the cave is clearly audible, not a whisper under the mixvar music = Mixer.group("Music")music.addReverbPreset("Cave") // a reverb space
var guitar = Mixer.group("Guitar")var dist = guitar.addEffect("distortion") // a chain: distortion -> echodist.setParam("edge", 0.3)var echo = guitar.addEffect("echo")echo.setParam("delay", 0.2)echo.setParam("feedback", 0.5)Filters
Section titled “Filters”Separate from the effect chain, a group can carry one direct-path filter — a low-pass (muffle),
high-pass (thin out), or band-pass — that shapes the dry signal every source on the bus produces (the
OpenAL AL_DIRECT_FILTER). It composes with the effects: a group can be both low-passed and sent through a
reverb. Introduced by PLM-157 (ADR 0052). Also needs ALC_EXT_EFX.
Gains are linear 0..1 (1 = pass unchanged, 0 = fully cut). OpenAL’s filters are shelf gains, not a
swept cutoff with resonance — you attenuate a band, you don’t sweep a resonant frequency.
group.setLowPass(gain, gainHF)
Section titled “group.setLowPass(gain, gainHF)”Low-pass the group: gain scales the whole signal, gainHF how much of the highs pass (lower = more
muffled).
group.setHighPass(gain, gainLF)
Section titled “group.setHighPass(gain, gainLF)”High-pass the group: gainLF how much of the lows pass (lower = thinner).
group.setBandPass(gain, gainLF, gainHF)
Section titled “group.setBandPass(gain, gainLF, gainHF)”Band-pass: attenuate both the lows (gainLF) and the highs (gainHF).
group.clearFilter()
Section titled “group.clearFilter()”Remove the group’s filter (back to full-range dry).
var voice = Mixer.group("Voice")voice.setLowPass(1.0, 0.1) // muffled, like through a wall// voice.setHighPass(1.0, 0.1) // thin, like a telephone// voice.clearFilter()MixerEffect
Section titled “MixerEffect”A handle to one effect on a group, returned by addEffect / addReverbPreset.
effect.setPreset(name)
Section titled “effect.setPreset(name)”Set a reverb preset (see the list above). Only meaningful for a reverb effect.
effect.setParam(name, value)
Section titled “effect.setParam(name, value)”Set a named parameter to a number. Names are case-insensitive; a name that doesn’t belong to this effect type is ignored. Int-valued params (waveforms, phase, tunings, on/off, shift direction) take a whole number.
Per-effect parameter names:
| Effect | Parameters |
|---|---|
reverb | density, diffusion, gain, gainhf, gainlf, decaytime, decayhfratio, decaylfratio, reflectionsgain, reflectionsdelay, latereverbgain, latereverbdelay, echotime, echodepth, modulationtime, modulationdepth, airabsorptiongainhf, hfreference, lfreference, roomrollofffactor |
chorus | waveform, phase, rate, depth, feedback, delay |
flanger | waveform, phase, rate, depth, feedback, delay |
echo | delay, lrdelay, damping, feedback, spread |
distortion | edge, gain, lowpasscutoff, eqcenter, eqbandwidth |
equalizer | lowgain, lowcutoff, mid1gain, mid1center, mid1width, mid2gain, mid2center, mid2width, highgain, highcutoff |
frequencyshifter | frequency, leftdirection, rightdirection |
vocalmorpher | phonemea, phonemeacoarsetuning, phonemeb, phonemebcoarsetuning, waveform, rate |
pitchshifter | coarsetune, finetune |
ringmodulator | frequency, highpasscutoff, waveform |
autowah | attacktime, releasetime, resonance, peakgain |
compressor | onoff |
OpenAL’s Compressor is on/off only, and its Equalizer is a fixed 4-band — a parameterized compressor (threshold/ratio) and an arbitrary parametric EQ are a later software-mix feature, not part of the EFX set.
var music = Mixer.group("Music")var verb = music.addReverbPreset("Cave")verb.setParam("decaytime", 3.5) // lengthen the tail// music.clearEffects() // back to dryDefaults and player settings
Section titled “Defaults and player settings”Set default group volumes in game.toml’s [Mixer] section; the
engine applies them at startup (pre-creating the groups). Persist a player’s changes with:
Mixer.saveSettings()
Section titled “Mixer.saveSettings()”Returns: Bool — writes the current group volumes and mute to settings.toml in the app directory, so
the player’s mix survives relaunch. Call it when the player changes a level.
Mixer.loadSettings()
Section titled “Mixer.loadSettings()”Returns: Bool — re-applies settings.toml now. The engine already auto-loads it at startup (after the
game.toml defaults, so player overrides win).
music.volume = 0.4Mixer.saveSettings() // remember the player's choice- Applied every frame. The engine reads each group’s effective gain each frame and applies it to the
live sources routed to it, on top of each source’s own
setVolume. YoursetVolumevalue is preserved — the group multiplier does not leak back into it. - Nesting. A group’s effective gain includes all its ancestors’, so a
Mastervolume of0.5halves everything, and aSFXvolume scalesSFX/Footstepstoo. get-or-create.Mixer.group(name)returns the same group each time, so you can fetch a bus by name from anywhere without tracking the handle.
Related
Section titled “Related”- Audio — create the Sources you route.
- SoundFontInstrument — instruments route to groups too.
- Example: Audio Mixer — Music / SFX / Ambience with live faders, mute, solo.
- Example: Audio Reverb — a reverb space on a music bus, live presets.
- Example: Audio Effects — every EFX effect + a serial chain on real music.