use super::mode::{AmrFrameType, AmrVariant};
const SID_UPDATE_PERIOD: i16 = 8;
const SID_FIRST_FOLLOWUP: i16 = 3;
#[derive(Debug, Clone)]
pub struct SidCadence {
variant: AmrVariant,
countdown: i16,
handover_debt: i16,
previous_was_speech: bool,
last_sid_was_update: bool,
}
impl SidCadence {
#[must_use]
pub const fn new(variant: AmrVariant) -> Self {
Self {
variant,
countdown: SID_FIRST_FOLLOWUP,
handover_debt: 0,
previous_was_speech: true,
last_sid_was_update: false,
}
}
#[allow(clippy::missing_const_for_fn)]
pub fn next(&mut self, comfort_noise: bool, mode: super::mode::AmrMode) -> AmrFrameType {
if !comfort_noise {
self.countdown = SID_UPDATE_PERIOD;
self.previous_was_speech = true;
return AmrFrameType::Speech(mode);
}
self.countdown -= 1;
let frame_type = if self.previous_was_speech {
self.countdown = SID_FIRST_FOLLOWUP;
self.last_sid_was_update = false;
AmrFrameType::Sid(self.variant)
} else if self.handover_debt > 0 && self.countdown > 2 {
self.handover_debt -= 1;
self.last_sid_was_update = true;
AmrFrameType::Sid(self.variant)
} else if self.countdown == 0 {
self.countdown = SID_UPDATE_PERIOD;
self.last_sid_was_update = true;
AmrFrameType::Sid(self.variant)
} else {
AmrFrameType::NoData
};
self.previous_was_speech = false;
frame_type
}
#[must_use]
pub const fn last_sid_was_an_update(&self) -> bool {
self.last_sid_was_update
}
#[allow(clippy::missing_const_for_fn)]
pub fn reset(&mut self) {
*self = Self::new(self.variant);
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::codecs::amr::mode::AmrMode;
fn mode() -> AmrMode {
AmrMode::new(AmrVariant::WideBand, 2).expect("12.65 kbit/s")
}
#[test]
fn the_schedule_is_first_then_two_gaps_then_every_eighth() {
let mut cadence = SidCadence::new(AmrVariant::WideBand);
let m = mode();
for _ in 0..3 {
assert_eq!(cadence.next(false, m), AmrFrameType::Speech(m));
}
let mut types = Vec::new();
for _ in 0..20 {
types.push(cadence.next(true, m));
}
let sid = AmrFrameType::Sid(AmrVariant::WideBand);
assert_eq!(types[0], sid, "the first quiet frame is a SID");
assert_eq!(types[1], AmrFrameType::NoData);
assert_eq!(types[2], AmrFrameType::NoData);
assert_eq!(types[3], sid, "the first update follows after two gaps");
let updates: Vec<usize> = types
.iter()
.enumerate()
.filter(|(_, &t)| t == sid)
.map(|(i, _)| i)
.collect();
assert_eq!(updates, vec![0, 3, 11, 19]);
}
#[test]
fn a_talk_spurt_restarts_the_schedule() {
let mut cadence = SidCadence::new(AmrVariant::WideBand);
let m = mode();
for _ in 0..6 {
cadence.next(true, m);
}
assert_eq!(cadence.next(false, m), AmrFrameType::Speech(m));
assert_eq!(
cadence.next(true, m),
AmrFrameType::Sid(AmrVariant::WideBand),
"the frame after speech is always a SID_FIRST"
);
}
#[test]
fn both_variants_carry_their_own_sid_frame_type() {
let m = mode();
let mut wide = SidCadence::new(AmrVariant::WideBand);
let mut narrow = SidCadence::new(AmrVariant::NarrowBand);
assert_eq!(
wide.next(true, m).frame_type_index(),
AmrVariant::WideBand.sid_frame_type()
);
let nb_mode = AmrMode::new(AmrVariant::NarrowBand, 4).expect("7.40 kbit/s");
assert_eq!(
narrow.next(true, nb_mode).frame_type_index(),
AmrVariant::NarrowBand.sid_frame_type()
);
}
}