Tryst::SDL::Mixer
An SDL3_mixer mixer: the thing audio is loaded into and played through. Two kinds, and the difference matters:
Mixer.newopens an audio device and mixes in real time on SDL's own audio thread. What an application wants.Mixer.bufferedhas no device at all and produces audio only when#generateasks, as fast as it is asked. What a test wants: no hardware, no waiting, and the mixed samples in hand.
A mixer is an object you make and own; there is no implicit global
one. Mixer.default is a settable convenience for code that does
not want to name one, and nothing else reaches for it.
Constructors
A mixer with no device behind it, producing audio only when
#generate asks. The readable spelling of new(spec, buffered: true).
Opens an audio device and mixes on SDL's audio thread. A nil spec lets the device choose; the mixer converts everything to whatever it settled on either way, so naming one only saves conversion work.
buffered: true builds the device-less kind instead, where the
spec is what the mixer produces rather than a request.
Class methods
The decoders this build has, e.g. WAV, MP3, OGG. Decided during
init, so the library has to be up for this to answer usefully.
Nilable so it can be put back to "nothing yet", which is what a test needs to leave the next one a clean slate.
Reference counted: repeated calls succeed and each needs its own
quit. Every mixer takes a reference in its constructor and
drops it in #destroy, so calling these directly is only needed
to load the decoders before there is any mixer to load them for.
The SDL3_mixer actually loaded into this process. Safe to call
before init, unlike everything else here.
Instance methods
@api private - the AudioCapture currently tapping this mixer, if any. SDL allows one post-mix callback per mixer, so this is what lets a second capture say so instead of silently unhooking the first and leaving it writing to a file nothing feeds.
@api private - the AudioCapture currently tapping this mixer, if any. SDL allows one post-mix callback per mixer, so this is what lets a second capture say so instead of silently unhooking the first and leaving it writing to a file nothing feeds.
Frees the mixer and drops this object's reference on the library. SDL destroys every track and every loaded audio attached to it at the same time, so anything still holding those must not use them afterwards.
Runs the on_stopped block of every track that has finished
since the last call, and reports how many were delivered.
This exists because SDL fires its stopped callback ON THE AUDIO THREAD, where running arbitrary Crystal is not safe. The audio thread only bumps a counter; this is what turns those counters into calls, on whichever thread calls it. An application drives it from its own loop - in a Tk program, a timer:
session.every(50) { mixer.dispatch_stopped }
Cheap to call with nothing pending. A block that raises propagates and leaves the rest undelivered until the next call.
The format this mixer settled on, which for a device mixer is the
device's choice and not necessarily what was asked for.
#generate produces bytes in this format.
Master gain over everything this mixer plays: 1.0 unchanged, 0.0 silent, above 1.0 amplifies. A multiplier, not a 0-100 volume - there is no upper bound, and it gets loud fast.
Mixes into into and reports how many bytes of it are REAL
audio. The whole buffer is always written; anything past the
return value is silence appended because every track ran out,
which is how a test tells "it played" from "it didn't".
Buffered mixers only - a device mixer generates on its own audio thread whenever the device asks, and MIX_Generate refuses it.
Mixes frames sample frames and hands back the whole buffer,
trailing silence included.
Stops the mixer running for the duration of the block, so its state can be changed without racing the audio thread. Nestable.
Starts every track carrying tag, all at the same instant in the
mix. Same options as Track#play, applied to each.
ASSIGNS the gain of every track carrying tag. It is a bulk
write to each track's own gain, not a group fader layered over
the top - SDL3_mixer has no such thing, and Track#gain reads
back whatever was set here.
Which means a tag alone cannot be an effects slider that keeps sounds at their relative volumes: setting the tag flattens every tagged track to the same gain. An application that mixes a quiet footstep against a loud explosion has to keep each sound's base gain itself and set them individually, or re-tag by loudness.
No matching getter, because SDL keeps no per-tag value to read.
Halts every track on this mixer, optionally fading out first.