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
#[derive(Clone, Copy)]
pub enum SamplingFrequency {
    Hz8000,
    Hz16000,
    Hz24000,
    Hz32000,
    Hz44100,
    Hz48000,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum FrameDuration {
    SevenPointFiveMs,
    TenMs,
}

#[derive(Clone, Copy)]
pub struct Lc3Config {
    /// Number of channels (e.g. 2)
    pub nc: usize,

    /// Sampling frequency index (e.g. 4)
    pub fs_ind: usize,

    /// Sampling frequency in hz (e.g. 48000)
    pub fs: usize,

    /// Number of encoded spectral lines (per frame and channel) (e.g. 400)
    pub ne: usize,

    /// Frame duration in milliseconds (e.g. TenMs)
    pub n_ms: FrameDuration,

    /// Number of bands (e.g. 64)
    pub nb: usize,

    /// Number of samples processed in one frame of one channel (also known as frame size) (e.g. 480)
    pub nf: usize,

    /// Number of leading zeros in MDCT window (e.g. 180)
    pub z: usize,
}

impl Lc3Config {
    pub fn new(sampling_frequency: SamplingFrequency, frame_duration: FrameDuration, num_channels: usize) -> Self {
        let (fs_ind, fs) = match sampling_frequency {
            SamplingFrequency::Hz8000 => (0, 8000),
            SamplingFrequency::Hz16000 => (1, 16000),
            SamplingFrequency::Hz24000 => (2, 24000),
            SamplingFrequency::Hz32000 => (3, 32000),
            SamplingFrequency::Hz44100 => (4, 44100),
            SamplingFrequency::Hz48000 => (4, 48000),
        };

        let nf;
        let ne;
        let nb;
        let z;

        match frame_duration {
            FrameDuration::SevenPointFiveMs => {
                nf = match sampling_frequency {
                    SamplingFrequency::Hz8000 => 60,
                    SamplingFrequency::Hz16000 => 120,
                    SamplingFrequency::Hz24000 => 180,
                    SamplingFrequency::Hz32000 => 240,
                    SamplingFrequency::Hz44100 => 360,
                    SamplingFrequency::Hz48000 => 360,
                };

                ne = if nf == 360 { 300 } else { nf };
                nb = match sampling_frequency {
                    SamplingFrequency::Hz8000 => 60,
                    _ => 64,
                };
                z = 7 * nf / 30;
            }
            FrameDuration::TenMs => {
                nf = match sampling_frequency {
                    SamplingFrequency::Hz8000 => 80,
                    SamplingFrequency::Hz16000 => 160,
                    SamplingFrequency::Hz24000 => 240,
                    SamplingFrequency::Hz32000 => 320,
                    SamplingFrequency::Hz44100 => 480,
                    SamplingFrequency::Hz48000 => 480,
                };

                ne = if nf == 480 { 400 } else { nf };
                nb = 64;
                z = 3 * nf / 8;
            }
        }

        Self {
            fs_ind,
            fs,
            n_ms: frame_duration,
            nb,
            nc: num_channels,
            ne,
            nf,
            z,
        }
    }
}

#[cfg(test)]
mod tests {
    extern crate std;
    use super::*;

    #[test]
    fn simple_config() {
        let config = Lc3Config::new(SamplingFrequency::Hz48000, FrameDuration::TenMs, 1);

        assert_eq!(config.fs, 48000);
        assert_eq!(config.nc, 1);
        assert_eq!(config.fs_ind, 4);
        assert_eq!(config.n_ms, FrameDuration::TenMs);
        assert_eq!(config.z, 180);
        assert_eq!(config.nf, 480);
        assert_eq!(config.nb, 64);
        assert_eq!(config.ne, 400);
    }
}