use super::homing_tables::{DHF, PRMS_FIRST_SUBFRAME};
pub const HOMING_SAMPLE: i16 = 0x0008;
const PRML: usize = 15;
const NB_OF_BITS: [usize; 9] = [132, 177, 253, 285, 317, 365, 397, 461, 477];
#[must_use]
pub fn is_encoder_homing_frame(samples: &[i16]) -> bool {
samples.len() == 320 && samples.iter().all(|&s| s == HOMING_SAMPLE)
}
#[must_use]
pub fn is_decoder_homing_frame(payload: &[u8], mode: usize) -> bool {
codec_bits(payload, mode).is_some_and(|bits| dhf_test(&bits, mode, NB_OF_BITS[mode]))
}
fn codec_bits(payload: &[u8], mode: usize) -> Option<Vec<u8>> {
let mode = crate::codecs::amr::mode::AmrMode::new(
crate::codecs::amr::mode::AmrVariant::WideBand,
u8::try_from(mode).ok()?,
)
.ok()?;
super::bitstream::CodecBits::unpack(mode, payload).map(|b| b.bits().to_vec())
}
#[must_use]
pub fn is_decoder_homing_frame_first(payload: &[u8], mode: usize) -> bool {
codec_bits(payload, mode).is_some_and(|bits| dhf_test(&bits, mode, PRMS_FIRST_SUBFRAME[mode]))
}
fn dhf_test(codec: &[u8], mode: usize, bits: usize) -> bool {
assert!(mode < 9, "homing is defined for the nine speech modes");
if codec.len() < bits {
return false;
}
let want = &DHF[mode];
let mut read = SerialBits::new(codec);
let mut param = [0i16; 32];
let (count, shift) = if mode == 8 {
for slot in param.iter_mut().take(10) {
*slot = read.take(PRML);
}
param[10] = read.take(PRML) & 0x61FF;
for slot in param.iter_mut().take(17).skip(11) {
*slot = read.take(PRML);
}
param[17] = read.take(PRML) & -0x1F00; for slot in param.iter_mut().take(24).skip(18) {
*slot = read.take(PRML);
}
param[24] = read.take(PRML) & 0x7F0F;
for slot in param.iter_mut().take(31).skip(25) {
*slot = read.take(PRML);
}
param[31] = read.take(8) << 7;
(31, 0)
} else {
let mut i = 0usize;
let mut consumed = 0usize;
while bits.saturating_sub(PRML) > consumed {
param[i] = read.take(PRML);
consumed += PRML;
i += 1;
}
let tail = bits - consumed;
param[i] = read.take(tail) << (PRML - tail);
(i, PRML - tail)
};
for i in 0..count {
if param[i] != want[i] {
return false;
}
}
let mask = (0x7fffi16 >> shift) << shift;
param[count] == (want[count] & mask)
}
struct SerialBits<'a> {
codec: &'a [u8],
at: usize,
}
impl<'a> SerialBits<'a> {
const fn new(codec: &'a [u8]) -> Self {
Self { codec, at: 0 }
}
fn take(&mut self, width: usize) -> i16 {
let mut value = 0i16;
for _ in 0..width {
value = (value << 1) | i16::from(self.codec[self.at]);
self.at += 1;
}
value
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn the_encoder_homing_frame_is_exactly_that_and_nothing_near_it() {
assert!(is_encoder_homing_frame(&[HOMING_SAMPLE; 320]));
let mut nearly = [HOMING_SAMPLE; 320];
nearly[319] = 9;
assert!(!is_encoder_homing_frame(&nearly));
assert!(!is_encoder_homing_frame(&[0i16; 320]));
assert!(!is_encoder_homing_frame(&[HOMING_SAMPLE; 319]));
}
#[test]
#[allow(clippy::needless_range_loop)]
fn each_mode_recognises_its_own_pattern_and_rejects_the_others() {
let mut checked = 0;
for mode in 0..9usize {
let payload = homing_payload(mode);
assert!(
is_decoder_homing_frame(&payload, mode),
"mode {mode} did not recognise its own homing frame"
);
assert!(is_decoder_homing_frame_first(&payload, mode));
for other in 0..9usize {
if other == mode || NB_OF_BITS[other] > NB_OF_BITS[mode] {
continue;
}
assert!(
!is_decoder_homing_frame(&payload, other),
"mode {other} accepted mode {mode}'s homing frame"
);
checked += 1;
}
}
assert!(checked >= 30, "only {checked} cross-mode pairs compared");
}
#[test]
fn the_encoder_emits_each_modes_homing_pattern() {
use crate::codecs::amr::wb::enc::encoder::{Rate, WbEncoder};
for mode in 0..9u8 {
let rate = Rate::from_index(mode).expect("a speech mode");
let mut encoder = WbEncoder::new();
let payload = encoder.encode_frame(&[HOMING_SAMPLE; 320], rate);
assert!(
is_decoder_homing_frame(&payload, mode as usize),
"mode {mode} did not emit its homing pattern"
);
}
}
#[test]
fn an_ordinary_frame_is_not_a_homing_frame() {
for mode in 0..9usize {
let payload = vec![0x5Au8; 64];
assert!(!is_decoder_homing_frame(&payload, mode));
let zeros = vec![0u8; 64];
assert!(!is_decoder_homing_frame(&zeros, mode));
}
}
#[test]
fn the_high_band_gains_are_masked_at_23_85_and_nowhere_else() {
let masked = 10 * PRML + 2;
assert!(
is_decoder_homing_frame(&homing_payload_with_flip(8, Some(masked)), 8),
"23.85 must ignore the masked high-band bits"
);
let kept = 10 * PRML + 1;
assert!(!is_decoder_homing_frame(
&homing_payload_with_flip(8, Some(kept)),
8
));
assert!(!is_decoder_homing_frame(
&homing_payload_with_flip(7, Some(masked)),
7
));
}
fn homing_payload(mode: usize) -> Vec<u8> {
homing_payload_with_flip(mode, None)
}
fn homing_payload_with_flip(mode: usize, flip: Option<usize>) -> Vec<u8> {
let bits = NB_OF_BITS[mode];
let want = &DHF[mode];
let mut codec = vec![0u8; bits];
let (mut at, mut word) = (0usize, 0usize);
while at < bits {
let width = PRML.min(bits - at);
let value = if width == PRML {
want[word]
} else {
want[word] >> (PRML - width)
};
for k in 0..width {
codec[at] = u8::try_from((value >> (width - 1 - k)) & 1).expect("one bit");
at += 1;
}
word += 1;
}
if let Some(bit) = flip {
codec[bit] ^= 1;
}
let amr = crate::codecs::amr::mode::AmrMode::new(
crate::codecs::amr::mode::AmrVariant::WideBand,
u8::try_from(mode).expect("a speech mode"),
)
.expect("a speech mode");
let sort = super::super::bitstream::sort_table_for(amr);
let mut out = vec![0u8; bits.div_ceil(8)];
for (i, &source) in sort.iter().enumerate() {
out[i / 8] |= codec[source as usize] << (7 - (i % 8));
}
out
}
}