Tryst::SDL::AudioStream
Push-based real-time PCM output, with no mixer involved: open a device, hand it raw samples as fast as you make them.
Where Sound and Music play audio that already exists, this is for audio that does not - a synthesiser, a decoder, an emulator's sound chip.
stream = Tryst::SDL::AudioStream.new(
Tryst::SDL::AudioSpec.new(format: :S16LE, channels: 1, freq: 44_100))
stream.queue(samples)
stream.resume # starts paused; queue first, then start
It starts PAUSED deliberately, which is SDL's behaviour and the right one: resuming with an empty queue plays a gap.
Constructors
allow_silent: when the device fails to open, fall back to a
silent no-op stream instead of raising. available?/
device_count cannot be trusted to predict this in advance on
every platform (confirmed directly: some containers report a
nonzero device count from a stale ALSA config entry with no real
node behind it, so the count says yes while the open still
fails) - actually attempting the open is the only reliable
check, which is why this is a fallback on failure rather than a
pre-check.
Class methods
Whether SDL can see any playback device at all. Brings up SDL's audio subsystem to find out, since there is no way to ask otherwise; that is reference counted and harmless to repeat.
The audio backend SDL chose - "coreaudio", "pulseaudio", "dummy" and so on. Brings the audio subsystem up to answer, since SDL has not picked a driver before that and would just say nothing; nil only if it comes up without one.
Instance methods
This stream's own volume: 1.0 unchanged, 0.0 silent, above 1.0 amplifies. SDL applies it during the format conversion it is already doing on the way to the device, so handing it a gain costs nothing over scaling every sample first - and it is the only volume control this API has, SDL3_mixer's Track/Mixer gain belonging to a mixer no AudioStream goes through.
Set it on a silent stream and it is remembered rather than applied, the same way #playing? answers from @silent_playing: there is no device to scale anything for, but a caller that sets a volume should still read that volume back.
Whether the device is running. An empty queue still counts as playing - it is playing silence.
Hands raw PCM to the device. The bytes must be in this stream's own format and channel count - SDL cannot tell that they are not, and will happily play the misinterpretation.
Bytes still waiting to be played, measured in the format they were pushed in. Always 0 while silent - nothing is ever really queued.
Sample FRAMES still waiting - one frame being one sample per channel. The number to pace generation against: keep a few thousand frames buffered and the output never gaps.
True when this stream opened no real device and is quietly
discarding everything instead - see allow_silent below.