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    #[must_use]
89    #[allow(clippy::needless_pass_by_value)]
90    pub const fn new(variant: G711Variant) -> Self {
91        Self {
92            variant,
93            frame_size: 160, // Default 20ms at 8kHz
94        }
95    }
96
97    /// Create a new G.711 codec with configuration
98    ///
99    /// # Errors
100    ///
101    /// Returns an error when the configuration uses a sample rate, channel
102    /// count, or frame duration unsupported by G.711.
103    #[allow(
104        clippy::cast_possible_truncation,
105        clippy::cast_sign_loss,
106        clippy::needless_pass_by_value
107    )]
108    pub fn new_with_config(
109        variant: G711Variant,
110        config: crate::types::CodecConfig,
111    ) -> Result<Self, CodecError> {
112        // Validate sample rate
113        if config.sample_rate.hz() != 8000 {
114            return Err(CodecError::InvalidSampleRate {
115                rate: config.sample_rate.hz(),
116                supported: vec![8000],
117            });
118        }
119
120        // Validate channels
121        if config.channels != 1 {
122            return Err(CodecError::InvalidChannelCount {
123                channels: config.channels,
124                supported: vec![1],
125            });
126        }
127
128        // Calculate frame size from config
129        let frame_size = config.frame_size_ms.map_or(160, |frame_ms| {
130            let samples_per_ms = 8000.0 / 1000.0; // 8 samples per ms at 8kHz
131            (samples_per_ms * frame_ms) as usize
132        });
133
134        // Validate frame size
135        let valid_sizes = [80, 160, 240, 320];
136        if !valid_sizes.contains(&frame_size) {
137            return Err(CodecError::InvalidFrameSize {
138                expected: 160,
139                actual: frame_size,
140            });
141        }
142
143        Ok(Self {
144            variant,
145            frame_size,
146        })
147    }
148
149    /// Create a new G.711 μ-law (PCMU) codec
150    ///
151    /// # Errors
152    ///
153    /// Returns an error when `config` is not a supported G.711 configuration.
154    pub fn new_pcmu(config: crate::types::CodecConfig) -> Result<Self, CodecError> {
155        Self::new_with_config(G711Variant::MuLaw, config)
156    }
157
158    /// Create a new G.711 A-law (PCMA) codec
159    ///
160    /// # Errors
161    ///
162    /// Returns an error when `config` is not a supported G.711 configuration.
163    pub fn new_pcma(config: crate::types::CodecConfig) -> Result<Self, CodecError> {
164        Self::new_with_config(G711Variant::ALaw, config)
165    }
166
167    /// Get the codec variant
168    #[must_use]
169    pub const fn variant(&self) -> G711Variant {
170        self.variant
171    }
172
173    /// Compress samples using the configured variant
174    ///
175    /// # Errors
176    ///
177    /// This implementation is infallible; the result type is retained for API
178    /// compatibility with other codecs.
179    pub fn compress(&self, samples: &[i16]) -> Result<Vec<u8>, CodecError> {
180        match self.variant {
181            G711Variant::ALaw => Ok(samples
182                .iter()
183                .map(|&sample| alaw_compress(sample))
184                .collect()),
185            G711Variant::MuLaw => Ok(samples
186                .iter()
187                .map(|&sample| ulaw_compress(sample))
188                .collect()),
189        }
190    }
191
192    /// Expand samples using the configured variant
193    ///
194    /// # Errors
195    ///
196    /// This implementation is infallible; the result type is retained for API
197    /// compatibility with other codecs.
198    pub fn expand(&self, compressed: &[u8]) -> Result<Vec<i16>, CodecError> {
199        match self.variant {
200            G711Variant::ALaw => Ok(compressed
201                .iter()
202                .map(|&sample| alaw_expand(sample))
203                .collect()),
204            G711Variant::MuLaw => Ok(compressed
205                .iter()
206                .map(|&sample| ulaw_expand(sample))
207                .collect()),
208        }
209    }
210
211    /// Compress samples using A-law
212    ///
213    /// # Errors
214    ///
215    /// This implementation is infallible; the result type is retained for API
216    /// compatibility.
217    pub fn compress_alaw(&self, samples: &[i16]) -> Result<Vec<u8>, CodecError> {
218        Ok(samples
219            .iter()
220            .map(|&sample| alaw_compress(sample))
221            .collect())
222    }
223
224    /// Expand A-law samples
225    ///
226    /// # Errors
227    ///
228    /// This implementation is infallible; the result type is retained for API
229    /// compatibility.
230    pub fn expand_alaw(&self, compressed: &[u8]) -> Result<Vec<i16>, CodecError> {
231        Ok(compressed
232            .iter()
233            .map(|&sample| alaw_expand(sample))
234            .collect())
235    }
236
237    /// Compress samples using μ-law
238    ///
239    /// # Errors
240    ///
241    /// This implementation is infallible; the result type is retained for API
242    /// compatibility.
243    pub fn compress_ulaw(&self, samples: &[i16]) -> Result<Vec<u8>, CodecError> {
244        Ok(samples
245            .iter()
246            .map(|&sample| ulaw_compress(sample))
247            .collect())
248    }
249
250    /// Expand μ-law samples
251    ///
252    /// # Errors
253    ///
254    /// This implementation is infallible; the result type is retained for API
255    /// compatibility.
256    pub fn expand_ulaw(&self, compressed: &[u8]) -> Result<Vec<i16>, CodecError> {
257        Ok(compressed
258            .iter()
259            .map(|&sample| ulaw_expand(sample))
260            .collect())
261    }
262}
263
264// Implement AudioCodec trait for backward compatibility
265impl crate::types::AudioCodec for G711Codec {
266    fn encode(&mut self, samples: &[i16]) -> Result<Vec<u8>, CodecError> {
267        self.compress(samples)
268    }
269
270    fn decode(&mut self, data: &[u8]) -> Result<Vec<i16>, CodecError> {
271        self.expand(data)
272    }
273
274    fn info(&self) -> crate::types::CodecInfo {
275        let (name, payload_type) = match self.variant {
276            G711Variant::ALaw => ("PCMA", Some(8)),
277            G711Variant::MuLaw => ("PCMU", Some(0)),
278        };
279
280        crate::types::CodecInfo {
281            name,
282            sample_rate: 8000,
283            channels: 1,
284            bitrate: 64000,
285            frame_size: self.frame_size,
286            payload_type,
287        }
288    }
289
290    fn reset(&mut self) -> Result<(), CodecError> {
291        // G.711 is stateless, no reset needed
292        Ok(())
293    }
294
295    fn frame_size(&self) -> usize {
296        self.frame_size
297    }
298
299    fn supports_variable_frame_size(&self) -> bool {
300        true
301    }
302}
303
304// Implement AudioCodecExt trait for additional functionality
305impl crate::types::AudioCodecExt for G711Codec {
306    fn encode_to_buffer(
307        &mut self,
308        samples: &[i16],
309        output: &mut [u8],
310    ) -> Result<usize, CodecError> {
311        if output.len() < samples.len() {
312            return Err(CodecError::BufferTooSmall {
313                needed: samples.len(),
314                actual: output.len(),
315            });
316        }
317
318        match self.variant {
319            G711Variant::ALaw => {
320                for (i, &sample) in samples.iter().enumerate() {
321                    output[i] = alaw_compress(sample);
322                }
323            }
324            G711Variant::MuLaw => {
325                for (i, &sample) in samples.iter().enumerate() {
326                    output[i] = ulaw_compress(sample);
327                }
328            }
329        }
330
331        Ok(samples.len())
332    }
333
334    fn decode_to_buffer(&mut self, data: &[u8], output: &mut [i16]) -> Result<usize, CodecError> {
335        if output.len() < data.len() {
336            return Err(CodecError::BufferTooSmall {
337                needed: data.len(),
338                actual: output.len(),
339            });
340        }
341
342        match self.variant {
343            G711Variant::ALaw => {
344                for (i, &encoded) in data.iter().enumerate() {
345                    output[i] = alaw_expand(encoded);
346                }
347            }
348            G711Variant::MuLaw => {
349                for (i, &encoded) in data.iter().enumerate() {
350                    output[i] = ulaw_expand(encoded);
351                }
352            }
353        }
354
355        Ok(data.len())
356    }
357
358    fn max_encoded_size(&self, input_samples: usize) -> usize {
359        input_samples // G.711 has 1:1 sample to byte ratio
360    }
361
362    fn max_decoded_size(&self, input_bytes: usize) -> usize {
363        input_bytes // G.711 has 1:1 byte to sample ratio
364    }
365}
366
367/// Initialize G.711 lookup tables (stub for compatibility)
368pub const fn init_tables() {
369    // No initialization needed with our implementation
370}