Skip to main content

🎵 sound_rs

PyPI Downloads Python Platforms Typed Docs License

sound_rs is a fast, Rust-powered audio library for Python that I built and maintain — decoding with symphonia (plus minimp3 for MP3), resampling with rubato, output through cpal.

Documentation: neksand.github.io/sound_rs — guides, API reference, the soundrs command and the benchmark methodology.

Latest release: 0.3.0 — reliability, datasets, recording, queues. Build report: docs/reports/0.3.0-report.md.


🚀 Features

  • Formats: WAV, MP3, FLAC, OGG/Vorbis, M4A, AAC — any bit depth, including 24-bit
  • Streaming playback: a two-hour file costs ~4 MB of RAM, not gigabytes, and the first sound arrives in ~11 ms instead of after the whole decode
  • Player: one output stream stays open — play, pause, resume, seek, stop, volume, position, duration
  • Decode to NumPy: load(path, sample_rate=, mono=, offset=, duration=) — resample to 16 kHz mono in one call, or decode only a window
  • Parallel decode: load_many(paths, sample_rate=, mono=) uses every core, GIL released
  • Recording: Recorder() captures from the microphone to a float32 array
  • Queues and live input: Player.enqueue(path) chains tracks; begin_feed/feed push samples from Python with backpressure
  • Gapless (MP3, OGG, AAC/MP4): encoder delay and padding are trimmed, so decoded audio lines up with the original
  • Fast metadata: get_audio_info reads the container header, ~1 ms regardless of file length
  • Resampling: rubato's FFT resampler with a proper anti-aliasing filter
  • Device-native output: the stream opens in whatever sample format the device wants (f32, f64, i16, i32, u8, u16)
  • Errors: everything raises sound_rs.SoundRsError with a stable .code (io, format, device, stalled, unsupported, usage)
  • Tested guarantees: no panics on corrupt input (mutation fuzzing), deterministic decode (PCM hashes in CI), underrun reporting. The full list is on the docs site.
  • No ffmpeg, no system libraries: MP3, FLAC, OGG, AAC and WAV are decoded inside the wheel; pip install sound_rs needs nothing else
  • Typed: ships sound_rs.pyi and py.typed, so editors autocomplete everything

🧠 Installation

pip install sound_rs

Wheels are built with the stable ABI (abi3-py310): one per platform — Linux x86_64 and aarch64, Windows amd64, macOS arm64 and Intel — each covering Python 3.10 and newer. Other platforms build from source.


⌨️ Command line

Installing the package also installs soundrs, so you can check the setup and read the docs without opening Python:

soundrs --help       # commands and the documentation link
soundrs devices      # output devices, default marked with *
soundrs info a.flac  # rate, channels, duration from the header
soundrs play a.flac --volume 0.5
soundrs docs         # prints https://neksand.github.io/sound_rs/

Exit codes: 0 success, 1 runtime failure, 2 bad usage.

💻 Usage

One-shot playback

import sound_rs

sound_rs.play_audio("sound.wav")          # blocks, GIL released
sound_rs.play_audio("sound.mp3", device="MacBook Pro Speakers")

The player

import sound_rs

with sound_rs.Player() as player:
    player.play_file("music.flac")   # returns immediately, streams from disk
    player.volume = 0.7
    player.seek(42.0)
    player.pause()
    player.resume()
    print(player.position, player.duration)
    player.wait()                    # block until it ends

sound_rs.list_devices()              # [(0, "MacBook Pro Speakers", True), ...]

Record

import sound_rs

with sound_rs.Recorder() as recorder:
    audio, rate = recorder.record(5.0)     # float32, (frames, channels)

Queue and live input

import numpy as np
import sound_rs

with sound_rs.Player() as player:
    player.play_file("a.flac")
    player.enqueue("b.mp3")                # plays next, own rate and layout
    player.wait()

player = sound_rs.Player()
player.begin_feed(48000)
player.feed(np.zeros(4800, dtype=np.float32))   # blocks when the buffer is full
player.end_feed()
player.wait()

Inspect and decode

sample_rate, channels, duration = sound_rs.get_audio_info("music.mp3")  # no decode
audio, rate = sound_rs.load("music.flac")                               # (frames, channels) float32
audio, rate = sound_rs.load("music.flac", sample_rate=16000, mono=True) # dataset shape

pairs = sound_rs.load_many(["a.flac", "b.flac", "c.flac"])              # parallel

From a NumPy array

import numpy as np

rate = 44100
t = np.linspace(0, 2.0, rate * 2, endpoint=False, dtype=np.float32)
sound_rs.play_array(np.sin(2 * np.pi * 440 * t), rate)   # mono, 1D
sound_rs.play_array(np.stack([left, right]), rate)       # 2D, channels first
sound_rs.play_sine_wave(440.0, 1.0, 44100)

🧩 Benchmark

