rvoip-codec-core 0.3.8

G.711 and optional G.729/Opus/AMR audio codec implementations for RVOIP
Documentation
#!/usr/bin/env python3
"""Emit the AMR-WB payload bit-ordering tables as Rust from TS 26.173."""
import re
import sys

tab_path, out_path = sys.argv[1], sys.argv[2]
raw = open(tab_path).read()

# (C name, Rust name, length, human label)
TABLES = [
    ("sort_660", "SORT_660", 132, "6.60 kbit/s"),
    ("sort_885", "SORT_885", 177, "8.85 kbit/s"),
    ("sort_1265", "SORT_1265", 253, "12.65 kbit/s"),
    ("sort_1425", "SORT_1425", 285, "14.25 kbit/s"),
    ("sort_1585", "SORT_1585", 317, "15.85 kbit/s"),
    ("sort_1825", "SORT_1825", 365, "18.25 kbit/s"),
    ("sort_1985", "SORT_1985", 397, "19.85 kbit/s"),
    ("sort_2305", "SORT_2305", 461, "23.05 kbit/s"),
    ("sort_2385", "SORT_2385", 477, "23.85 kbit/s"),
    ("sort_SID", "SORT_SID", 35, "SID (comfort noise)"),
]


def values(name, count):
    body = raw.split(f"{name}[", 1)[1].split("{", 1)[1].split("}", 1)[0]
    body = re.sub(r"/\*.*?\*/", "", body, flags=re.S)
    vals = [int(x) for x in re.findall(r"-?\d+", body)]
    assert len(vals) == count, f"{name}: got {len(vals)}, want {count}"
    # A sorting table is a permutation; anything else is a transcription error.
    assert sorted(vals) == list(range(count)), f"{name} is not a permutation"
    return vals


parts = []
for c_name, rust_name, count, label in TABLES:
    vals = values(c_name, count)
    lines = [
        f"/// Payload bit order for the {label} mode, {count} bits.",
        f"pub const {rust_name}: [u16; {count}] = [",
    ]
    for i in range(0, count, 10):
        lines.append("    " + ", ".join(str(v) for v in vals[i : i + 10]) + ",")
    lines.append("];")
    parts.append("\n".join(lines))

HEADER = '''//! Payload bit-ordering tables for AMR-WB, from the TS 26.173 reference.
//!
//! RFC 4867 carries the codec bits sorted by subjective importance rather than
//! in the order the decoder reads them, so unpacking a frame is a permutation
//! before it is a field walk. Entry `i` of a table gives the codec-bit index
//! that payload bit `i` belongs to:
//!
//! ```text
//! codec_bits[SORT_2385[i]] = payload_bit[i]
//! ```
//!
//! The sorting is what makes unequal error protection possible: put the
//! perceptually critical bits first and a channel codec can protect the front
//! of the frame more heavily than the tail. It is also why the class A bit
//! counts in [`crate::codecs::amr::mode`] are simply prefix lengths.
//!
//! Generated by `tools/gen_sort_tables.py`; each table is checked to be a
//! genuine permutation of `0..n` at generation time.

'''

with open(out_path, "w") as f:
    f.write(HEADER)
    f.write("\n\n".join(parts) + "\n")

print(f"wrote {out_path}: {len(TABLES)} sorting tables")