rvoip-codec-core 0.3.8

G.711 and optional G.729/Opus/AMR audio codec implementations for RVOIP
Documentation
//! Oracle qualification: where the Apache-2.0 encoders agree with the
//! normative references, and where they do not.
//!
//! Test-only. The implementation plan's Phase 0 asked for this and it was never
//! measured; risk R8 ("`vo-amrwbenc` is not bit-exact with TS 26.173, so its
//! stage dumps cannot be trusted") stayed open on the strength of the library
//! being VisualOn-derived rather than reference-derived. It is now measured.
//!
//! # What is compared
//!
//! Two encoders were run over the *same* deterministic signal:
//!
//! - `amrnb_mode*.amr` / `amrwb_mode*.amr` — `opencore-amr` and `vo-amrwbenc`,
//!   25 frames, generated by `tools/build-amr-oracle.sh`.
//! - `amrnb_enc_mode*.amr` / `amrwb_enc_mode*.amr` — TS 26.073 and TS 26.173's
//!   own encoders, 50 frames of the same generator, from
//!   `tools/build-amr*-encoder-reference.sh`.
//!
//! The shorter file is compared against the longer one's prefix. Both are AMR
//! storage format, so a common prefix means the same magic, the same table of
//! contents and the same speech bits frame for frame.
//!
//! # What it does and does not establish
//!
//! It establishes agreement **on this signal**, not universal bit-exactness. A
//! conformance claim needs TS 26.074 / TS 26.174, which are not in the tree.
//! What it does buy is a defensible answer to "may a fixture derived from this
//! library be treated as ground truth for that path", which is the question the
//! plan actually needs answered — and for the two rates where the answer is no,
//! it says so rather than leaving it assumed.

use super::mode::{AmrMode, AmrVariant};

/// `(mode index, apache fixture, reference fixture)`.
const NARROWBAND: [(u8, &[u8], &[u8]); 8] = [
    (
        0,
        include_bytes!("testdata/amrnb_mode0.amr"),
        include_bytes!("testdata/amrnb_enc_mode0.amr"),
    ),
    (
        1,
        include_bytes!("testdata/amrnb_mode1.amr"),
        include_bytes!("testdata/amrnb_enc_mode1.amr"),
    ),
    (
        2,
        include_bytes!("testdata/amrnb_mode2.amr"),
        include_bytes!("testdata/amrnb_enc_mode2.amr"),
    ),
    (
        3,
        include_bytes!("testdata/amrnb_mode3.amr"),
        include_bytes!("testdata/amrnb_enc_mode3.amr"),
    ),
    (
        4,
        include_bytes!("testdata/amrnb_mode4.amr"),
        include_bytes!("testdata/amrnb_enc_mode4.amr"),
    ),
    (
        5,
        include_bytes!("testdata/amrnb_mode5.amr"),
        include_bytes!("testdata/amrnb_enc_mode5.amr"),
    ),
    (
        6,
        include_bytes!("testdata/amrnb_mode6.amr"),
        include_bytes!("testdata/amrnb_enc_mode6.amr"),
    ),
    (
        7,
        include_bytes!("testdata/amrnb_mode7.amr"),
        include_bytes!("testdata/amrnb_enc_mode7.amr"),
    ),
];

const WIDEBAND: [(u8, &[u8], &[u8]); 9] = [
    (
        0,
        include_bytes!("testdata/amrwb_mode0.amr"),
        include_bytes!("testdata/amrwb_enc_mode0.amr"),
    ),
    (
        1,
        include_bytes!("testdata/amrwb_mode1.amr"),
        include_bytes!("testdata/amrwb_enc_mode1.amr"),
    ),
    (
        2,
        include_bytes!("testdata/amrwb_mode2.amr"),
        include_bytes!("testdata/amrwb_enc_mode2.amr"),
    ),
    (
        3,
        include_bytes!("testdata/amrwb_mode3.amr"),
        include_bytes!("testdata/amrwb_enc_mode3.amr"),
    ),
    (
        4,
        include_bytes!("testdata/amrwb_mode4.amr"),
        include_bytes!("testdata/amrwb_enc_mode4.amr"),
    ),
    (
        5,
        include_bytes!("testdata/amrwb_mode5.amr"),
        include_bytes!("testdata/amrwb_enc_mode5.amr"),
    ),
    (
        6,
        include_bytes!("testdata/amrwb_mode6.amr"),
        include_bytes!("testdata/amrwb_enc_mode6.amr"),
    ),
    (
        7,
        include_bytes!("testdata/amrwb_mode7.amr"),
        include_bytes!("testdata/amrwb_enc_mode7.amr"),
    ),
    (
        8,
        include_bytes!("testdata/amrwb_mode8.amr"),
        include_bytes!("testdata/amrwb_enc_mode8.amr"),
    ),
];

