1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
use crate::common::{complex::Scaler, config::FrameDuration};

use super::side_info::Bandwidth;

// checked against spec

/// Applies stable noise to the spectral lines in the frame given the noise filling seed
///
/// # Arguments
///
/// * `is_zero_frame` - If this is a zero frame, noise filling is skipped
/// * `noise_filling_seed` - The noise filling seed (e.g. 56909) is the seed for psudo-random noise
/// * `bandwidth` - Bandwidth cutoff index (e.g. 4) (P_BW)
/// * `duration` - Frame duration (7.5 ms or 10 ms) (N_MS)
/// * `noise_factor` - Transmitted noise factor (e.g. 3) which is quantised from the calculated noise level (F_NF)
/// * `spec_lines_int` - Input integer spectral lines (used for checking for zeros only)
/// * `spec_lines_float` - Input / Output floating point spectral lines
pub fn apply_noise_filling(
    is_zero_frame: bool,
    noise_filling_seed: i32,
    bandwidth: Bandwidth,
    duration: FrameDuration,
    noise_factor: usize,
    spec_lines_int: &[i32],
    spec_lines_float: &mut [Scaler],
) {
    if !is_zero_frame {
        const BW_STOP_TABLE_7P5MS: [usize; 5] = [60, 120, 180, 240, 300];
        const BW_STOP_TABLE_10MS: [usize; 5] = [80, 160, 240, 320, 400];

        let (bw_stop, nf_start, nf_width) = match duration {
            FrameDuration::SevenPointFiveMs => (BW_STOP_TABLE_7P5MS[bandwidth as usize], 18, 2),
            FrameDuration::TenMs => (BW_STOP_TABLE_10MS[bandwidth as usize], 24, 3),
        };

        let mut noise_fill = noise_filling_seed;
        let noise_level = (8.0 - noise_factor as Scaler) / 16.0;

        // loop though spectral lines between nf_start and bw_stop
        for (k, spec_line) in spec_lines_float.iter_mut().enumerate().take(bw_stop).skip(nf_start) {
            let from = k - nf_width;
            let to = (bw_stop - 1).min(k + nf_width);

            // NOTE: it is safe to use spec_lines_int for the check for a zero sample
            // because residual_spectrum::decode ignores zero samples.
            // It is faster to compare integers than floats and this is a hot loop
            let apply_noise_fill = &spec_lines_int[from..=to].iter().all(|x| *x == 0);

            // noise filling is only applied if all spectral lines in the range above are zero
            if *apply_noise_fill {
                noise_fill = (13849 + noise_fill * 31821) & 0xFFFF;
                *spec_line = if noise_fill < 0x8000 { noise_level } else { -noise_level };
            }
        }
    }
}

#[cfg(test)]
mod tests {
    extern crate std;
    use super::*;
    use crate::common::{config::FrameDuration, constants::MAX_LEN_SPECTRAL};

    #[test]
    fn decode_noise_filling() {
        let is_zero_frame = false;
        let noise_filling_seed = 56909;
        let bandwidth = Bandwidth::FullBand;
        let duration = FrameDuration::TenMs;
        let noise_factor = 3;
        let mut spec_lines_float: [f32; MAX_LEN_SPECTRAL] = [
            -9.3125, 32.3125, 65.3125, 40.3125, -4.3125, -48.3125, -36.3125, 2.3125, 20.8125, -6.3125, 27.3125, 9.3125,
            -8.8125, -26.3125, 8.8125, -4.3125, -0.8125, 12.3125, -7.8125, -2.3125, 23.3125, -33.3125, -9.8125, 3.3125,
            -15.3125, -3.3125, 5.3125, 17.3125, -1.3125, 12.3125, 27.3125, -4.3125, 8.3125, 2.3125, 10.3125, -7.3125,
            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,
            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,
            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,
            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,
            -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,
            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,
            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,
            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,
            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,
            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,
            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,
            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,
            -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,
            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,
            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,
            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,
            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,
            -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,
            0.0, 0.0, 0.0, -1.0, 0.0, 0.0, 1.0, 1.0,
        ];
        let spec_lines_int: [i32; MAX_LEN_SPECTRAL] = [
            -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,
            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,
            -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,
            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,
            -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,
            -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,
            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,
            -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,
            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,
            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,
            -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,
            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,
            0, -1, 0, 1, 0, 1, 0, 0, 0, 0, 0, -1, 0, 0, 1, 1,
        ];

        apply_noise_filling(
            is_zero_frame,
            noise_filling_seed,
            bandwidth,
            duration,
            noise_factor,
            &spec_lines_int,
            &mut spec_lines_float,
        );

        let x_hat_expected: [f32; MAX_LEN_SPECTRAL] = [
            -9.3125, 32.3125, 65.3125, 40.3125, -4.3125, -48.3125, -36.3125, 2.3125, 20.8125, -6.3125, 27.3125, 9.3125,
            -8.8125, -26.3125, 8.8125, -4.3125, -0.8125, 12.3125, -7.8125, -2.3125, 23.3125, -33.3125, -9.8125, 3.3125,
            -15.3125, -3.3125, 5.3125, 17.3125, -1.3125, 12.3125, 27.3125, -4.3125, 8.3125, 2.3125, 10.3125, -7.3125,
            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,
            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,
            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,
            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,
            -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,
            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.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,
            -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,
            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,
            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,
            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,
            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,
            -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,
            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,
            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,
            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,
            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,
            -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,
            -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,
        ];
        assert_eq![spec_lines_float, x_hat_expected];
    }
}