#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) struct MlowToc {
pub std_opus: bool,
pub sid: bool,
pub vad: bool,
pub sample_rate: i32,
pub frame_ms: i32,
pub voiced: bool,
pub active: bool,
pub flag2: bool,
pub flag0: bool,
}
fn standard_opus_frame_ms(b: u8) -> i32 {
let config = b >> 3;
if config < 12 {
[10, 20, 40, 60][(config & 3) as usize] } else if config < 16 {
[10, 20][((config - 12) & 1) as usize] } else {
match config & 3 {
0 => 3, 1 => 5,
2 => 10,
_ => 20,
}
}
}
pub(crate) fn parse_mlow_toc(b: u8) -> MlowToc {
if b & 0xC0 == 0xC0 {
let toc = MlowToc {
std_opus: true,
sid: false,
vad: false,
sample_rate: 16000,
frame_ms: standard_opus_frame_ms(b),
voiced: false,
active: false,
flag2: false,
flag0: false,
};
log::trace!(
"mlow TOC 0x{b:02x}: standard-opus frame_ms={}",
toc.frame_ms
);
return toc;
}
let bit1 = (b >> 1) & 1 != 0;
let vad = (b >> 6) & 1 != 0;
let toc = MlowToc {
std_opus: false,
sid: b >> 7 != 0,
vad,
sample_rate: if b & 0x20 != 0 { 32000 } else { 16000 },
frame_ms: [10, 20, 60, 120][((b >> 3) & 3) as usize],
voiced: vad && bit1,
active: vad || bit1,
flag2: (b >> 2) & 1 != 0,
flag0: b & 1 != 0,
};
log::trace!(
"mlow TOC 0x{b:02x}: sid={} vad={} sr={} ms={} voiced={} active={} f2={} f0={}",
toc.sid,
toc.vad,
toc.sample_rate,
toc.frame_ms,
toc.voiced,
toc.active,
toc.flag2,
toc.flag0
);
toc
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::Value;
#[test]
fn toc_matches_go_all_256() {
let recs: Value =
serde_json::from_str(include_str!("testdata/toc_vectors.json")).expect("toc_vectors");
let arr = recs.as_array().unwrap();
assert_eq!(arr.len(), 256);
for rec in arr {
let b = rec["b"].as_u64().unwrap() as u8;
let t = parse_mlow_toc(b);
assert_eq!(t.std_opus, rec["std"].as_bool().unwrap(), "std b=0x{b:02x}");
assert_eq!(t.sid, rec["sid"].as_bool().unwrap(), "sid b=0x{b:02x}");
assert_eq!(t.vad, rec["vad"].as_bool().unwrap(), "vad b=0x{b:02x}");
assert_eq!(
t.sample_rate,
rec["sr"].as_i64().unwrap() as i32,
"sr b=0x{b:02x}"
);
assert_eq!(
t.frame_ms,
rec["ms"].as_i64().unwrap() as i32,
"ms b=0x{b:02x}"
);
assert_eq!(
t.voiced,
rec["voiced"].as_bool().unwrap(),
"voiced b=0x{b:02x}"
);
assert_eq!(
t.active,
rec["active"].as_bool().unwrap(),
"active b=0x{b:02x}"
);
assert_eq!(t.flag2, rec["f2"].as_bool().unwrap(), "f2 b=0x{b:02x}");
assert_eq!(t.flag0, rec["f0"].as_bool().unwrap(), "f0 b=0x{b:02x}");
}
}
}