Skip to main content

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;
64
65#[cfg(test)]
66pub mod tests;
67
68// Re-export the core functions
69pub use reference::{alaw_compress, alaw_expand, ulaw_compress, ulaw_expand};
70
71/// G.711 codec implementation
72pub struct G711Codec {
73    variant: G711Variant,
74    frame_size: usize,
75}
76
77/// G.711 codec variants
78#[derive(Debug, Clone, Copy, PartialEq, Eq)]
79pub enum G711Variant {
80    /// A-law (PCMA) - Used primarily in Europe
81    ALaw,
82    /// μ-law (PCMU) - Used primarily in North America and Japan
83    MuLaw,
84}
85
86impl G711Codec {
87    /// Create a new G.711 codec with the specified variant
88    pub fn new(variant: G711Variant) -> Self {
89        Self {
90            variant,
91            frame_size: 160, // Default 20ms at 8kHz
92        }
93    }
94
95    /// Create a new G.711 codec with configuration
96    pub fn new_with_config(
97        variant: G711Variant,
98        config: crate::types::CodecConfig,
99    ) -> 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 {
134            variant,
135            frame_size,
136        })
137    }
138
139    /// Create a new G.711 μ-law (PCMU) codec
140    pub fn new_pcmu(config: crate::types::CodecConfig) -> Result<Self, CodecError> {
141        Self::new_with_config(G711Variant::MuLaw, config)
142    }
143
144    /// Create a new G.711 A-law (PCMA) codec
145    pub fn new_pcma(config: crate::types::CodecConfig) -> Result<Self, CodecError> {
146        Self::new_with_config(G711Variant::ALaw, config)
147    }
148
149    /// Get the codec variant
150    pub fn variant(&self) -> G711Variant {
151        self.variant
152    }
153
154    /// Compress samples using the configured variant
155    pub fn compress(&self, samples: &[i16]) -> Result<Vec<u8>, CodecError> {
156        match self.variant {
157            G711Variant::ALaw => Ok(samples
158                .iter()
159                .map(|&sample| alaw_compress(sample))
160                .collect()),
161            G711Variant::MuLaw => Ok(samples
162                .iter()
163                .map(|&sample| ulaw_compress(sample))
164                .collect()),
165        }
166    }
167
168    /// Expand samples using the configured variant
169    pub fn expand(&self, compressed: &[u8]) -> Result<Vec<i16>, CodecError> {
170        match self.variant {
171            G711Variant::ALaw => Ok(compressed
172                .iter()
173                .map(|&sample| alaw_expand(sample))
174                .collect()),
175            G711Variant::MuLaw => Ok(compressed
176                .iter()
177                .map(|&sample| ulaw_expand(sample))
178                .collect()),
179        }
180    }
181
182    /// Compress samples using A-law
183    pub fn compress_alaw(&self, samples: &[i16]) -> Result<Vec<u8>, CodecError> {
184        Ok(samples
185            .iter()
186            .map(|&sample| alaw_compress(sample))
187            .collect())
188    }
189
190    /// Expand A-law samples
191    pub fn expand_alaw(&self, compressed: &[u8]) -> Result<Vec<i16>, CodecError> {
192        Ok(compressed
193            .iter()
194            .map(|&sample| alaw_expand(sample))
195            .collect())
196    }
197
198    /// Compress samples using μ-law
199    pub fn compress_ulaw(&self, samples: &[i16]) -> Result<Vec<u8>, CodecError> {
200        Ok(samples
201            .iter()
202            .map(|&sample| ulaw_compress(sample))
203            .collect())
204    }
205
206    /// Expand μ-law samples
207    pub fn expand_ulaw(&self, compressed: &[u8]) -> Result<Vec<i16>, CodecError> {
208        Ok(compressed
209            .iter()
210            .map(|&sample| ulaw_expand(sample))
211            .collect())
212    }
213}
214
215// Implement AudioCodec trait for backward compatibility
216impl crate::types::AudioCodec for G711Codec {
217    fn encode(&mut self, samples: &[i16]) -> Result<Vec<u8>, CodecError> {
218        self.compress(samples)
219    }
220
221    fn decode(&mut self, data: &[u8]) -> Result<Vec<i16>, CodecError> {
222        self.expand(data)
223    }
224
225    fn info(&self) -> crate::types::CodecInfo {
226        let (name, payload_type) = match self.variant {
227            G711Variant::ALaw => ("PCMA", Some(8)),
228            G711Variant::MuLaw => ("PCMU", Some(0)),
229        };
230
231        crate::types::CodecInfo {
232            name,
233            sample_rate: 8000,
234            channels: 1,
235            bitrate: 64000,
236            frame_size: self.frame_size,
237            payload_type,
238        }
239    }
240
241    fn reset(&mut self) -> Result<(), CodecError> {
242        // G.711 is stateless, no reset needed
243        Ok(())
244    }
245
246    fn frame_size(&self) -> usize {
247        self.frame_size
248    }
249
250    fn supports_variable_frame_size(&self) -> bool {
251        true
252    }
253}
254
255// Implement AudioCodecExt trait for additional functionality
256impl crate::types::AudioCodecExt for G711Codec {
257    fn encode_to_buffer(
258        &mut self,
259        samples: &[i16],
260        output: &mut [u8],
261    ) -> Result<usize, CodecError> {
262        if output.len() < samples.len() {
263            return Err(CodecError::BufferTooSmall {
264                needed: samples.len(),
265                actual: output.len(),
266            });
267        }
268
269        match self.variant {
270            G711Variant::ALaw => {
271                for (i, &sample) in samples.iter().enumerate() {
272                    output[i] = alaw_compress(sample);
273                }
274            }
275            G711Variant::MuLaw => {
276                for (i, &sample) in samples.iter().enumerate() {
277                    output[i] = ulaw_compress(sample);
278                }
279            }
280        }
281
282        Ok(samples.len())
283    }
284
285    fn decode_to_buffer(&mut self, data: &[u8], output: &mut [i16]) -> Result<usize, CodecError> {
286        if output.len() < data.len() {
287            return Err(CodecError::BufferTooSmall {
288                needed: data.len(),
289                actual: output.len(),
290            });
291        }
292
293        match self.variant {
294            G711Variant::ALaw => {
295                for (i, &encoded) in data.iter().enumerate() {
296                    output[i] = alaw_expand(encoded);
297                }
298            }
299            G711Variant::MuLaw => {
300                for (i, &encoded) in data.iter().enumerate() {
301                    output[i] = ulaw_expand(encoded);
302                }
303            }
304        }
305
306        Ok(data.len())
307    }
308
309    fn max_encoded_size(&self, input_samples: usize) -> usize {
310        input_samples // G.711 has 1:1 sample to byte ratio
311    }
312
313    fn max_decoded_size(&self, input_bytes: usize) -> usize {
314        input_bytes // G.711 has 1:1 byte to sample ratio
315    }
316}
317
318/// Initialize G.711 lookup tables (stub for compatibility)
319pub fn init_tables() {
320    // No initialization needed with our implementation
321}