Every measurement runs in its own process: peak RSS is process-wide, so timing and memory cannot share one. Median and p95 over repeated runs, competitors run side by side on the same files.

python bench/run.py                 # sound_rs only
python bench/run.py --compare       # + soundfile / sounddevice
python bench/run.py --reps 9

Hardware I measured on: Apple M5 Pro, macOS 26.6.2, Python 3.14.7, release build (lto = "fat", opt-level = 3). Competitors: libsndfile 1.2 (soundfile 0.14), PortAudio (sounddevice). Corpus: 3 s and 10 min stereo 44.1 kHz, generated by the harness.

Scoreboard — every axis in this section, side by side:

axis sound_rs best competitor ratio
import 1.1 ms 31.8 ms (soundfile) 29×
metadata (60 s file) 1.2 ms 31.7 ms (soundfile) 26×
decode WAV 130 ms 157 ms 1.2×
decode FLAC 265 ms 455 ms 1.7×
decode MP3 241 ms 268 ms 1.1×
decode AAC 349 ms not supported —
time to first sound (10 min) 11 ms 302 ms 27×
peak RAM (10 min file) 43 MB 255 MB 6×
playback overhead 30 ms 241 ms 8×
batch decode (2 files) 177 ms 299 ms 1.7×

Import time (cold process):

time peak RSS
sound_rs 1.1 ms 25 MB
soundfile 31.8 ms 34 MB
sounddevice 75.3 ms 32 MB

Metadata (60 s file): sound_rs 1.2 ms, soundfile 31.7 ms.

Decode 60 s stereo, median / p95:

format sound_rs libsndfile result PCM check
WAV 131 / 160 ms 155 ms 1.2× faster identical
FLAC 265 / 267 ms 465 ms 1.8× faster identical
MP3 241 / 249 ms 268 ms 1.1× faster 1.6e-05, 47-sample offset
M4A (AAC) 361 / 365 ms unsupported sound_rs only matches ffmpeg to 8e-08

Streaming a 10-minute FLAC — time to the first sound and peak memory:

first sound peak RSS
sound_rs Player 11.3 ms 43 MB
soundfile + sounddevice 302 ms 256 MB

Playback of a 3 s tone — overhead over the track duration:

overhead
sound_rs Player (stream kept open) +30 ms
sounddevice.play (opens a stream per call) +241 ms
sound_rs.play_array (one-shot, opens a stream) +182 ms

Batch decode two FLAC files: load_many 272 ms vs a soundfile loop 448 ms.

What the numbers do not hide:

  • MP3 output is offset by 47 samples (~1 ms) versus libsndfile and ffmpeg. Sample values agree to 1.6e-05 once aligned; the offset comes from how the decoder delay is trimmed, and the length matches exactly.
  • WAV decode is a memcpy race with libsndfile; the gap there is small by nature, the real wins are FLAC, metadata, memory and latency.
  • AAC decoding is slower than everything else — it is a newer, less tuned decoder — but libsndfile cannot read AAC at all.

🛠️ Roadmap

Step Feature Description
✅ 1 Playback + formats WAV, MP3, FLAC, OGG, M4A, 24-bit
✅ 2 NumPy integration 1D/2D arrays, load, load_many
✅ 3 Player Persistent stream: pause, seek, volume, position
✅ 4 Streaming decode Constant memory, ~11 ms to first sound
✅ 5 Device selection list_devices, list_input_devices, device=
✅ 6 Recording Recorder: microphone to float32 array
✅ 7 Queues and live input enqueue, begin_feed/feed
✅ 8 Datasets load(..., sample_rate=16000, mono=True), parallel load_many
🔜 9 Hot-swap on disconnect Reopen automatically instead of raising stalled
🔜 10 Effects Basic gain/fade/mix utilities

🧪 Development

cargo test                      # decode, gapless, seek, fuzzing, properties, player
cargo clippy --all-targets -- -D warnings
maturin develop --release       # build into your venv
python tests/smoke.py           # end-to-end decode + API checks
SOUND_RS_PLAY=1 python tests/smoke.py   # also plays (needs an audio device)
python tests/decode_regression.py  # PCM hashes; must not change
python docs/check_api.py        # every public name is documented
python bench/run.py --compare --report bench/REPORT.md
python3 docs/_build.py          # rebuild the documentation site

⚙️ Built With

  • 🦀 Rust — performance and safety
  • 🔗 PyO3 — Python bridge
  • 🧰 Maturin — building and publishing
  • 🔊 cpal, symphonia, minimp3, rubato — output, decoding, resampling

👨‍💻 Author

Nurassyl Mussainov Kazakhstan 🇰🇿 Python / Rust Developer

🔗 GitHub: Neksand 📦 PyPI: sound_rs


📜 License

Licensed under the MIT License — free for personal and commercial use.

Metadata

