import re
import sys
tab_path, out_path = sys.argv[1], sys.argv[2]
raw = open(tab_path).read()
BOOKS = [
("mean_isf", "MEAN_ISF", 16, 1),
("dico1_isf", "DICO1", 256, 9),
("dico2_isf", "DICO2", 256, 7),
("dico21_isf", "DICO21", 64, 3),
("dico22_isf", "DICO22", 128, 3),
("dico23_isf", "DICO23", 128, 3),
("dico24_isf", "DICO24", 32, 3),
("dico25_isf", "DICO25", 32, 4),
("dico21_isf_36b", "DICO21_36B", 128, 5),
("dico22_isf_36b", "DICO22_36B", 128, 4),
("dico23_isf_36b", "DICO23_36B", 64, 7),
]
DOCS = {
"MEAN_ISF": "Long-term mean ISF vector, Q15. Subtracted before quantisation\n/// and added back after, so the codebooks only ever carry deviations.",
"DICO1": "First-stage codebook for the low nine ISFs, 256 entries of 9.",
"DICO2": "First-stage codebook for the high seven ISFs, 256 entries of 7.",
"DICO21": "Second-stage refinement, ISFs 0-2, 64 entries of 3.",
"DICO22": "Second-stage refinement, ISFs 3-5, 128 entries of 3.",
"DICO23": "Second-stage refinement, ISFs 6-8, 128 entries of 3.",
"DICO24": "Second-stage refinement, ISFs 9-11, 32 entries of 3.",
"DICO25": "Second-stage refinement, ISFs 12-15, 32 entries of 4.",
"DICO21_36B": "36-bit second-stage refinement, ISFs 0-4, 128 entries of 5.",
"DICO22_36B": "36-bit second-stage refinement, ISFs 5-8, 128 entries of 4.",
"DICO23_36B": "36-bit second-stage refinement, ISFs 9-15, 64 entries of 7.",
}
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}"
return vals
def table(rust_name, vals, dim, per_line=8):
doc = DOCS[rust_name]
lines = [f"/// {doc}", f"pub const {rust_name}: [i16; {len(vals)}] = ["]
step = dim if 1 < dim <= per_line else per_line
for i in range(0, len(vals), step):
lines.append(" " + ", ".join(str(v) for v in vals[i : i + step]) + ",")
lines.append("];")
return "\n".join(lines)
HEADER = '''//! ISF quantiser codebooks for AMR-WB, from the TS 26.173 reference.
//!
//! The quantiser is two-stage and split. The first stage codes the sixteen
//! ISFs as two vectors (nine low, seven high); the second stage refines the
//! result in narrower splits, five of them at 46 bits and three at 36. Splitting
//! is what makes the codebooks tractable: a joint 46-bit codebook over sixteen
//! dimensions would have 7e13 entries.
//!
//! Two rates because the 6.60 kbit/s mode cannot afford 46 bits for the
//! spectrum. Both share the first stage; only the refinement differs.
//!
//! These are normative constants -- the codec cannot be conformant without
//! exactly these numbers -- and are generated by `tools/gen_isf_codebooks.py`.
'''
with open(out_path, "w") as f:
f.write(HEADER)
parts = []
for c_name, rust_name, count, dim in BOOKS:
parts.append(table(rust_name, values(c_name, count * dim), dim))
f.write("\n\n".join(parts) + "\n")
print(f"wrote {out_path}: {len(BOOKS)} codebooks")