class

Tryst::SDL::AudioCapture

Inherits Reference < Object

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

BUFFER_SECONDS = 2

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.

HEADER_BYTES = 44

Bytes of the WAV header this writes before any audio: the canonical 44-byte RIFF/fmt /data layout for integer PCM.

Constructors

new(path : String, mixer : Mixer = Mixer.default)

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.

Source

Instance methods

bytes_written

Bytes of audio written so far, header excluded.

Source
mixer
Source
path
Source
stop

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.

Source
stopped?
Source

Nested types