Low-Level API ============= The ``ruopus.lowlevel`` submodule exposes the SILK, LPC, and CELT codec layers directly, below the Opus packet layer. These are advanced building blocks for research, educational, or custom codec work. **For ordinary encode/decode, prefer** :class:`~ruopus.OpusEncoder` **and** :class:`~ruopus.OpusDecoder`. .. code-block:: python import ruopus.lowlevel as ll # Inspect what's available print(dir(ll)) CELT Encoder and Decoder ------------------------ :class:`~ruopus.lowlevel.CeltEncoder` and :class:`~ruopus.lowlevel.CeltDecoder` operate on raw CELT frame bodies, with no Opus TOC byte and no packet framing. They are useful for research into the MDCT layer independently. .. code-block:: python import numpy as np import ruopus.lowlevel as ll enc = ll.CeltEncoder(channels=1, complexity=10, bitrate=64_000) dec = ll.CeltDecoder(channels=1) frame = np.zeros(960, dtype=np.float32) body = enc.encode(frame) # bytes, raw CELT frame body print(f"Encoded: {len(body)} bytes") # CELT decoder also provides packet-loss concealment: concealed = dec.decode_lost(frame_size=960) # (960, 1) float32 .. warning:: The ``CeltDecoder`` does not expose a full ``decode`` method because CELT decoding requires the range-coder state from the encoder, which is not exported. Decoding real CELT packets should go through :class:`~ruopus.OpusDecoder`. SILK Encoder and Decoder ------------------------- :class:`~ruopus.lowlevel.SilkEncoder` operates at SILK's *internal* sample rates (8, 12, or 16 kHz), **not** the 48 kHz Opus rate. Input PCM is ``int16``. :class:`~ruopus.lowlevel.SilkDecoder` decodes SILK bitstream bytes back to ``int16`` PCM. .. code-block:: python import numpy as np import ruopus.lowlevel as ll # 16 kHz, 20 ms frames (4 subframes) enc = ll.SilkEncoder(fs_khz=16, nb_subfr=4, bitrate=25_000, complexity=10) # One 20 ms frame at 16 kHz = 16000 * 0.02 = 320 samples frame_i16 = np.zeros(320, dtype=np.int16) payload = enc.encode(frame_i16) # bytes, SILK bitstream print(f"SILK payload: {len(payload)} bytes") SILK Stereo Encoding ~~~~~~~~~~~~~~~~~~~~ :class:`~ruopus.lowlevel.SilkStereoEncoder` extends the mono encoder with mid-side (M/S) stereo coding: .. code-block:: python enc_ms = ll.SilkStereoEncoder(fs_khz=16, nb_subfr=4, bitrate=40_000) stereo_frame = np.zeros(320 * 2, dtype=np.int16) # interleaved L/R payload = enc_ms.encode(stereo_frame) LPC Arithmetic -------------- The ``ruopus.lowlevel`` module exposes the complete LPC analysis pipeline used inside SILK. These functions are useful for DSP research, feature extraction, and understanding speech codec internals. .. code-block:: python import numpy as np import ruopus.lowlevel as ll signal = np.random.randn(320).astype(np.float32) Autocorrelation ~~~~~~~~~~~~~~~ .. code-block:: python # Biased autocorrelation up to lag `order` ac = ll.compute_autocorrelation(signal, order=16) print(f"r[0] = {ac[0]:.4f}") # signal energy Levinson-Durbin ~~~~~~~~~~~~~~~ Solve the Yule-Walker equations to get LPC coefficients: .. code-block:: python # From a pre-computed autocorrelation vector coeffs = ll.levinson_durbin(ac) print(coeffs) # LpcCoefficients(order=16) print(coeffs.coeffs) # list of float32 Full LPC Analysis ~~~~~~~~~~~~~~~~~ :func:`~ruopus.lowlevel.lpc_analysis` combines autocorrelation and Levinson-Durbin in one call: .. code-block:: python coeffs = ll.lpc_analysis(signal, order=16) print(f"LPC order: {coeffs.order}") Residual and Synthesis ~~~~~~~~~~~~~~~~~~~~~~ Compute the LPC prediction residual (analysis filter) and reconstruct PCM (synthesis filter): .. code-block:: python # Stateless: history is passed explicitly history = np.zeros(coeffs.order, dtype=np.float32) residual = ll.lpc_residual(signal, coeffs, history) reconstructed = ll.lpc_synthesis(residual, coeffs, history) # Stateful wrappers that maintain a rolling history buffer: residual2 = ll.lpc_residual_stateful(signal, coeffs) recon2 = ll.lpc_synthesis_stateful(residual2, coeffs) Long-Term Prediction (LTP) ~~~~~~~~~~~~~~~~~~~~~~~~~~ LTP models the pitch periodicity on top of the LPC short-term model: .. code-block:: python pitch_lag = ll.estimate_pitch(signal, fs_khz=16) print(f"Estimated pitch lag: {pitch_lag} samples") ltp_res = ll.ltp_residual(signal, pitch_lag) ltp_syn = ll.ltp_synthesis(ltp_res, pitch_lag) DecControl ---------- :class:`~ruopus.lowlevel.DecControl` carries the SILK decoder's control parameters (sample rate, frame length, and similar). It is passed to / returned from :class:`~ruopus.lowlevel.SilkDecoder`: .. code-block:: python ctrl = ll.DecControl(fs_khz=16, nb_subfr=4) dec = ll.SilkDecoder() pcm = dec.decode(silk_payload, ctrl) # (frame_samples,) int16