codec_core/codecs/g711/
mod.rs

1//! G.711 Audio Codec Implementation
2//!
3//! This module implements the G.711 codec with both μ-law (PCMU) and A-law (PCMA)
4//! variants. G.711 is the standard codec for telephony systems.
5//!
6//! ## Features
7//!
8//! - ITU-T G.711 compliant implementation
9//! - Both A-law and μ-law encoding/decoding  
10//! - Simple single-sample functions
11//! - Lookup table optimized for performance
12//!
13//! ## Usage
14//!
15//! ### Direct Function Calls
16//!
17//! ```rust
18//! use codec_core::codecs::g711::{alaw_compress, alaw_expand, ulaw_compress, ulaw_expand};
19//!
20//! // Single sample processing
21//! let sample = 1024i16;
22//! let alaw_encoded = alaw_compress(sample);
23//! let alaw_decoded = alaw_expand(alaw_encoded);
24//!
25//! let ulaw_encoded = ulaw_compress(sample);
26//! let ulaw_decoded = ulaw_expand(ulaw_encoded);
27//! ```
28//!
29//! ### Processing Multiple Samples
30//!
31//! ```rust
32//! use codec_core::codecs::g711::{alaw_compress, alaw_expand};
33//!
34//! let samples = vec![0i16, 100, -100, 1000, -1000];
35//! let encoded: Vec<u8> = samples.iter().map(|&s| alaw_compress(s)).collect();
36//! let decoded: Vec<i16> = encoded.iter().map(|&e| alaw_expand(e)).collect();
37//! ```
38//!
39//! ### Using the G711Codec Struct
40//!
41//! ```rust
42//! use codec_core::codecs::g711::{G711Codec, G711Variant};
43//! use codec_core::types::{AudioCodec, CodecConfig, CodecType, SampleRate};
44//!
45//! // Create μ-law codec
46//! let config = CodecConfig::new(CodecType::G711Pcmu)
47//!     .with_sample_rate(SampleRate::Rate8000)
48//!     .with_channels(1);
49//! let mut codec = G711Codec::new_pcmu(config)?;
50//!
51//! // Or create A-law codec directly
52//! let mut alaw_codec = G711Codec::new(G711Variant::ALaw);
53//!
54//! // Encode/decode
55//! let samples = vec![0i16; 160]; // 20ms at 8kHz
56//! let encoded = codec.encode(&samples)?;
57//! let decoded = codec.decode(&encoded)?;
58//! # Ok::<(), Box<dyn std::error::Error>>(())
59//! ```
60
61use crate::error::CodecError;
62
63mod reference;
64mod tables;
65
66#[cfg(test)]
67pub mod tests;
68
69// Re-export the core functions
70pub use reference::{
71    alaw_compress, alaw_expand, ulaw_compress, ulaw_expand
72};
73
74/// G.711 codec implementation
75pub struct G711Codec {
76    variant: G711Variant,
77    frame_size: usize,
78}
79
80/// G.711 codec variants
81#[derive(Debug, Clone, Copy, PartialEq, Eq)]
82pub enum G711Variant {
83    /// A-law (PCMA) - Used primarily in Europe
84    ALaw,
85    /// μ-law (PCMU) - Used primarily in North America and Japan
86    MuLaw,
87}
88
89impl G711Codec {
90    /// Create a new G.711 codec with the specified variant
91    pub fn new(variant: G711Variant) -> Self {
92        Self { 
93            variant, 
94            frame_size: 160, // Default 20ms at 8kHz
95        }
96    }
97    
98    /// Create a new G.711 codec with configuration
99    pub fn new_with_config(variant: G711Variant, config: crate::types::CodecConfig) -> Result<Self, CodecError> {
100        // Validate sample rate
101        if config.sample_rate.hz() != 8000 {
102            return Err(CodecError::InvalidSampleRate {
103                rate: config.sample_rate.hz(),
104                supported: vec![8000],
105            });
106        }
107        
108        // Validate channels
109        if config.channels != 1 {
110            return Err(CodecError::InvalidChannelCount {
111                channels: config.channels,
112                supported: vec![1],
113            });
114        }
115        
116        // Calculate frame size from config
117        let frame_size = if let Some(frame_ms) = config.frame_size_ms {
118            let samples_per_ms = 8000.0 / 1000.0; // 8 samples per ms at 8kHz
119            (samples_per_ms * frame_ms) as usize
120        } else {
121            160 // Default 20ms
122        };
123        
124        // Validate frame size
125        let valid_sizes = [80, 160, 240, 320];
126        if !valid_sizes.contains(&frame_size) {
127            return Err(CodecError::InvalidFrameSize {
128                expected: 160,
129                actual: frame_size,
130            });
131        }
132        
133        Ok(Self { variant, frame_size })
134    }
135    
136    /// Create a new G.711 μ-law (PCMU) codec
137    pub fn new_pcmu(config: crate::types::CodecConfig) -> Result<Self, CodecError> {
138        Self::new_with_config(G711Variant::MuLaw, config)
139    }
140    
141    /// Create a new G.711 A-law (PCMA) codec
142    pub fn new_pcma(config: crate::types::CodecConfig) -> Result<Self, CodecError> {
143        Self::new_with_config(G711Variant::ALaw, config)
144    }
145    
146    /// Get the codec variant
147    pub fn variant(&self) -> G711Variant {
148        self.variant
149    }
150    
151    /// Compress samples using the configured variant
152    pub fn compress(&self, samples: &[i16]) -> Result<Vec<u8>, CodecError> {
153        match self.variant {
154            G711Variant::ALaw => Ok(samples.iter().map(|&sample| alaw_compress(sample)).collect()),
155            G711Variant::MuLaw => Ok(samples.iter().map(|&sample| ulaw_compress(sample)).collect()),
156        }
157    }
158    
159    /// Expand samples using the configured variant
160    pub fn expand(&self, compressed: &[u8]) -> Result<Vec<i16>, CodecError> {
161        match self.variant {
162            G711Variant::ALaw => Ok(compressed.iter().map(|&sample| alaw_expand(sample)).collect()),
163            G711Variant::MuLaw => Ok(compressed.iter().map(|&sample| ulaw_expand(sample)).collect()),
164        }
165    }
166    
167    /// Compress samples using A-law
168    pub fn compress_alaw(&self, samples: &[i16]) -> Result<Vec<u8>, CodecError> {
169        Ok(samples.iter().map(|&sample| alaw_compress(sample)).collect())
170    }
171    
172    /// Expand A-law samples
173    pub fn expand_alaw(&self, compressed: &[u8]) -> Result<Vec<i16>, CodecError> {
174        Ok(compressed.iter().map(|&sample| alaw_expand(sample)).collect())
175    }
176    
177    /// Compress samples using μ-law
178    pub fn compress_ulaw(&self, samples: &[i16]) -> Result<Vec<u8>, CodecError> {
179        Ok(samples.iter().map(|&sample| ulaw_compress(sample)).collect())
180    }
181    
182    /// Expand μ-law samples
183    pub fn expand_ulaw(&self, compressed: &[u8]) -> Result<Vec<i16>, CodecError> {
184        Ok(compressed.iter().map(|&sample| ulaw_expand(sample)).collect())
185    }
186}
187
188// Implement AudioCodec trait for backward compatibility
189impl crate::types::AudioCodec for G711Codec {
190    fn encode(&mut self, samples: &[i16]) -> Result<Vec<u8>, CodecError> {
191        self.compress(samples)
192    }
193
194    fn decode(&mut self, data: &[u8]) -> Result<Vec<i16>, CodecError> {
195        self.expand(data)
196    }
197
198    fn info(&self) -> crate::types::CodecInfo {
199        let (name, payload_type) = match self.variant {
200            G711Variant::ALaw => ("PCMA", Some(8)),
201            G711Variant::MuLaw => ("PCMU", Some(0)),
202        };
203        
204        crate::types::CodecInfo {
205            name,
206            sample_rate: 8000,
207            channels: 1,
208            bitrate: 64000,
209            frame_size: self.frame_size,
210            payload_type,
211        }
212    }
213
214    fn reset(&mut self) -> Result<(), CodecError> {
215        // G.711 is stateless, no reset needed
216        Ok(())
217    }
218
219    fn frame_size(&self) -> usize {
220        self.frame_size
221    }
222
223    fn supports_variable_frame_size(&self) -> bool {
224        true
225    }
226}
227
228// Implement AudioCodecExt trait for additional functionality
229impl crate::types::AudioCodecExt for G711Codec {
230    fn encode_to_buffer(&mut self, samples: &[i16], output: &mut [u8]) -> Result<usize, CodecError> {
231        if output.len() < samples.len() {
232            return Err(CodecError::BufferTooSmall { needed: samples.len(), actual: output.len() });
233        }
234        
235        match self.variant {
236            G711Variant::ALaw => {
237                for (i, &sample) in samples.iter().enumerate() {
238                    output[i] = alaw_compress(sample);
239                }
240            }
241            G711Variant::MuLaw => {
242                for (i, &sample) in samples.iter().enumerate() {
243                    output[i] = ulaw_compress(sample);
244                }
245            }
246        }
247        
248        Ok(samples.len())
249    }
250
251    fn decode_to_buffer(&mut self, data: &[u8], output: &mut [i16]) -> Result<usize, CodecError> {
252        if output.len() < data.len() {
253            return Err(CodecError::BufferTooSmall { needed: data.len(), actual: output.len() });
254        }
255        
256        match self.variant {
257            G711Variant::ALaw => {
258                for (i, &encoded) in data.iter().enumerate() {
259                    output[i] = alaw_expand(encoded);
260                }
261            }
262            G711Variant::MuLaw => {
263                for (i, &encoded) in data.iter().enumerate() {
264                    output[i] = ulaw_expand(encoded);
265                }
266            }
267        }
268        
269        Ok(data.len())
270    }
271
272    fn max_encoded_size(&self, input_samples: usize) -> usize {
273        input_samples // G.711 has 1:1 sample to byte ratio
274    }
275
276    fn max_decoded_size(&self, input_bytes: usize) -> usize {
277        input_bytes // G.711 has 1:1 byte to sample ratio
278    }
279}
280
281/// Initialize G.711 lookup tables (stub for compatibility)
282pub fn init_tables() {
283    // No initialization needed with our implementation
284}