ruopus 0.1.2

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

This guide covers the most common workflows: encode, decode, Ogg Opus files,
and a basic pipeline for streaming audio.

Basic Encode → Decode
---------------------

The simplest path: create an encoder and a decoder for the same channel count
and bounce a frame through both.

.. code-block:: python

   import numpy as np
   import ruopus

   # Stereo encoder at 64 kbps (default: fullband, complexity 10, auto mode)
   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]

   print(f"Packet size: {len(packet)} bytes")
   print(f"Decoded shape: {pcm.shape}, dtype: {pcm.dtype}")

Encoder input can be a 1-D interleaved array or a 2-D ``(frames, channels)``
array. Both are accepted without extra copies.

Choosing a Bitrate
------------------

Bitrate controls the quality/size tradeoff:

.. code-block:: python

   # Phone-quality voice (8 kbps SILK narrowband)
   enc_voice = ruopus.OpusEncoder(
       1,
       bitrate=8_000,
       application=ruopus.Application.Voip,
       signal=ruopus.Signal.Voice,
   )

   # High-quality music (128 kbps CELT fullband)
   enc_music = ruopus.OpusEncoder(
       2,
       bitrate=128_000,
       application=ruopus.Application.Audio,
       signal=ruopus.Signal.Music,
   )

   # Low-latency game voice (32 kbps, restricted low delay = CELT only)
   enc_ld = ruopus.OpusEncoder(
       1,
       bitrate=32_000,
       application=ruopus.Application.RestrictedLowDelay,
   )

See :doc:`codec_modes` for an explanation of what these settings select under
the hood.

Mono Audio
----------

For single-channel audio pass ``channels=1``. The PCM array is
``(frames, 1)`` after decoding.

.. code-block:: python

   enc = ruopus.OpusEncoder(1, bitrate=32_000)
   dec = ruopus.OpusDecoder(1)

   frame = np.zeros((960, 1), dtype=np.float32)   # or shape (960,)
   packet = enc.encode_auto(frame)
   pcm    = dec.decode_packet(packet)              # (960, 1) float32

Decoding to int16
-----------------

:meth:`~ruopus.OpusDecoder.decode_packet_i16` returns ``int16`` PCM scaled to
``[-32768, 32767]``, identical to the ``opus_demo`` reference output.

.. code-block:: python

   dec = ruopus.OpusDecoder(2)
   pcm_i16 = dec.decode_packet_i16(packet)    # (frames, 2) int16

Streaming Pipeline
------------------

The encoder and decoder are both stateful: maintain them across frames so
inter-frame state (mode hysteresis, overlap buffers, concealment history) is
continuous:

.. code-block:: python

   import numpy as np
   import ruopus

   ENC = ruopus.OpusEncoder(2, bitrate=64_000)
   DEC = ruopus.OpusDecoder(2)
   FRAME = 960   # 20 ms at 48 kHz

   def process_stream(pcm_44k: np.ndarray) -> np.ndarray:
       """Resample, encode, and decode a stereo 44.1 kHz recording."""
       # ruopus always works at 48 kHz; resample externally if needed
       pcm = pcm_44k.astype(np.float32)
       packets, decoded = [], []
       for start in range(0, len(pcm) - FRAME, FRAME):
           chunk = pcm[start : start + FRAME]
           pkt   = ENC.encode_auto(chunk)
           out   = DEC.decode_packet(pkt)
           packets.append(pkt)
           decoded.append(out)
       return np.concatenate(decoded, axis=0)

Adjusting Encoder Settings at Runtime
--------------------------------------

All encoder properties are writable; changes take effect on the next frame:

.. code-block:: python

   enc = ruopus.OpusEncoder(2, bitrate=64_000)

   # Reduce bitrate mid-stream (e.g. bandwidth dropped)
   enc.bitrate = 24_000

   # Enable DTX to suppress silent frames
   enc.dtx = True

   # Bump complexity for a batch-encode job
   enc.complexity = 10

Next Steps
----------

- Understand :doc:`codec_modes` and when to use each encode method
- Learn about valid :doc:`frame_sizes`
- Encode and decode complete files with :doc:`ogg`
- Handle packet loss with :doc:`fec_and_loss`
- Inspect packet structure with :doc:`packet_inspection`