ruopus 0.1.2

A pure-Rust implementation of the Opus audio codec (RFC 6716). No FFI; unsafe confined to documented SIMD kernels.
Documentation
Ogg Opus Files
==============

Ogg Opus (`RFC 7845 <https://www.rfc-editor.org/rfc/rfc7845>`_) is the
standard container for Opus audio. ruopus provides two convenience functions,
:func:`~ruopus.encode_ogg_opus` and :func:`~ruopus.decode_ogg_opus`, that
handle the full container round-trip in a single call, plus the
:class:`~ruopus.OpusHead` identification-header object returned by the decoder.

Encoding to Ogg Opus
--------------------

:func:`~ruopus.encode_ogg_opus` takes raw 48 kHz float32 PCM and returns a
complete Ogg Opus file as ``bytes``:

.. code-block:: python

   import numpy as np
   import ruopus

   # Generate 3 seconds of stereo audio
   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
   ])   # shape (144000, 2)

   ogg = ruopus.encode_ogg_opus(pcm, channels=2, bitrate=128_000)

   with open("output.opus", "wb") as f:
       f.write(ogg)

   print(f"Ogg Opus file: {len(ogg) / 1024:.1f} KB")

Input can also be a 1-D interleaved array (samples interleaved L, R, L, R, …):

.. code-block:: python

   flat_pcm = pcm.ravel()    # shape (288000,)
   ogg = ruopus.encode_ogg_opus(flat_pcm, channels=2, bitrate=128_000)

Decoding from Ogg Opus
-----------------------

:func:`~ruopus.decode_ogg_opus` handles the complete demuxing pipeline:

- Reads the ``OpusHead`` identification header
- Applies the :attr:`~ruopus.OpusHead.pre_skip` (discards leading silence)
- Trims trailing granule padding
- Applies the :attr:`~ruopus.OpusHead.output_gain_q8` to the decoded PCM

.. code-block:: python

   with open("input.opus", "rb") as f:
       data = f.read()

   pcm, head = ruopus.decode_ogg_opus(data)

   print(f"Channels:    {head.channel_count}")
   print(f"Input rate:  {head.input_sample_rate} Hz")
   print(f"Pre-skip:    {head.pre_skip} samples")
   print(f"Output gain: {head.output_gain_q8} (Q7.8 dB)")
   print(f"Decoded PCM: {pcm.shape}, dtype={pcm.dtype}")

The output is always ``float32`` at 48 kHz, shaped ``(frames, channels)``.

Reading the OpusHead
--------------------

The :class:`~ruopus.OpusHead` object exposes every field of the RFC 7845
identification header:

.. code-block:: python

   pcm, head = ruopus.decode_ogg_opus(data)

   print(repr(head))
   # OpusHead(version=1, channel_count=2, pre_skip=312,
   #          input_sample_rate=44100, mapping_family=0)

   # Round-trip the header back to bytes (e.g. for remuxing):
   header_bytes = head.to_bytes()

Channel-mapping family 0 (mono and stereo) is the only family currently
supported by :func:`~ruopus.decode_ogg_opus`. For surround layouts, see
:doc:`multistream`.

Round-Trip Fidelity
-------------------

A full encode → decode round-trip introduces the encoder lookahead (typically
120 samples) as latency and the codec's lossy compression artifacts:

.. code-block:: python

   import numpy as np
   import ruopus

   original = np.random.randn(48_000).astype(np.float32)
   ogg      = ruopus.encode_ogg_opus(original, channels=1, bitrate=256_000)
   decoded, head = ruopus.decode_ogg_opus(ogg)

   # The decoded length may differ slightly due to pre_skip and frame alignment
   min_len = min(len(original), len(decoded))
   rms_err = np.sqrt(np.mean((original[:min_len] - decoded[:min_len, 0]) ** 2))
   print(f"RMS error vs original: {rms_err:.6f}")

Saving Decoded Audio
--------------------

The decoded float32 PCM can be saved with `audio_samples
<https://pypi.org/project/audio-samples/>`_:

.. code-block:: python

   import numpy as np
   import audio_samples as aus

   pcm, head = ruopus.decode_ogg_opus(data)
   samples = aus.AudioSamples.new_mono(pcm, 48_000)
   aus.io.save("decoded.wav", samples, as_type=np.float32)