Release files for sound-rs 0.3.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for sound-rs 0.3.1
File Size Uploaded Provenance Actions
sound_rs-0.3.1.tar.gz 222.5 kB Without provenance Details for sound_rs-0.3.1.tar.gz

Built distributions (wheels)

Table of built distributions (wheels) for sound-rs 0.3.1
File Actions
sound_rs-0.3.1-cp310-abi3-win_amd64.whl CPython 3.10 abi3 Windows x86-64 Details for sound_rs-0.3.1-cp310-abi3-win_amd64.whl
sound_rs-0.3.1-cp310-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl CPython 3.10 abi3 Linux glibc 2.17+ x86-64 Details for sound_rs-0.3.1-cp310-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl
sound_rs-0.3.1-cp310-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl CPython 3.10 abi3 Linux glibc 2.17+ ARM64 Details for sound_rs-0.3.1-cp310-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl
sound_rs-0.3.1-cp310-abi3-macosx_11_0_arm64.whl CPython 3.10 abi3 macOS 11.0+ ARM64 Details for sound_rs-0.3.1-cp310-abi3-macosx_11_0_arm64.whl
sound_rs-0.3.1-cp310-abi3-macosx_10_12_x86_64.whl CPython 3.10 abi3 macOS 10.12+ x86-64 Details for sound_rs-0.3.1-cp310-abi3-macosx_10_12_x86_64.whl

Total release size: 7.7 MB

Release files / sound_rs-0.3.1.tar.gz

Download URL sound_rs-0.3.1.tar.gz
Size 222.5 kB
Tags Source
SHA-256 checksum
How to use checksums
b034a9b6ecf011f964be98e0e90e97eabd46dd199afc4992be09ec4ab439aa0a
BLAKE2b-256 checksum
How to use checksums
cb242ca2febd2f622bd6cb12de0ae8c57030925e7a7467ce14db6d03579a1d4a
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.16

Release files / sound_rs-0.3.1-cp310-abi3-win_amd64.whl

Download URL sound_rs-0.3.1-cp310-abi3-win_amd64.whl
Size 1.4 MB
Tags CPython 3.10 Windows x86-64 abi3
SHA-256 checksum
How to use checksums
f6850ec586ae3f3987f457ef19bf666d12ab19775c1f3bca3e61c8b61a4a4a68
BLAKE2b-256 checksum
How to use checksums
05b0b2c01ed901664ab1a996db84a346c890603cb21819950437d93c7effd57e
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.16

Release files / sound_rs-0.3.1-cp310-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl

Download URL sound_rs-0.3.1-cp310-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl
Size 1.9 MB
Tags CPython 3.10 Linux glibc 2.17+ x86-64 abi3
SHA-256 checksum
How to use checksums
4e1314ea84cac08155c79c942bb2ac1b141893f91f857f4f9730ba4f644c8016
BLAKE2b-256 checksum
How to use checksums
6c53cf6cf77eb10702d6d548d172972ce63d504dd845d517e3df5518bcc13bbc
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.16

Release files / sound_rs-0.3.1-cp310-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl

Download URL sound_rs-0.3.1-cp310-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl
Size 1.6 MB
Tags CPython 3.10 Linux glibc 2.17+ ARM64 abi3
SHA-256 checksum
How to use checksums
4442036a28bf6823bcf2843a91a5341fc80afb39e0e8612d0bcda078dce5dc44
BLAKE2b-256 checksum
How to use checksums
ccb01f5ddfd626e27ad1bec186eff53b305a0f0b69bbccf718588024e720a0d6
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.16

Release files / sound_rs-0.3.1-cp310-abi3-macosx_11_0_arm64.whl

Download URL sound_rs-0.3.1-cp310-abi3-macosx_11_0_arm64.whl
Size 1.2 MB
Tags CPython 3.10 abi3 macOS 11.0+ ARM64
SHA-256 checksum
How to use checksums
e6e970dcad61fe160a62559ba55bcbd7a850437f8eb9bf72efebd80a0eee734b
BLAKE2b-256 checksum
How to use checksums
d378248d402d9e1cfde3649939b4ccf869e1b7c845c3dc05154e119eb49c68b6
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.16

Release files / sound_rs-0.3.1-cp310-abi3-macosx_10_12_x86_64.whl

Download URL sound_rs-0.3.1-cp310-abi3-macosx_10_12_x86_64.whl
Size 1.4 MB
Tags CPython 3.10 abi3 macOS 10.12+ x86-64
SHA-256 checksum
How to use checksums
7912eaf4c21bb7702f26071cc6909dcb5edc3bd2217a98d085daa3ba0ad426c2
BLAKE2b-256 checksum
How to use checksums
d38b3e049ada90aea8028c94dbe8c17511919a8d395b39a52680006e2f3d2f59
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.16

Security for sound-rs 0.3.1

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page