codec_core/codecs/g711/
reference.rs

1//! G.711 Audio Codec Reference Implementation
2//!
3//! This module contains the reference implementation of G.711 μ-law and A-law
4//! encoding and decoding algorithms as specified in ITU-T Recommendation G.711.
5//!
6//!
7//! ## Reference
8//!
9//! - ITU-T Recommendation G.711: "Pulse code modulation (PCM) of voice frequencies"
10//!
11//! ## Algorithm Details
12//!
13//! ### A-law Compression
14//! - Uses 13 Most Significant Bits (MSBs) from input
15//! - Produces 8 Least Significant Bits (LSBs) on output
16//! - Applies 1's complement for negative values
17//! - Toggles even bits (XOR with 0x55)
18//!
19//! ### μ-law Compression  
20//! - Uses 14 Most Significant Bits (MSBs) from input
21//! - Produces 8 Least Significant Bits (LSBs) on output
22//! - Adds bias of 33 (0x21) for processing
23//! - Applies 1's complement and inversion
24
25/// A-law compression according to ITU-T G.711
26///
27/// Compresses a 16-bit linear PCM sample to 8-bit A-law encoding.
28///
29/// # Arguments
30///
31/// * `sample` - Input linear PCM sample (16-bit signed)
32///
33/// # Returns
34///
35/// A-law encoded sample (8-bit)
36///
37/// # Algorithm
38///
39/// Based on ITU-T G.711 specification and reference implementation.
40pub fn alaw_compress(sample: i16) -> u8 {
41    let mut ix = if sample < 0 {
42        (((!sample) as u16) >> 4) as i16
43    } else {
44        sample >> 4
45    };
46    
47    if ix > 15 {
48        let mut iexp = 1;
49        while ix > 16 + 15 {
50            ix >>= 1;
51            iexp += 1;
52        }
53        ix -= 16;
54        ix += iexp << 4;
55    }
56    
57    if sample >= 0 {
58        ix |= 0x0080;
59    }
60    
61    (ix ^ 0x0055) as u8
62}
63
64/// A-law expansion according to ITU-T G.711
65///
66/// Expands an 8-bit A-law encoded sample to 16-bit linear PCM.
67///
68/// # Arguments
69///
70/// * `compressed` - A-law encoded sample (8-bit)
71///
72/// # Returns
73///
74/// Linear PCM sample (16-bit signed)
75///
76/// # Algorithm
77///
78/// Based on ITU-T G.711 specification and reference implementation.
79pub fn alaw_expand(compressed: u8) -> i16 {
80    let mut ix = (compressed ^ 0x0055) as i16;
81    
82    ix &= 0x007F;
83    let iexp = ix >> 4;
84    let mut mant = ix & 0x000F;
85    
86    if iexp > 0 {
87        mant = mant + 16;
88    }
89    
90    mant = (mant << 4) + 0x0008;
91    
92    if iexp > 1 {
93        mant = mant << (iexp - 1);
94    }
95    
96    if compressed > 127 {
97        mant
98    } else {
99        -mant
100    }
101}
102
103/// μ-law compression according to ITU-T G.711
104///
105/// Compresses a 16-bit linear PCM sample to 8-bit μ-law encoding.
106///
107/// # Arguments
108///
109/// * `sample` - Input linear PCM sample (16-bit signed)
110///
111/// # Returns
112///
113/// μ-law encoded sample (8-bit)
114///
115/// # Algorithm
116///
117/// Based on ITU-T G.711 specification.
118pub fn ulaw_compress(sample: i16) -> u8 {
119    let absno = if sample < 0 {
120        (((!sample) as u16) >> 2) as i16 + 33
121    } else {
122        (sample >> 2) + 33
123    };
124    
125    let absno = if absno > 0x1FFF { 0x1FFF } else { absno };
126    
127    let mut i = absno >> 6;
128    let mut segno = 1;
129    while i != 0 {
130        segno += 1;
131        i >>= 1;
132    }
133    
134    let high_nibble = 0x0008 - segno;
135    let low_nibble = 0x000F - ((absno >> segno) & 0x000F);
136    let mut result = (high_nibble << 4) | low_nibble;
137    
138    if sample >= 0 {
139        result |= 0x0080;
140    }
141    
142    result as u8
143}
144
145/// μ-law expansion according to ITU-T G.711
146///
147/// Expands an 8-bit μ-law encoded sample to 16-bit linear PCM.
148///
149/// # Arguments
150///
151/// * `compressed` - μ-law encoded sample (8-bit)
152///
153/// # Returns
154///
155/// Linear PCM sample (16-bit signed)
156///
157/// # Algorithm
158///
159/// Based on ITU-T G.711 specification.
160pub fn ulaw_expand(compressed: u8) -> i16 {
161    let sign = if compressed < 0x0080 { -1 } else { 1 };
162    let mantissa = (!compressed) as i16;
163    let exponent = (mantissa >> 4) & 0x0007;
164    let segment = exponent + 1;
165    let mantissa = mantissa & 0x000F;
166    
167    let step = 4 << segment;
168    
169    sign * (
170        ((0x0080) << exponent) +
171        step * mantissa +
172        step / 2 -
173        4 * 33
174    )
175}
176
177
178
179
180
181
182
183
184
185#[cfg(test)]
186mod tests {
187    use super::*;
188
189    #[test]
190    fn test_alaw_basic_round_trip() {
191        let test_samples = vec![0i16, 100, -100, 1000, -1000, 10000, -10000];
192        
193        for sample in test_samples {
194            let encoded = alaw_compress(sample);
195            let decoded = alaw_expand(encoded);
196            let error = (decoded - sample).abs();
197            
198            // A-law should have reasonable quantization error
199            assert!(error < 2000, "A-law error too large for {}: {} (error: {})", sample, decoded, error);
200        }
201    }
202
203    #[test]
204    fn test_ulaw_basic_round_trip() {
205        let test_samples = vec![0i16, 100, -100, 1000, -1000, 10000, -10000];
206        
207        for sample in test_samples {
208            let encoded = ulaw_compress(sample);
209            let decoded = ulaw_expand(encoded);
210            let error = (decoded - sample).abs();
211            
212            // μ-law should have reasonable quantization error
213            assert!(error < 2000, "μ-law error too large for {}: {} (error: {})", sample, decoded, error);
214        }
215    }
216
217
218
219
220
221    #[test]
222    fn test_boundary_values() {
223        let boundary_samples = vec![-32768i16, -32767, -1, 0, 1, 32766, 32767];
224        
225        for sample in boundary_samples {
226            // Test A-law doesn't panic on boundary values
227            let alaw_encoded = alaw_compress(sample);
228            let alaw_decoded = alaw_expand(alaw_encoded);
229            
230            // Test μ-law doesn't panic on boundary values
231            let ulaw_encoded = ulaw_compress(sample);
232            let ulaw_decoded = ulaw_expand(ulaw_encoded);
233            
234            // Basic sanity check - decoded values should be in reasonable range
235            assert!(alaw_decoded >= -32768 && alaw_decoded <= 32767);
236            assert!(ulaw_decoded >= -32768 && ulaw_decoded <= 32767);
237        }
238    }
239
240    #[test]
241    fn test_known_values() {
242        // Test cases from ITU-T reference implementation
243        assert_eq!(alaw_compress(0), 0xd5);
244        assert_eq!(alaw_compress(128), 0xdd);
245        assert_eq!(alaw_compress(1024), 0xe5);
246        assert_eq!(alaw_compress(-128), 0x52);
247        assert_eq!(alaw_compress(-1024), 0x7a);
248        
249        assert_eq!(ulaw_compress(0), 0xff);
250        assert_eq!(ulaw_compress(128), 0xef);
251        assert_eq!(ulaw_compress(1024), 0xcd);
252        assert_eq!(ulaw_compress(-128), 0x6f);
253        assert_eq!(ulaw_compress(-1024), 0x4d);
254        
255        assert_eq!(alaw_expand(0xd5), 8);
256        assert_eq!(alaw_expand(0xdd), 136);
257        assert_eq!(alaw_expand(0xe5), 1056);
258        assert_eq!(alaw_expand(0x52), -120);
259        assert_eq!(alaw_expand(0x7a), -1008);
260        
261        assert_eq!(ulaw_expand(0xff), 0);
262        assert_eq!(ulaw_expand(0xef), 132);
263        assert_eq!(ulaw_expand(0xcd), 1052);
264        assert_eq!(ulaw_expand(0x6f), -132);
265        assert_eq!(ulaw_expand(0x4d), -1052);
266    }
267}