Frame Sizes =========== Opus operates on fixed-duration frames. The frame duration determines latency, overhead, and which modes are available. This guide explains the trade-offs and shows which frame sizes are valid for each encode method. Valid Frame Sizes ----------------- Opus supports six frame durations: .. list-table:: :header-rows: 1 :widths: 15 25 20 40 * - Duration - Samples at 48 kHz - ``FrameSize`` enum - Available modes * - 2.5 ms - 120 - ``FrameSize.Ms2_5`` - CELT-only * - 5 ms - 240 - ``FrameSize.Ms5`` - CELT-only * - 10 ms - 480 - ``FrameSize.Ms10`` - SILK, hybrid, CELT * - 20 ms - 960 - ``FrameSize.Ms20`` - SILK, hybrid, CELT * - 40 ms - 1920 - ``FrameSize.Ms40`` - SILK-only * - 60 ms - 2880 - ``FrameSize.Ms60`` - SILK-only The sample counts above are *per channel*. A stereo 20 ms frame has 960 samples per channel, so the 2-D input array has shape ``(960, 2)``. Choosing a Frame Size --------------------- **20 ms** is the standard choice for most applications. It gives a good balance of latency, packet overhead, and codec efficiency, and it is the only frame size that works with all three modes (SILK, hybrid, CELT). **10 ms** is the minimum latency frame that still supports SILK and hybrid. Use it when 20 ms end-to-end latency is too much (e.g. a real-time call with tight jitter buffer requirements). **2.5 ms / 5 ms** are for CELT-only applications (e.g. ``Application.RestrictedLowDelay``) that need the absolute minimum algorithmic delay. **40 ms / 60 ms** are SILK-only and reduce packetisation overhead at the cost of latency. Useful for store-and-forward voice (e.g. voicemail) or very bandwidth-constrained links. Per-Method Valid Frame Sizes ----------------------------- .. list-table:: :header-rows: 1 :widths: 30 70 * - Method - Valid durations * - :meth:`~ruopus.OpusEncoder.encode_auto` - 2.5, 5, 10, 20, 40, 60 ms * - :meth:`~ruopus.OpusEncoder.encode` - 2.5, 5, 10, 20 ms (CELT-only) * - :meth:`~ruopus.OpusEncoder.encode_silk` - 10, 20, 40, 60 ms (SILK-only) * - :meth:`~ruopus.OpusEncoder.encode_hybrid` - 10, 20 ms (hybrid only) Passing an invalid frame size raises :exc:`~ruopus.EncodeError`. Samples vs Duration ------------------- Since Opus always works at 48 kHz internally, compute sample counts from durations like this: .. code-block:: python import ruopus for fs in ruopus.FrameSize: print( f"{fs.name}: {fs.tenth_ms / 10:.1f} ms " f"= {fs.samples_per_channel_48k} samples/ch" ) Output:: Ms2_5: 2.5 ms = 120 samples/ch Ms5: 5.0 ms = 240 samples/ch Ms10: 10.0 ms = 480 samples/ch Ms20: 20.0 ms = 960 samples/ch Ms40: 40.0 ms = 1920 samples/ch Ms60: 60.0 ms = 2880 samples/ch Constructing frames of the right size: .. code-block:: python import numpy as np import ruopus CHANNELS = 2 FRAME_SIZE = ruopus.FrameSize.Ms20 # 960 samples/ch n = FRAME_SIZE.samples_per_channel_48k frame = np.zeros((n, CHANNELS), dtype=np.float32) Encoder Lookahead ----------------- The encoder adds a small amount of algorithmic delay (``pre_skip``) so the MDCT windows can overlap properly. Read it from the encoder: .. code-block:: python enc = ruopus.OpusEncoder(2) print(f"Lookahead: {enc.lookahead} samples at 48 kHz") # Typically 120 for fullband CELT (the MDCT overlap) When the decoded output is aligned with the input for round-trip testing, skip the first ``enc.lookahead`` output samples.