sound_rs v0.2.2

05 · Guides

Streaming and memory

play_file never loads the track. A decoder thread fills a one-second ring buffer, and the audio callback drains it. Memory stays flat no matter how long the file is.

How it works

  1. The decoder thread reads a packet, decodes it to float32, converts the channel count and resamples to the device rate.
  2. The result goes into a ring buffer sized for one second of device audio.
  3. The audio callback pops samples from that buffer. When it is empty, the callback writes silence and keeps the stream alive.
  4. Seeking replaces the ring buffer and repositions the decoder; the old audio that was still queued is discarded and does not count towards the new position.

Because the decoder runs ahead by at most a second, the first sound arrives as soon as the first packet is decoded, not when the file ends.

Numbers

Ten-minute stereo FLAC, 44.1 kHz, measured with bench/run.py on an Apple M5 Pro:

sound_rssoundfile + sounddevice
Time to first sound11.3 ms302 ms
Peak RSS43 MB256 MB

The competitor number is not a flaw in those libraries: their API takes an array, so the file has to be decoded before playback can start. Streaming is a different design.

play_file or load?

You wantUseMemory
Play a filePlayer.play_fileconstant, ~1 s of audio
Analyse the samplesloadwhole file as float32
Play an array you already haveplay_arrayone copy at device format
Play a file, need the samples tooload, then play_arraywhole file

A 10-minute stereo file is 211 MB in float32. That is fine for analysis and wasteful for playback.

import sound_rs

# streaming: memory stays flat, start is immediate
with sound_rs.Player() as player:
    player.play_file("lecture-2h.m4a")
    player.wait()

# analysis: everything in RAM, decoded in parallel with other files
clips = sound_rs.load_many(["a.flac", "b.flac"])

Caveats