lc3_codec/decoder/
packet_loss_concealment.rs

1use crate::common::{complex::Scaler, config::Lc3Config};
2
3use super::side_info::LongTermPostFilterInfo;
4
5// checked against spec
6
7pub struct PacketLossConcealment<'a> {
8    // A copy of the last saved spectral lines
9    spec_lines_last_good: &'a mut [Scaler],
10
11    /// Number of consecutive erased frames
12    num_lost_frames: usize,
13
14    /// Attenuation factor (fade out speed)
15    alpha: Scaler,
16
17    /// Packet loss concealment seed
18    plc_seed: usize,
19
20    /// Number of spectral lines (e.g. 400)
21    ne: usize,
22}
23
24impl<'a> PacketLossConcealment<'a> {
25    pub fn new(ne: usize, scaler_buf: &'a mut [Scaler]) -> (Self, &'a mut [Scaler]) {
26        let (spec_lines_last_good, scaler_buf) = scaler_buf.split_at_mut(ne);
27
28        (
29            Self {
30                spec_lines_last_good,
31                plc_seed: 24607,
32                num_lost_frames: 0,
33                alpha: 1.0,
34                ne,
35            },
36            scaler_buf,
37        )
38    }
39
40    pub const fn calc_working_buffer_length(config: &Lc3Config) -> usize {
41        config.ne
42    }
43
44    /// Saves the last good decoded frame so that we have something to work with
45    /// if subsquent frames are corrupted or missing
46    ///
47    /// # Arguments
48    ///
49    /// * `spec_lines` - Decoded spectral lines to save
50    pub fn save(&mut self, spec_lines: &[Scaler]) {
51        self.num_lost_frames = 0;
52        self.alpha = 1.0;
53        self.spec_lines_last_good[..self.ne].copy_from_slice(&spec_lines[..self.ne]);
54    }
55
56    /// Loads the last good frame from cache
57    /// Every time we load spectral lines for a frame we add some randomness and lower the volume a little
58    ///
59    /// # Arguments
60    ///
61    /// * `spec_lines` - Empty spectral lines to load data into (will ovewrite if data exists)
62    //
63    pub fn load_into(&mut self, spec_lines: &mut [Scaler]) -> LongTermPostFilterInfo {
64        if self.num_lost_frames >= 4 {
65            self.alpha *= if self.num_lost_frames < 8 { 0.9 } else { 0.85 };
66        }
67        self.num_lost_frames += 1;
68
69        for (current, last_good) in spec_lines.iter_mut().zip(self.spec_lines_last_good.iter()) {
70            self.plc_seed = (16831 + self.plc_seed * 12821) & 0xFFFF;
71
72            *current = if self.plc_seed < 0x8000 {
73                last_good * self.alpha
74            } else {
75                last_good * -self.alpha
76            };
77        }
78
79        // since we cannot read the side info we have to use defaults for this
80        LongTermPostFilterInfo {
81            is_active: false,
82            pitch_index: 0,
83            pitch_present: false,
84        }
85    }
86}
87
88#[cfg(test)]
89mod tests {
90    extern crate std;
91    use super::*;
92
93    #[test]
94    fn save_and_load() {
95        let mut spec_lines = [-2268.137, 7869.9785, 15884.984, 9776.979];
96        let mut scaler_buf = [0.; 4];
97        let (mut packet_loss, _) = PacketLossConcealment::new(spec_lines.len(), &mut scaler_buf);
98
99        packet_loss.save(&spec_lines);
100        packet_loss.load_into(&mut spec_lines);
101        packet_loss.load_into(&mut spec_lines);
102        packet_loss.load_into(&mut spec_lines);
103
104        let spec_lines_expected = [2268.137, 7869.9785, -15884.984, -9776.979];
105        assert_eq!(spec_lines, spec_lines_expected);
106    }
107}