ruopus 0.1.2

A pure-Rust implementation of the Opus audio codec (RFC 6716). No FFI; unsafe confined to documented SIMD kernels.
Documentation
//! Pitch lag resolution (RFC 6716 ยง4.2.7.6.1).
//!
//! The decoded lag index gives a base lag in `[2 ms, 18 ms]` at the
//! internal rate; the contour index selects a per-subframe offset vector
//! from the rate/duration-specific codebook.

#![allow(dead_code, reason = "consumed incrementally as the SILK decoder stages land")]

use super::indices::MAX_NB_SUBFR;
use super::tables::{CB_LAGS_STAGE2, CB_LAGS_STAGE2_10_MS, CB_LAGS_STAGE3, CB_LAGS_STAGE3_10_MS};

const PE_MIN_LAG_MS: i32 = 2;
const PE_MAX_LAG_MS: i32 = 18;

/// Per-subframe pitch lags (in samples at `fs_khz`)
/// from the lag and contour indices.
pub(crate) fn decode_pitch(lag_index: i16, contour_index: i8, fs_khz: i32, nb_subfr: usize) -> [i32; MAX_NB_SUBFR] {
    let contour = contour_index as usize;
    // Row k = subframe, column = contour index.
    let cb_row = |k: usize| -> i32 {
        if fs_khz == 8 {
            if nb_subfr == MAX_NB_SUBFR {
                i32::from(CB_LAGS_STAGE2[k][contour])
            } else {
                i32::from(CB_LAGS_STAGE2_10_MS[k][contour])
            }
        } else if nb_subfr == MAX_NB_SUBFR {
            i32::from(CB_LAGS_STAGE3[k][contour])
        } else {
            i32::from(CB_LAGS_STAGE3_10_MS[k][contour])
        }
    };

    let min_lag = PE_MIN_LAG_MS * fs_khz;
    let max_lag = PE_MAX_LAG_MS * fs_khz;
    let lag = min_lag + i32::from(lag_index);

    let mut pitch_lags = [0i32; MAX_NB_SUBFR];
    for (k, out) in pitch_lags.iter_mut().enumerate().take(nb_subfr) {
        *out = (lag + cb_row(k)).clamp(min_lag, max_lag);
    }
    pitch_lags
}

#[cfg(test)]
mod tests {
    use super::*;

    /// Reference pins.
    #[test]
    fn decode_pitch_matches_reference_pins() {
        assert_eq!(decode_pitch(100, 5, 8, 4), [116, 116, 116, 117]);
        assert_eq!(decode_pitch(100, 1, 8, 2), [117, 116, 0, 0]);
        assert_eq!(decode_pitch(200, 20, 12, 4), [216, 216, 216, 216]);
        assert_eq!(decode_pitch(250, 30, 16, 4), [288, 284, 280, 276]);
        assert_eq!(decode_pitch(250, 11, 16, 2), [279, 285, 0, 0]);
        // Clamping at both extremes.
        assert_eq!(decode_pitch(0, 33, 16, 4), [32, 32, 35, 41]);
        assert_eq!(decode_pitch(255, 33, 16, 4), [278, 284, 288, 288]);
    }
}