lc3_codec/decoder/
noise_filling.rs

1use crate::common::{complex::Scaler, config::FrameDuration};
2
3use super::side_info::Bandwidth;
4
5// checked against spec
6
7/// Applies stable noise to the spectral lines in the frame given the noise filling seed
8///
9/// # Arguments
10///
11/// * `is_zero_frame` - If this is a zero frame, noise filling is skipped
12/// * `noise_filling_seed` - The noise filling seed (e.g. 56909) is the seed for psudo-random noise
13/// * `bandwidth` - Bandwidth cutoff index (e.g. 4) (P_BW)
14/// * `duration` - Frame duration (7.5 ms or 10 ms) (N_MS)
15/// * `noise_factor` - Transmitted noise factor (e.g. 3) which is quantised from the calculated noise level (F_NF)
16/// * `spec_lines_int` - Input integer spectral lines (used for checking for zeros only)
17/// * `spec_lines_float` - Input / Output floating point spectral lines
18pub fn apply_noise_filling(
19    is_zero_frame: bool,
20    noise_filling_seed: i32,
21    bandwidth: Bandwidth,
22    duration: FrameDuration,
23    noise_factor: usize,
24    spec_lines_int: &[i32],
25    spec_lines_float: &mut [Scaler],
26) {
27    if !is_zero_frame {
28        const BW_STOP_TABLE_7P5MS: [usize; 5] = [60, 120, 180, 240, 300];
29        const BW_STOP_TABLE_10MS: [usize; 5] = [80, 160, 240, 320, 400];
30
31        let (bw_stop, nf_start, nf_width) = match duration {
32            FrameDuration::SevenPointFiveMs => (BW_STOP_TABLE_7P5MS[bandwidth as usize], 18, 2),
33            FrameDuration::TenMs => (BW_STOP_TABLE_10MS[bandwidth as usize], 24, 3),
34        };
35
36        let mut noise_fill = noise_filling_seed;
37        let noise_level = (8.0 - noise_factor as Scaler) / 16.0;
38
39        // loop though spectral lines between nf_start and bw_stop
40        for (k, spec_line) in spec_lines_float.iter_mut().enumerate().take(bw_stop).skip(nf_start) {
41            let from = k - nf_width;
42            let to = (bw_stop - 1).min(k + nf_width);
43
44            // NOTE: it is safe to use spec_lines_int for the check for a zero sample
45            // because residual_spectrum::decode ignores zero samples.
46            // It is faster to compare integers than floats and this is a hot loop
47            let apply_noise_fill = &spec_lines_int[from..=to].iter().all(|x| *x == 0);
48
49            // noise filling is only applied if all spectral lines in the range above are zero
50            if *apply_noise_fill {
51                noise_fill = (13849 + noise_fill * 31821) & 0xFFFF;
52                *spec_line = if noise_fill < 0x8000 { noise_level } else { -noise_level };
53            }
54        }
55    }
56}
57
58#[cfg(test)]
59mod tests {
60    extern crate std;
61    use super::*;
62    use crate::common::{config::FrameDuration, constants::MAX_LEN_SPECTRAL};
63
64    #[test]
65    fn decode_noise_filling() {
66        let is_zero_frame = false;
67        let noise_filling_seed = 56909;
68        let bandwidth = Bandwidth::FullBand;
69        let duration = FrameDuration::TenMs;
70        let noise_factor = 3;
71        let mut spec_lines_float: [f32; MAX_LEN_SPECTRAL] = [
72            -9.3125, 32.3125, 65.3125, 40.3125, -4.3125, -48.3125, -36.3125, 2.3125, 20.8125, -6.3125, 27.3125, 9.3125,
73            -8.8125, -26.3125, 8.8125, -4.3125, -0.8125, 12.3125, -7.8125, -2.3125, 23.3125, -33.3125, -9.8125, 3.3125,
74            -15.3125, -3.3125, 5.3125, 17.3125, -1.3125, 12.3125, 27.3125, -4.3125, 8.3125, 2.3125, 10.3125, -7.3125,
75            8.3125, -6.3125, 3.3125, -21.8125, 23.8125, -6.3125, -0.8125, 2.3125, 0.0, 1.3125, -8.0, 3.0, -2.0, 0.0,
76            0.0, 2.0, -6.0, -2.0, -4.0, -3.0, 0.0, 1.0, 0.0, -7.0, -4.0, -11.0, -7.0, -6.0, 0.0, 3.0, 10.0, 2.0, 9.0,
77            3.0, 7.0, -2.0, 2.0, -2.0, -1.0, -1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 4.0, -1.0, -1.0, -1.0, 2.0, 1.0, 3.0, 3.0,
78            1.0, -2.0, -2.0, 1.0, 5.0, 2.0, -2.0, -5.0, -4.0, -2.0, 0.0, 0.0, 0.0, -1.0, 1.0, -1.0, 1.0, 1.0, 1.0,
79            -1.0, -3.0, -1.0, 1.0, 3.0, 0.0, 0.0, 1.0, 2.0, -1.0, -1.0, 1.0, 2.0, 2.0, 1.0, 2.0, 0.0, -2.0, -3.0, 0.0,
80            3.0, 2.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 0.0, -1.0, 0.0, 0.0, 0.0, -1.0, -1.0, 1.0, 2.0, 0.0, 0.0, 0.0, 0.0,
81            0.0, 0.0, 0.0, -1.0, 2.0, 0.0, -1.0, -1.0, 0.0, 2.0, 0.0, 2.0, 0.0, 0.0, -2.0, -1.0, 0.0, 1.0, 0.0, -1.0,
82            0.0, 1.0, 2.0, 0.0, -1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0, -1.0, -1.0, 0.0, 0.0, 0.0, 0.0, -1.0, -1.0, 0.0,
83            0.0, 1.0, 0.0, -1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 0.0,
84            0.0, -1.0, 1.0, 2.0, -1.0, 0.0, 2.0, 0.0, 1.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, 2.0, -1.0, -1.0, 0.0, 0.0,
85            0.0, 0.0, -1.0, 1.0, -1.0, 1.0, 1.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 0.0, 0.0, 1.0, 0.0,
86            0.0, 1.0, 2.0, -2.0, 0.0, -1.0, -1.0, 0.0, 0.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 1.0, 1.0, 1.0, -2.0, -1.0,
87            -1.0, 0.0, 1.0, -1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, -1.0, 1.0, -1.0, 0.0, 0.0, -1.0, 1.0, 0.0, 0.0, 0.0,
88            1.0, 1.0, 0.0, -1.0, -1.0, 0.0, -1.0, 0.0, -1.0, -1.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, -1.0, 0.0, 1.0, 1.0,
89            0.0, 0.0, 0.0, 0.0, -1.0, -1.0, -1.0, 0.0, 0.0, 0.0, -1.0, 0.0, 0.0, 1.0, 0.0, -1.0, 0.0, 1.0, 0.0, 0.0,
90            0.0, -1.0, 0.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, 0.0, 0.0, -1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0,
91            0.0, 0.0, 0.0, 0.0, -1.0, -1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -1.0,
92            -1.0, 0.0, 0.0, 1.0, -1.0, -1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, -1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 0.0,
93            0.0, 0.0, 0.0, -1.0, 0.0, 0.0, 1.0, 1.0,
94        ];
95        let spec_lines_int: [i32; MAX_LEN_SPECTRAL] = [
96            -9, 32, 65, 40, -4, -48, -36, 2, 21, -6, 27, 9, -9, -26, 9, -4, -1, 12, -8, -2, 23, -33, -10, 3, -15, -3,
97            5, 17, -1, 12, 27, -4, 8, 2, 10, -7, 8, -6, 3, -22, 24, -6, -1, 2, 0, 1, -8, 3, -2, 0, 0, 2, -6, -2, -4,
98            -3, 0, 1, 0, -7, -4, -11, -7, -6, 0, 3, 10, 2, 9, 3, 7, -2, 2, -2, -1, -1, 0, 0, 0, 0, 1, 4, -1, -1, -1, 2,
99            1, 3, 3, 1, -2, -2, 1, 5, 2, -2, -5, -4, -2, 0, 0, 0, -1, 1, -1, 1, 1, 1, -1, -3, -1, 1, 3, 0, 0, 1, 2, -1,
100            -1, 1, 2, 2, 1, 2, 0, -2, -3, 0, 3, 2, 0, 0, 0, 0, 1, 1, 0, -1, 0, 0, 0, -1, -1, 1, 2, 0, 0, 0, 0, 0, 0, 0,
101            -1, 2, 0, -1, -1, 0, 2, 0, 2, 0, 0, -2, -1, 0, 1, 0, -1, 0, 1, 2, 0, -1, 0, 1, 0, 1, 0, 1, -1, -1, 0, 0, 0,
102            0, -1, -1, 0, 0, 1, 0, -1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, -1, 1, 2, -1, 0, 2, 0, 1,
103            -1, 0, 0, -1, 0, 0, 2, -1, -1, 0, 0, 0, 0, -1, 1, -1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 1,
104            2, -2, 0, -1, -1, 0, 0, 0, 0, -1, 0, 0, -1, 1, 1, 1, -2, -1, -1, 0, 1, -1, 0, 0, 1, 0, 0, 0, -1, 1, -1, 0,
105            0, -1, 1, 0, 0, 0, 1, 1, 0, -1, -1, 0, -1, 0, -1, -1, 0, 1, 0, 0, 0, 0, -1, 0, 1, 1, 0, 0, 0, 0, -1, -1,
106            -1, 0, 0, 0, -1, 0, 0, 1, 0, -1, 0, 1, 0, 0, 0, -1, 0, 0, 0, -1, 0, 0, -1, 0, 0, 0, 0, -1, 1, 0, 0, 0, 0,
107            1, 0, 0, 0, 0, -1, -1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, -1, 0, 0, 1, -1, -1, 0, 0, 1, 0, 0, 0,
108            0, -1, 0, 1, 0, 1, 0, 0, 0, 0, 0, -1, 0, 0, 1, 1,
109        ];
110
111        apply_noise_filling(
112            is_zero_frame,
113            noise_filling_seed,
114            bandwidth,
115            duration,
116            noise_factor,
117            &spec_lines_int,
118            &mut spec_lines_float,
119        );
120
121        let x_hat_expected: [f32; MAX_LEN_SPECTRAL] = [
122            -9.3125, 32.3125, 65.3125, 40.3125, -4.3125, -48.3125, -36.3125, 2.3125, 20.8125, -6.3125, 27.3125, 9.3125,
123            -8.8125, -26.3125, 8.8125, -4.3125, -0.8125, 12.3125, -7.8125, -2.3125, 23.3125, -33.3125, -9.8125, 3.3125,
124            -15.3125, -3.3125, 5.3125, 17.3125, -1.3125, 12.3125, 27.3125, -4.3125, 8.3125, 2.3125, 10.3125, -7.3125,
125            8.3125, -6.3125, 3.3125, -21.8125, 23.8125, -6.3125, -0.8125, 2.3125, 0.0, 1.3125, -8.0, 3.0, -2.0, 0.0,
126            0.0, 2.0, -6.0, -2.0, -4.0, -3.0, 0.0, 1.0, 0.0, -7.0, -4.0, -11.0, -7.0, -6.0, 0.0, 3.0, 10.0, 2.0, 9.0,
127            3.0, 7.0, -2.0, 2.0, -2.0, -1.0, -1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 4.0, -1.0, -1.0, -1.0, 2.0, 1.0, 3.0, 3.0,
128            1.0, -2.0, -2.0, 1.0, 5.0, 2.0, -2.0, -5.0, -4.0, -2.0, 0.0, 0.0, 0.0, -1.0, 1.0, -1.0, 1.0, 1.0, 1.0,
129            -1.0, -3.0, -1.0, 1.0, 3.0, 0.0, 0.0, 1.0, 2.0, -1.0, -1.0, 1.0, 2.0, 2.0, 1.0, 2.0, 0.0, -2.0, -3.0, 0.0,
130            3.0, 2.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 0.0, -1.0, 0.0, 0.0, 0.0, -1.0, -1.0, 1.0, 2.0, 0.0, 0.0, 0.0,
131            0.3125, 0.0, 0.0, 0.0, -1.0, 2.0, 0.0, -1.0, -1.0, 0.0, 2.0, 0.0, 2.0, 0.0, 0.0, -2.0, -1.0, 0.0, 1.0, 0.0,
132            -1.0, 0.0, 1.0, 2.0, 0.0, -1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0, -1.0, -1.0, 0.0, 0.0, 0.0, 0.0, -1.0, -1.0,
133            0.0, 0.0, 1.0, 0.0, -1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0,
134            0.0, 0.0, -1.0, 1.0, 2.0, -1.0, 0.0, 2.0, 0.0, 1.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, 2.0, -1.0, -1.0, 0.0,
135            0.0, 0.0, 0.0, -1.0, 1.0, -1.0, 1.0, 1.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 0.0, 0.0, 1.0,
136            0.0, 0.0, 1.0, 2.0, -2.0, 0.0, -1.0, -1.0, 0.0, 0.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 1.0, 1.0, 1.0, -2.0,
137            -1.0, -1.0, 0.0, 1.0, -1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, -1.0, 1.0, -1.0, 0.0, 0.0, -1.0, 1.0, 0.0, 0.0,
138            0.0, 1.0, 1.0, 0.0, -1.0, -1.0, 0.0, -1.0, 0.0, -1.0, -1.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, -1.0, 0.0, 1.0,
139            1.0, 0.0, 0.0, 0.0, 0.0, -1.0, -1.0, -1.0, 0.0, 0.0, 0.0, -1.0, 0.0, 0.0, 1.0, 0.0, -1.0, 0.0, 1.0, 0.0,
140            0.0, 0.0, -1.0, 0.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, 0.0, 0.0, -1.0, 1.0, 0.0, 0.0, 0.0, 0.0,
141            1.0, 0.0, 0.0, 0.0, 0.0, -1.0, -1.0, 1.0, 0.0, 0.0, 0.0, -0.3125, -0.3125, -0.3125, -0.3125, 0.3125,
142            -0.3125, 0.3125, 0.0, 0.0, 0.0, -1.0, -1.0, 0.0, 0.0, 1.0, -1.0, -1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0,
143            -1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, -1.0, 0.0, 0.0, 1.0, 1.0,
144        ];
145        assert_eq![spec_lines_float, x_hat_expected];
146    }
147}