codec_core/lib.rs
1//! # `Codec-Core`: Audio Codec Library for `VoIP`
2//!
3//! Audio codecs for `VoIP` applications: `ITU-T` compliant G.711 μ-law and
4//! A-law in the default build, and G.729A/G.729AB, Opus and AMR-NB/AMR-WB
5//! behind feature flags. Every codec reaches the same [`types::AudioCodec`]
6//! interface, so the layers above pick one by negotiation rather than by type.
7//!
8//! ## Features
9//!
10//! - **ITU-T G.711 Compliant**: Passes official compliance tests
11//! - **Reference-validated speech codecs**: G.729 and both AMR variants are
12//! checked against their reference implementations, not just round-tripped
13//! - **Real Audio Tested**: Validated with actual speech samples
14//! - **Good Quality**: ~37 dB SNR with real speech
15//! - **Lookup Table Optimized**: Fast O(1) encoding/decoding for G.711
16//!
17//! ## Implementation
18//!
19//! - **Lookup Tables**: Pre-computed tables for O(1) operations
20//! - **Simple APIs**: Straightforward encoding/decoding functions
21//!
22//! ## Usage
23//!
24//! ### Quick Start
25//!
26//! ```rust
27//! # #[cfg(feature = "g711")]
28//! # {
29//! use codec_core::codecs::g711::G711Codec;
30//! use codec_core::types::{AudioCodec, CodecConfig, CodecType, SampleRate};
31//!
32//! // Create a G.711 μ-law codec
33//! let config = CodecConfig::new(CodecType::G711Pcmu)
34//! .with_sample_rate(SampleRate::Rate8000)
35//! .with_channels(1);
36//! let mut codec = G711Codec::new_pcmu(config)?;
37//!
38//! // Encode audio samples (20ms at 8kHz = 160 samples)
39//! let samples = vec![0i16; 160];
40//! let encoded = codec.encode(&samples)?;
41//!
42//! // Decode back to samples
43//! let decoded = codec.decode(&encoded)?;
44//! # }
45//! # Ok::<(), Box<dyn std::error::Error>>(())
46//! ```
47//!
48//! ## Testing & Validation
49//!
50//! The library includes comprehensive testing including real audio validation:
51//!
52//! ```bash
53//! # Run all codec tests including WAV roundtrip tests
54//! cargo test
55//!
56//! # Run only G.711 WAV roundtrip tests (downloads real speech audio)
57//! cargo test wav_roundtrip_test -- --nocapture
58//! ```
59//!
60//! The WAV roundtrip tests automatically download real speech samples and validate:
61//! - Signal-to-Noise Ratio (SNR) measurement
62//! - Round-trip audio quality preservation
63//! - Proper encoding/decoding with real audio data
64//! - Output WAV files for manual quality assessment
65//!
66//! ## Error Handling
67//!
68//! All codec operations return `Result` types with detailed error information:
69//!
70//! ```rust
71//! # #[cfg(feature = "g711")]
72//! # {
73//! use codec_core::codecs::g711::G711Codec;
74//! use codec_core::types::{CodecConfig, CodecType, SampleRate};
75//! use codec_core::error::CodecError;
76//!
77//! // Handle configuration errors
78//! let config = CodecConfig::new(CodecType::G711Pcmu)
79//! .with_sample_rate(SampleRate::Rate48000) // Invalid for G.711
80//! .with_channels(1);
81//!
82//! match G711Codec::new_pcmu(config) {
83//! Ok(codec) => println!("Codec created successfully"),
84//! Err(CodecError::InvalidSampleRate { rate, supported }) => {
85//! println!("Invalid sample rate {}, supported: {:?}", rate, supported);
86//! }
87//! Err(e) => println!("Other error: {}", e),
88//! }
89//! # }
90//! ```
91//!
92//! ## Performance Tips
93//!
94
95//! - Use appropriate frame sizes (160 samples for G.711 at 8kHz/20ms)
96//!
97//! ### Direct G.711 Functions
98//!
99//! ```rust
100//! # #[cfg(feature = "g711")]
101//! # {
102//! use codec_core::codecs::g711::{alaw_compress, alaw_expand, ulaw_compress, ulaw_expand};
103//!
104//! // Single sample processing
105//! let sample = 1024i16;
106//! let alaw_encoded = alaw_compress(sample);
107//! let alaw_decoded = alaw_expand(alaw_encoded);
108//!
109//! let ulaw_encoded = ulaw_compress(sample);
110//! let ulaw_decoded = ulaw_expand(ulaw_encoded);
111//! # }
112//! ```
113//!
114//! ### Frame-Based Processing
115//!
116//! ```rust
117//! # #[cfg(feature = "g711")]
118//! # {
119//! use codec_core::codecs::g711::{G711Codec, G711Variant};
120//!
121//! let mut codec = G711Codec::new(G711Variant::MuLaw);
122//!
123//! // Process 160 samples (20ms at 8kHz)
124//! let input_frame = vec![1000i16; 160]; // Some test samples
125//! let encoded = codec.compress(&input_frame).unwrap();
126//!
127//! // Decode back to samples (same count for G.711)
128//! let decoded = codec.expand(&encoded).unwrap();
129//! assert_eq!(input_frame.len(), decoded.len());
130//! # }
131//! # Ok::<(), Box<dyn std::error::Error>>(())
132//! ```
133//!
134//! ## Supported Codecs
135//!
136//! | Codec | Sample Rate | Channels | Bitrate | Frame Size | Feature |
137//! |-------|-------------|----------|---------|------------|---------|
138//! | **G.711 μ-law (PCMU)** | 8 kHz | 1 | 64 kbps | 160 samples | `g711`, default |
139//! | **G.711 A-law (PCMA)** | 8 kHz | 1 | 64 kbps | 160 samples | `g711`, default |
140//! | **G.729A / G.729AB** | 8 kHz | 1 | 8 kbps | 80 samples | `g729` |
141//! | **Opus** | 8–48 kHz | 1–2 | 6–510 kbps | 2.5–60 ms | `opus` |
142//! | **AMR-NB** | 8 kHz | 1 | 4.75–12.2 kbps, 8 modes | 160 samples | `amr-nb` |
143//! | **AMR-WB (G.722.2)** | 16 kHz | 1 | 6.6–23.85 kbps, 9 modes | 320 samples | `amr-wb` |
144//!
145//! ## Quality Metrics
146//!
147//! Based on real audio testing with the included WAV roundtrip tests:
148//!
149//! - **G.711**: 37+ dB SNR (excellent quality, industry standard)
150//!
151//! The speech codecs are lossy by design, so SNR is not the useful measure for
152//! them. They are validated against their reference implementations instead:
153//!
154//! - **AMR-NB / AMR-WB**: bit-exact against the 3GPP reference encoders and
155//! decoders over the committed fixtures, plus the normative test sequences
156//! the reference distributions ship. Bit-exactness is not certification —
157//! see the status document linked under Feature Flags for the boundary.
158//!
159//! ## Feature Flags
160//!
161//! ### Core Codecs (enabled by default)
162//! - `g711`: G.711 μ-law/A-law codecs
163//!
164//! ### Optional Codecs
165//! - `g729`: G.729A/G.729AB
166//! - `opus`: Opus, backed by libopus
167//! - `amr-nb` / `amr-wb` / `amr`: AMR narrowband and wideband (G.722.2), with
168//! RFC 4867 payload framing, DTX, CMR and mode negotiation. Encoders and
169//! decoders are bit-exact against the 3GPP reference implementations over the
170//! committed fixtures. See [`docs/AMR_IMPLEMENTATION_STATUS.md`] for the
171//! evidence and its boundaries.
172//! - `all-codecs`: every codec above
173//!
174//! [`docs/AMR_IMPLEMENTATION_STATUS.md`]:
175//! https://github.com/eisenzopf/rvoip/blob/main/crates/media/codec-core/docs/AMR_IMPLEMENTATION_STATUS.md
176
177#![deny(missing_docs)]
178#![warn(clippy::all)]
179#![warn(clippy::pedantic)]
180#![warn(clippy::nursery)]
181#![allow(clippy::module_name_repetitions)]
182
183pub mod codecs;
184pub mod error;
185
186/// ITU-T / 3GPP fixed-point basic operators (the ETSI "basicop" library).
187///
188/// Shared by every fixed-point speech codec here: G.729 and AMR both specify
189/// their arithmetic in terms of these exact saturating operations, so a single
190/// implementation is the only way both can be bit-exact against the same
191/// definitions. Originally written for the G.729A port and promoted out of it
192/// when AMR needed the same foundation.
193///
194/// Crate-internal: an implementation detail shared between codecs, not a public
195/// API surface this crate wants to commit to.
196#[cfg(any(feature = "g729", feature = "amr-nb", feature = "amr-wb"))]
197pub(crate) mod fixed_point;
198pub mod types;
199pub mod utils;
200
201// Re-export commonly used types and traits
202pub use codecs::{CodecFactory, CodecRegistry};
203pub use error::{CodecError, Result};
204pub use types::{
205 AudioCodec, AudioFrame, CodecCapability, CodecConfig, CodecInfo, CodecType, CodedFrame,
206 FrameKind, SampleRate, VariableRateCodec,
207};
208
209/// Version information for the codec library
210pub const VERSION: &str = env!("CARGO_PKG_VERSION");
211
212/// Supported codec types
213pub const SUPPORTED_CODECS: &[&str] = &[
214 #[cfg(feature = "g711")]
215 "PCMU",
216 #[cfg(feature = "g711")]
217 "PCMA",
218 #[cfg(feature = "g729")]
219 "G729",
220 #[cfg(feature = "g729")]
221 "G729A",
222 #[cfg(feature = "g729")]
223 "G729BA",
224 #[cfg(feature = "opus")]
225 "opus",
226 #[cfg(feature = "amr-nb")]
227 "AMR",
228 #[cfg(feature = "amr-wb")]
229 "AMR-WB",
230];
231
232/// Initialize the codec library
233///
234/// This function should be called once at program startup to initialize
235/// any global state or lookup tables. It's safe to call multiple times.
236///
237/// # Errors
238///
239/// Returns an error if initialization fails (e.g., SIMD detection fails)
240pub fn init() -> Result<()> {
241 // Initialize logging if not already done
242 let _ = tracing_subscriber::fmt::try_init();
243
244 // Initialize lookup tables
245 #[cfg(feature = "g711")]
246 codecs::g711::init_tables();
247
248 tracing::info!("Codec-Core v{} initialized", VERSION);
249 tracing::info!("Supported codecs: {:?}", SUPPORTED_CODECS);
250
251 Ok(())
252}
253
254/// Get library information
255#[must_use]
256pub fn info() -> LibraryInfo {
257 LibraryInfo {
258 version: VERSION,
259 supported_codecs: SUPPORTED_CODECS.to_vec(),
260 }
261}
262
263/// Library information structure
264#[derive(Debug, Clone)]
265pub struct LibraryInfo {
266 /// Library version
267 pub version: &'static str,
268 /// List of supported codec names
269 pub supported_codecs: Vec<&'static str>,
270}
271
272#[cfg(test)]
273mod tests {
274 use super::*;
275
276 #[test]
277 fn test_init() {
278 assert!(init().is_ok());
279 }
280
281 #[test]
282 fn test_info() {
283 let info = info();
284 assert_eq!(info.version, VERSION);
285
286 #[cfg(any(feature = "g711", feature = "g729", feature = "opus"))]
287 assert!(!info.supported_codecs.is_empty());
288
289 #[cfg(not(any(feature = "g711", feature = "g729", feature = "opus")))]
290 assert!(info.supported_codecs.is_empty());
291 }
292
293 #[test]
294 fn test_supported_codecs() {
295 #[cfg(any(feature = "g711", feature = "g729", feature = "opus"))]
296 const {
297 assert!(!SUPPORTED_CODECS.is_empty())
298 };
299
300 #[cfg(not(any(feature = "g711", feature = "g729", feature = "opus")))]
301 assert!(SUPPORTED_CODECS.is_empty());
302
303 #[cfg(feature = "g711")]
304 {
305 assert!(SUPPORTED_CODECS.contains(&"PCMU"));
306 assert!(SUPPORTED_CODECS.contains(&"PCMA"));
307 }
308
309 #[cfg(feature = "g729")]
310 {
311 assert!(SUPPORTED_CODECS.contains(&"G729"));
312 assert!(SUPPORTED_CODECS.contains(&"G729A"));
313 assert!(SUPPORTED_CODECS.contains(&"G729BA"));
314 }
315
316 #[cfg(feature = "opus")]
317 assert!(SUPPORTED_CODECS.contains(&"opus"));
318 }
319}