audio_codec_algorithms/
alaw.rs

1
2#[cfg(feature = "internal-no-panic")]
3use no_panic::no_panic;
4
5// decoding table generated using G.191 softare tools at https://www.itu.int/rec/T-REC-G.191/en
6const ALAW_VALUES: &[i16; 256] = &[
7    -5504, -5248, -6016, -5760, -4480, -4224, -4992, -4736,
8    -7552, -7296, -8064, -7808, -6528, -6272, -7040, -6784,
9    -2752, -2624, -3008, -2880, -2240, -2112, -2496, -2368,
10    -3776, -3648, -4032, -3904, -3264, -3136, -3520, -3392,
11    -22016, -20992, -24064, -23040, -17920, -16896, -19968, -18944,
12    -30208, -29184, -32256, -31232, -26112, -25088, -28160, -27136,
13    -11008, -10496, -12032, -11520, -8960, -8448, -9984, -9472,
14    -15104, -14592, -16128, -15616, -13056, -12544, -14080, -13568,
15    -344, -328, -376, -360, -280, -264, -312, -296,
16    -472, -456, -504, -488, -408, -392, -440, -424,
17    -88, -72, -120, -104, -24, -8, -56, -40,
18    -216, -200, -248, -232, -152, -136, -184, -168,
19    -1376, -1312, -1504, -1440, -1120, -1056, -1248, -1184,
20    -1888, -1824, -2016, -1952, -1632, -1568, -1760, -1696,
21    -688, -656, -752, -720, -560, -528, -624, -592,
22    -944, -912, -1008, -976, -816, -784, -880, -848,
23    5504, 5248, 6016, 5760, 4480, 4224, 4992, 4736,
24    7552, 7296, 8064, 7808, 6528, 6272, 7040, 6784,
25    2752, 2624, 3008, 2880, 2240, 2112, 2496, 2368,
26    3776, 3648, 4032, 3904, 3264, 3136, 3520, 3392,
27    22016, 20992, 24064, 23040, 17920, 16896, 19968, 18944,
28    30208, 29184, 32256, 31232, 26112, 25088, 28160, 27136,
29    11008, 10496, 12032, 11520, 8960, 8448, 9984, 9472,
30    15104, 14592, 16128, 15616, 13056, 12544, 14080, 13568,
31    344, 328, 376, 360, 280, 264, 312, 296,
32    472, 456, 504, 488, 408, 392, 440, 424,
33    88, 72, 120, 104, 24, 8, 56, 40,
34    216, 200, 248, 232, 152, 136, 184, 168,
35    1376, 1312, 1504, 1440, 1120, 1056, 1248, 1184,
36    1888, 1824, 2016, 1952, 1632, 1568, 1760, 1696,
37    688, 656, 752, 720, 560, 528, 624, 592,
38    944, 912, 1008, 976, 816, 784, 880, 848,
39];
40
41/// Decodes a 8-bit encoded G.711 A-law value to a linear 16-bit signed integer sample value.
42#[cfg_attr(feature = "internal-no-panic", no_panic)]
43#[inline(always)]
44pub fn decode_alaw(encoded: u8) -> i16 {
45    ALAW_VALUES[usize::from(encoded)]
46}
47
48// encoding algorithm is based on "A-Law and mu-Law Companding Implementations Using the TMS320C54x,
49// Application Note: SPRA163A", page 16: https://www.ti.com/lit/an/spra163a/spra163a.pdf
50// see also https://en.wikipedia.org/wiki/G.711#A-law
51
52/// Encodes a linear 16-bit signed integer sample value to a 8-bit encoded G.711 A-law value.
53#[cfg_attr(feature = "internal-no-panic", no_panic)]
54#[inline(always)]
55pub fn encode_alaw(linear: i16) -> u8 {
56    let sign = if linear >= 0 {
57        0x00
58    } else {
59        0x80
60    };
61    #[allow(clippy::cast_sign_loss)] // sign loss is expected and handled after the cast to u16
62    let linear = (linear >> 3) as u16;
63    let inputval = if sign == 0x80 {
64        // make a positive value using 1s' complement (a tip from wikipedia)
65        linear ^ 0xffff
66    } else {
67        linear
68    };
69    let compressed_code_word: u16 = match inputval {
70        #[allow(clippy::identity_op)]
71        0b000000000000..=0b000000011111 => 0b000_0000 | (inputval & 0b000000011110) >> 1,
72        0b000000100000..=0b000000111111 => 0b001_0000 | (inputval & 0b000000011110) >> 1,
73        0b000001000000..=0b000001111111 => 0b010_0000 | (inputval & 0b000000111100) >> 2,
74        0b000010000000..=0b000011111111 => 0b011_0000 | (inputval & 0b000001111000) >> 3,
75        0b000100000000..=0b000111111111 => 0b100_0000 | (inputval & 0b000011110000) >> 4,
76        0b001000000000..=0b001111111111 => 0b101_0000 | (inputval & 0b000111100000) >> 5,
77        0b010000000000..=0b011111111111 => 0b110_0000 | (inputval & 0b001111000000) >> 6,
78        0b100000000000..=0b111111111111 => 0b111_0000 | (inputval & 0b011110000000) >> 7,
79        4096.. => 0b111_1111
80    };
81    #[allow(clippy::cast_possible_truncation)] // compressed_code_word is always less than 255
82    let result = (sign | compressed_code_word as u8) ^ 0xd5;
83    result
84}
85
86#[cfg(test)]
87mod tests {
88    use super::*;
89
90    #[test]
91    fn test_decode_alaw() {
92        // no need to test all values because they are read from a static table
93        assert_eq!(decode_alaw(0), -5504);
94        assert_eq!(decode_alaw(128), 5504);
95        assert_eq!(decode_alaw(255), 848);
96    }
97
98    #[test]
99    fn test_encode_alaw() {
100        // test against reference values generated for all input values -32768..=32767
101        // the reference values were generated with the G.191 software tools
102        let buffer = include_bytes!("../test-files/alaw-reference.bin");
103        let mut bi = 0;
104        for i in -32768..=32767 {
105            let encoded = encode_alaw(i);
106            assert_eq!(buffer[bi], encoded);
107            bi += 1;
108        }
109    }
110}