use crate::celt_redundancy::RedundancyDecision;
use crate::framing::OperatingMode;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CeltResetPlacement {
None,
BeforeFrame,
BeforeRedundantOnly,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct StateReset {
pub silk: bool,
pub celt: CeltResetPlacement,
}
impl StateReset {
pub fn celt_resets(&self) -> bool {
!matches!(self.celt, CeltResetPlacement::None)
}
pub fn is_noop(&self) -> bool {
!self.silk && !self.celt_resets()
}
}
fn redundancy_is_present(decision: RedundancyDecision) -> bool {
decision.is_present()
}
pub fn decide_state_resets(
prev_mode: OperatingMode,
next_mode: OperatingMode,
redundancy: RedundancyDecision,
) -> StateReset {
let silk = silk_reset_required(prev_mode, next_mode);
let celt = celt_reset_placement(prev_mode, next_mode, redundancy_is_present(redundancy));
StateReset { silk, celt }
}
fn silk_reset_required(prev_mode: OperatingMode, next_mode: OperatingMode) -> bool {
matches!(next_mode, OperatingMode::SilkOnly | OperatingMode::Hybrid)
&& matches!(prev_mode, OperatingMode::CeltOnly)
}
fn celt_reset_placement(
prev_mode: OperatingMode,
next_mode: OperatingMode,
redundancy_present: bool,
) -> CeltResetPlacement {
if prev_mode == next_mode {
return CeltResetPlacement::None;
}
match (prev_mode, next_mode) {
(OperatingMode::CeltOnly, OperatingMode::SilkOnly) => CeltResetPlacement::None,
(OperatingMode::CeltOnly, OperatingMode::Hybrid) => {
if redundancy_present {
CeltResetPlacement::None
} else {
CeltResetPlacement::BeforeFrame
}
}
(OperatingMode::SilkOnly | OperatingMode::Hybrid, OperatingMode::CeltOnly) => {
if redundancy_present {
CeltResetPlacement::BeforeRedundantOnly
} else {
CeltResetPlacement::BeforeFrame
}
}
(OperatingMode::SilkOnly, OperatingMode::Hybrid) => CeltResetPlacement::BeforeFrame,
(OperatingMode::Hybrid, OperatingMode::SilkOnly) => CeltResetPlacement::None,
(OperatingMode::SilkOnly, OperatingMode::SilkOnly)
| (OperatingMode::Hybrid, OperatingMode::Hybrid)
| (OperatingMode::CeltOnly, OperatingMode::CeltOnly) => CeltResetPlacement::None,
}
}
#[cfg(test)]
mod tests {
use super::*;
fn not_present() -> RedundancyDecision {
RedundancyDecision::NotPresent
}
fn present() -> RedundancyDecision {
RedundancyDecision::Present {
position: crate::celt_redundancy::RedundancyPosition::End,
size_bytes: 2,
}
}
fn invalid() -> RedundancyDecision {
RedundancyDecision::Invalid
}
#[test]
fn state_reset_celt_resets_is_true_when_celt_resets() {
let r = StateReset {
silk: false,
celt: CeltResetPlacement::BeforeFrame,
};
assert!(r.celt_resets());
let r2 = StateReset {
silk: true,
celt: CeltResetPlacement::BeforeRedundantOnly,
};
assert!(r2.celt_resets());
}
#[test]
fn state_reset_celt_resets_is_false_when_celt_none() {
let r = StateReset {
silk: true,
celt: CeltResetPlacement::None,
};
assert!(!r.celt_resets());
}
#[test]
fn state_reset_is_noop_only_when_both_false() {
let r = StateReset {
silk: false,
celt: CeltResetPlacement::None,
};
assert!(r.is_noop());
assert!(!StateReset {
silk: true,
celt: CeltResetPlacement::None
}
.is_noop());
assert!(!StateReset {
silk: false,
celt: CeltResetPlacement::BeforeFrame
}
.is_noop());
assert!(!StateReset {
silk: false,
celt: CeltResetPlacement::BeforeRedundantOnly
}
.is_noop());
}
#[test]
fn rule1_silk_resets_celt_to_silk() {
let r = decide_state_resets(
OperatingMode::CeltOnly,
OperatingMode::SilkOnly,
not_present(),
);
assert!(r.silk, "SILK must reset on CELT-only → SILK-only");
}
#[test]
fn rule1_silk_resets_celt_to_hybrid() {
let r = decide_state_resets(
OperatingMode::CeltOnly,
OperatingMode::Hybrid,
not_present(),
);
assert!(r.silk, "SILK must reset on CELT-only → Hybrid");
}
#[test]
fn rule1_silk_does_not_reset_silk_to_silk() {
let r = decide_state_resets(
OperatingMode::SilkOnly,
OperatingMode::SilkOnly,
not_present(),
);
assert!(!r.silk);
}
#[test]
fn rule1_silk_does_not_reset_hybrid_to_silk() {
let r = decide_state_resets(
OperatingMode::Hybrid,
OperatingMode::SilkOnly,
not_present(),
);
assert!(!r.silk);
}
#[test]
fn rule1_silk_does_not_reset_when_next_is_celtonly() {
for prev in [
OperatingMode::SilkOnly,
OperatingMode::Hybrid,
OperatingMode::CeltOnly,
] {
let r = decide_state_resets(prev, OperatingMode::CeltOnly, not_present());
assert!(!r.silk, "rule 1 must not fire when next is CELT-only");
}
}
#[test]
fn rule1_silk_reset_is_independent_of_redundancy_presence() {
for red in [not_present(), present(), invalid()] {
let r = decide_state_resets(OperatingMode::CeltOnly, OperatingMode::SilkOnly, red);
assert!(r.silk);
}
}
#[test]
fn rule2_celt_resets_silk_to_hybrid() {
let r = decide_state_resets(
OperatingMode::SilkOnly,
OperatingMode::Hybrid,
not_present(),
);
assert_eq!(r.celt, CeltResetPlacement::BeforeFrame);
}
#[test]
fn rule2_celt_resets_silk_to_celtonly_without_redundancy() {
let r = decide_state_resets(
OperatingMode::SilkOnly,
OperatingMode::CeltOnly,
not_present(),
);
assert_eq!(r.celt, CeltResetPlacement::BeforeFrame);
}
#[test]
fn rule2_celt_resets_hybrid_to_celtonly_without_redundancy() {
let r = decide_state_resets(
OperatingMode::Hybrid,
OperatingMode::CeltOnly,
not_present(),
);
assert_eq!(r.celt, CeltResetPlacement::BeforeFrame);
}
#[test]
fn rule2_celt_does_not_reset_hybrid_to_silk() {
let r = decide_state_resets(
OperatingMode::Hybrid,
OperatingMode::SilkOnly,
not_present(),
);
assert_eq!(r.celt, CeltResetPlacement::None);
}
#[test]
fn rule2_celt_does_not_reset_when_mode_unchanged() {
for mode in [
OperatingMode::SilkOnly,
OperatingMode::Hybrid,
OperatingMode::CeltOnly,
] {
for red in [not_present(), present(), invalid()] {
let r = decide_state_resets(mode, mode, red);
assert_eq!(
r.celt,
CeltResetPlacement::None,
"same-mode {:?} must not reset CELT under any redundancy state",
mode
);
}
}
}
#[test]
fn rule3_silk_to_celtonly_with_redundancy_places_reset_before_redundant() {
let r = decide_state_resets(OperatingMode::SilkOnly, OperatingMode::CeltOnly, present());
assert_eq!(r.celt, CeltResetPlacement::BeforeRedundantOnly);
}
#[test]
fn rule3_hybrid_to_celtonly_with_redundancy_places_reset_before_redundant() {
let r = decide_state_resets(OperatingMode::Hybrid, OperatingMode::CeltOnly, present());
assert_eq!(r.celt, CeltResetPlacement::BeforeRedundantOnly);
}
#[test]
fn rule3_invalid_redundancy_falls_back_to_rule2_default() {
let r = decide_state_resets(OperatingMode::SilkOnly, OperatingMode::CeltOnly, invalid());
assert_eq!(r.celt, CeltResetPlacement::BeforeFrame);
}
#[test]
fn rule4_celt_to_silk_with_redundancy_does_not_reset_celt() {
let r = decide_state_resets(OperatingMode::CeltOnly, OperatingMode::SilkOnly, present());
assert_eq!(r.celt, CeltResetPlacement::None);
assert!(r.silk);
}
#[test]
fn rule4_celt_to_hybrid_with_redundancy_does_not_reset_celt() {
let r = decide_state_resets(OperatingMode::CeltOnly, OperatingMode::Hybrid, present());
assert_eq!(r.celt, CeltResetPlacement::None);
assert!(r.silk);
}
#[test]
fn rule4_celt_to_silk_without_redundancy_also_does_not_reset_celt() {
let r = decide_state_resets(
OperatingMode::CeltOnly,
OperatingMode::SilkOnly,
not_present(),
);
assert_eq!(r.celt, CeltResetPlacement::None);
}
#[test]
fn rule4_celt_to_hybrid_without_redundancy_still_resets_celt() {
let r = decide_state_resets(
OperatingMode::CeltOnly,
OperatingMode::Hybrid,
not_present(),
);
assert_eq!(r.celt, CeltResetPlacement::BeforeFrame);
assert!(r.silk);
}
#[test]
fn full_transition_matrix_is_consistent() {
use CeltResetPlacement as P;
use OperatingMode::{CeltOnly as C, Hybrid as H, SilkOnly as S};
let cases: &[(OperatingMode, OperatingMode, bool, bool, P)] = &[
(S, S, false, false, P::None),
(S, S, true, false, P::None),
(H, H, false, false, P::None),
(H, H, true, false, P::None),
(C, C, false, false, P::None),
(C, C, true, false, P::None),
(S, H, false, false, P::BeforeFrame),
(S, H, true, false, P::BeforeFrame),
(H, S, false, false, P::None),
(H, S, true, false, P::None),
(S, C, false, false, P::BeforeFrame),
(S, C, true, false, P::BeforeRedundantOnly),
(H, C, false, false, P::BeforeFrame),
(H, C, true, false, P::BeforeRedundantOnly),
(C, S, false, true, P::None),
(C, S, true, true, P::None),
(C, H, false, true, P::BeforeFrame),
(C, H, true, true, P::None),
];
for (prev, next, red_present, expected_silk, expected_celt) in cases {
let red = if *red_present {
present()
} else {
not_present()
};
let r = decide_state_resets(*prev, *next, red);
assert_eq!(
r.silk, *expected_silk,
"silk mismatch on prev={:?}, next={:?}, red={}",
prev, next, red_present
);
assert_eq!(
r.celt, *expected_celt,
"celt mismatch on prev={:?}, next={:?}, red={}",
prev, next, red_present
);
}
}
#[test]
fn figure18_silk_to_celt_with_redundancy_resets_celt_before_redundant() {
let r = decide_state_resets(OperatingMode::SilkOnly, OperatingMode::CeltOnly, present());
assert_eq!(r.celt, CeltResetPlacement::BeforeRedundantOnly);
assert!(!r.silk, "SILK is not reset entering CELT-only");
}
#[test]
fn figure18_celt_to_silk_with_redundancy_marks_silk_reset_only() {
let r = decide_state_resets(OperatingMode::CeltOnly, OperatingMode::SilkOnly, present());
assert!(r.silk);
assert_eq!(r.celt, CeltResetPlacement::None);
}
#[test]
fn figure18_celt_to_hybrid_with_redundancy_marks_silk_reset_only() {
let r = decide_state_resets(OperatingMode::CeltOnly, OperatingMode::Hybrid, present());
assert!(r.silk);
assert_eq!(r.celt, CeltResetPlacement::None);
}
#[test]
fn figure18_hybrid_to_wb_silk_resets_silk_only_under_rule_layer() {
let r = decide_state_resets(
OperatingMode::Hybrid,
OperatingMode::SilkOnly,
not_present(),
);
assert!(r.is_noop());
}
#[test]
fn redundancy_is_present_treats_invalid_as_absent() {
assert!(!super::redundancy_is_present(RedundancyDecision::Invalid));
assert!(!super::redundancy_is_present(
RedundancyDecision::NotPresent
));
assert!(super::redundancy_is_present(RedundancyDecision::Present {
position: crate::celt_redundancy::RedundancyPosition::End,
size_bytes: 2,
}));
}
}