Tryst::SDL::AudioCapture
Records everything a mixer plays - every sound and every music, already mixed together - to a WAV file.
capture = Tryst::SDL::AudioCapture.new("demo.wav")
# ... play things ...
capture.stop
For recording a demo with sound, pairing the WAV with a screen recording afterwards:
ffmpeg -i screen.mp4 -i demo.wav -c:v copy -c:a aac -shortest out.mp4
The tap is MIX_SetPostMixCallback, which fires ON SDL'S AUDIO THREAD with the finished mix. SDL's own guidance is that a callback there should return quickly - heavy I/O belongs elsewhere. So the callback below only ever copies floats into a preallocated ring (#push on RingState): it allocates nothing, takes no locks, raises nothing and calls no Crystal method that might. A separate fiber (#drain_loop) - which is ordinary Crystal code, free of every one of those constraints - drains the ring, does the float32-to-int16 conversion and writes the file.
Constants
Seconds of audio the ring can hold before the audio thread starts dropping samples - generously more than the drain fiber (writing to a local file) should ever need to catch up.
Bytes of the WAV header this writes before any audio: the canonical 44-byte RIFF/fmt /data layout for integer PCM.
Constructors
Starts recording immediately. Channel count and sample rate come from the mixer, since that is what the mixed output is in.
The recording runs continuously, including through stretches where nothing is playing - those come out as silent samples rather than as a gap. That matters for the reason this exists: a WAV with quiet stretches missing would not line up with the screen recording it is meant to be muxed onto.
Instance methods
Removes the tap, waits for the drain fiber to flush everything already in the ring, patches the sizes into the header and closes the file. Safe to call twice; the second is a no-op.