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}