/// First byte at which the shorter file leaves the longer one's prefix.
fn first_difference(apache: &[u8], reference: &[u8]) -> Option<usize> {
    assert!(
        apache.len() <= reference.len(),
        "the reference run is the longer of the two by construction"
    );
    apache.iter().zip(reference).position(|(a, b)| a != b)
}

/// `opencore-amr`'s AMR-NB encoder reproduces TS 26.073 exactly, every rate.
///
/// This is the stronger of the two results and the more useful one: it means an
/// AMR-NB encode fixture derived from `opencore-amr` — which is Apache-2.0 and
/// so unambiguously committable — carries the same authority as one generated
/// by running the 3GPP reference, whose redistribution status the plan leaves
/// open as IP-2b.
#[test]
fn opencore_amr_narrowband_agrees_with_ts26073_at_every_rate() {
    for (index, apache, reference) in NARROWBAND {
        let diff = first_difference(apache, reference);
        assert!(
            diff.is_none(),
            "AMR-NB mode {index}: opencore-amr leaves TS 26.073 at byte {}",
            diff.unwrap()
        );
    }
}

/// `vo-amrwbenc` reproduces TS 26.173 at seven of the nine wideband rates, and
/// **not** at 12.65 or 14.25 kbit/s.
///
/// Risk R8 in the plan is therefore real but narrow. The consequence is
/// specific: a wideband *encoder* fixture derived from `vo-amrwbenc` is ground
/// truth at every rate except those two, where it is a lead to investigate
/// rather than an answer. The decoder is unaffected — it was validated against
/// TS 26.173 directly, and `vo-amrwbenc` only ever supplied it with input.
///
/// The two divergent rates are adjacent in the mode table and both fall in the
/// middle of the range, which suggests one shared code path rather than two
/// independent defects; 15.85 and above agree, as do 6.60 and 8.85.
#[test]
fn vo_amrwbenc_agrees_with_ts26173_at_seven_of_nine_rates() {
    let mut divergent = Vec::new();
    for (index, apache, reference) in WIDEBAND {
        if let Some(at) = first_difference(apache, reference) {
            divergent.push((index, at));
        }
    }

    // Pinned exactly rather than as "at most two": a rate that starts or stops
    // agreeing is a change in what the fixtures are worth, and should be read
    // before it is accepted.
    let indices: Vec<u8> = divergent.iter().map(|&(i, _)| i).collect();
    assert_eq!(
        indices,
        vec![2, 3],
        "vo-amrwbenc's disagreement with TS 26.173 moved; \
         divergences (mode, byte) = {divergent:?}"
    );

    for (index, _) in divergent {
        let mode = AmrMode::new(AmrVariant::WideBand, index).expect("mode");
        assert!(
            (12_000..=15_000).contains(&mode.bitrate()),
            "the divergent rates were 12.65 and 14.25 kbit/s"
        );
    }
}

/// The comparison would be vacuous if the two runs used different inputs, and
/// nothing in the byte comparison itself would reveal that — two files that
/// share no prefix look exactly like two encoders that disagree immediately.
///
/// The signal is the same generator at the variant's own rate, so the wideband
/// and narrowband fixtures should agree with their references *for a different
/// reason* than each other, and the reference runs should be strictly longer
/// (50 frames against 25).
#[test]
fn the_two_runs_are_comparable_at_all() {
    for (index, apache, reference) in NARROWBAND {
        assert!(
            reference.len() > apache.len(),
            "AMR-NB mode {index}: the reference run should be the longer one"
        );
        assert_eq!(&apache[..6], b"#!AMR\n", "mode {index} apache magic");
        assert_eq!(&reference[..6], b"#!AMR\n", "mode {index} reference magic");
    }
    for (index, apache, reference) in WIDEBAND {
        assert!(
            reference.len() > apache.len(),
            "AMR-WB mode {index}: the reference run should be the longer one"
        );
        assert_eq!(&apache[..9], b"#!AMR-WB\n", "mode {index} apache magic");
        assert_eq!(
            &reference[..9],
            b"#!AMR-WB\n",
            "mode {index} reference magic"
        );
    }
}