mcelp/preprocess.rs
1//! Encoder input conditioning.
2//!
3//! The encoder takes 320 mu-law samples, expands them to linear and runs them
4//! through a second-order high-pass that removes DC and the lowest part of the
5//! band before any analysis happens.
6
7use crate::fixed::{acc, hi, sat, shift, trunc32};
8
9/// Samples per frame.
10pub use crate::FRAME;
11/// Samples per half-frame, which is the unit the analysis works on.
12pub use crate::HALF;
13
14/// Expand one mu-law byte to linear.
15///
16/// A plain table lookup, against the same table the reference uses.
17pub fn ulaw_to_linear(sample: u8) -> i16 {
18 crate::tables::ULAW_TO_LINEAR[sample as usize]
19}
20
21/// State of the input high-pass filter.
22#[derive(Clone, Copy, Default)]
23pub struct Highpass {
24 /// The two previous outputs, kept at full 32-bit precision.
25 output: [i64; 2],
26 /// The two previous inputs.
27 input: [i16; 2],
28}
29
30impl Highpass {
31 /// Filter one half-frame in place.
32 ///
33 /// `y[n] = 8 * (a1 y[n-1] + a2 y[n-2] + b0 x[n] + b1 x[n-1] + b2 x[n-2])`,
34 /// where the recursive part is evaluated against the undamaged 32-bit
35 /// history rather than the rounded output.
36 pub fn run(&mut self, block: &mut [i16]) {
37 let c = &crate::tables::INPUT_HIGHPASS[..5];
38 for sample in block.iter_mut() {
39 let x = *sample;
40 let mut a = acc(recursive(self.output[0], c[0]) + recursive(self.output[1], c[1]));
41 a = acc(a + (x as i64) * (c[2] as i64) * 2);
42 a = acc(a + (self.input[0] as i64) * (c[3] as i64) * 2);
43 a = acc(a + (self.input[1] as i64) * (c[4] as i64) * 2);
44 a = sat(shift(a, 3));
45
46 self.input[1] = self.input[0];
47 self.input[0] = x;
48 self.output[1] = self.output[0];
49 self.output[0] = trunc32(a);
50
51 // The rounding add saturates, so a sample that overshoots full scale
52 // clips there instead of wrapping round to the opposite rail.
53 *sample = hi(sat(acc(a + 32768)));
54 }
55 }
56}
57
58/// One recursive tap: a 32x16 product built from the halves of the state, the
59/// low half being pre-shifted so the two partial products line up.
60fn recursive(state: i64, coef: i16) -> i64 {
61 let low = ((state as i16 as u16) >> 1) as i64;
62 let partial = shift(acc(low * (coef as i64) * 2), -16);
63 acc(shift(partial, 1) + (hi(state) as i64) * (coef as i64) * 2)
64}