A pure-Rust implementation of the Opus audio codec (RFC 6716) - fast, with first-class NumPy interop.
Project description
ruopus encodes and decodes Opus audio
(RFC 6716) with first-class NumPy
interop. The codec itself is a from-scratch Rust implementation with no C and
no FFI, but day to day you just call Python methods that take and return
NumPy arrays - the Rust is an implementation detail.
Features
- Encode and decode: automatic mode selection (SILK / hybrid / CELT) with full control over bitrate, complexity, DTX, in-band FEC, bandwidth, and application profile
- NumPy native: PCM crosses the boundary as
(frames, channels)float32 (or int16) arrays, moved out of Rust with no extra copy - No GIL stalls: every encode/decode call releases the GIL
- Ogg Opus files: single-call
encode_ogg_opus/decode_ogg_opusfor complete.opusfiles - Packet loss handling: concealment (
decode_lost) and in-band FEC recovery (decode_fec) - Packet introspection: parse the TOC byte and frame structure of raw Opus packets without decoding audio
- Surround: multistream decoding for 5.1/7.1 and other RFC 7845 layouts
- Low-level access: the
ruopus.lowlevelsubmodule exposes the SILK and CELT codecs and the LPC analysis pipeline directly, for research and custom codec work - Fully typed: ships with
.pyistubs and apy.typedmarker
Installation
pip install ruopus
Requires Python 3.9+ and NumPy 1.22+. Pre-built wheels are available for Linux, macOS, and Windows (CPython 3.9-3.13) - no Rust toolchain needed.
Quick Start
import numpy as np
import ruopus
# Stereo encoder at 64 kbps, matching decoder
enc = ruopus.OpusEncoder(2, bitrate=64_000)
dec = ruopus.OpusDecoder(2)
# 20 ms stereo frame at 48 kHz -> 960 samples per channel
frame = np.zeros((960, 2), dtype=np.float32)
packet = enc.encode_auto(frame) # bytes - encoded Opus packet
pcm = dec.decode_packet(packet) # (960, 2) float32 in [-1, 1]
Encoder input can be a 1-D interleaved array or a 2-D (frames, channels)
array; both are accepted without extra copies.
Ogg Opus Files
Write and read complete .opus files in one call each:
import numpy as np
import ruopus
sr = 48_000
t = np.linspace(0, 3, sr * 3, dtype=np.float32)
pcm = np.column_stack([
np.sin(2 * np.pi * 440 * t), # left: A4
np.sin(2 * np.pi * 880 * t), # right: A5
])
ogg = ruopus.encode_ogg_opus(pcm, channels=2, bitrate=128_000)
with open("output.opus", "wb") as f:
f.write(ogg)
with open("output.opus", "rb") as f:
decoded_pcm, head = ruopus.decode_ogg_opus(f.read())
print(f"{head.channel_count}-ch, {decoded_pcm.shape[0] / sr:.2f}s")
Packet Loss and FEC
import ruopus
dec = ruopus.OpusDecoder(2)
FRAME = 960 # 20 ms at 48 kHz
for pkt in stream:
if pkt is None:
pcm = dec.decode_lost(frame_size=FRAME) # concealment
else:
pcm = dec.decode_packet(pkt)
# Recover a lost packet from in-band FEC carried by its successor
recovered = dec.decode_fec(next_pkt, frame_size=FRAME)
Choosing a Bitrate and Application
# Phone-quality voice
enc_voice = ruopus.OpusEncoder(
1,
bitrate=8_000,
application=ruopus.Application.Voip,
signal=ruopus.Signal.Voice,
)
# High-quality music
enc_music = ruopus.OpusEncoder(
2,
bitrate=128_000,
application=ruopus.Application.Audio,
signal=ruopus.Signal.Music,
)
# Low-latency game voice (CELT only)
enc_ld = ruopus.OpusEncoder(
1,
bitrate=32_000,
application=ruopus.Application.RestrictedLowDelay,
)
Surround Decoding
import ruopus
# 5.1 surround: 4 elementary streams, 2 stereo-coupled
dec = ruopus.MultistreamDecoder(
streams=4,
coupled=2,
mapping=[0, 4, 1, 2, 3, 5], # L R C LFE Ls Rs
)
pcm = dec.decode_packet(raw_packet) # (frames, 6) float32
Packet Inspection
import ruopus
pkt = ruopus.Packet(raw_bytes)
print(f"mode={pkt.toc.mode}, bandwidth={pkt.toc.bandwidth}, frames={len(pkt)}")
Low-Level API
For research or custom codec work, ruopus.lowlevel exposes the SILK and
CELT layers and the LPC analysis pipeline directly, below the Opus packet
format:
import numpy as np
import ruopus.lowlevel as ll
enc = ll.CeltEncoder(channels=1, complexity=10, bitrate=64_000)
frame = np.zeros(960, dtype=np.float32)
body = enc.encode(frame) # raw CELT frame body, no Opus framing
Performance
Measured against libopus 1.6.1 (SIMD-enabled C), one core, pinned to a single performance core. Figures are x realtime.
| Mode | ruopus | libopus |
|---|---|---|
| Decode, SILK wideband 16 kb/s | 2095x | 1171x |
| Decode, CELT fullband 64 kb/s | 1389x | 1566x |
| Encode, matched complexity (all modes) | parity with libopus |
See the full benchmark breakdown for the complete table and methodology.
Documentation
The full guide and API reference are at jmg049.github.io/ruopus, covering codec modes, frame sizes, FEC/loss handling, multistream layouts, and the low-level API in depth.
License
MIT License.
Links
- GitHub: https://github.com/jmg049/ruopus
- Documentation: https://jmg049.github.io/ruopus/
- PyPI: https://pypi.org/project/ruopus/
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file ruopus-0.1.2.tar.gz.
File metadata
- Download URL: ruopus-0.1.2.tar.gz
- Upload date:
- Size: 337.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: maturin/1.14.1
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
40cb87bd6be2842fb3feb6683f0c9edaf9bddc97cb818601cb6a90e08f9ada3c
|
|
| MD5 |
dc1414f097b41cd289c70919ba685926
|
|
| BLAKE2b-256 |
2ca8476587d0d527f0e7dfd33fc6ec4a2534e71f2976b436bea107de89258228
|
File details
Details for the file ruopus-0.1.2-cp313-cp313-manylinux_2_34_x86_64.whl.
File metadata
- Download URL: ruopus-0.1.2-cp313-cp313-manylinux_2_34_x86_64.whl
- Upload date:
- Size: 1.0 MB
- Tags: CPython 3.13, manylinux: glibc 2.34+ x86-64
- Uploaded using Trusted Publishing? No
- Uploaded via: maturin/1.14.1
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9610e4b9de3452dda0753b0d3c36cc7036fe9a5e3f8822e29829578da1ba7a17
|
|
| MD5 |
e37cfaf35854b8b2d4011f884eee6b9e
|
|
| BLAKE2b-256 |
b8e42c37221482ad1064766e6b8a8964e1cf612bffcc4a230c0abcd71a434a9b
|