1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
//! M-CELP speech codec.
//!
//! A 3.6 kbit/s CELP codec: 320 mu-law samples in, 18 bytes out, and back.
//! Each stage is written as the algorithm it implements, and the routine label
//! quoted in its doc comment names the reference behaviour the test suite
//! checks it against.
//!
//! # Using it
//!
//! [`Encoder`] and [`Decoder`] are the entry points. Both work one frame at a
//! time: [`FRAME`] mu-law samples in, [`FRAME_BYTES`] transport bytes out, and
//! back. Both carry state between frames, so one instance must see a stream in
//! order; a partial frame at the end of a stream is dropped, as the reference
//! does.
//!
//! ```
//! use mcelp::{Decoder, Encoder, FRAME};
//!
//! let speech: Vec<u8> = vec![0xff; 4 * FRAME]; // mu-law, 8 kHz mono
//!
//! let (mut encoder, mut decoder) = (Encoder::new(), Decoder::new());
//! let mut out = Vec::new();
//! for block in speech.chunks_exact(FRAME) {
//! let frame = encoder.encode(block.try_into().unwrap());
//! assert_eq!(frame.len(), mcelp::FRAME_BYTES);
//! if let Some(pcm) = decoder.decode(&frame) {
//! out.extend_from_slice(&pcm);
//! }
//! }
//! assert_eq!(out.len(), speech.len());
//! ```
//!
//! [`Decoder::decode_linear`] gives the same samples as 16-bit linear PCM, and
//! [`bitstream`] holds the frame's parameter fields and the hex container the
//! bundled examples use.
//!
//! Everything else is the stages the two are built from. They are public so
//! that each can be replayed against the reference on its own, which is what
//! the test suite does; they are not a stable interface, and a caller that only
//! wants to code speech never needs them.
//!
//! # The codec
//!
//! The bit stream is very close to ITU-T G.729 in its parameter set and in most
//! of its arithmetic — the LSF quantiser's `GAP1`/`GAP2`/`GAP3` spacings, the
//! `L_LIMIT`/`M_LIMIT` clamps, the 1/3-resolution pitch lag coding and the
//! conjugate two-stage gain codebook are all recognisably G.729 — but it runs
//! four times slower: one frame is **320 samples (40 ms) split into four
//! 80-sample subframes**, carried in 18 bytes.
//!
//! ## Decoding ([`Decoder`])
//!
//! | stage | module |
//! |-------|--------|
//! | frame unpacking | [`bitstream`] |
//! | LSF dequantisation, LSP/LPC conversion | [`lsp`] |
//! | pitch lag, adaptive codebook | [`pitch`] |
//! | fixed codebook | [`codebook`] |
//! | gain decoding | [`gain`] |
//! | short-term synthesis | [`synth`] |
//! | postfiltering | [`postfilter`], [`ltp`] |
//!
//! ## Encoding ([`Encoder`])
//!
//! | stage | module |
//! |-------|--------|
//! | input conditioning | [`preprocess`] |
//! | spectral analysis | [`analysis`], [`bands`] |
//! | voice activity, noise floor | [`vad`], [`noise`], [`frontend`] |
//! | noise suppression | [`weights`], [`shaping`] |
//! | LPC analysis, LSF quantisation | [`lpc`], [`lsf_weight`] |
//! | perceptual weighting | [`weighting`], [`weight_lpc`] |
//! | closed-loop pitch search | [`pitch_search`], [`convolve`] |
//! | fixed-codebook search | [`pulses`], [`mode`] |
//!
//! # Numerics
//!
//! [`fixed`] reproduces the three arithmetic properties the code depends on:
//! fractional
//! multiplies, 40-bit wrapping accumulators, and sign
//! extension of memory operands. Everything else is ordinary arithmetic.
/// Samples per frame: 40 ms at 8 kHz.
pub const FRAME: usize = 320;
/// Samples per half-frame, which is the unit both halves of the codec work in.
pub const HALF: usize = FRAME / 2;
/// Samples per subframe, the unit the two codebooks are searched over.
pub const SUBFRAME: usize = FRAME / 4;
/// Order of the LPC predictor used throughout analysis and synthesis.
pub const LPC_ORDER: usize = 10;
/// Past excitation retained before the half-frame being processed.
pub const EXCITATION_HISTORY: usize = 154;
pub use FRAME_BYTES;
pub use Decoder;
pub use Encoder;