RFC 3951
Internet Low Bit Rate Codec (iLBC)
RFC 3951: Internet Low Bit Rate Codec (iLBC) ๐
Okay bestie, let's start with the basics ๐ . This is RFC 3951, and it's called Internet Low Bit Rate Codec (iLBC). It's an Experimental doc, dated December 2004, from the Network Working Group ๐ธ. The authors are S. Andersen from Aalborg University, A. Duric from Telio, and H. Astrom, R. Hagen, W. Kleijn and J. Linden. Global IP Sound is listed under J. Linden, and the other three don't get their own affiliation line. The title block doesn't list anything this RFC obsoletes or updates, so it's a standalone doc ๐ฆ.
Status of This Memo ๐ธ
Let's get the official vibe check out of the way first โจ. This memo defines an Experimental Protocol for the Internet community. It is not an Internet standard of any kind. The authors actually want feedback, so discussion and suggestions for improvement are requested ๐ซถ. And distribution of this memo is unlimited, so share it with everyone ๐.
Copyright Notice
Copyright (C) The Internet Society (2004). ๐ฅ
Abstract
Okay, the abstract is where the slay happens ๐. This doc specifies a speech codec made for robust voice communication over IP. The codec was developed by Global IP Sound (GIPS) ๐ง. It's designed for narrow band speech, and it gives a payload bit rate of 13.33 kbit/s for 30 ms frames and 15.20 kbit/s for 20 ms frames ๐. The really iconic part is that it handles lost frames gracefully. Frames get lost when IP packets are lost or delayed, and the speech still sounds decent instead of falling apart ๐ญโจ.
Table of Contents ๐ง
1. Introduction . . . . . . . . . . . . . . . . . . . . . . . . . 4
2. Outline of the Codec . . . . . . . . . . . . . . . . . . . . . 5
2.1. Encoder. . . . . . . . . . . . . . . . . . . . . . . . . 5
2.2. Decoder. . . . . . . . . . . . . . . . . . . . . . . . . 7
3. Encoder Principles . . . . . . . . . . . . . . . . . . . . . . 7
3.1. Pre-processing . . . . . . . . . . . . . . . . . . . . . 9
3.2. LPC Analysis and Quantization. . . . . . . . . . . . . . 9
3.2.1. Computation of Autocorrelation Coefficients. . . 10
3.2.2. Computation of LPC Coefficients. . . . . . . . . 11
3.2.3. Computation of LSF Coefficients from LPC
Coefficients . . . . . . . . . . . . . . . . . . 11
3.2.4. Quantization of LSF Coefficients . . . . . . . . 12
3.2.5. Stability Check of LSF Coefficients. . . . . . . 13
3.2.6. Interpolation of LSF Coefficients. . . . . . . . 13
3.2.7. LPC Analysis and Quantization for 20 ms Frames . 14
3.3. Calculation of the Residual. . . . . . . . . . . . . . . 15
3.4. Perceptual Weighting Filter. . . . . . . . . . . . . . . 15
3.5. Start State Encoder. . . . . . . . . . . . . . . . . . . 15
3.5.1. Start State Estimation . . . . . . . . . . . . . 16
3.5.2. All-Pass Filtering and Scale Quantization. . . . 17
3.5.3. Scalar Quantization. . . . . . . . . . . . . . . 18
3.6. Encoding the Remaining Samples . . . . . . . . . . . . . 19
3.6.1. Codebook Memory. . . . . . . . . . . . . . . . . 20
3.6.2. Perceptual Weighting of Codebook Memory
and Target . . . . . . . . . . . . . . . . . . . 22
3.6.3. Codebook Creation. . . . . . . . . . . . . . . . 23
3.6.3.1. Creation of a Base Codebook . . . . . . 23
3.6.3.2. Codebook Expansion. . . . . . . . . . . 24
3.6.3.3. Codebook Augmentation . . . . . . . . . 24
3.6.4. Codebook Search. . . . . . . . . . . . . . . . . 26
3.6.4.1. Codebook Search at Each Stage . . . . . 26
3.6.4.2. Gain Quantization at Each Stage . . . . 27
3.6.4.3. Preparation of Target for Next Stage. . 28
3.7. Gain Correction Encoding . . . . . . . . . . . . . . . . 28
3.8. Bitstream Definition . . . . . . . . . . . . . . . . . . 29
4. Decoder Principles . . . . . . . . . . . . . . . . . . . . . . 32
4.1. LPC Filter Reconstruction. . . . . . . . . . . . . . . . 33
4.2. Start State Reconstruction . . . . . . . . . . . . . . . 33
4.3. Excitation Decoding Loop . . . . . . . . . . . . . . . . 34
4.4. Multistage Adaptive Codebook Decoding. . . . . . . . . . 35
4.4.1. Construction of the Decoded Excitation Signal. . 35
4.5. Packet Loss Concealment. . . . . . . . . . . . . . . . . 35
4.5.1. Block Received Correctly and Previous Block
Also Received. . . . . . . . . . . . . . . . . . 35
4.5.2. Block Not Received . . . . . . . . . . . . . . . 36
4.5.3. Block Received Correctly When Previous Block
Not Received . . . . . . . . . . . . . . . . . . 36
4.6. Enhancement. . . . . . . . . . . . . . . . . . . . . . . 37
4.6.1. Estimating the Pitch . . . . . . . . . . . . . . 39
4.6.2. Determination of the Pitch-Synchronous
Sequences. . . . . . . . . . . . . . . . . . . . 39
4.6.3. Calculation of the Smoothed Excitation . . . . . 41
4.6.4. Enhancer Criterion . . . . . . . . . . . . . . . 41
4.6.5. Enhancing the Excitation . . . . . . . . . . . . 42
4.7. Synthesis Filtering. . . . . . . . . . . . . . . . . . . 43
4.8. Post Filtering . . . . . . . . . . . . . . . . . . . . . 43
5. Security Considerations. . . . . . . . . . . . . . . . . . . . 43
6. Evaluation of the iLBC Implementations . . . . . . . . . . . . 43
7. References . . . . . . . . . . . . . . . . . . . . . . . . . . 43
7.1. Normative References . . . . . . . . . . . . . . . . . . 43
7.2. Informative References . . . . . . . . . . . . . . . . . 44
8. ACKNOWLEDGEMENTS . . . . . . . . . . . . . . . . . . . . . . . 44
APPENDIX A: Reference Implementation . . . . . . . . . . . . . . . 45
A.1. iLBC_test.c. . . . . . . . . . . . . . . . . . . . . . . 46
A.2 iLBC_encode.h. . . . . . . . . . . . . . . . . . . . . . 52
A.3. iLBC_encode.c. . . . . . . . . . . . . . . . . . . . . . 53
A.4. iLBC_decode.h. . . . . . . . . . . . . . . . . . . . . . 63
A.5. iLBC_decode.c. . . . . . . . . . . . . . . . . . . . . . 64
A.6. iLBC_define.h. . . . . . . . . . . . . . . . . . . . . . 76
A.7. constants.h. . . . . . . . . . . . . . . . . . . . . . . 80
A.8. constants.c. . . . . . . . . . . . . . . . . . . . . . . 82
A.9. anaFilter.h. . . . . . . . . . . . . . . . . . . . . . . 96
A.10. anaFilter.c. . . . . . . . . . . . . . . . . . . . . . . 97
A.11. createCB.h . . . . . . . . . . . . . . . . . . . . . . . 98
A.12. createCB.c . . . . . . . . . . . . . . . . . . . . . . . 99
A.13. doCPLC.h . . . . . . . . . . . . . . . . . . . . . . . .104
A.14. doCPLC.c . . . . . . . . . . . . . . . . . . . . . . . .104
A.15. enhancer.h . . . . . . . . . . . . . . . . . . . . . . .109
A.16. enhancer.c . . . . . . . . . . . . . . . . . . . . . . .110
A.17. filter.h . . . . . . . . . . . . . . . . . . . . . . . .123
A.18. filter.c . . . . . . . . . . . . . . . . . . . . . . . .125
A.19. FrameClassify.h. . . . . . . . . . . . . . . . . . . . .128
A.20. FrameClassify.c. . . . . . . . . . . . . . . . . . . . .129
A.21. gainquant.h. . . . . . . . . . . . . . . . . . . . . . .131
A.22. gainquant.c. . . . . . . . . . . . . . . . . . . . . . .131
A.23. getCBvec.h . . . . . . . . . . . . . . . . . . . . . . .134
A.24. getCBvec.c . . . . . . . . . . . . . . . . . . . . . . .134
A.25. helpfun.h. . . . . . . . . . . . . . . . . . . . . . . .138
A.26. helpfun.c. . . . . . . . . . . . . . . . . . . . . . . .140
A.27. hpInput.h. . . . . . . . . . . . . . . . . . . . . . . .146
A.28. hpInput.c. . . . . . . . . . . . . . . . . . . . . . . .146
A.29. hpOutput.h . . . . . . . . . . . . . . . . . . . . . . .148
A.30. hpOutput.c . . . . . . . . . . . . . . . . . . . . . . .148
A.31. iCBConstruct.h . . . . . . . . . . . . . . . . . . . . .149
A.32. iCBConstruct.c . . . . . . . . . . . . . . . . . . . . .150
A.33. iCBSearch.h. . . . . . . . . . . . . . . . . . . . . . .152
A.34. iCBSearch.c. . . . . . . . . . . . . . . . . . . . . . .153
A.35. LPCdecode.h. . . . . . . . . . . . . . . . . . . . . . .163
A.36. LPCdecode.c. . . . . . . . . . . . . . . . . . . . . . .164
A.37. LPCencode.h. . . . . . . . . . . . . . . . . . . . . . .167
A.38. LPCencode.c. . . . . . . . . . . . . . . . . . . . . . .167
A.39. lsf.h. . . . . . . . . . . . . . . . . . . . . . . . . .172
A.40. lsf.c. . . . . . . . . . . . . . . . . . . . . . . . . .172
A.41. packing.h. . . . . . . . . . . . . . . . . . . . . . . .178
A.42. packing.c. . . . . . . . . . . . . . . . . . . . . . . .179
A.43. StateConstructW.h. . . . . . . . . . . . . . . . . . . .182
A.44. StateConstructW.c. . . . . . . . . . . . . . . . . . . .183
A.45. StateSearchW.h . . . . . . . . . . . . . . . . . . . . .185
A.46. StateSearchW.c . . . . . . . . . . . . . . . . . . . . .186
A.47. syntFilter.h . . . . . . . . . . . . . . . . . . . . . .190
A.48. syntFilter.c . . . . . . . . . . . . . . . . . . . . . .190
Authors' Addresses . . . . . . . . . . . . . . . . . . . . . . . .192
Full Copyright Statement . . . . . . . . . . . . . . . . . . . . .194
1. Introduction ๐
Okay so the intro sets the whole scene ๐ฌ. This doc describes an algorithm for coding speech signals sampled at 8 kHz ๐๏ธ. The algorithm is called iLBC, and it uses a block-independent linear-predictive coding (LPC) algorithm. It supports two basic frame lengths: 20 ms at 15.2 kbit/s, and 30 ms at 13.33 kbit/s ๐ธ. When the codec runs with 20 ms blocks, it produces 304 bits per block, which SHOULD be packetized as in [1]. For 30 ms blocks, it produces 400 bits per block, which SHOULD be packetized as in [1] too ๐ง. The two modes work in a very similar way, honestly they're basically sisters ๐ฏโโ๏ธ. When they differ, the text says so explicitly, usually with the notation x/y, where x is the 20 ms mode and y is the 30 ms mode โจ.
The codec gives a speech coding system with a controlled response to packet losses. That's similar to pulse code modulation (PCM) with packet loss concealment (PLC), like the ITU-T G.711 standard [4], which runs at a fixed bit rate of 64 kbit/s ๐ฆ. The bonus is that iLBC still gives fixed bit rate coding with a quality-versus-bit rate tradeoff that's close to state-of-the-art ๐. A suitable RTP payload format for iLBC is specified in [1] ๐ .
You'd use this coder for real time stuff like telephony and videoconferencing, plus streaming audio, archival, and messaging ๐๐ง.
Cable Television Laboratories (CableLabs(R)) has adopted iLBC as a mandatory PacketCable(TM) audio codec standard for VoIP over Cable applications [3]. That's a whole flex ๐.
The doc is organized like this ๐บ๏ธ. Section 2 gives a brief outline of the codec. Sections 3 and 4 explain the specific encoder and decoder algorithms, respectively. Appendix A provides a c-code reference implementation ๐ง.
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in BCP 14, RFC 2119 [2]. Those are the big requirement words, so pay attention to them ๐ซถ๐.
That's the front matter and Section 1 wrapped up! Part 2 is next, so keep your eyes peeled ๐โจ
2. Outline of the Codec โจ
okay so the codec is basically two halves: an encoder and a decoder. the encoder is explained in section 2.1 and the decoder in section 2.2 ๐
The big idea is LPC plus block-based coding of the LPC residual signal ๐ Each block is 160/240 samples (20 ms/30 ms). For every block, the codec works out a set of LPC filters and runs the speech through them to get the residual signal. Then it scalar-quantizes the dominant part of the residual, the part with the most energy ๐ฅ That dominant part is 57/58 samples long (20 ms/30 ms), and it acts as a start state for dynamic codebooks. Those codebooks get built from residual parts that were already coded, and they're used to code everything else in the block ๐ฆ
Here's why that's so iconic: every block stands on its own, so the codec has coding independence between blocks. A lost packet can't keep dragging its perceptual damage into the next blocks ๐ญ It also makes high-quality packet loss concealment (PLC) way easier to pull off ๐ซถ
2.1. Encoder
The input to the encoder SHOULD be 16 bit uniform PCM sampled at 8 kHz ๐ง It SHOULD be cut into blocks of BLOCKL=160/240 samples for the 20/30 ms frame size. Each block then gets split into NSUB=4/6 consecutive sub-blocks, and each sub-block is SUBL=40 samples long โจ
For the 30 ms frame size, the encoder does two LPC_FILTERORDER=10 linear-predictive coding (LPC) analyses ๐ The first one uses a smooth window centered on the second sub-block and stretching to the middle of the fifth sub-block. The second one uses a smooth asymmetric window centered on the fifth sub-block and stretching to the end of the sixth. For the 20 ms frame size, there's just one LPC_FILTERORDER=10 LPC analysis, with a smooth window centered over the third sub-frame ๐
For each LPC analysis, the encoder gets a set of line-spectral frequencies (LSFs), quantizes them, and interpolates them so every sub-block ends up with its own LSF coefficients ๐ After that, the LPC residual gets computed using those quantized, interpolated LPC analysis filters.
Next up is finding the good part. The encoder looks for the two consecutive sub-blocks of the residual with the maximal weighted energy ๐ Inside those two sub-blocks, the start state (segment) is picked from two options: the first 57/58 samples or the last 57/58 samples. Whichever one has higher energy wins ๐ The start state then gets encoded with scalar quantization.
Now the dynamic codebook encoding procedure kicks in ๐ง It codes three things: 1) the 23/22 (20 ms/30 ms) samples left over in the two sub-blocks that contain the start state, 2) the sub-blocks after the start state in time, and 3) the sub-blocks before the start state in time. So the target is either those 23/22 leftover samples or a 40-sample sub-block. The target might be indexed forward in time or backward in time, depending on where the start state sits ๐
The codebook coding uses an adaptive codebook. Its memory holds decoded LPC excitation samples from the part of the block that's already encoded ๐ Those samples are indexed in the same time direction as the target vector, and they end right before the first sample instant in the target. The codebook gets used in CB_NSTAGES=3 stages through a successive refinement approach, and the three resulting code vector gains are encoded with 5-, 4-, and 3-bit scalar quantization, in that order ๐
The codebook search uses noise shaping that's derived from the LPC filters. The main goal is to minimize the squared error between the target vector and the code vectors ๐ฏ Each code vector comes from one of CB_EXPAND=2 codebook sections. The first section is filled with delayed, already encoded residual vectors. The code vectors in the second section are built from predefined linear combinations of vectors in the first section ๐ฆ
Here's a catch: codebook encoding with squared-error matching is known to produce a coded signal with less power than the scalar quantized start state signal ๐ญ To fix that, the encoder runs a gain re-scaling method. It does a refined search for a better set of codebook gains, aiming for power matching after encoding. It does this by searching for a higher value of the gain factor for the first stage codebook, since the later stage gains get scaled by the first stage gain โจ
2.2. Decoder
Usually in packet communications, a jitter buffer on the receiving end decides whether a packet containing an encoded signal block has arrived or got lost ๐ฆ That logic isn't part of this codec, so don't go looking for it here ๐ โโ๏ธ For every encoded block that comes in, the decoder decodes it. For every lost block, the decoder runs a PLC operation ๐ซถ
Decoding a block starts with decoding and interpolating the LPC coefficients. After that, the start state gets decoded ๐งฉ
For the codebook-encoded segments, the decoder rebuilds the three code vectors using the received codebook indices, the exact same way the encoder built them ๐ It also decodes the three gain factors. The decoded signal is the sum of the three codebook vectors, each one scaled by its own gain ๐ธ
Then there's an enhancement algorithm applied to the reconstructed excitation signal. It boosts the periodicity of voiced speech regions, which is so satisfying ๐ The enhancement is optimized with one rule: the modification signal (the difference between the enhanced excitation and the excitation before enhancement) must have a short-time energy that doesn't go over a preset fraction of the short-time energy of the excitation before enhancement ๐
PLC is super easy to plug into the decoder ๐ For example, it can be based on repeating LPC filters, with the LPC residual signal found by using a long-term prediction estimate from previous residual blocks ๐ง
3. Encoder Principles ๐
Here's the big-picture block diagram for the whole iLBC encoding procedure. Each block in it points to the section where that step gets explained in more detail ๐บ๏ธ
+-----------+ +---------+ +---------+
speech -> | 1. Pre P | -> | 2. LPC | -> | 3. Ana | ->
+-----------+ +---------+ +---------+
+---------------+ +--------------+
-> | 4. Start Sel | ->| 5. Scalar Qu | ->
+---------------+ +--------------+
+--------------+ +---------------+
-> |6. CB Search | -> | 7. Packetize | -> payload
| +--------------+ | +---------------+
----<---------<------
sub-frame 0..2/4 (20 ms/30 ms)
Figure 3.1. Flow chart of the iLBC encoder
Here's the encoder run-down in order, step by step ๐
- Pre-process speech with a HP filter, if needed (section 3.1).
- Compute LPC parameters, quantize, and interpolate (section 3.2).
- Use analysis filters on speech to compute residual (section 3.3).
- Select position of 57/58-sample start state (section 3.5).
- Quantize the 57/58-sample start state with scalar quantization (section 3.5).
- Search the codebook for each sub-frame. Start with 23/22 sample block, then encode sub-blocks forward in time, and then encode sub-blocks backward in time. For each block, the steps in Figure 3.4 are performed (section 3.6).
- Packetize the bits into the payload specified in Table 3.2.
The input to the encoder SHOULD be 16-bit uniform PCM sampled at 8 kHz ๐ถ It also SHOULD be cut into blocks of BLOCKL=160/240 samples. Each block that goes into the encoder gets split into NSUB=4/6 consecutive sub-blocks, each SUBL=40 samples long โจ
0 39 79 119 159
+---------------------------------------+
| 1 | 2 | 3 | 4 |
+---------------------------------------+
20 ms frame
0 39 79 119 159 199 239
+-----------------------------------------------------------+
| 1 | 2 | 3 | 4 | 5 | 6 |
+-----------------------------------------------------------+
30 ms frame
Figure 3.2. One input block to the encoder for 20 ms (with four sub-frames) and 30 ms (with six sub-frames).
3.1. Pre-processing
Sometimes the recorded speech has DC level and/or 50/60 Hz noise hanging around ๐ If those components haven't been removed before the encoder call, they should be removed by a high-pass filter. There's a reference implementation of this, using a filter with a cutoff frequency of 90 Hz, in Appendix A.28 ๐
3.2. LPC Analysis and Quantization
The input to the LPC analysis module is a possibly high-pass filtered speech buffer, speech_hp. It holds 240/300 samples (LPC_LOOKBACK + BLOCKL = 80/60 + 160/240 = 240/300). Samples 0 through 79/59 come from the previous block, and samples 80/60 through 239/299 come from the current block ๐ง No look-ahead into the next block is used. For the very first block processed, the look-back samples are assumed to be zeros ๐
For each input block, the LPC analysis works out one or two sets (depending on frame size) of LPC_FILTERORDER=10 LPC filter coefficients. It uses the autocorrelation method and the Levinson-Durbin recursion for that ๐ข Those coefficients then get converted into the Line Spectrum Frequency representation ๐
In the 20 ms case, the single LSF set describes the spectral characteristics measured at the center of the third sub-block ๐ For 30 ms frames, the first set, lsf1, describes the spectral properties of the input signal at the center of the second sub-block. The other set, lsf2, describes the spectral characteristics measured at the center of the fifth sub-block ๐ซ The details of the 30 ms computation are in sections 3.2.1 through 3.2.6. Section 3.2.7 explains how LPC Analysis and Quantization differs for 20 ms frames ๐ฆ
3.2.1. Computation of Autocorrelation Coefficients โจ
okay so the first step of LPC analysis is working out autocorrelation coefficients from windowed speech samples ๐ this windowing is the ONLY thing that's different between the two sets of coefficients, so it's giving consistency ๐ง
for the first set, we slap a 240-sample-long standard symmetric Hanning window on samples 0 through 239 of the input data ๐ธ the first window, lpc_winTbl, is defined like this:
lpc_winTbl[i]= 0.5 * (1.0 - cos((2*PI*(i+1))/(BLOCKL+1)));
i=0,...,119
lpc_winTbl[i] = winTbl[BLOCKL - i - 1]; i=120,...,239
then we get the windowed speech (speech_hp_win1) by multiplying the first 240 samples of the input speech buffer with those window coefficients ๐ฆ
speech_hp_win1[i] = speech_hp[i] * lpc_winTbl[i];
i=0,...,BLOCKL-1
from these 240 windowed samples, we calculate 11 (LPC_FILTERORDER + 1) autocorrelation coefficients, acf1 ๐
acf1[lag] += speech_hp_win1[n] * speech_hp_win1[n + lag];
lag=0,...,LPC_FILTERORDER; n=0,...,BLOCKL-lag-1
now here's the lowkey genius part ๐ง to make the analysis more robust against numerical precision problems, we do a spectral smoothing procedure by windowing the autocorrelation coefficients before the LPC coefficients get computed. we also add a white noise floor by multiplying coefficient zero by 1.0001, which is 40dB below the energy of the windowed speech signal ๐ both of these steps happen in one go by multiplying the autocorrelation coefficients with this window:
lpc_lagwinTbl[0] = 1.0001;
lpc_lagwinTbl[i] = exp(-0.5 * ((2 * PI * 60.0 * i) /FS)^2);
i=1,...,LPC_FILTERORDER
where FS=8000 is the sampling frequency
then the windowed ACF, acf1_win, comes out as:
acf1_win[i] = acf1[i] * lpc_lagwinTbl[i];
i=0,...,LPC_FILTERORDER
the second set, acf2_win, is done in a similar way โจ but this time the window, lpc_asymwinTbl, gets applied to samples 60 through 299, which is the entire current block ๐ the window has two segments: the first (samples 0 to 219) is half a Hanning window with length 440, and the second is a quarter of a cosine wave cycle. the reason this is so iconic is that an asymmetric window like this centers the LPC analysis in the fifth sub-block without needing any look-ahead, and look-ahead would add delay ๐ so the asymmetric window is:
lpc_asymwinTbl[i] = (sin(PI * (i + 1) / 441))^2; i=0,...,219
lpc_asymwinTbl[i] = cos((i - 220) * PI / 40); i=220,...,239
and the windowed speech is computed as:
speech_hp_win2[i] = speech_hp[i + LPC_LOOKBACK] *
lpc_asymwinTbl[i]; i=0,....BLOCKL-1
the windowed autocorrelation coefficients then come out in exactly the same way as they did for the first analysis instance ๐ซถ
last thing: the windows lpc_winTbl, lpc_asymwinTbl, and lpc_lagwinTbl are typically generated ahead of time and stored in ROM, so we don't have to redo the calculation for every single block. very efficient, very bestie ๐
3.2.2. Computation of LPC Coefficients ๐
so now we have 2 x 11 smoothed autocorrelation coefficients (acf1_win and acf2_win), and from those we calculate the 2 x 11 LPC coefficients, lp1 and lp2, the same way for both analysis locations, using the well known Levinson-Durbin recursion ๐ฅ the first LPC coefficient is always 1.0, which leaves us with ten unique coefficients ๐
after we've got the LPC coefficients, we apply a bandwidth expansion procedure to smooth out the spectral peaks in the short-term spectrum. this is like softening the edges so nothing is too sharp, no cap ๐ฆ the bandwidth addition comes from this modification:
lp1_bw[i] = lp1[i] * chirp^i; i=0,...,LPC_FILTERORDER
lp2_bw[i] = lp2[i] * chirp^i; i=0,...,LPC_FILTERORDER
where "chirp" is a real number between 0 and 1. It is RECOMMENDED to use a value of 0.9. ๐ธ
3.2.3. Computation of LSF Coefficients from LPC Coefficients ๐ง
okay recap: so far we've determined two sets of LPC coefficients, which represent the short-term spectral characteristics of the speech signal at two different time locations within the current block ๐ these coefficients SHOULD be quantized and interpolated. but before that happens, it's advantageous to convert the LPC parameters into a different representation called Line Spectral Frequencies (LSF) โจ LSFs are used because they're better suited for quantization and interpolation than regular LPC coefficients, and that's the whole vibe ๐
there are lots of computationally efficient methods in the literature for calculating LSFs from LPC coefficients. the detailed implementation of one applicable method can be found in Appendix A.26 ๐ the two arrays of LSF coefficients we get, lsf1 and lsf2, have dimension 10 (LPC_FILTERORDER) ๐
3.2.4. Quantization of LSF Coefficients ๐
because the LPC filters defined by the two sets of LSFs are also needed in the decoder, the LSF parameters need to be quantized and sent over as side information ๐ฆ the total number of bits needed to represent the quantization of the two LSF representations for one block of speech is 40, with 20 bits for each of lsf1 and lsf2 ๐ข
for computational and storage reasons, the LSF vectors are quantized using three-split vector quantization (VQ). so the LSF vectors get split into three sub-vectors, and each one is quantized with a regular VQ ๐งฉ the quantized versions of lsf1 and lsf2, namely qlsf1 and qlsf2, come from the same memoryless split VQ. each LSF vector has length 10, and they're split into sub-vectors containing 3, 3, and 4 values, respectively ๐
each sub-vector has its own codebook of quantized values, designed with a standard VQ training method on a large database with speech from lots of speakers recorded under various conditions ๐ the size of each of the three codebooks for these splits is:
int size_lsfCbTbl[LSF_NSPLIT] = {64,128,128};
the actual values of the VQ codebook that must be used can be found in the reference code of Appendix A. Both sets of LSF coefficients, lsf1 and lsf2, get quantized with a standard memoryless split vector quantization (VQ) structure, using the squared error criterion in the LSF domain ๐ the split VQ quantization goes like this:
- Quantize the first three LSF coefficients (1 - 3) with a VQ codebook of size 64.
- Quantize the next three LSF coefficients 4 - 6 with VQ a codebook of size 128.
- Quantize the last four LSF coefficients (7 - 10) with a VQ codebook of size 128.
this procedure gets repeated for lsf1 and lsf2, and it gives six quantization indices plus the quantized sets of LSF coefficients, qlsf1 and qlsf2 ๐ฆ each set of three indices gets encoded with 6 + 7 + 7 = 20 bits, so the total number of bits for LSF quantization in a block is 40 bits. the math checks out, periodt ๐ซถ
3.2.5. Stability Check of LSF Coefficients ๐
the LSF representation of the LPC filter has a really convenient property: the coefficients are ordered by increasing value, i.e., lsf(n-1) < lsf(n), 0 < n < 10, if the corresponding synthesis filter is stable ๐ but since we're using a split VQ scheme, it's possible that at the split boundaries the LSF coefficients aren't ordered correctly, which means the corresponding LP filter could be unstable ๐ญ
to make sure the filter we use is stable, a stability check runs on the quantized LSF vectors. if the coefficients aren't ordered appropriately, they get moved apart, using a safety margin of 50 Hz so that formant peaks don't end up too narrow ๐ธ the detailed method is in Appendix A.40. the decoder does the same procedure too, and this is what ensures that exactly the same LSF representations are used in both the encoder and the decoder. no mismatch, no drama ๐ง
This is the part where the encoder and decoder have to agree perfectly, or everything goes sideways. Same steps, same results, every time. ๐
3.2.6. Interpolation of LSF Coefficients ๐
from the two sets of LSF coefficients computed for each block of speech, we get different LSFs for each sub-block through interpolation ๐ฆ this gets done for the original LSFs (lsf1 and lsf2) AND the quantized versions (qlsf1 and qlsf2), since both versions are used in the encoder ๐
here's a quick summary of the interpolation scheme. the details live in the c-code of Appendix A.
- In the first sub-block, we use the average of the second LSF vector from the previous block and the first LSF vector in the current block.
- For sub-blocks two through five, the LSFs are obtained by linear interpolation from
lsf1(andqlsf1) tolsf2(andqlsf2).lsf1is used in sub-block two, andlsf2in sub-block five. - In the last sub-block,
lsf2is used.
for the very first block, we assume the last LSF vector of the previous block equals a predefined vector, lsfmeanTbl, which is the mean LSF vector of the LSF design database ๐
lsfmeanTbl[LPC_FILTERORDER] = {0.281738, 0.445801, 0.663330,
0.962524, 1.251831, 1.533081, 1.850586, 2.137817,
2.481445, 2.777344}
the interpolation method is standard linear interpolation in the LSF domain โจ then the interpolated LSF values get converted into LPC coefficients for each sub-block. the unquantized and quantized LPC coefficients form two sets of filters, respectively ๐ the unquantized analysis filter for sub-block k is defined as follows:
___
\
Ak(z)= 1 + > ak(i)*z^(-i)
/__
i=1...LPC_FILTERORDER
and the quantized analysis filter for sub-block k is defined as follows:
___
\
A~k(z)= 1 + > a~k(i)*z^(-i)
/__
i=1...LPC_FILTERORDER
a reference implementation of the LSF encoding is in Appendix A.38, and a reference implementation of the corresponding decoding is in Appendix A.36 ๐ the vibes are immaculate and the math is locked in ๐
3.2.7. LPC Analysis and Quantization for 20 ms Frames โจ
okay so, as we said before, the codec only calculates one set of LPC parameters for the 20 ms frame size ๐ (the 30 ms frames get two sets). so we take one set of autocorrelation coefficients over LPC_LOOKBACK + BLOCKL = 80 + 160 = 240 samples. those samples get windowed with the asymmetric window lpc_asymwinTbl, centered over the third sub-frame, to make speech_hp_win. then we calculate the autocorrelation coefficients, acf, on those 240 samples and window them exactly like in section 3.2.1 (giving us acf_win) ๐
this one set of windowed autocorrelation coefficients is used to get the LPC coefficients, the LSF coefficients, and the quantized LSF coefficients, all in exactly the same way as sections 3.2.3 through 3.2.4 ๐ง same as the 30 ms case, the ten LSF coefficients get split into three sub-vectors of size 3, 3, and 4, and each one is quantized with the same scheme and codebook as in section 3.2.4. the end result is 3 quantization indices ๐ซถ then the quantized LSF coefficients get stabilized with the algorithm from section 3.2.5. no cap, it's the same vibe, just one set instead of two ๐
next up, interpolation ๐ฆ we take the LSF coefficients from this block and the ones from the previous block, and get a different set of LSFs for each sub-block by interpolating. the interpolation is linear in the LSF domain across the four sub-blocks. so the n-th sub-frame uses the weight (4-n)/4 on the LSF from the old frame and the weight n/4 on the LSF from the current frame. for the very first block, the mean LSF, lsfmeanTbl, stands in as the LSF from the previous block ๐ธ just like in section 3.2.6, we calculate both the unquantized analysis filter, A(z), and the quantized analysis filter, A~(z), for each of the four sub-blocks. iconic ๐
3.3. Calculation of the Residual ๐
okay bestie, now we filter the speech block with the quantized and interpolated LPC analysis filters to get the residual signal โจ each 40 sample sub-block gets filtered by its own LPC analysis filter, and the filter memory at the end of one sub-block gets carried over into the LPC filter of the next sub-block ๐ง the signal coming out of each LP analysis filter is the residual for that sub-block. literally that's it ๐
A reference implementation of the LPC analysis filters is in Appendix A.10 ๐
3.4. Perceptual Weighting Filter ๐
so technically any good perceptual weighting filter design can go in the encoder without messing up the codec definition ๐ซถ that said, it is RECOMMENDED to use the perceptual weighting filter Wk for sub-block k, which is specified below ๐
Wk(z)=1/Ak(z/LPC_CHIRP_WEIGHTDENUM), where
LPC_CHIRP_WEIGHTDENUM = 0.4222
this one is a simple design with low complexity, and it gets applied in the LPC residual domain ๐ธ here, Ak(z) is the filter we got for sub-block k from the unquantized but interpolated LSF coefficients. iykyk ๐
3.5. Start State Encoder ๐
the start state gets quantized using a common 6-bit scalar quantizer for the block, plus a 3-bit scalar quantizer that works on scaled samples in the weighted speech domain โจ we'll go through the state encoding in more detail now, so grab a snack ๐
3.5.1. Start State Estimation ๐ฅ
so first we figure out which two sub-blocks hold the start state. that's done by finding the two consecutive sub-blocks in the block with the highest power ๐ฆ we use down-weighting at the beginning and end of the sub-frames, so the following measure gets computed (NSUB=4/6 for 20/30 ms frame size):
nsub=1,...,NSUB-1
ssqn[nsub] = 0.0;
for (i=(nsub-1)*SUBL; i<(nsub-1)*SUBL+5; i++)
ssqn[nsub] += sampEn_win[i-(nsub-1)*SUBL]*
residual[i]*residual[i];
for (i=(nsub-1)*SUBL+5; i<(nsub+1)*SUBL-5; i++)
ssqn[nsub] += residual[i]*residual[i];
for (i=(nsub+1)*SUBL-5; i<(nsub+1)*SUBL; i++)
ssqn[nsub] += sampEn_win[(nsub+1)*SUBL-i-1]*
residual[i]*residual[i];
where sampEn_win[5]={1/6, 2/6, 3/6, 4/6, 5/6}; MAY be used. ๐ the sub-frame number that matches the maximum value of ssqEn_win[nsub-1]*ssqn[nsub] gets picked as the start state indicator ๐ง there's also a weighting of ssqEn_win[]={0.8,0.9,1.0,0.9,0.8} for 30 ms frames and ssqEn_win[]={0.9,1.0,0.9} for 20 ms frames. it MAY advantageously be used to bias the start state toward the middle of the frame, which is very main character energy ๐
for 20 ms frames there are three possible spots for the two-sub-block max power segment, and the start state position gets encoded with 2 bits ๐ the start state position, start, MUST be encoded as
start=1: start state in sub-frame 0 and 1
start=2: start state in sub-frame 1 and 2
start=3: start state in sub-frame 2 and 3
for 30 ms frames there are five possible spots, and the start state position gets encoded with 3 bits ๐ the start state position, start, MUST be encoded as
start=1: start state in sub-frame 0 and 1
start=2: start state in sub-frame 1 and 2
start=3: start state in sub-frame 2 and 3
start=4: start state in sub-frame 3 and 4
start=5: start state in sub-frame 4 and 5
so in both cases index 0 is not used ๐ to make the start state shorter for bit rate efficiency, it gets brought down to STATE_SHORT_LEN=57 samples for 20 ms frames and STATE_SHORT_LEN=58 samples for 30 ms frames ๐ซถ the power of the first 23/22 and last 23/22 samples of the two sub-frame blocks we found above gets computed as the sum of the squared signal sample values. then the 23/22-sample segment with the lowest power gets excluded from the start state. one bit is sent to say which of the two possible 57/58 sample segments is used ๐ the start state position inside the two sub-frames, state_first, MUST be encoded as
state_first=1: start state is first STATE_SHORT_LEN samples
state_first=0: start state is last STATE_SHORT_LEN samples
3.5.2. All-Pass Filtering and Scale Quantization ๐ฆ
first, the block of residual samples in the start state gets run through an all-pass filter. this one has the quantized LPC coefficients as its denominator and the reversed quantized LPC coefficients as its numerator โจ the whole point of this phase-dispersion filter is to get a more even spread of sample values in the residual signal ๐ธ the filtering is done with circular convolution, and the initial filter memory is set to zero.
res(0..(STATE_SHORT_LEN-1)) = uncoded start state residual
res((STATE_SHORT_LEN)..(2*STATE_SHORT_LEN-1)) = 0
Pk(z) = A~rk(z)/A~k(z), where
___
\
A~rk(z)= z^(-LPC_FILTERORDER)+>a~k(i+1)*z^(i-(LPC_FILTERORDER-1))
/__
i=0...(LPC_FILTERORDER-1)
and A~k(z) is taken from the block where the start state begins
res -> Pk(z) -> filtered
ccres(k) = filtered(k) + filtered(k+STATE_SHORT_LEN),
k=0..(STATE_SHORT_LEN-1)
then the all-pass filtered block gets searched for its largest magnitude sample ๐ฅ the 10-logarithm of that magnitude gets quantized with a 6-bit quantizer, state_frgqTbl, by finding the nearest representation ๐
that gives an index, idxForMax, which matches a quantized value, qmax ๐ง then the all-pass filtered residual samples in the block get multiplied by the scaling factor scal=4.5/(10^qmax) to give normalized samples. ta-da ๐
state_frgqTbl[64] = {1.000085, 1.071695, 1.140395, 1.206868,
1.277188, 1.351503, 1.429380, 1.500727, 1.569049,
1.639599, 1.707071, 1.781531, 1.840799, 1.901550,
1.956695, 2.006750, 2.055474, 2.102787, 2.142819,
2.183592, 2.217962, 2.257177, 2.295739, 2.332967,
2.369248, 2.402792, 2.435080, 2.468598, 2.503394,
2.539284, 2.572944, 2.605036, 2.636331, 2.668939,
2.698780, 2.729101, 2.759786, 2.789834, 2.818679,
2.848074, 2.877470, 2.906899, 2.936655, 2.967804,
3.000115, 3.033367, 3.066355, 3.104231, 3.141499,
3.183012, 3.222952, 3.265433, 3.308441, 3.350823,
3.395275, 3.442793, 3.490801, 3.542514, 3.604064,
3.666050, 3.740994, 3.830749, 3.938770, 4.101764}
3.5.3. Scalar Quantization ๐
the normalized samples get quantized in the perceptually weighted speech domain using sample-by-sample scalar DPCM quantization, as shown in Figure 3.3 ๐ each sample in the block goes through a weighting filter Wk(z) (from section 3.4) to form a weighted speech sample x[n]. then the target sample d[n] is made by subtracting a predicted sample y[n], where the prediction filter is given by
Pk(z) = 1 - 1 / Wk(z).
+-------+ x[n] + d[n] +-----------+ u[n]
residual -->| Wk(z) |-------->(+)---->| Quantizer |------> quantized
+-------+ - /|\ +-----------+ | residual
| \|/
y[n] +--------------------->(+)
| |
| +------+ |
+--------| Pk(z)|<------+
+------+
Figure 3.3. Quantization of start state samples by DPCM in weighted
speech domain.
the coded state sample u[n] comes from quantizing d[n] with a 3-bit quantizer that uses the quantization table state_sq3Tbl ๐ฆ
state_sq3Tbl[8] = {-3.719849, -2.177490, -1.130005, -0.309692,
0.444214, 1.329712, 2.436279, 3.983887}
to go back to the residual domain, the quantized samples get: 1) scaled by 1/scal; 2) time-reversed; 3) filtered by the same all-pass filter from section 3.5.2, using circular convolution; and 4) time-reversed again โจ (more detail is in section 4.2, bestie ๐ซถ)
a reference implementation of the start-state encoding can be found in Appendix A.46 ๐
3.6. Encoding the Remaining Samples โจ
okay so bestie, a dynamic codebook gets used to encode three things: 1) the 23/22 remaining samples in the two sub-blocks containing the start state ๐, 2) the sub-blocks after the start state in time ๐ฆ, and 3) the sub-blocks before the start state in time ๐ธ. So the encoding target is either those 23/22 leftover samples from the 2 start-state sub-blocks, or a 40-sample sub-block. The target can be indexed forward or backward in time, depending on where the start state sits. The target length is called lTarget ๐
The coding is built on an adaptive codebook made from a codebook memory full of decoded LPC excitation samples from the part of the block that's already been encoded ๐ง. These samples are indexed in the same time direction as the target and end right before the first sample instant in the target vector. The codebook memory has length lMem, which is CB_MEML=147 for the two/four 40-sample sub-blocks and 85 for the 23/22-sample sub-block. Iconic memory length energy ๐
Here's the overview of the encoding procedure, bestie ๐
+------------+ +---------------+ +-------------+
-> | 1. Decode | -> | 2. Mem setup | -> | 3. Perc. W. | ->
+------------+ +---------------+ +-------------+
+------------+ +-----------------+
-> | 4. Search | -> | 5. Upd. Target | ------------------>
| +------------+ +------------------ |
----<-------------<-----------<----------
stage=0..2
+----------------+
-> | 6. Recalc G[0] | ---------------> gains and CB indices
+----------------+
Figure 3.4. Flow chart of the codebook search in the iLBC encoder.
- Decode the part of the residual that has been encoded so far, using the codebook without perceptual weighting ๐
- Set up the memory by taking data from the decoded residual. This memory is used to construct codebooks. For blocks preceding the start state, both the decoded residual and the target are time reversed (section 3.6.1) ๐
- Filter the memory + target with the perceptual weighting filter (section 3.6.2) โจ
- Search for the best match between the target and the codebook vector. Compute the optimal gain for this match and quantize that gain (section 3.6.4) ๐
- Update the perceptually weighted target by subtracting the contribution from the selected codebook vector from the perceptually weighted memory (quantized gain times selected vector). Repeat 4 and 5 for the two additional stages ๐
- Calculate the energy loss due to encoding of the residual. If needed, compensate for this loss by an upscaling and requantization of the gain for the first stage (section 3.7) ๐
The following sections give you the in-depth tea on each block of Figure 3.4 ๐ซถ
3.6.1. Codebook Memory
so the codebook memory is built from the sub-blocks that are already encoded, which means the data available for encoding grows with every new sub-block ๐ฑ Until there are enough encoded sub-blocks to fill the memory, it gets padded with zeros 0๏ธโฃ. Here's the order the sub-blocks get encoded in for the 30 ms frame size, if the start state lives in the last 58 samples of sub-block 2 and 3 ๐
+-----------------------------------------------------+
| 5 | 1 |///|////////| 2 | 3 | 4 |
+-----------------------------------------------------+
Figure 3.5. The order from 1 to 5 in which the sub-blocks are encoded. The slashed area is the start state.
Sub-block 1 goes first ๐ฏ and its codebook memory is shown below. Since the target comes before the start state in time, the codebook memory and target vector get time reversed, so the search algorithm can be reused after the block is time reversed ๐ Only the start state has been encoded at this point, so the last samples of the codebook memory are padded with zeros ๐
+-------------------------
|zeros|\\\\\\\\|\\\\| 1 |
+-------------------------
Figure 3.6. The codebook memory, length lMem=85 samples, and the target vector 1, length 22 samples.
Next up is sub-block 2, which gets encoded using the memory that has grown since sub-block 1 was encoded ๐ Here's the codebook memory for that one ๐
+-----------------------------------
| zeros | 1 |///|////////| 2 |
+-----------------------------------
Figure 3.7. The codebook memory, length lMem=147 samples, and the target vector 2, length 40 samples.
Then sub-block 3 gets encoded with the memory that's grown even more since sub-blocks 1 and 2 were encoded. The sub-block still needs a few zeros for padding ๐ซง Here's the codebook memory for sub-block 3 ๐ธ
+------------------------------------------
|zeros| 1 |///|////////| 2 | 3 |
+------------------------------------------
Figure 3.8. The codebook memory, length lMem=147 samples, and the target vector 3, length 40 samples.
Sub-block 4 is next, using the memory that's grown once more since sub-blocks 1, 2, and 3 were encoded. This time the memory doesn't need zero padding at all ๐ฅณ Here's the codebook memory for sub-block 4 ๐ฆ
+------------------------------------------
|1|///|////////| 2 | 3 | 4 |
+------------------------------------------
Figure 3.9. The codebook memory, length lMem=147 samples, and the target vector 4, length 40 samples.
The last target sub-block is number 5, and its codebook memory is shown here ๐ Because the target comes before the start state in time, the codebook memory and target vector get time reversed.
+-------------------------------------------
| 3 | 2 |\\\\\\\\|\\\\| 1 | 5 |
+-------------------------------------------
Figure 3.10. The codebook memory, length lMem=147 samples, and the target vector 5, length 40 samples.
For 20 ms frames the encoding procedure looks almost exactly the same ๐ฏ The only difference is that the start state is 57 samples long and there are only three sub-blocks to encode. The encoding order is the same as above: it starts with the 23-sample target, then encodes the two remaining 40-sample sub-blocks, going forward in time first and then backward in time relative to the start state ๐ง
3.6.2. Perceptual Weighting of Codebook Memory and Target
to give the coding error a perceptual weighting โจ, the concatenation of the codebook memory and the target to be coded gets all-pole filtered with the perceptual weighting filter from section 3.4. The filter state of the weighting filter is set to zero 0๏ธโฃ
in(0..(lMem-1)) = unweighted codebook memory
in(lMem..(lMem+lTarget-1)) = unweighted target signal
in -> Wk(z) -> filtered,
where Wk(z) is taken from the sub-block of the target
weighted codebook memory = filtered(0..(lMem-1))
weighted target signal = filtered(lMem..(lMem+lTarget-1))
The codebook search runs on the weighted codebook memory and the weighted target ๐ Meanwhile, the decoding and the codebook memory update use the unweighted codebook memory ๐
3.6.3. Codebook Creation
the codebook for the search gets created from the perceptually weighted codebook memory ๐ซถ It has two sections: the first is the base codebook, and the second is the expanded codebook, since it's made from linear combinations of the first ๐งฉ Each of those two sections also has a subsection called the augmented codebook. The augmented codebook only gets created and used for the 40-sample sub-blocks, not the 23/22-sample sub-block case ๐ โโ๏ธ The codebook sizes for the different sub-blocks and stages are summed up in the table below ๐
Stage
1 2 & 3
--------------------------------------------
22 128 (64+0)*2 128 (64+0)*2
Sub- 1:st 40 256 (108+20)*2 128 (44+20)*2
Blocks 2:nd 40 256 (108+20)*2 256 (108+20)*2
3:rd 40 256 (108+20)*2 256 (108+20)*2
4:th 40 256 (108+20)*2 256 (108+20)*2
Table 3.1. Codebook sizes for the 30 ms mode.
Table 3.1 shows the codebook size for each sub-block and stage for 30 ms frames ๐ The numbers in parentheses show how the codebook vectors are spread across the two sections, between the base/expanded codebook and the augmented base/expanded codebook. Read it like this: (base/expanded cb + augmented base/expanded cb). The total number of codebook vectors for a given sub-block and stage comes from this formula:
Tot. cb vectors = base cb + aug. base cb + exp. cb + aug. exp. cb
The values that line up with Figure 3.1 for 20 ms frames are only slightly modified ๐ท The short sub-block is 23 instead of 22 samples, and the 3:rd and 4:th sub-frame are not present.
3.6.3.1. Creation of a Base Codebook
the base codebook is given by the perceptually weighted codebook memory mentioned in section 3.5.3 ๐ The different codebook vectors come from sliding a window of length 23/22 or 40 (given by the variable lTarget) over the lMem-long perceptually weighted codebook memory ๐ช The indices are ordered so that the codebook vector containing samples (lMem-lTarget-n) to (lMem-n-1) of the codebook memory vector has index n, where n=0..lMem-lTarget. So the total number of base codebook vectors is lMem-lTarget+1, and the indices go from sample delay lTarget (23/22 or 40) to lMem+1 (86 or 148) ๐
3.6.3.2. Codebook Expansion
the base codebook gets expanded by a factor of 2, which creates an extra section in the codebook ๐ This new section comes from filtering the base codebook, base_cb, with a FIR filter of filter length CB_FILTERLEN=8 ๐ง The expanded codebook construction makes up for the four-sample delay the FIR filter introduces โฑ๏ธ
cbfiltersTbl[CB_FILTERLEN]={-0.033691, 0.083740, -0.144043,
0.713379, 0.806152, -0.184326,
0.108887, -0.034180};
___
\
exp_cb(k)= + > cbfiltersTbl(i)*x(k-i+4)
/__
i=0...(LPC_FILTERORDER-1)
where x(j) = base_cb(j) for j=0..lMem-1 and 0 otherwise
The individual codebook vectors of the new filtered codebook, exp_cb, and their indices are obtained the same way as described above for the base codebook โจ
3.6.3.3. Codebook Augmentation โจ
Okay bestie, this part is where we get extra vectors for the codebooks when we're encoding a whole 40-sample sub-block (cbveclen=40) ๐ง. The base and expanded codebooks get augmented to make them richer ๐. The augmentation vectors come from interpolating segments, which is so clever ๐.
Here's the vibe. The base and expanded codebooks, once built, hold vectors for sample delays from cbveclen up to lMem. Augmentation tries to add vectors for sample delays 20 to 39. Problem is, not every one of those samples is actually in the base or expanded codebook, so we build each augmentation vector as a linear combination of samples in the 20 to 39 range ๐ซถ. The same procedure runs on both the base codebook and the expanded codebook.
- - ------------------------|
codebook memory |
- - ------------------------|
|-5-|---15---|-5-|
pi pp po
| | Codebook vector
|---15---|-5-|-----20-----| <- corresponding to
i ii iii sample delay 20
Figure 3.11. Generation of the first augmented codebook.
Figure 3.11 shows the codebook memory with three pointers: pi is at sample 25, pp is at sample 20, and po is at sample 5 ๐. The augmented vector for sample delay 20 sits underneath. Segment i is fifteen samples from pp going forward in time. Segment ii is five interpolated samples, taken from pi going forward and from po going forward, with weights [0.0, 0.2, 0.4, 0.6, 0.8] for pi and [1.0, 0.8, 0.6, 0.4, 0.2] for po ๐ธ. Segment iii is twenty samples from pp going forward. Then, to get the vector for sample delay 21, we just move pp and pi one sample backward in time. Iconic, right? ๐ฆ
- - ------------------------|
codebook memory |
- - ------------------------|
|-5-|---16---|-5-|
pi pp po
| | Codebook vector
|---16---|-5-|-----19-----| <- corresponding to
i ii iii sample delay 21
Figure 3.12. Generation of the second augmented codebook.
In Figure 3.12, pi is at sample 26, pp is at sample 21, and po is still at sample 5 ๐. The augmented vector for sample delay 21 has segment i as sixteen samples from pp going forward. Segment ii is still five interpolated samples from pi and po, going forward, and the interpolation weights stay the same the whole time ๐
. Segment iii is nineteen samples from pp going forward. We keep moving the two pointers like this until we hit the last augmented vector, the one for sample delay 39 ๐. That's twenty brand new codebook vectors for each of the two sections. So with the augmented codebook included, each section ends up with lMem-SUBL+1+SUBL/2 codebook vectors total. This only happens when augmentation gets evoked, meaning lTarget=SUBL ๐ฅ.
3.6.4. Codebook Search ๐
This is where we actually hunt for the best match to the perceptually weighted target (see section 3.6.2) ๐ค. The search is a multi-stage gain-shape matching process. Each stage goes like this: find the best shape vector, calculate and quantize the gain, then update the target so the next stage can do its thing ๐. There are CB_NSTAGES=3 stages.
If the target is the 23/22-sample vector, the codebooks get indexed with the base codebook first, then the expanded codebook ๐. If the target is 40 samples, the order is base codebook, augmented base codebook, expanded codebook, then augmented expanded codebook ๐ง. The size of each codebook section and its augmented section is in Table 3.1 in section 3.6.3.
For example, when the second 40-sample sub-block gets coded, indices 0 to 107 belong to the base codebook, 108 to 127 to the augmented base codebook, 128 to 235 to the expanded codebook, and 236 to 255 to the augmented expanded codebook ๐ฆ. The indices are split up the same way for every stage in this example. The only time the stages differ is when coding the first 40-sample sub-block (see Table 3.1) โจ.
3.6.4.1. Codebook Search at Each Stage ๐ฏ
The codebooks get searched at each stage to find the best match for the target ๐. Once a best match is found, the target gets updated and the next-stage search kicks off. The three chosen codebook vectors and their gains together make up the encoded sub-block ๐ซถ. The best match is picked using these three criteria:
- Compute the measure
(target*cbvec)^2 / ||cbvec||^2for all codebook vectors,cbvec, and choose the codebook vector that maximizes the measure ๐. Here(target*cbvec)is the dot product between the target vector we're coding and the codebook vector we're measuring. The norm,||x||, is defined as the square root of(x*x). - The absolute value of the gain, for the chosen codebook vector
cbvec, must be smaller than a fixed limit,CB_MAXGAIN=1.3๐:
|gain| < CB_MAXGAIN
Where the gain gets computed like this:
gain = (target*cbvec) / ||cbvec||^2
- For the first stage, the dot product of the chosen codebook vector and the target must be positive ๐ :
target*cbvec > 0
In practice, these criteria get applied in a sequential search through all the codebook vectors ๐. Whenever a new candidate beats the previously registered max measure and meets all the other criteria, we register that new max measure and its index as the best so far ๐. If none of the codebook vectors pass (2) and (3), the first codebook vector gets selected as the fallback ๐ซ .
3.6.4.2. Gain Quantization at Each Stage ๐
The gain comes from this computation:
gain = (target*cbvec) / ||cbvec||^2
It's computed for the optimal codebook vector found by the procedure in section 3.6.4.1 ๐ .
The three stages quantize the gain using 5, 4, and 3 bits, in that order โจ. In the first stage, the gain is limited to positive values ๐. This gain gets quantized by finding the nearest value in the quantization table gain_sq5Tbl:
gain_sq5Tbl[32]={0.037476, 0.075012, 0.112488, 0.150024, 0.187500,
0.224976, 0.262512, 0.299988, 0.337524, 0.375000,
0.412476, 0.450012, 0.487488, 0.525024, 0.562500,
0.599976, 0.637512, 0.674988, 0.712524, 0.750000,
0.787476, 0.825012, 0.862488, 0.900024, 0.937500,
0.974976, 1.012512, 1.049988, 1.087524, 1.125000,
1.162476, 1.200012}
The gains for the second and third stages can be positive or negative ๐. They get quantized with a quantization table times a scale factor. The second stage uses gain_sq4Tbl and the third stage uses gain_sq3Tbl ๐ง. The scale factor equals 0.1 or the absolute value of the quantized gain representation value from the previous stage, whichever is larger ๐. Again, the resulting gain index is the index of the nearest value in the quantization table times the scale factor:
gainQ = scaleFact * gain_sqXTbl[index]
gain_sq4Tbl[16]={-1.049988, -0.900024, -0.750000, -0.599976,
-0.450012, -0.299988, -0.150024, 0.000000, 0.150024,
0.299988, 0.450012, 0.599976, 0.750000, 0.900024,
1.049988, 1.200012}
gain_sq3Tbl[8]={-1.000000, -0.659973, -0.330017,0.000000,
0.250000, 0.500000, 0.750000, 1.00000}
3.6.4.3. Preparation of Target for Next Stage ๐ท
Before the next stage's search starts, the perceptually weighted target vector gets updated ๐. We subtract the selected codebook vector (from the perceptually weighted codebook), multiplied by the matching quantized gain:
target[i] = target[i] - gainQ * selected_vec[i];
A reference implementation of the codebook encoding is in Appendix A.34 ๐.
3.7. Gain Correction Encoding ๐
The start state gets quantized in a pretty model-independent way, using 3 bits per sample ๐ซถ. The rest of the block, though, gets encoded with an adaptive codebook ๐. That codebook gives really accurate matches whenever the target and the best codebook vector are highly correlated ๐ฅ. For unvoiced speech and background noise, that's not necessarily true ๐. Because of the squared error criterion, the coded signal ends up with less power than the target signal ๐. The coded start state has good power matching, though, so the power ends up fluctuating inside the encoded frame ๐ญ. Perceptually, the big problem is that the time envelope of the signal energy gets unsteady ๐. To fix this, the codebook gains get re-scaled after the codebook encoding. We search for a new gain factor for the first-stage codebook that gives better power matching โจ.
First, we compute the energy of the target signal, tene, and the energy of the coded signal, cene, which is the sum of the three gain-scaled codebook vectors ๐งฎ. The second and third stage gains scale with the first-stage gain. So when the first-stage gain changes from gain[0] to gain_sq5Tbl[i], the coded signal energy changes from cene to:
cene*(gain_sq5Tbl[i]*gain_sq5Tbl[i])/(gain[0]*gain[0])
Here gain[0] is the first-stage gain found in the original codebook search ๐. A refined search tests gain indices i=0 to 31. As long as the new codebook energy, given by the formula above, is less than tene, the stage 1 gain index goes up ๐. There's a restriction: the new stage 1 gain value can't be more than two times higher than the original value found in the codebook search ๐. And note that this method doesn't change the shape of the encoded vector, only its gain or amplitude ๐
๐ธ.
3.8. Bitstream Definition
Okay bestie, one frame of 20 ms speech is 304 bits ๐, which fits in 38 bytes and gives you a bit rate of 15.20 kbit/s โจ. For 30 ms frames it's 400 bits, which fits in 50 bytes and lands at 13.33 kbit/s ๐. The bitstream sorts the bits into three classes based on how sensitive they are to bit errors or loss ๐ฆ. The most sensitive bits (class 1) go first in each frame ๐. The less sensitive ones (class 2) come right after class 1 ๐ธ, and the least sensitive ones (class 3) sit at the very end of the frame ๐ซถ.
In the 20/30 ms cases, here's the tea: class 1 takes 6/8 bytes (48/64 bits), class 2 takes 8/12 bytes (64/96 bits), and class 3 takes 24/30 bytes (191/239 bits) ๐ . This split is what makes uneven level protection (ULP) possible, and the payload format for iLBC [1] takes advantage of it ๐ฅ. The detailed bit allocation is in the table below ๐. If a quantization index gets spread across more than one class, the more significant bits go in the lowest class ๐ง.
Bitstream structure:
-------------------------------------------------------------------+
Parameter | Bits Class <1,2,3> |
| 20 ms frame | 30 ms frame |
----------------------------------+---------------+---------------+
Split 1 | 6 <6,0,0> | 6 <6,0,0> |
LSF 1 Split 2 | 7 <7,0,0> | 7 <7,0,0> |
LSF Split 3 | 7 <7,0,0> | 7 <7,0,0> |
------------------+---------------+---------------+
Split 1 | NA (Not Appl.)| 6 <6,0,0> |
LSF 2 Split 2 | NA | 7 <7,0,0> |
Split 3 | NA | 7 <7,0,0> |
------------------+---------------+---------------+
Sum | 20 <20,0,0> | 40 <40,0,0> |
----------------------------------+---------------+---------------+
Block Class | 2 <2,0,0> | 3 <3,0,0> |
----------------------------------+---------------+---------------+
Position 22 sample segment | 1 <1,0,0> | 1 <1,0,0> |
----------------------------------+---------------+---------------+
Scale Factor State Coder | 6 <6,0,0> | 6 <6,0,0> |
----------------------------------+---------------+---------------+
Sample 0 | 3 <0,1,2> | 3 <0,1,2> |
Quantized Sample 1 | 3 <0,1,2> | 3 <0,1,2> |
Residual : | : : | : : |
State : | : : | : : |
Samples : | : : | : : |
Sample 56 | 3 <0,1,2> | 3 <0,1,2> |
Sample 57 | NA | 3 <0,1,2> |
------------------+---------------+---------------+
Sum | 171 <0,57,114>| 174 <0,58,116>|
----------------------------------+---------------+---------------+
Stage 1 | 7 <6,0,1> | 7 <4,2,1> |
CB for 22/23 Stage 2 | 7 <0,0,7> | 7 <0,0,7> |
sample block Stage 3 | 7 <0,0,7> | 7 <0,0,7> |
------------------+---------------+---------------+
Sum | 21 <6,0,15> | 21 <4,2,15> |
----------------------------------+---------------+---------------+
Stage 1 | 5 <2,0,3> | 5 <1,1,3> |
Gain for 22/23 Stage 2 | 4 <1,1,2> | 4 <1,1,2> |
sample block Stage 3 | 3 <0,0,3> | 3 <0,0,3> |
------------------+---------------+---------------+
Sum | 12 <3,1,8> | 12 <2,2,8> |
----------------------------------+---------------+---------------+
Stage 1 | 8 <7,0,1> | 8 <6,1,1> |
sub-block 1 Stage 2 | 7 <0,0,7> | 7 <0,0,7> |
Stage 3 | 7 <0,0,7> | 7 <0,0,7> |
------------------+---------------+---------------+
Stage 1 | 8 <0,0,8> | 8 <0,7,1> |
sub-block 2 Stage 2 | 8 <0,0,8> | 8 <0,0,8> |
Indices Stage 3 | 8 <0,0,8> | 8 <0,0,8> |
for CB ------------------+---------------+---------------+
sub-blocks Stage 1 | NA | 8 <0,7,1> |
sub-block 3 Stage 2 | NA | 8 <0,0,8> |
Stage 3 | NA | 8 <0,0,8> |
------------------+---------------+---------------+
Stage 1 | NA | 8 <0,7,1> |
sub-block 4 Stage 2 | NA | 8 <0,0,8> |
Stage 3 | NA | 8 <0,0,8> |
------------------+---------------+---------------+
Sum | 46 <7,0,39> | 94 <6,22,66> |
----------------------------------+---------------+---------------+
Stage 1 | 5 <1,2,2> | 5 <1,2,2> |
sub-block 1 Stage 2 | 4 <1,1,2> | 4 <1,2,1> |
Stage 3 | 3 <0,0,3> | 3 <0,0,3> |
------------------+---------------+---------------+
Stage 1 | 5 <1,1,3> | 5 <0,2,3> |
sub-block 2 Stage 2 | 4 <0,2,2> | 4 <0,2,2> |
Stage 3 | 3 <0,0,3> | 3 <0,0,3> |
Gains for ------------------+---------------+---------------+
sub-blocks Stage 1 | NA | 5 <0,1,4> |
sub-block 3 Stage 2 | NA | 4 <0,1,3> |
Stage 3 | NA | 3 <0,0,3> |
------------------+---------------+---------------+
Stage 1 | NA | 5 <0,1,4> |
sub-block 4 Stage 2 | NA | 4 <0,1,3> |
Stage 3 | NA | 3 <0,0,3> |
------------------+---------------+---------------+
Sum | 24 <3,6,15> | 48 <2,12,34> |
----------------------------------+---------------+---------------+
Empty frame indicator | 1 <0,0,1> | 1 <0,0,1> |
-------------------------------------------------------------------
SUM 304 <48,64,192> 400 <64,96,240>
Table 3.2. The bitstream definition for iLBC for both the 20 ms frame size mode and the 30 ms frame size mode.
When it gets packetized into the payload, the bits MUST be sorted like this: all the class 1 bits in order (top to bottom, the way the table lays them out), then all the class 2 bits (top to bottom), and then all the class 3 bits in that same sequential order ๐. The very last bit, the empty frame indicator, SHOULD be set to zero by the encoder ๐ฅบ. If it's set to 1, the decoder SHOULD treat the data as a lost frame ๐. For example, this bit can be set to 1 to indicate a lost frame for file storage format, as in [1] ๐.
4. Decoder Principles โจ
This section breaks down the principles behind each part of the decoder algorithm ๐ง.
+-------------+ +--------+ +---------------+
payload -> | 1. Get para | -> | 2. LPC | -> | 3. Sc Dequant | ->
+-------------+ +--------+ +---------------+
+-------------+ +------------------+
-> | 4. Mem setup| -> | 5. Construct res |------->
| +-------------+ +------------------- |
---------<-----------<-----------<------------
Sub-frame 0...2/4 (20 ms/30 ms)
+----------------+ +----------+
-> | 6. Enhance res | -> | 7. Synth | ------------>
+----------------+ +----------+
+-----------------+
-> | 8. Post Process | ----------------> decoded speech
+-----------------+
Figure 4.1. Flow chart of the iLBC decoder. If a frame was lost, steps 1 to 5 SHOULD be replaced by a PLC algorithm.
- Extract the parameters from the bitstream ๐ฆ.
- Decode the LPC and interpolate (section 4.1).
- Construct the 57/58-sample start state (section 4.2).
- Set up the memory using data from the decoded residual. This memory gets used for codebook construction. For blocks before the start state, both the decoded residual and the target get time reversed โช. Sub-frames are decoded in the same order they were encoded ๐.
- Construct the residuals of this sub-frame (
gain[0]*cbvec[0] + gain[1]*cbvec[1] + gain[2]*cbvec[2]). Repeat 4 and 5 until the residual of all sub-blocks has been constructed. - Enhance the residual with the post filter (section 4.6).
- Synthesis of the residual (section 4.7).
- Post process with HP filter, if desired (section 4.8).
4.1. LPC Filter Reconstruction
Decoding the LP filter parameters is super straightforward ๐ง. For a set of three/six indices, the matching LSF vector(s) come from a simple table lookup ๐. Each LSF vector's three split vectors get concatenated to make qlsf1 and qlsf2, respectively (in the 20 ms mode there's only one LSF vector, qlsf) ๐. Next comes the stability check from section 3.2.5, followed by the interpolation scheme from section 3.2.6 (3.2.7 for 20 ms frames) โจ. The only difference is that the decoder only knows the quantized LSFs, so the unquantized LSFs don't get processed ๐.
You can find a reference implementation of the LPC filter reconstruction in Appendix A.36 ๐.
4.2. Start State Reconstruction โจ
okay bestie, time to rebuild the start state ๐ The scalar encoded STATE_SHORT_LEN=58 (STATE_SHORT_LEN=57 in the 20 ms mode) state samples get reconstructed in five steps, and it's giving precision ๐ฅ:
- form a set of samples (by table lookup) from the index stream
idxVec[n] - multiply the set with
1/scal=(10^qmax)/4.5 - time reverse the 57/58 samples
- filter the time reversed block with the dispersion (all-pass) filter used in the encoder (as described in section 3.5.2). This compensates for the phase distortion of the earlier filter operation ๐ธ
- reverse the 57/58 samples from the previous step
in(0..(STATE_SHORT_LEN-1)) = time reversed samples from table
look-up,
idxVecDec((STATE_SHORT_LEN-1)..0)
in(STATE_SHORT_LEN..(2*STATE_SHORT_LEN-1)) = 0
Pk(z) = A~rk(z)/A~k(z), where
___
\
A~rk(z)= z^(-LPC_FILTERORDER) + > a~ki*z^(i-(LPC_FILTERORDER-1))
/__
i=0...(LPC_FILTERORDER-1)
and A~k(z) is taken from the block where the start state begins
in -> Pk(z) -> filtered
out(k) = filtered(STATE_SHORT_LEN-1-k) +
filtered(2*STATE_SHORT_LEN-1-k),
k=0..(STATE_SHORT_LEN-1)
The remaining 23/22 samples in the state get rebuilt with the same adaptive codebook technique from section 4.3 ๐ฆ. The location bit tells you whether those are the first or the last 23/22 samples of the 80-sample state vector. If they're the first samples, the scalar encoded STATE_SHORT_LEN state samples get time-reversed before the adaptive codebook memory vector is initialized โจ
a reference implementation of the start state reconstruction is in Appendix A.44 ๐
4.3. Excitation Decoding Loop ๐
the decoder basically follows the same order the encoder used for the residual ๐ง so after the whole 80-sample state vector is decoded, you do the forward sub-blocks (the samples that come after the state vector), and then the backward sub-blocks (the samples that come before it). Ta-da, you get one fully decoded block of excitation signal samples ๐
Each sub-block is decoded with the multistage adaptive codebook decoding module from section 4.4. That module leans on an adaptive codebook memory that gets built before every run of the adaptive codebook decoding. Building it in the decoder is identical to the method in section 3.6.3, except it's done on the codebook memory without perceptual weighting ๐
For the initial forward sub-block, the last STATE_LEN=80 samples of the length CB_LMEM=147 adaptive codebook memory are filled with the samples of the state vector. For the next forward sub-blocks, the first SUBL=40 samples of the adaptive codebook memory get discarded, the rest shift by SUBL samples toward the beginning of the vector, and the newly decoded SUBL=40 samples get placed at the end of the memory ๐ For backward sub-blocks it's similar, except every vector of samples involved gets time reversed first.
a reference implementation of the excitation decoding loop is in Appendix A.5 ๐ซถ
4.4. Multistage Adaptive Codebook Decoding ๐
The Multistage Adaptive Codebook Decoding module gets used at both the sender (encoder) and the receiver (decoder) ends. Its whole job is to make a synthetic signal in the residual domain, which eventually turns into synthetic speech ๐ฅ It takes the index values used to build vectors, scales them, sums them together, and that sum is its output.
4.4.1. Construction of the Decoded Excitation Signal โจ
The unpacked index values that go into the module are references to extended codebooks. Those get constructed the way section 3.6.3 describes, except they're based on the codebook memory without the perceptual weighting ๐ The three unpacked indices look up three codebook vectors, and the three unpacked gain indices decode the 3 gains that go with them. While doing this decoding, the successive rescaling from section 3.6.4.2 gets applied ๐
a reference implementation of the adaptive codebook decoding is listed in Appendix A.32 ๐
4.5. Packet Loss Concealment ๐ฆ
If packet loss happens, the decoder gets a signal saying that info about a block is lost ๐ญ For those blocks, it is RECOMMENDED to use a Packet Loss Concealment (PLC) unit to make a decoded signal that masks the effect of the loss. Below we walk through an example PLC unit that works with the iLBC codec ๐ง The PLC unit only runs at the decoder, so it doesn't affect interoperability between implementations. Other PLC implementations MAY therefore be used โจ
The PLC described here works on the LP filters and the excitation signals, and it's built on these principles:
4.5.1. Block Received Correctly and Previous Block Also Received ๐ธ
If the block comes in correctly, the PLC only records state info about the current block, in case the next block gets lost. The LP filter coefficients for each sub-block and the entire decoded excitation signal are all saved in the decoder state structure, because all of that info is needed if the following block is lost ๐
4.5.2. Block Not Received ๐
If the block isn't received, the substitution is based on a pitch-synchronous repetition of the excitation signal, which gets filtered by the last LP filter of the previous block. The previous block's info is stored in the decoder state structure ๐ซถ
A correlation analysis runs on the previous block's excitation signal to figure out how much pitch periodicity there is, plus a pitch value. The correlation measure is also used to decide the voicing level (how voiced, or roughly periodic, the previous block's excitation was) ๐ฅ The excitation from the previous block makes an excitation for the block being substituted, so the pitch of the previous block is kept. That means the new excitation is built in a pitch-synchronous manner ๐ To avoid a buzzy-sounding substituted block, a random excitation gets mixed with the new pitch periodic excitation, and the relative use of the two is computed from the correlation measure (voicing level) ๐
For the block being substituted, the newly built excitation signal goes through the LP filter to make the speech that replaces the lost block ๐
When several blocks in a row get lost, the packet loss concealment keeps going in a similar way. The correlation measure of the last block received is still used, along with the same pitch value. The LP filters of the last block received get reused too. The energy of the substituted excitation drops for each consecutive lost block, which leads to a dampened excitation, and so to dampened speech ๐ฅบ
4.5.3. Block Received Correctly When Previous Block Not Received ๐ง
When a block arrives correctly but the previous block didn't, the correctly received block's directly decoded speech (based only on the received block) is not used as the actual output. Why? Because that directly decoded speech doesn't necessarily merge smoothly into the synthetic speech made for the previous lost block, and if the two signals don't merge smoothly, you get an audible discontinuity, which is so not it ๐ญ So a correlation analysis runs between the two blocks of excitation signal (the excitation of the previous concealed block and that of the current received block) to find the best phase match. Then a simple overlap-add procedure merges the previous excitation smoothly into the current block's excitation โจ
The exact implementation of the packet loss concealment does not influence interoperability of the codec ๐
a reference implementation of the packet loss concealment is suggested in Appendix A.14. Exact compliance with this suggested algorithm is not needed for a reference implementation to be fully compatible with the overall codec specification ๐ซถ
4.6. Enhancement โจ
Okay bestie, the decoder has an enhancement unit ๐ that works on the reconstructed excitation signal. Its whole job is to make the speech sound cleaner by cutting down the speech-correlated noise in the voiced parts ๐. Compared to traditional postfilters, the enhancer is lowkey better because it can only modify the excitation signal slightly, so there's no risk of over enhancement ๐. It works very similarly for both the 20 ms and 30 ms frame size modes ๐ฆ.
For the 20 ms frame size, the enhancer keeps a memory of six 80-sample excitation blocks from earlier in time, plus the two new 80-sample excitation blocks. Every 160 new unenhanced excitation samples turn into 160 enhanced samples โญ. The enhanced excitation comes out 40 samples later than the unenhanced one, because the enhancer algorithm uses lookahead ๐.
For the 30 ms frame size, the enhancer keeps five 80-sample blocks from the past, plus the three new 80-sample blocks. Every 240 new unenhanced samples turn into 240 enhanced samples ๐. This time the enhanced excitation is 80 samples delayed compared to the unenhanced one, again because of that lookahead ๐ซถ.
Outline of Enhancer
Okay so the speech enhancement unit works on sub-blocks of 80 samples, which means there are two or three of these 80-sample sub-blocks per frame ๐ธ. Each one gets enhanced separately, but they all go through the exact same process, just in their own turn ๐.
unenhanced residual
|
| +---------------+ +--------------+
+-> | 1. Pitch Est | -> | 2. Find PSSQ | -------->
+---------------+ | +--------------+
+-----<-------<------<--+
+------------+ enh block 0..1/2 |
-> | 3. Smooth | |
+------------+ |
\ |
/\ |
/ \ Already |
/ 4. \----------->----------->-----------+ |
\Crit/ Fulfilled | |
\? / v |
\/ | |
\ +-----------------+ +---------+ | |
Not +->| 5. Use Constr. | -> | 6. Mix | ----->
Fulfilled +-----------------+ +---------+
---------------> enhanced residual
Figure 4.2. Flow chart of the enhancer.
Here's the flow, step by step ๐ฆ:
- Pitch estimation of each of the two or three new 80-sample blocks.
- Find the pitch-period-synchronous sequence n (for block k) by searching around the estimated pitch value. Do this for n=1,2,3,-1,-2,-3.
- Calculate the smoothed residual generated by the six pitch-period-synchronous sequences from the prior step.
- Check if the smoothed residual satisfies the criterion (section 4.6.4).
- Use the constraint to calculate the mixing factor (section 4.6.5).
- Mix the smoothed signal with the unenhanced residual (pssq(n) n=0).
The big idea of the enhancer is to grab three 80-sample blocks from before and three from after the sub-block being analyzed, then use them to improve the quality of the excitation in that sub-block ๐. The six blocks get picked so they're as highly correlated as possible with the unenhanced sub-block, which means they're the pitch-period-synchronous sequences for that sub-block ๐.
Then a linear combination of those six sequences gets calculated to approximate the sub-block โจ. If the squared error between that approximation and the unenhanced sub-block is small enough, the enhanced residual is just set equal to the approximation ๐. If the squared error criterion isn't met, the enhanced residual becomes a linear combination of the approximation and the unenhanced residual instead ๐ .
4.6.1. Estimating the Pitch
Pitch estimates are how the enhancer figures out where the pitch-period-synchronous sequences live, and it does this in a complexity-efficient way ๐ง. For each of the new two or three sub-blocks, a pitch estimate gets calculated by finding the maximum correlation in the range from lag 20 to lag 120 ๐. These estimates then narrow down the search for the best possible pitch-period-synchronous sequences ๐ฅ.
4.6.2. Determination of the Pitch-Synchronous Sequences
Once the enhancer has the pitch estimates from the prior step, it analyzes and enhances one 80-sample sub-block at a time ๐. The pitch-period-synchronous sequences pssq(n) can be pictured as vectors of length 80 samples, each one shifted by n*lag samples from the current sub-block ๐ฆ. The six sequences (pssq(-3) to pssq(-1) and pssq(1) to pssq(3)) get found one at a time using these steps:
- Estimate where pssq(n) is located. For pssq(n) in front of pssq(0) (n > 0), the location is estimated by moving one pitch estimate forward in time from the exact location of pssq(n-1). Similarly, pssq(n) behind pssq(0) (n < 0) is estimated by moving one pitch estimate backward in time from the exact location of pssq(n+1). If the estimated pssq(n) vector location is totally within the enhancer memory (Figure 4.3), steps 2, 3, and 4 are performed. Otherwise, pssq(n) is set to zeros ๐.
- Compute the correlation between the unenhanced excitation and vectors around the estimated location interval of pssq(n). The correlation is calculated in the interval estimated location +/- 2 samples, which gives five correlation values โจ.
- Upsample those five correlation values by a factor of 4, using four simple upsampling filters (MA filters with coefficients upsFilter1 to upsFilter4). The maximum value found specifies the best pitch-period, with a resolution of a quarter of a sample ๐ .
upsFilter1[7]={0.000000 0.000000 0.000000 1.000000
0.000000 0.000000 0.000000}
upsFilter2[7]={0.015625 -0.076904 0.288330 0.862061
-0.106445 0.018799 -0.015625}
upsFilter3[7]={0.023682 -0.124268 0.601563 0.601563
-0.124268 0.023682 -0.023682}
upsFilter4[7]={0.018799 -0.106445 0.862061 0.288330
-0.076904 0.015625 -0.018799}
- Generate the pssq(n) vector by upsampling the excitation memory and extracting the sequence that corresponds to the lag delay calculated in the prior step ๐.
With those steps, every pssq(n) can be found iteratively: first moving backward in time from pssq(0), then forward in time from pssq(0) ๐ธ.
0 159 319 479 639
+---------------------------------------------------------------+
| -5 | -4 | -3 | -2 | -1 | 0 | 1 | 2 |
+---------------------------------------------------------------+
|pssq 0 |
|pssq -1| |pssq 1 |
|pssq -2| |pssq 2 |
|pssq -3| |pssq 3 |
Figure 4.3. Enhancement for 20 ms frame size.
Figure 4.3 shows the pitch-period-synchronous sequences while enhancing the first 80-sample block in the 20 ms frame size mode ๐ง. The unenhanced signal input lives in the last two sub-blocks (1 - 2), and the other six sub-blocks hold unenhanced residual from earlier in time ๐. The enhancement algorithm runs on two 80-sample blocks: the first is the last 40 samples of sub-block 0 plus the first 40 samples of sub-block 1, and the second is the last 40 samples of sub-block 1 plus the first 40 samples of sub-block 2 ๐.
0 159 319 479 639
+---------------------------------------------------------------+
| -4 | -3 | -2 | -1 | 0 | 1 | 2 | 3 |
+---------------------------------------------------------------+
|pssq 0 |
|pssq -1| |pssq 1 |
|pssq -2| |pssq 2 |
|pssq -3| |pssq 3 |
Figure 4.4. Enhancement for 30 ms frame size.
Figure 4.4 shows the same kind of sequences for the first 80-sample block in the 30 ms frame size mode ๐ฆ. The unenhanced signal input is stored in the last three sub-blocks (1 - 3), and the five other sub-blocks contain unenhanced residual from earlier in time ๐. The enhancement algorithm runs on the three 80-sample sub-blocks 0, 1, and 2 ๐.
4.6.3. Calculation of the Smoothed Excitation
Here's the vibe: a linear combination of the six pssq(n) (n!=0) forms a smoothed approximation, z, of pssq(0) ๐ซถ. Most of the weight goes to the sequences that sit close to pssq(0), since those are the ones most likely to look like pssq(0) ๐ฅ. The smoothed vector also gets rescaled so that its energy matches the energy of pssq(0) โจ.
___
\
y = > pssq(i) * pssq_weight(i)
/__
i=-3,-2,-1,1,2,3
pssq_weight(i) = 0.5*(1-cos(2*pi*(i+4)/(2*3+2)))
z = C * y, where C = ||pssq(0)||/||y||
4.6.4. Enhancer Criterion
The criterion is that the enhanced excitation isn't allowed to differ much from the unenhanced excitation ๐. This gets checked for each 80-sample sub-block ๐ .
e < (b * ||pssq(0)||^2), where b=0.05 and (Constraint 1)
e = (pssq(0)-z)*(pssq(0)-z), and "*" means the dot product
4.6.5. Enhancing the excitation
Okay so from the criterion in the previous section, it's clear the excitation isn't allowed to change much ๐ธ. The whole point of this constraint is to stop the enhancer from creating a signal that's significantly different from the original one ๐. It also limits how big the errors from the enhancement procedure can get ๐. That matters a lot in unvoiced segments and background noise segments, where extra periodicity could make things sound worse ๐ญ.
When the constraint from the prior section isn't met, the enhanced residual is instead calculated through a constrained optimization using the Lagrange multiplier technique ๐ง. The new constraint is:
e = (b * ||pssq(0)||^2) (Constraint 2)
There are two solution regions for this optimization ๐: 1) the region where the first constraint is fulfilled, and 2) the region where the first constraint is not fulfilled and the second constraint has to be used ๐ฆ.
In the first case, the second constraint isn't needed, so the optimized re-estimated vector is simply z, the energy-scaled version of y โจ.
In the second case, the second constraint kicks in and becomes an equality constraint ๐. Then we have:
z= A*y + B*pssq(0)
where:
A = sqrt((b-b^2/4)*(w00*w00)/ (w11*w00 + w10*w10)) and
w11 = pssq(0)*pssq(0)
w00 = y*y
w10 = y*pssq(0) (* symbolizes the dot product)
and:
B = 1 - b/2 - A * w10/w00
Appendix A.16 has a reference implementation listing for the enhancement method ๐โจ
4.7. Synthesis Filtering โจ
Okay so bestie, once we've decoded (or concealed) the LP excitation block, the speech block comes out by running the decoded LP synthesis filter, 1/A~k(z), over the whole block ๐. The synthesis filters have to be shifted to make up for the delay in the enhancer ๐
. In 20 ms frame size mode they SHOULD be shifted by one 40-sample sub-block, and in 30 ms frame size mode they SHOULD be shifted by two 40-sample sub-blocks ๐ฆ. The LP coefficients SHOULD be changed at the first sample of every sub-block, but the filter state gets to stay put, no reset ๐ธ. For PLC blocks, one solution is to use the last LP coefficients from the last decoded speech block for every sub-block ๐ง.
The reference implementation for the synthesis filtering can be found in Appendix A.48 ๐.
4.8. Post Filtering ๐
If you want, the decoded block can go through a high-pass filter ๐. That gets rid of the low frequencies in the decoded signal, which is lowkey iconic. A reference implementation with a cutoff at 65 Hz is in Appendix A.30 โญ.
5. Security Considerations ๐ฅ
Okay, this is the serious part ๐. The algorithm for coding speech signals is not subject to any known security consideration, so the codec itself is clean โจ. BUT its RTP payload format [1] has several considerations of its own, and those get handled over there, so go read it ๐. Confidentiality of the media streams is achieved by encryption, which means external mechanisms such as SRTP [5] MAY be used for that purpose ๐. No cap, that's the whole story.
6. Evaluation of the iLBC Implementations ๐
It is possible, and suggested, to evaluate certain iLBC implementations by using the methodology and tools available at http://www.ilbcfreeware.org/evaluation.html ๐ซถ.
7. References ๐
7.1. Normative References
[1] Duric, A. and S. Andersen, "Real-time Transport Protocol (RTP) Payload Format for internet Low Bit Rate Codec (iLBC) Speech", RFC 3952, December 2004.
[2] Bradner, S., "Key words for use in RFCs to Indicate Requirement Levels", BCP 14, RFC 2119, March 1997.
[3] PacketCable(TM) Audio/Video Codecs Specification, Cable Television Laboratories, Inc.
7.2. Informative References
[4] ITU-T Recommendation G.711, available online from the ITU bookstore at http://www.itu.int.
[5] Baugher, M., McGrew, D., Naslund, M., Carrara, E., and K. Norman, "The Secure Real Time Transport Protocol (SRTP)", RFC 3711, March 2004.
8. Acknowledgements ๐ซถ
Okay, the squad goes way beyond the listed authors ๐. This extensive work also has the following authors, who could not be listed as "official" authors because of IESG restrictions on how many authors can be listed:
Manohar N. Murthi (Department of Electrical and Computer Engineering, University of Miami), Fredrik Galschiodt, Julian Spittka, and Jan Skoglund (Global IP Sound) โจ.
The authors are deeply indebted to these people and thank them sincerely ๐:
Henry Sinnreich, Patrik Faltstrom, Alan Johnston, and Jean-Francois Mule, for great support of the iLBC initiative and for valuable feedback and comments ๐ธ.
Peter Vary, Frank Mertz, and Christoph Erdmann (RWTH Aachen); Vladimir Cuperman (Niftybox LLC); Thomas Eriksson (Chalmers Univ of Tech), and Gernot Kubin (TU Graz), for thorough review of the iLBC document and their valuable feedback and remarks ๐ฅน.
APPENDIX A. Reference Implementation ๐
This appendix contains the complete C code for a reference implementation of the encoder and decoder for the specified codec ๐ง.
The C code consists of the following files with the highest-level functions:
iLBC_test.c: main function for evaluation purpose
iLBC_encode.h: encoder header
iLBC_encode.c: encoder function
iLBC_decode.h: decoder header
iLBC_decode.c: decoder function
The following files contain global defines and constants:
iLBC_define.h: global defines
constants.h: global constants header
constants.c: global constants memory allocations
The following files contain subroutines:
anaFilter.h: lpc analysis filter header
anaFilter.c: lpc analysis filter function
createCB.h: codebook construction header
createCB.c: codebook construction function
doCPLC.h: packet loss concealment header
doCPLC.c: packet loss concealment function
enhancer.h: signal enhancement header
enhancer.c: signal enhancement function
filter.h: general filter header
filter.c: general filter functions
FrameClassify.h: start state classification header
FrameClassify.c: start state classification function
gainquant.h: gain quantization header
gainquant.c: gain quantization function
getCBvec.h: codebook vector construction header
getCBvec.c: codebook vector construction function
helpfun.h: general purpose header
helpfun.c: general purpose functions
hpInput.h: input high pass filter header
hpInput.c: input high pass filter function
hpOutput.h: output high pass filter header
hpOutput.c: output high pass filter function
iCBConstruct.h: excitation decoding header
iCBConstruct.c: excitation decoding function
iCBSearch.h: excitation encoding header
iCBSearch.c: excitation encoding function
LPCdecode.h: lpc decoding header
LPCdecode.c: lpc decoding function
LPCencode.h: lpc encoding header
LPCencode.c: lpc encoding function
lsf.h: line spectral frequencies header
lsf.c: line spectral frequencies functions
packing.h: bitstream packetization header
packing.c: bitstream packetization functions
StateConstructW.h: state decoding header
StateConstructW.c: state decoding functions
StateSearchW.h: state encoding header
StateSearchW.c: state encoding function
syntFilter.h: lpc synthesis filter header
syntFilter.c: lpc synthesis filter function
The implementation is portable and should work on many different platforms ๐. However, it is not difficult to optimize the implementation on particular platforms, which is an exercise left to the reader ๐ โจ
A.1. iLBC_test.c ๐
okay bestie, this is the test harness ๐ it reads a 16-bit PCM file, encodes it block by block, optionally applies a packet loss pattern from a channel file, decodes it, and writes the speech back out. the stats at the end are literally the vibe check ๐
/******************************************************************
iLBC Speech Coder ANSI-C Source Code
iLBC_test.c
Copyright (C) The Internet Society (2004).
All Rights Reserved.
******************************************************************/
#include <math.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "iLBC_define.h"
#include "iLBC_encode.h"
#include "iLBC_decode.h"
/* Runtime statistics */
#include <time.h>
#define ILBCNOOFWORDS_MAX (NO_OF_BYTES_30MS/2)
/*----------------------------------------------------------------*
* Encoder interface function
*---------------------------------------------------------------*/
short encode( /* (o) Number of bytes encoded */
iLBC_Enc_Inst_t *iLBCenc_inst,
/* (i/o) Encoder instance */
short *encoded_data, /* (o) The encoded bytes */
short *data /* (i) The signal block to encode*/
){
float block[BLOCKL_MAX];
int k;
/* convert signal to float */
for (k=0; k<iLBCenc_inst->blockl; k++)
block[k] = (float)data[k];
/* do the actual encoding */
iLBC_encode((unsigned char *)encoded_data, block, iLBCenc_inst);
return (iLBCenc_inst->no_of_bytes);
}
/*----------------------------------------------------------------*
* Decoder interface function
*---------------------------------------------------------------*/
short decode( /* (o) Number of decoded samples */
iLBC_Dec_Inst_t *iLBCdec_inst, /* (i/o) Decoder instance */
short *decoded_data, /* (o) Decoded signal block*/
short *encoded_data, /* (i) Encoded bytes */
short mode /* (i) 0=PL, 1=Normal */
){
int k;
float decblock[BLOCKL_MAX], dtmp;
/* check if mode is valid */
if (mode<0 || mode>1) {
printf("\nERROR - Wrong mode - 0, 1 allowed\n"); exit(3);}
/* do actual decoding of block */
iLBC_decode(decblock, (unsigned char *)encoded_data,
iLBCdec_inst, mode);
/* convert to short */
for (k=0; k<iLBCdec_inst->blockl; k++){
dtmp=decblock[k];
if (dtmp<MIN_SAMPLE)
dtmp=MIN_SAMPLE;
else if (dtmp>MAX_SAMPLE)
dtmp=MAX_SAMPLE;
decoded_data[k] = (short) dtmp;
}
return (iLBCdec_inst->blockl);
}
/*---------------------------------------------------------------*
* Main program to test iLBC encoding and decoding
*
* Usage:
* exefile_name.exe <infile> <bytefile> <outfile> <channel>
*
* <infile> : Input file, speech for encoder (16-bit pcm file)
* <bytefile> : Bit stream output from the encoder
* <outfile> : Output file, decoded speech (16-bit pcm file)
* <channel> : Bit error file, optional (16-bit)
* 1 - Packet received correctly
* 0 - Packet Lost
*
*--------------------------------------------------------------*/
int main(int argc, char* argv[])
{
/* Runtime statistics */
float starttime;
float runtime;
float outtime;
FILE *ifileid,*efileid,*ofileid, *cfileid;
short data[BLOCKL_MAX];
short encoded_data[ILBCNOOFWORDS_MAX], decoded_data[BLOCKL_MAX];
int len;
short pli, mode;
int blockcount = 0;
int packetlosscount = 0;
/* Create structs */
iLBC_Enc_Inst_t Enc_Inst;
iLBC_Dec_Inst_t Dec_Inst;
/* get arguments and open files */
if ((argc!=5) && (argc!=6)) {
fprintf(stderr,
"\n*-----------------------------------------------*\n");
fprintf(stderr,
" %s <20,30> input encoded decoded (channel)\n\n",
argv[0]);
fprintf(stderr,
" mode : Frame size for the encoding/decoding\n");
fprintf(stderr,
" 20 - 20 ms\n");
fprintf(stderr,
" 30 - 30 ms\n");
fprintf(stderr,
" input : Speech for encoder (16-bit pcm file)\n");
fprintf(stderr,
" encoded : Encoded bit stream\n");
fprintf(stderr,
" decoded : Decoded speech (16-bit pcm file)\n");
fprintf(stderr,
" channel : Packet loss pattern, optional (16-bit)\n");
fprintf(stderr,
" 1 - Packet received correctly\n");
fprintf(stderr,
" 0 - Packet Lost\n");
fprintf(stderr,
"*-----------------------------------------------*\n\n");
exit(1);
}
mode=atoi(argv[1]);
if (mode != 20 && mode != 30) {
fprintf(stderr,"Wrong mode %s, must be 20, or 30\n",
argv[1]);
exit(2);
}
if ( (ifileid=fopen(argv[2],"rb")) == NULL) {
fprintf(stderr,"Cannot open input file %s\n", argv[2]);
exit(2);}
if ( (efileid=fopen(argv[3],"wb")) == NULL) {
fprintf(stderr, "Cannot open encoded file %s\n",
argv[3]); exit(1);}
if ( (ofileid=fopen(argv[4],"wb")) == NULL) {
fprintf(stderr, "Cannot open decoded file %s\n",
argv[4]); exit(1);}
if (argc==6) {
if( (cfileid=fopen(argv[5],"rb")) == NULL) {
fprintf(stderr, "Cannot open channel file %s\n",
argv[5]);
exit(1);
}
} else {
cfileid=NULL;
}
/* print info */
fprintf(stderr, "\n");
fprintf(stderr,
"*---------------------------------------------------*\n");
fprintf(stderr,
"* *\n");
fprintf(stderr,
"* iLBC test program *\n");
fprintf(stderr,
"* *\n");
fprintf(stderr,
"* *\n");
fprintf(stderr,
"*---------------------------------------------------*\n");
fprintf(stderr,"\nMode : %2d ms\n", mode);
fprintf(stderr,"Input file : %s\n", argv[2]);
fprintf(stderr,"Encoded file : %s\n", argv[3]);
fprintf(stderr,"Output file : %s\n", argv[4]);
if (argc==6) {
fprintf(stderr,"Channel file : %s\n", argv[5]);
}
fprintf(stderr,"\n");
/* Initialization */
initEncode(&Enc_Inst, mode);
initDecode(&Dec_Inst, mode, 1);
/* Runtime statistics */
starttime=clock()/(float)CLOCKS_PER_SEC;
/* loop over input blocks */
while (fread(data,sizeof(short),Enc_Inst.blockl,ifileid)==
Enc_Inst.blockl) {
blockcount++;
/* encoding */
fprintf(stderr, "--- Encoding block %i --- ",blockcount);
len=encode(&Enc_Inst, encoded_data, data);
fprintf(stderr, "\r");
/* write byte file */
fwrite(encoded_data, sizeof(unsigned char), len, efileid);
/* get channel data if provided */
if (argc==6) {
if (fread(&pli, sizeof(short), 1, cfileid)) {
if ((pli!=0)&&(pli!=1)) {
fprintf(stderr, "Error in channel file\n");
exit(0);
}
if (pli==0) {
/* Packet loss -> remove info from frame */
memset(encoded_data, 0,
sizeof(short)*ILBCNOOFWORDS_MAX);
packetlosscount++;
}
} else {
fprintf(stderr, "Error. Channel file too short\n");
exit(0);
}
} else {
pli=1;
}
/* decoding */
fprintf(stderr, "--- Decoding block %i --- ",blockcount);
len=decode(&Dec_Inst, decoded_data, encoded_data, pli);
fprintf(stderr, "\r");
/* write output file */
fwrite(decoded_data,sizeof(short),len,ofileid);
}
/* Runtime statistics */
runtime = (float)(clock()/(float)CLOCKS_PER_SEC-starttime);
outtime = (float)((float)blockcount*(float)mode/1000.0);
printf("\n\nLength of speech file: %.1f s\n", outtime);
printf("Packet loss : %.1f%%\n",
100.0*(float)packetlosscount/(float)blockcount);
printf("Time to run iLBC :");
printf(" %.1f s (%.1f %% of realtime)\n\n", runtime,
(100*runtime/outtime));
/* close files */
fclose(ifileid); fclose(efileid); fclose(ofileid);
if (argc==6) {
fclose(cfileid);
}
return(0);
}
A.2. iLBC_encode.h ๐ฆ
next up is the encoder header, which just declares the init function and the main encode function so everything else can call them โจ
/******************************************************************
iLBC Speech Coder ANSI-C Source Code
iLBC_encode.h
Copyright (C) The Internet Society (2004).
All Rights Reserved.
******************************************************************/
#ifndef __iLBC_ILBCENCODE_H
#define __iLBC_ILBCENCODE_H
#include "iLBC_define.h"
short initEncode( /* (o) Number of bytes
encoded */
iLBC_Enc_Inst_t *iLBCenc_inst, /* (i/o) Encoder instance */
int mode /* (i) frame size mode */
);
void iLBC_encode(
unsigned char *bytes, /* (o) encoded data bits iLBC */
float *block, /* (o) speech vector to
encode */
iLBC_Enc_Inst_t *iLBCenc_inst /* (i/o) the general encoder
state */
);
#endif
A.3. iLBC_encode.c ๐ธ
okay and now the encoder source itself ๐ it pulls in a whole crew of helper modules (LPC, frame classification, state search, codebook search, filters, the works) and kicks off with the encoder instance initiation
/******************************************************************
iLBC Speech Coder ANSI-C Source Code
iLBC_encode.c
Copyright (C) The Internet Society (2004).
All Rights Reserved.
******************************************************************/
#include <math.h>
#include <stdlib.h>
#include <string.h>
#include "iLBC_define.h"
#include "LPCencode.h"
#include "FrameClassify.h"
#include "StateSearchW.h"
#include "StateConstructW.h"
#include "helpfun.h"
#include "constants.h"
#include "packing.h"
#include "iCBSearch.h"
#include "iCBConstruct.h"
#include "hpInput.h"
<purple>anaFilter.h</purple>
#include "anaFilter.h"
#include "syntFilter.h"
/*----------------------------------------------------------------*
* Initiation of encoder instance.
*---------------------------------------------------------------*/
okay so the setup function comes first ๐ง it looks at whether you want 30ms or 20ms frames and fills in every size and table pointer for that mode, then zeroes out the memory so nothing carries over from a previous call ๐ then iLBC_encode does the actual work, from the high-pass filter and LPC analysis all the way to the start of packing bytes into the three ULP classes ๐ฅ
short initEncode( /* (o) Number of bytes
encoded */
iLBC_Enc_Inst_t *iLBCenc_inst, /* (i/o) Encoder instance */
int mode /* (i) frame size mode */
){
iLBCenc_inst->mode = mode;
if (mode==30) {
iLBCenc_inst->blockl = BLOCKL_30MS;
iLBCenc_inst->nsub = NSUB_30MS;
iLBCenc_inst->nasub = NASUB_30MS;
iLBCenc_inst->lpc_n = LPC_N_30MS;
iLBCenc_inst->no_of_bytes = NO_OF_BYTES_30MS;
iLBCenc_inst->no_of_words = NO_OF_WORDS_30MS;
iLBCenc_inst->state_short_len=STATE_SHORT_LEN_30MS;
/* ULP init */
iLBCenc_inst->ULP_inst=&ULP_30msTbl;
}
else if (mode==20) {
iLBCenc_inst->blockl = BLOCKL_20MS;
iLBCenc_inst->nsub = NSUB_20MS;
iLBCenc_inst->nasub = NASUB_20MS;
iLBCenc_inst->lpc_n = LPC_N_20MS;
iLBCenc_inst->no_of_bytes = NO_OF_BYTES_20MS;
iLBCenc_inst->no_of_words = NO_OF_WORDS_20MS;
iLBCenc_inst->state_short_len=STATE_SHORT_LEN_20MS;
/* ULP init */
iLBCenc_inst->ULP_inst=&ULP_20msTbl;
}
else {
exit(2);
}
memset((*iLBCenc_inst).anaMem, 0,
LPC_FILTERORDER*sizeof(float));
memcpy((*iLBCenc_inst).lsfold, lsfmeanTbl,
LPC_FILTERORDER*sizeof(float));
memcpy((*iLBCenc_inst).lsfdeqold, lsfmeanTbl,
LPC_FILTERORDER*sizeof(float));
memset((*iLBCenc_inst).lpc_buffer, 0,
(LPC_LOOKBACK+BLOCKL_MAX)*sizeof(float));
memset((*iLBCenc_inst).hpimem, 0, 4*sizeof(float));
return (iLBCenc_inst->no_of_bytes);
}
/*----------------------------------------------------------------*
* main encoder function
*---------------------------------------------------------------*/
void iLBC_encode(
unsigned char *bytes, /* (o) encoded data bits iLBC */
float *block, /* (o) speech vector to
encode */
iLBC_Enc_Inst_t *iLBCenc_inst /* (i/o) the general encoder
state */
){
float data[BLOCKL_MAX];
float residual[BLOCKL_MAX], reverseResidual[BLOCKL_MAX];
int start, idxForMax, idxVec[STATE_LEN];
float reverseDecresidual[BLOCKL_MAX], mem[CB_MEML];
int n, k, meml_gotten, Nfor, Nback, i, pos;
int gain_index[CB_NSTAGES*NASUB_MAX],
extra_gain_index[CB_NSTAGES];
int cb_index[CB_NSTAGES*NASUB_MAX],extra_cb_index[CB_NSTAGES];
int lsf_i[LSF_NSPLIT*LPC_N_MAX];
unsigned char *pbytes;
int diff, start_pos, state_first;
float en1, en2;
int index, ulp, firstpart;
int subcount, subframe;
float weightState[LPC_FILTERORDER];
float syntdenum[NSUB_MAX*(LPC_FILTERORDER+1)];
float weightdenum[NSUB_MAX*(LPC_FILTERORDER+1)];
float decresidual[BLOCKL_MAX];
/* high pass filtering of input signal if such is not done
prior to calling this function */
hpInput(block, iLBCenc_inst->blockl,
data, (*iLBCenc_inst).hpimem);
/* otherwise simply copy */
/*memcpy(data,block,iLBCenc_inst->blockl*sizeof(float));*/
/* LPC of hp filtered input data */
LPCencode(syntdenum, weightdenum, lsf_i, data, iLBCenc_inst);
/* inverse filter to get residual */
for (n=0; n<iLBCenc_inst->nsub; n++) {
anaFilter(&data[n*SUBL], &syntdenum[n*(LPC_FILTERORDER+1)],
SUBL, &residual[n*SUBL], iLBCenc_inst->anaMem);
}
/* find state location */
start = FrameClassify(iLBCenc_inst, residual);
/* check if state should be in first or last part of the
two subframes */
diff = STATE_LEN - iLBCenc_inst->state_short_len;
en1 = 0;
index = (start-1)*SUBL;
for (i = 0; i < iLBCenc_inst->state_short_len; i++) {
en1 += residual[index+i]*residual[index+i];
}
en2 = 0;
index = (start-1)*SUBL+diff;
for (i = 0; i < iLBCenc_inst->state_short_len; i++) {
en2 += residual[index+i]*residual[index+i];
}
if (en1 > en2) {
state_first = 1;
start_pos = (start-1)*SUBL;
} else {
state_first = 0;
start_pos = (start-1)*SUBL + diff;
}
/* scalar quantization of state */
StateSearchW(iLBCenc_inst, &residual[start_pos],
&syntdenum[(start-1)*(LPC_FILTERORDER+1)],
&weightdenum[(start-1)*(LPC_FILTERORDER+1)], &idxForMax,
idxVec, iLBCenc_inst->state_short_len, state_first);
StateConstructW(idxForMax, idxVec,
&syntdenum[(start-1)*(LPC_FILTERORDER+1)],
&decresidual[start_pos], iLBCenc_inst->state_short_len);
/* predictive quantization in state */
if (state_first) { /* put adaptive part in the end */
/* setup memory */
memset(mem, 0,
(CB_MEML-iLBCenc_inst->state_short_len)*sizeof(float));
memcpy(mem+CB_MEML-iLBCenc_inst->state_short_len,
decresidual+start_pos,
iLBCenc_inst->state_short_len*sizeof(float));
memset(weightState, 0, LPC_FILTERORDER*sizeof(float));
/* encode sub-frames */
iCBSearch(iLBCenc_inst, extra_cb_index, extra_gain_index,
&residual[start_pos+iLBCenc_inst->state_short_len],
mem+CB_MEML-stMemLTbl,
stMemLTbl, diff, CB_NSTAGES,
&weightdenum[start*(LPC_FILTERORDER+1)],
weightState, 0);
/* construct decoded vector */
iCBConstruct(
&decresidual[start_pos+iLBCenc_inst->state_short_len],
extra_cb_index, extra_gain_index,
mem+CB_MEML-stMemLTbl,
stMemLTbl, diff, CB_NSTAGES);
}
else { /* put adaptive part in the beginning */
/* create reversed vectors for prediction */
for (k=0; k<diff; k++) {
reverseResidual[k] = residual[(start+1)*SUBL-1
-(k+iLBCenc_inst->state_short_len)];
}
/* setup memory */
meml_gotten = iLBCenc_inst->state_short_len;
for (k=0; k<meml_gotten; k++) {
mem[CB_MEML-1-k] = decresidual[start_pos + k];
}
memset(mem, 0, (CB_MEML-k)*sizeof(float));
memset(weightState, 0, LPC_FILTERORDER*sizeof(float));
/* encode sub-frames */
iCBSearch(iLBCenc_inst, extra_cb_index, extra_gain_index,
reverseResidual, mem+CB_MEML-stMemLTbl, stMemLTbl,
diff, CB_NSTAGES,
&weightdenum[(start-1)*(LPC_FILTERORDER+1)],
weightState, 0);
/* construct decoded vector */
iCBConstruct(reverseDecresidual, extra_cb_index,
extra_gain_index, mem+CB_MEML-stMemLTbl, stMemLTbl,
diff, CB_NSTAGES);
/* get decoded residual from reversed vector */
for (k=0; k<diff; k++) {
decresidual[start_pos-1-k] = reverseDecresidual[k];
}
}
/* counter for predicted sub-frames */
subcount=0;
/* forward prediction of sub-frames */
Nfor = iLBCenc_inst->nsub-start-1;
if ( Nfor > 0 ) {
/* setup memory */
memset(mem, 0, (CB_MEML-STATE_LEN)*sizeof(float));
memcpy(mem+CB_MEML-STATE_LEN, decresidual+(start-1)*SUBL,
STATE_LEN*sizeof(float));
memset(weightState, 0, LPC_FILTERORDER*sizeof(float));
/* loop over sub-frames to encode */
for (subframe=0; subframe<Nfor; subframe++) {
/* encode sub-frame */
iCBSearch(iLBCenc_inst, cb_index+subcount*CB_NSTAGES,
gain_index+subcount*CB_NSTAGES,
&residual[(start+1+subframe)*SUBL],
mem+CB_MEML-memLfTbl[subcount],
memLfTbl[subcount], SUBL, CB_NSTAGES,
&weightdenum[(start+1+subframe)*
(LPC_FILTERORDER+1)],
weightState, subcount+1);
/* construct decoded vector */
iCBConstruct(&decresidual[(start+1+subframe)*SUBL],
cb_index+subcount*CB_NSTAGES,
gain_index+subcount*CB_NSTAGES,
mem+CB_MEML-memLfTbl[subcount],
memLfTbl[subcount], SUBL, CB_NSTAGES);
/* update memory */
memcpy(mem, mem+SUBL, (CB_MEML-SUBL)*sizeof(float));
memcpy(mem+CB_MEML-SUBL,
&decresidual[(start+1+subframe)*SUBL],
SUBL*sizeof(float));
memset(weightState, 0, LPC_FILTERORDER*sizeof(float));
subcount++;
}
}
/* backward prediction of sub-frames */
Nback = start-1;
if ( Nback > 0 ) {
/* create reverse order vectors */
for (n=0; n<Nback; n++) {
for (k=0; k<SUBL; k++) {
reverseResidual[n*SUBL+k] =
residual[(start-1)*SUBL-1-n*SUBL-k];
reverseDecresidual[n*SUBL+k] =
decresidual[(start-1)*SUBL-1-n*SUBL-k];
}
}
/* setup memory */
meml_gotten = SUBL*(iLBCenc_inst->nsub+1-start);
if ( meml_gotten > CB_MEML ) {
meml_gotten=CB_MEML;
}
for (k=0; k<meml_gotten; k++) {
mem[CB_MEML-1-k] = decresidual[(start-1)*SUBL + k];
}
memset(mem, 0, (CB_MEML-k)*sizeof(float));
memset(weightState, 0, LPC_FILTERORDER*sizeof(float));
/* loop over sub-frames to encode */
for (subframe=0; subframe<Nback; subframe++) {
/* encode sub-frame */
iCBSearch(iLBCenc_inst, cb_index+subcount*CB_NSTAGES,
gain_index+subcount*CB_NSTAGES,
&reverseResidual[subframe*SUBL],
mem+CB_MEML-memLfTbl[subcount],
memLfTbl[subcount], SUBL, CB_NSTAGES,
&weightdenum[(start-2-subframe)*
(LPC_FILTERORDER+1)],
weightState, subcount+1);
/* construct decoded vector */
iCBConstruct(&reverseDecresidual[subframe*SUBL],
cb_index+subcount*CB_NSTAGES,
gain_index+subcount*CB_NSTAGES,
mem+CB_MEML-memLfTbl[subcount],
memLfTbl[subcount], SUBL, CB_NSTAGES);
/* update memory */
memcpy(mem, mem+SUBL, (CB_MEML-SUBL)*sizeof(float));
memcpy(mem+CB_MEML-SUBL,
&reverseDecresidual[subframe*SUBL],
SUBL*sizeof(float));
memset(weightState, 0, LPC_FILTERORDER*sizeof(float));
subcount++;
}
/* get decoded residual from reversed vector */
for (i=0; i<SUBL*Nback; i++) {
decresidual[SUBL*Nback - i - 1] =
reverseDecresidual[i];
}
}
/* end encoding part */
/* adjust index */
index_conv_enc(cb_index);
/* pack bytes */
pbytes=bytes;
pos=0;
/* loop over the 3 ULP classes */
for (ulp=0; ulp<3; ulp++) {
๐ซถ the packing part is next, and it's where all these indexes finally get squished into bytes, so stay tuned ๐
/* LSF */
for (k=0; k<LSF_NSPLIT*iLBCenc_inst->lpc_n; k++) {
packsplit(&lsf_i[k], &firstpart, &lsf_i[k],
iLBCenc_inst->ULP_inst->lsf_bits[k][ulp],
iLBCenc_inst->ULP_inst->lsf_bits[k][ulp]+
iLBCenc_inst->ULP_inst->lsf_bits[k][ulp+1]+
iLBCenc_inst->ULP_inst->lsf_bits[k][ulp+2]);
dopack( &pbytes, firstpart,
iLBCenc_inst->ULP_inst->lsf_bits[k][ulp], &pos);
}
/* Start block info */
packsplit(&start, &firstpart, &start,
iLBCenc_inst->ULP_inst->start_bits[ulp],
iLBCenc_inst->ULP_inst->start_bits[ulp]+
iLBCenc_inst->ULP_inst->start_bits[ulp+1]+
iLBCenc_inst->ULP_inst->start_bits[ulp+2]);
dopack( &pbytes, firstpart,
iLBCenc_inst->ULP_inst->start_bits[ulp], &pos);
packsplit(&state_first, &firstpart, &state_first,
iLBCenc_inst->ULP_inst->startfirst_bits[ulp],
iLBCenc_inst->ULP_inst->startfirst_bits[ulp]+
iLBCenc_inst->ULP_inst->startfirst_bits[ulp+1]+
iLBCenc_inst->ULP_inst->startfirst_bits[ulp+2]);
dopack( &pbytes, firstpart,
iLBCenc_inst->ULP_inst->startfirst_bits[ulp], &pos);
packsplit(&idxForMax, &firstpart, &idxForMax,
iLBCenc_inst->ULP_inst->scale_bits[ulp],
iLBCenc_inst->ULP_inst->scale_bits[ulp]+
iLBCenc_inst->ULP_inst->scale_bits[ulp+1]+
iLBCenc_inst->ULP_inst->scale_bits[ulp+2]);
dopack( &pbytes, firstpart,
iLBCenc_inst->ULP_inst->scale_bits[ulp], &pos);
for (k=0; k<iLBCenc_inst->state_short_len; k++) {
packsplit(idxVec+k, &firstpart, idxVec+k,
iLBCenc_inst->ULP_inst->state_bits[ulp],
iLBCenc_inst->ULP_inst->state_bits[ulp]+
iLBCenc_inst->ULP_inst->state_bits[ulp+1]+
iLBCenc_inst->ULP_inst->state_bits[ulp+2]);
dopack( &pbytes, firstpart,
iLBCenc_inst->ULP_inst->state_bits[ulp], &pos);
}
/* 23/22 (20ms/30ms) sample block */
for (k=0;k<CB_NSTAGES;k++) {
packsplit(extra_cb_index+k, &firstpart,
extra_cb_index+k,
iLBCenc_inst->ULP_inst->extra_cb_index[k][ulp],
iLBCenc_inst->ULP_inst->extra_cb_index[k][ulp]+
iLBCenc_inst->ULP_inst->extra_cb_index[k][ulp+1]+
iLBCenc_inst->ULP_inst->extra_cb_index[k][ulp+2]);
dopack( &pbytes, firstpart,
iLBCenc_inst->ULP_inst->extra_cb_index[k][ulp],
&pos);
}
for (k=0;k<CB_NSTAGES;k++) {
packsplit(extra_gain_index+k, &firstpart,
extra_gain_index+k,
iLBCenc_inst->ULP_inst->extra_cb_gain[k][ulp],
iLBCenc_inst->ULP_inst->extra_cb_gain[k][ulp]+
iLBCenc_inst->ULP_inst->extra_cb_gain[k][ulp+1]+
iLBCenc_inst->ULP_inst->extra_cb_gain[k][ulp+2]);
dopack( &pbytes, firstpart,
iLBCenc_inst->ULP_inst->extra_cb_gain[k][ulp],
&pos);
}
/* The two/four (20ms/30ms) 40 sample sub-blocks */
for (i=0; i<iLBCenc_inst->nasub; i++) {
for (k=0; k<CB_NSTAGES; k++) {
packsplit(cb_index+i*CB_NSTAGES+k, &firstpart,
cb_index+i*CB_NSTAGES+k,
iLBCenc_inst->ULP_inst->cb_index[i][k][ulp],
iLBCenc_inst->ULP_inst->cb_index[i][k][ulp]+
iLBCenc_inst->ULP_inst->cb_index[i][k][ulp+1]+
iLBCenc_inst->ULP_inst->cb_index[i][k][ulp+2]);
dopack( &pbytes, firstpart,
iLBCenc_inst->ULP_inst->cb_index[i][k][ulp],
&pos);
}
}
for (i=0; i<iLBCenc_inst->nasub; i++) {
for (k=0; k<CB_NSTAGES; k++) {
packsplit(gain_index+i*CB_NSTAGES+k, &firstpart,
gain_index+i*CB_NSTAGES+k,
iLBCenc_inst->ULP_inst->cb_gain[i][k][ulp],
iLBCenc_inst->ULP_inst->cb_gain[i][k][ulp]+
iLBCenc_inst->ULP_inst->cb_gain[i][k][ulp+1]+
iLBCenc_inst->ULP_inst->cb_gain[i][k][ulp+2]);
dopack( &pbytes, firstpart,
iLBCenc_inst->ULP_inst->cb_gain[i][k][ulp],
&pos);
}
}
}
/* set the last bit to zero (otherwise the decoder
will treat it as a lost frame) */
dopack( &pbytes, 0, 1, &pos);
}
A.4. iLBC_decode.h
Okay bestie, this is the header file for the decoder ๐ It's basically the front door of the decoder: it declares the two functions everyone else talks to, plus the instance type they both pass around. Super clean, super iconic ๐
/******************************************************************
iLBC Speech Coder ANSI-C Source Code
iLBC_decode.h
Copyright (C) The Internet Society (2004).
All Rights Reserved.
******************************************************************/
#ifndef __iLBC_ILBCDECODE_H
#define __iLBC_ILBCDECODE_H
#include "iLBC_define.h"
short initDecode( /* (o) Number of decoded
samples */
iLBC_Dec_Inst_t *iLBCdec_inst, /* (i/o) Decoder instance */
int mode, /* (i) frame size mode */
int use_enhancer /* (i) 1 to use enhancer
0 to run without
enhancer */
);
void iLBC_decode(
float *decblock, /* (o) decoded signal block */
unsigned char *bytes, /* (i) encoded signal bits */
iLBC_Dec_Inst_t *iLBCdec_inst, /* (i/o) the decoder state
structure */
int mode /* (i) 0: bad packet, PLC,
1: normal */
);
#endif
A.5. iLBC_decode.c
Now the big one, the actual decoder source ๐ It starts with the setup function, initDecode, which picks the right settings for 20ms or 30ms frames and then resets all the state so everything starts fresh โจ Then the Decode helper gets going on the start state, and this excerpt stops right where that part kicks off ๐ฆ
/******************************************************************
iLBC Speech Coder ANSI-C Source Code
iLBC_decode.c
Copyright (C) The Internet Society (2004).
All Rights Reserved.
******************************************************************/
#include <math.h>
#include <stdlib.h>
#include "iLBC_define.h"
#include "StateConstructW.h"
#include "LPCdecode.h"
#include "iCBConstruct.h"
#include "doCPLC.h"
#include "helpfun.h"
#include "constants.h"
#include "packing.h"
#include "string.h"
#include "enhancer.h"
#include "hpOutput.h"
#include "syntFilter.h"
/*----------------------------------------------------------------*
* Initiation of decoder instance.
*---------------------------------------------------------------*/
short initDecode( /* (o) Number of decoded
samples */
iLBC_Dec_Inst_t *iLBCdec_inst, /* (i/o) Decoder instance */
int mode, /* (i) frame size mode */
int use_enhancer /* (i) 1 to use enhancer
0 to run without
enhancer */
){
int i;
iLBCdec_inst->mode = mode;
if (mode==30) {
iLBCdec_inst->blockl = BLOCKL_30MS;
iLBCdec_inst->nsub = NSUB_30MS;
iLBCdec_inst->nasub = NASUB_30MS;
iLBCdec_inst->lpc_n = LPC_N_30MS;
iLBCdec_inst->no_of_bytes = NO_OF_BYTES_30MS;
iLBCdec_inst->no_of_words = NO_OF_WORDS_30MS;
iLBCdec_inst->state_short_len=STATE_SHORT_LEN_30MS;
/* ULP init */
iLBCdec_inst->ULP_inst=&ULP_30msTbl;
}
else if (mode==20) {
iLBCdec_inst->blockl = BLOCKL_20MS;
iLBCdec_inst->nsub = NSUB_20MS;
iLBCdec_inst->nasub = NASUB_20MS;
iLBCdec_inst->lpc_n = LPC_N_20MS;
iLBCdec_inst->no_of_bytes = NO_OF_BYTES_20MS;
iLBCdec_inst->no_of_words = NO_OF_WORDS_20MS;
iLBCdec_inst->state_short_len=STATE_SHORT_LEN_20MS;
/* ULP init */
iLBCdec_inst->ULP_inst=&ULP_20msTbl;
}
else {
exit(2);
}
memset(iLBCdec_inst->syntMem, 0,
LPC_FILTERORDER*sizeof(float));
memcpy((*iLBCdec_inst).lsfdeqold, lsfmeanTbl,
LPC_FILTERORDER*sizeof(float));
memset(iLBCdec_inst->old_syntdenum, 0,
((LPC_FILTERORDER + 1)*NSUB_MAX)*sizeof(float));
for (i=0; i<NSUB_MAX; i++)
iLBCdec_inst->old_syntdenum[i*(LPC_FILTERORDER+1)]=1.0;
iLBCdec_inst->last_lag = 20;
iLBCdec_inst->prevLag = 120;
iLBCdec_inst->per = 0.0;
iLBCdec_inst->consPLICount = 0;
iLBCdec_inst->prevPLI = 0;
iLBCdec_inst->prevLpc[0] = 1.0;
memset(iLBCdec_inst->prevLpc+1,0,
LPC_FILTERORDER*sizeof(float));
memset(iLBCdec_inst->prevResidual, 0, BLOCKL_MAX*sizeof(float));
iLBCdec_inst->seed=777;
memset(iLBCdec_inst->hpomem, 0, 4*sizeof(float));
iLBCdec_inst->use_enhancer = use_enhancer;
memset(iLBCdec_inst->enh_buf, 0, ENH_BUFL*sizeof(float));
for (i=0;i<ENH_NBLOCKS_TOT;i++)
iLBCdec_inst->enh_period[i]=(float)40.0;
iLBCdec_inst->prev_enh_pl = 0;
return (iLBCdec_inst->blockl);
}
/*----------------------------------------------------------------*
* frame residual decoder function (subrutine to iLBC_decode)
*---------------------------------------------------------------*/
void Decode(
iLBC_Dec_Inst_t *iLBCdec_inst, /* (i/o) the decoder state
structure */
float *decresidual, /* (o) decoded residual frame */
int start, /* (i) location of start
state */
int idxForMax, /* (i) codebook index for the
maximum value */
int *idxVec, /* (i) codebook indexes for the
samples in the start
state */
float *syntdenum, /* (i) the decoded synthesis
filter coefficients */
int *cb_index, /* (i) the indexes for the
adaptive codebook */
int *gain_index, /* (i) the indexes for the
corresponding gains */
int *extra_cb_index, /* (i) the indexes for the
adaptive codebook part
of start state */
int *extra_gain_index, /* (i) the indexes for the
corresponding gains */
int state_first /* (i) 1 if non adaptive part
of start state comes
first 0 if that part
comes last */
){
float reverseDecresidual[BLOCKL_MAX], mem[CB_MEML];
int k, meml_gotten, Nfor, Nback, i;
int diff, start_pos;
int subcount, subframe;
diff = STATE_LEN - iLBCdec_inst->state_short_len;
if (state_first == 1) {
start_pos = (start-1)*SUBL;
} else {
start_pos = (start-1)*SUBL + diff;
}
/* decode scalar part of start state */
StateConstructW(idxForMax, idxVec, &syntdenum[(start-1)*(LPC_FILTERORDER+1)], &decresidual[start_pos], iLBCdec_inst->state_short_len);
if (state_first) { /* put adaptive part in the end */
/* setup memory */
memset(mem, 0, (CB_MEML-iLBCdec_inst->state_short_len)*sizeof(float)); memcpy(mem+CB_MEML-iLBCdec_inst->state_short_len, decresidual+start_pos, iLBCdec_inst->state_short_len*sizeof(float));
/* construct decoded vector */
iCBConstruct( &decresidual[start_pos+iLBCdec_inst->state_short_len], extra_cb_index, extra_gain_index, mem+CB_MEML-stMemLTbl, stMemLTbl, diff, CB_NSTAGES);
} else {/* put adaptive part in the beginning */
/* create reversed vectors for prediction */
for (k=0; k<diff; k++) { reverseDecresidual[k] = decresidual[(start+1)*SUBL-1- (k+iLBCdec_inst->state_short_len)]; }
/* setup memory */
meml_gotten = iLBCdec_inst->state_short_len; for (k=0; k<meml_gotten; k++){ mem[CB_MEML-1-k] = decresidual[start_pos + k];
} memset(mem, 0, (CB_MEML-k)*sizeof(float));
/* construct decoded vector */
iCBConstruct(reverseDecresidual, extra_cb_index, extra_gain_index, mem+CB_MEML-stMemLTbl, stMemLTbl, diff, CB_NSTAGES);
/* get decoded residual from reversed vector */
for (k=0; k<diff; k++) { decresidual[start_pos-1-k] = reverseDecresidual[k]; } }
/* counter for predicted sub-frames */
subcount=0;
/* forward prediction of sub-frames */
Nfor = iLBCdec_inst->nsub-start-1;
if ( Nfor > 0 ){
/* setup memory */
memset(mem, 0, (CB_MEML-STATE_LEN)*sizeof(float)); memcpy(mem+CB_MEML-STATE_LEN, decresidual+(start-1)*SUBL, STATE_LEN*sizeof(float));
/* loop over sub-frames to encode */
for (subframe=0; subframe<Nfor; subframe++) {
/* construct decoded vector */
iCBConstruct(&decresidual[(start+1+subframe)*SUBL], cb_index+subcount*CB_NSTAGES, gain_index+subcount*CB_NSTAGES, mem+CB_MEML-memLfTbl[subcount], memLfTbl[subcount], SUBL, CB_NSTAGES);
/* update memory */
memcpy(mem, mem+SUBL, (CB_MEML-SUBL)*sizeof(float)); memcpy(mem+CB_MEML-SUBL,
&decresidual[(start+1+subframe)*SUBL], SUBL*sizeof(float));
subcount++;
}
}
/* backward prediction of sub-frames */
Nback = start-1;
if ( Nback > 0 ) {
/* setup memory */
meml_gotten = SUBL*(iLBCdec_inst->nsub+1-start);
if ( meml_gotten > CB_MEML ) { meml_gotten=CB_MEML; } for (k=0; k<meml_gotten; k++) { mem[CB_MEML-1-k] = decresidual[(start-1)*SUBL + k]; } memset(mem, 0, (CB_MEML-k)*sizeof(float));
/* loop over subframes to decode */
for (subframe=0; subframe<Nback; subframe++) {
/* construct decoded vector */
iCBConstruct(&reverseDecresidual[subframe*SUBL], cb_index+subcount*CB_NSTAGES, gain_index+subcount*CB_NSTAGES, mem+CB_MEML-memLfTbl[subcount], memLfTbl[subcount], SUBL, CB_NSTAGES);
/* update memory */
memcpy(mem, mem+SUBL, (CB_MEML-SUBL)*sizeof(float)); memcpy(mem+CB_MEML-SUBL, &reverseDecresidual[subframe*SUBL], SUBL*sizeof(float));
subcount++; } /* get decoded residual from reversed vector */
for (i=0; i<SUBL*Nback; i++) decresidual[SUBL*Nback - i - 1] = reverseDecresidual[i]; } }
/----------------------------------------------------------------
- main decoder function ---------------------------------------------------------------/
void iLBC_decode( float decblock, / (o) decoded signal block / unsigned char *bytes, / (i) encoded signal bits / iLBC_Dec_Inst_t *iLBCdec_inst, / (i/o) the decoder state structure / int mode / (i) 0: bad packet, PLC, 1: normal / ){ float data[BLOCKL_MAX]; float lsfdeq[LPC_FILTERORDERLPC_N_MAX]; float PLCresidual[BLOCKL_MAX], PLClpc[LPC_FILTERORDER + 1]; float zeros[BLOCKL_MAX], one[LPC_FILTERORDER + 1]; int k, i, start, idxForMax, pos, lastpart, ulp; int lag, ilag; float cc, maxcc; int idxVec[STATE_LEN]; int check; int gain_index[NASUB_MAX*CB_NSTAGES], extra_gain_index[CB_NSTAGES]; int cb_index[CB_NSTAGES*NASUB_MAX], extra_cb_index[CB_NSTAGES]; int lsf_i[LSF_NSPLIT*LPC_N_MAX]; int state_first; int last_bit; unsigned char pbytes; float weightdenum[(LPC_FILTERORDER + 1)NSUB_MAX]; int order_plus_one; float syntdenum[NSUB_MAX*(LPC_FILTERORDER+1)]; float decresidual[BLOCKL_MAX];
if (mode>0) { /* the data are good */
/* decode data */
pbytes=bytes; pos=0; /* Set everything to zero before decoding */
for (k=0; k<LSF_NSPLIT*LPC_N_MAX; k++) { lsf_i[k]=0; } start=0; state_first=0; idxForMax=0; for (k=0; k<iLBCdec_inst->state_short_len; k++) { idxVec[k]=0; } for (k=0; k<CB_NSTAGES; k++) { extra_cb_index[k]=0; } for (k=0; k<CB_NSTAGES; k++) { extra_gain_index[k]=0; } for (i=0; i<iLBCdec_inst->nasub; i++) { for (k=0; k<CB_NSTAGES; k++) { cb_index[i*CB_NSTAGES+k]=0; } } for (i=0; i<iLBCdec_inst->nasub; i++) { for (k=0; k<CB_NSTAGES; k++) { gain_index[i*CB_NSTAGES+k]=0; } }
/* loop over ULP classes */
for (ulp=0; ulp<3; ulp++) {
/* LSF */ for (k=0; k<LSF_NSPLIT*iLBCdec_inst->lpc_n; k++){ unpack( &pbytes, &lastpart, iLBCdec_inst->ULP_inst->lsf_bits[k][ulp], &pos); packcombine(&lsf_i[k], lastpart, iLBCdec_inst->ULP_inst->lsf_bits[k][ulp]); }
/* Start block info */
unpack( &pbytes, &lastpart, iLBCdec_inst->ULP_inst->start_bits[ulp], &pos); packcombine(&start, lastpart, iLBCdec_inst->ULP_inst->start_bits[ulp]);
unpack( &pbytes, &lastpart,
iLBCdec_inst->ULP_inst->startfirst_bits[ulp], &pos); packcombine(&state_first, lastpart, iLBCdec_inst->ULP_inst->startfirst_bits[ulp]);
unpack( &pbytes, &lastpart, iLBCdec_inst->ULP_inst->scale_bits[ulp], &pos); packcombine(&idxForMax, lastpart, iLBCdec_inst->ULP_inst->scale_bits[ulp]);
for (k=0; k<iLBCdec_inst->state_short_len; k++) { unpack( &pbytes, &lastpart, iLBCdec_inst->ULP_inst->state_bits[ulp], &pos); packcombine(idxVec+k, lastpart, iLBCdec_inst->ULP_inst->state_bits[ulp]); }
/* 23/22 (20ms/30ms) sample block */
for (k=0; k<CB_NSTAGES; k++) { unpack( &pbytes, &lastpart, iLBCdec_inst->ULP_inst->extra_cb_index[k][ulp], &pos); packcombine(extra_cb_index+k, lastpart, iLBCdec_inst->ULP_inst->extra_cb_index[k][ulp]); } for (k=0; k<CB_NSTAGES; k++) { unpack( &pbytes, &lastpart, iLBCdec_inst->ULP_inst->extra_cb_gain[k][ulp], &pos); packcombine(extra_gain_index+k, lastpart, iLBCdec_inst->ULP_inst->extra_cb_gain[k][ulp]); }
/* The two/four (20ms/30ms) 40 sample sub-blocks */
for (i=0; i<iLBCdec_inst->nasub; i++) { for (k=0; k<CB_NSTAGES; k++) { unpack( &pbytes, &lastpart, iLBCdec_inst->ULP_inst->cb_index[i][k][ulp], &pos); packcombine(cb_index+i*CB_NSTAGES+k, lastpart, iLBCdec_inst->ULP_inst->cb_index[i][k][ulp]); } }
for (i=0; i<iLBCdec_inst->nasub; i++) { for (k=0; k<CB_NSTAGES; k++) { unpack( &pbytes, &lastpart, iLBCdec_inst->ULP_inst->cb_gain[i][k][ulp], &pos); packcombine(gain_index+i*CB_NSTAGES+k, lastpart, iLBCdec_inst->ULP_inst->cb_gain[i][k][ulp]); } } } /* Extract last bit. If it is 1 this indicates an empty/lost frame */ unpack( &pbytes, &last_bit, 1, &pos);
/* Check for bit errors or empty/lost frames */ if (start<1) mode = 0; if (iLBCdec_inst->mode==20 && start>3) mode = 0; if (iLBCdec_inst->mode==30 && start>5) mode = 0; if (last_bit==1) mode = 0;
if (mode==1) { /* No bit errors was detected, continue decoding */
/* adjust index */ index_conv_dec(cb_index);
/* decode the lsf */
SimplelsfDEQ(lsfdeq, lsf_i, iLBCdec_inst->lpc_n); check=LSF_check(lsfdeq, LPC_FILTERORDER, iLBCdec_inst->lpc_n); DecoderInterpolateLSF(syntdenum, weightdenum, lsfdeq, LPC_FILTERORDER, iLBCdec_inst);
Decode(iLBCdec_inst, decresidual, start, idxForMax, idxVec, syntdenum, cb_index, gain_index, extra_cb_index, extra_gain_index, state_first);
/* preparing the plc for a future loss! */
doThePLC(PLCresidual, PLClpc, 0, decresidual, syntdenum + (LPC_FILTERORDER + 1)*(iLBCdec_inst->nsub - 1), (*iLBCdec_inst).last_lag, iLBCdec_inst);
memcpy(decresidual, PLCresidual, iLBCdec_inst->blockl*sizeof(float)); }
}
if (mode == 0) { /* the data is bad (either a PLC call
- was made or a severe bit error was detected) */
/* packet loss conceal */
memset(zeros, 0, BLOCKL_MAX*sizeof(float));
one[0] = 1; memset(one+1, 0, LPC_FILTERORDER*sizeof(float));
start=0;
doThePLC(PLCresidual, PLClpc, 1, zeros, one, (*iLBCdec_inst).last_lag, iLBCdec_inst); memcpy(decresidual, PLCresidual, iLBCdec_inst->blockl*sizeof(float));
Okay, the decoder is wrapping up here, bestie ๐ Let's keep going with the header files ๐
order_plus_one = LPC_FILTERORDER + 1;
for (i = 0; i < iLBCdec_inst->nsub; i++) {
memcpy(syntdenum+(i*order_plus_one), PLClpc,
order_plus_one*sizeof(float));
}
}
if (iLBCdec_inst->use_enhancer == 1) {
/* post filtering */
iLBCdec_inst->last_lag =
enhancerInterface(data, decresidual, iLBCdec_inst);
/* synthesis filtering */
if (iLBCdec_inst->mode==20) {
/* Enhancer has 40 samples delay */
i=0;
syntFilter(data + i*SUBL,
iLBCdec_inst->old_syntdenum +
(i+iLBCdec_inst->nsub-1)*(LPC_FILTERORDER+1),
SUBL, iLBCdec_inst->syntMem);
for (i=1; i < iLBCdec_inst->nsub; i++) {
syntFilter(data + i*SUBL,
syntdenum + (i-1)*(LPC_FILTERORDER+1),
SUBL, iLBCdec_inst->syntMem);
}
} else if (iLBCdec_inst->mode==30) {
/* Enhancer has 80 samples delay */
for (i=0; i < 2; i++) {
syntFilter(data + i*SUBL,
iLBCdec_inst->old_syntdenum +
(i+iLBCdec_inst->nsub-2)*(LPC_FILTERORDER+1),
SUBL, iLBCdec_inst->syntMem);
}
for (i=2; i < iLBCdec_inst->nsub; i++) {
syntFilter(data + i*SUBL,
syntdenum + (i-2)*(LPC_FILTERORDER+1), SUBL,
iLBCdec_inst->syntMem);
}
}
} else {
/* Find last lag */
lag = 20;
maxcc = xCorrCoef(&decresidual[BLOCKL_MAX-ENH_BLOCKL],
&decresidual[BLOCKL_MAX-ENH_BLOCKL-lag], ENH_BLOCKL);
for (ilag=21; ilag<120; ilag++) {
cc = xCorrCoef(&decresidual[BLOCKL_MAX-ENH_BLOCKL],
&decresidual[BLOCKL_MAX-ENH_BLOCKL-ilag],
ENH_BLOCKL);
if (cc > maxcc) {
maxcc = cc;
lag = ilag;
}
}
iLBCdec_inst->last_lag = lag;
/* copy data and run synthesis filter */
memcpy(data, decresidual,
iLBCdec_inst->blockl*sizeof(float));
for (i=0; i < iLBCdec_inst->nsub; i++) {
syntFilter(data + i*SUBL,
syntdenum + i*(LPC_FILTERORDER+1), SUBL,
iLBCdec_inst->syntMem);
}
}
/* high pass filtering on output if desired, otherwise
copy to out */
hpOutput(data, iLBCdec_inst->blockl,
decblock,iLBCdec_inst->hpomem);
/* memcpy(decblock,data,iLBCdec_inst->blockl*sizeof(float));*/
memcpy(iLBCdec_inst->old_syntdenum, syntdenum,
iLBCdec_inst->nsub*(LPC_FILTERORDER+1)*sizeof(float));
iLBCdec_inst->prev_enh_pl=0;
if (mode==0) { /* PLC was used */
iLBCdec_inst->prev_enh_pl=1;
}
}
A.6. iLBC_define.h โจ
/******************************************************************
iLBC Speech Coder ANSI-C Source Code
iLBC_define.h
Copyright (C) The Internet Society (2004).
All Rights Reserved.
******************************************************************/
#include <string.h>
#ifndef __iLBC_ILBCDEFINE_H
#define __iLBC_ILBCDEFINE_H
/* general codec settings */
#define FS (float)8000.0
#define BLOCKL_20MS 160
#define BLOCKL_30MS 240
#define BLOCKL_MAX 240
#define NSUB_20MS 4
#define NSUB_30MS 6
#define NSUB_MAX 6
#define NASUB_20MS 2
#define NASUB_30MS 4
#define NASUB_MAX 4
#define SUBL 40
#define STATE_LEN 80
#define STATE_SHORT_LEN_30MS 58
#define STATE_SHORT_LEN_20MS 57
/* LPC settings */
#define LPC_FILTERORDER 10
#define LPC_CHIRP_SYNTDENUM (float)0.9025
#define LPC_CHIRP_WEIGHTDENUM (float)0.4222
#define LPC_LOOKBACK 60
#define LPC_N_20MS 1
#define LPC_N_30MS 2
#define LPC_N_MAX 2
#define LPC_ASYMDIFF 20
#define LPC_BW (float)60.0
#define LPC_WN (float)1.0001
#define LSF_NSPLIT 3
#define LSF_NUMBER_OF_STEPS 4
#define LPC_HALFORDER (LPC_FILTERORDER/2)
/* cb settings */
#define CB_NSTAGES 3
#define CB_EXPAND 2
#define CB_MEML 147
#define CB_FILTERLEN 2*4
#define CB_HALFFILTERLEN 4
#define CB_RESRANGE 34
#define CB_MAXGAIN (float)1.3
/* enhancer */
#define ENH_BLOCKL 80 /* block length */
#define ENH_BLOCKL_HALF (ENH_BLOCKL/2)
#define ENH_HL 3 /* 2*ENH_HL+1 is number blocks
in said second sequence */
#define ENH_SLOP 2 /* max difference estimated and
correct pitch period */
#define ENH_PLOCSL 20 /* pitch-estimates and pitch-
locations buffer length */
#define ENH_OVERHANG 2
#define ENH_UPS0 4 /* upsampling rate */
#define ENH_FL0 3 /* 2*FLO+1 is the length of
each filter */
#define ENH_VECTL (ENH_BLOCKL+2*ENH_FL0)
#define ENH_CORRDIM (2*ENH_SLOP+1)
#define ENH_NBLOCKS (BLOCKL_MAX/ENH_BLOCKL)
#define ENH_NBLOCKS_EXTRA 5
#define ENH_NBLOCKS_TOT 8 /* ENH_NBLOCKS +
ENH_NBLOCKS_EXTRA */
#define ENH_BUFL (ENH_NBLOCKS_TOT)*ENH_BLOCKL
#define ENH_ALPHA0 (float)0.05
/* Down sampling */
#define FILTERORDER_DS 7
#define DELAY_DS 3
#define FACTOR_DS 2
/* bit stream defs */
#define NO_OF_BYTES_20MS 38
#define NO_OF_BYTES_30MS 50
#define NO_OF_WORDS_20MS 19
#define NO_OF_WORDS_30MS 25
#define STATE_BITS 3
#define BYTE_LEN 8
#define ULP_CLASSES 3
/* help parameters */
#define FLOAT_MAX (float)1.0e37
#define EPS (float)2.220446049250313e-016
#define PI (float)3.14159265358979323846
#define MIN_SAMPLE -32768
#define MAX_SAMPLE 32767
#define TWO_PI (float)6.283185307
#define PI2 (float)0.159154943
/* type definition encoder instance */
typedef struct iLBC_ULP_Inst_t_ {
int lsf_bits[6][ULP_CLASSES+2];
int start_bits[ULP_CLASSES+2];
int startfirst_bits[ULP_CLASSES+2];
int scale_bits[ULP_CLASSES+2];
int state_bits[ULP_CLASSES+2];
int extra_cb_index[CB_NSTAGES][ULP_CLASSES+2];
int extra_cb_gain[CB_NSTAGES][ULP_CLASSES+2];
int cb_index[NSUB_MAX][CB_NSTAGES][ULP_CLASSES+2];
int cb_gain[NSUB_MAX][CB_NSTAGES][ULP_CLASSES+2];
} iLBC_ULP_Inst_t;
/* type definition encoder instance */
typedef struct iLBC_Enc_Inst_t_ {
/* flag for frame size mode */
int mode;
/* basic parameters for different frame sizes */
int blockl;
int nsub;
int nasub;
int no_of_bytes, no_of_words;
int lpc_n;
int state_short_len;
const iLBC_ULP_Inst_t *ULP_inst;
/* analysis filter state */
float anaMem[LPC_FILTERORDER];
/* old lsf parameters for interpolation */
float lsfold[LPC_FILTERORDER];
float lsfdeqold[LPC_FILTERORDER];
/* signal buffer for LP analysis */
float lpc_buffer[LPC_LOOKBACK + BLOCKL_MAX];
/* state of input HP filter */
float hpimem[4];
} iLBC_Enc_Inst_t;
/* type definition decoder instance */
typedef struct iLBC_Dec_Inst_t_ {
/* flag for frame size mode */
int mode;
/* basic parameters for different frame sizes */
int blockl;
int nsub;
int nasub;
int no_of_bytes, no_of_words;
int lpc_n;
int state_short_len;
const iLBC_ULP_Inst_t *ULP_inst;
/* synthesis filter state */
float syntMem[LPC_FILTERORDER];
/* old LSF for interpolation */
float lsfdeqold[LPC_FILTERORDER];
/* pitch lag estimated in enhancer and used in PLC */
int last_lag;
/* PLC state information */
int prevLag, consPLICount, prevPLI, prev_enh_pl;
float prevLpc[LPC_FILTERORDER+1];
float prevResidual[NSUB_MAX*SUBL];
float per;
unsigned long seed;
/* previous synthesis filter parameters */
float old_syntdenum[(LPC_FILTERORDER + 1)*NSUB_MAX];
/* state of output HP filter */
float hpomem[4];
/* enhancer state information */
int use_enhancer;
float enh_buf[ENH_BUFL];
float enh_period[ENH_NBLOCKS_TOT];
} iLBC_Dec_Inst_t;
#endif
A.7. constants.h โจ
/******************************************************************
iLBC Speech Coder ANSI-C Source Code
constants.h
Copyright (C) The Internet Society (2004).
All Rights Reserved.
******************************************************************/
#ifndef __iLBC_CONSTANTS_H
#define __iLBC_CONSTANTS_H
#include "iLBC_define.h"
/* ULP bit allocation */
extern const iLBC_ULP_Inst_t ULP_20msTbl;
extern const iLBC_ULP_Inst_t ULP_30msTbl;
/* high pass filters */
extern float hpi_zero_coefsTbl[];
extern float hpi_pole_coefsTbl[];
extern float hpo_zero_coefsTbl[];
extern float hpo_pole_coefsTbl[];
/* low pass filters */
extern float lpFilt_coefsTbl[];
/* LPC analysis and quantization */
extern float lpc_winTbl[];
extern float lpc_asymwinTbl[];
extern float lpc_lagwinTbl[];
extern float lsfCbTbl[];
extern float lsfmeanTbl[];
extern int dim_lsfCbTbl[];
extern int size_lsfCbTbl[];
extern float lsf_weightTbl_30ms[];
extern float lsf_weightTbl_20ms[];
/* state quantization tables */
extern float state_sq3Tbl[];
extern float state_frgqTbl[];
/* gain quantization tables */
extern float gain_sq3Tbl[];
extern float gain_sq4Tbl[];
extern float gain_sq5Tbl[];
/* adaptive codebook definitions */
extern int search_rangeTbl[5][CB_NSTAGES];
extern int memLfTbl[];
extern int stMemLTbl;
extern float cbfiltersTbl[CB_FILTERLEN];
/* enhancer definitions */
extern float polyphaserTbl[];
extern float enh_plocsTbl[];
#endif
A.8. constants.c โจ
/******************************************************************
iLBC Speech Coder ANSI-C Source Code
constants.c
Copyright (C) The Internet Society (2004).
All Rights Reserved.
******************************************************************/
#include "iLBC_define.h"
/* ULP bit allocation */
/* 20 ms frame */
okay so this part is basically one giant pile of lookup tables, and honestly they're giving spreadsheet energy ๐ง๐ the ULP tables come first, and then the filter coefficients, the state and gain quantization tables, the CB tables, the enhancer stuff, and the LPC bits. no notes, just the numbers, exactly as they are ๐
const iLBC_ULP_Inst_t ULP_20msTbl = {
/* LSF */
{ {6,0,0,0,0}, {7,0,0,0,0}, {7,0,0,0,0},
{0,0,0,0,0}, {0,0,0,0,0}, {0,0,0,0,0}},
/* Start state location, gain and samples */
{2,0,0,0,0},
{1,0,0,0,0},
{6,0,0,0,0},
{0,1,2,0,0},
/* extra CB index and extra CB gain */
{{6,0,1,0,0}, {0,0,7,0,0}, {0,0,7,0,0}},
{{2,0,3,0,0}, {1,1,2,0,0}, {0,0,3,0,0}},
/* CB index and CB gain */
{ {{7,0,1,0,0}, {0,0,7,0,0}, {0,0,7,0,0}},
{{0,0,8,0,0}, {0,0,8,0,0}, {0,0,8,0,0}},
{{0,0,0,0,0}, {0,0,0,0,0}, {0,0,0,0,0}},
{{0,0,0,0,0}, {0,0,0,0,0}, {0,0,0,0,0}}},
{ {{1,2,2,0,0}, {1,1,2,0,0}, {0,0,3,0,0}},
{{1,1,3,0,0}, {0,2,2,0,0}, {0,0,3,0,0}},
{{0,0,0,0,0}, {0,0,0,0,0}, {0,0,0,0,0}},
{{0,0,0,0,0}, {0,0,0,0,0}, {0,0,0,0,0}}}
};
/* 30 ms frame */
const iLBC_ULP_Inst_t ULP_30msTbl = {
/* LSF */
{ {6,0,0,0,0}, {7,0,0,0,0}, {7,0,0,0,0},
{6,0,0,0,0}, {7,0,0,0,0}, {7,0,0,0,0}},
/* Start state location, gain and samples */
{3,0,0,0,0},
{1,0,0,0,0},
{6,0,0,0,0},
{0,1,2,0,0},
/* extra CB index and extra CB gain */
{{4,2,1,0,0}, {0,0,7,0,0}, {0,0,7,0,0}},
{{1,1,3,0,0}, {1,1,2,0,0}, {0,0,3,0,0}},
/* CB index and CB gain */
{ {{6,1,1,0,0}, {0,0,7,0,0}, {0,0,7,0,0}},
{{0,7,1,0,0}, {0,0,8,0,0}, {0,0,8,0,0}},
{{0,7,1,0,0}, {0,0,8,0,0}, {0,0,8,0,0}},
{{0,7,1,0,0}, {0,0,8,0,0}, {0,0,8,0,0}}},
{ {{1,2,2,0,0}, {1,2,1,0,0}, {0,0,3,0,0}},
{{0,2,3,0,0}, {0,2,2,0,0}, {0,0,3,0,0}},
{{0,1,4,0,0}, {0,1,3,0,0}, {0,0,3,0,0}},
{{0,1,4,0,0}, {0,1,3,0,0}, {0,0,3,0,0}}}
};
the ULP tables are the 20 ms and 30 ms frame versions, and the 30 ms one is the one with the extra LSF rows, so it's a bit longer ๐ธ
next up are the high-pass filters, the 2 zero and 2 pole coefficient sets, all as float arrays. the names are a mouthful but they're iconic ๐
/* HP Filters */
float hpi_zero_coefsTbl[3] = {
(float)0.92727436, (float)-1.8544941, (float)0.92727436
};
float hpi_pole_coefsTbl[3] = {
(float)1.0, (float)-1.9059465, (float)0.9114024
};
float hpo_zero_coefsTbl[3] = {
(float)0.93980581, (float)-1.8795834, (float)0.93980581
};
float hpo_pole_coefsTbl[3] = {
(float)1.0, (float)-1.9330735, (float)0.93589199
};
/* LP Filter */
float lpFilt_coefsTbl[FILTERORDER_DS]={
(float)-0.066650, (float)0.125000, (float)0.316650,
(float)0.414063, (float)0.316650,
(float)0.125000, (float)-0.066650
};
/* State quantization tables */
float state_sq3Tbl[8] = {
(float)-3.719849, (float)-2.177490, (float)-1.130005,
(float)-0.309692, (float)0.444214, (float)1.329712,
(float)2.436279, (float)3.983887
};
float state_frgqTbl[64] = {
(float)1.000085, (float)1.071695, (float)1.140395,
(float)1.206868, (float)1.277188, (float)1.351503,
(float)1.429380, (float)1.500727, (float)1.569049,
(float)1.639599, (float)1.707071, (float)1.781531,
(float)1.840799, (float)1.901550, (float)1.956695,
(float)2.006750, (float)2.055474, (float)2.102787,
(float)2.142819, (float)2.183592, (float)2.217962,
(float)2.257177, (float)2.295739, (float)2.332967,
(float)2.369248, (float)2.402792, (float)2.435080,
(float)2.468598, (float)2.503394, (float)2.539284,
(float)2.572944, (float)2.605036, (float)2.636331,
(float)2.668939, (float)2.698780, (float)2.729101,
(float)2.759786, (float)2.789834, (float)2.818679,
(float)2.848074, (float)2.877470, (float)2.906899,
(float)2.936655, (float)2.967804, (float)3.000115,
(float)3.033367, (float)3.066355, (float)3.104231,
(float)3.141499, (float)3.183012, (float)3.222952,
(float)3.265433, (float)3.308441, (float)3.350823,
(float)3.395275, (float)3.442793, (float)3.490801,
(float)3.542514, (float)3.604064, (float)3.666050,
(float)3.740994, (float)3.830749, (float)3.938770,
(float)4.101764
};
the state quantization tables are where the real vibes are, the 8-entry one and the 64-entry one ๐ฆ
now the codebook tables. this is the search range stuff, the memory length values, and the expansion filter taps, all in one cute little group ๐
/* CB tables */
int search_rangeTbl[5][CB_NSTAGES]={{58,58,58}, {108,44,44},
{108,108,108}, {108,108,108}, {108,108,108}};
int stMemLTbl=85;
int memLfTbl[NASUB_MAX]={147,147,147,147};
/* expansion filter(s) */
float cbfiltersTbl[CB_FILTERLEN]={
(float)-0.034180, (float)0.108887, (float)-0.184326,
(float)0.806152, (float)0.713379, (float)-0.144043,
(float)0.083740, (float)-0.033691
};
then the gain quantization tables, the 8, 16 and 32 entry ones, which are basically the gain ladder of the whole thing, lowkey iconic ๐
/* Gain Quantization */
float gain_sq3Tbl[8]={
(float)-1.000000, (float)-0.659973, (float)-0.330017,
(float)0.000000, (float)0.250000, (float)0.500000,
(float)0.750000, (float)1.00000};
float gain_sq4Tbl[16]={
(float)-1.049988, (float)-0.900024, (float)-0.750000,
(float)-0.599976, (float)-0.450012, (float)-0.299988,
(float)-0.150024, (float)0.000000, (float)0.150024,
(float)0.299988, (float)0.450012, (float)0.599976,
(float)0.750000, (float)0.900024, (float)1.049988,
(float)1.200012};
float gain_sq5Tbl[32]={
(float)0.037476, (float)0.075012, (float)0.112488,
(float)0.150024, (float)0.187500, (float)0.224976,
(float)0.262512, (float)0.299988, (float)0.337524,
(float)0.375000, (float)0.412476, (float)0.450012,
(float)0.487488, (float)0.525024, (float)0.562500,
(float)0.599976, (float)0.637512, (float)0.674988,
(float)0.712524, (float)0.750000, (float)0.787476,
(float)0.825012, (float)0.862488, (float)0.900024,
(float)0.937500, (float)0.974976, (float)1.012512,
(float)1.049988, (float)1.087524, (float)1.125000,
(float)1.162476, (float)1.200012};
last bit of this part is the enhancer, which upsamples by a factor of 4, plus the block locations, and then the LPC analysis and quantization tables. the polyphaser table is a bit of a stack, so keep your eyes on the commas ๐โจ
/* Enhancer - Upsamling a factor 4 (ENH_UPS0 = 4) */
float polyphaserTbl[ENH_UPS0*(2*ENH_FL0+1)]={
(float)0.000000, (float)0.000000, (float)0.000000,
(float)1.000000,
(float)0.000000, (float)0.000000, (float)0.000000,
(float)0.015625, (float)-0.076904, (float)0.288330,
(float)0.862061,
(float)-0.106445, (float)0.018799, (float)-0.015625,
(float)0.023682, (float)-0.124268, (float)0.601563,
(float)0.601563,
(float)-0.124268, (float)0.023682, (float)-0.023682,
(float)0.018799, (float)-0.106445, (float)0.862061,
(float)0.288330,
(float)-0.076904, (float)0.015625, (float)-0.018799};
float enh_plocsTbl[ENH_NBLOCKS_TOT] = {(float)40.0, (float)120.0,
(float)200.0, (float)280.0, (float)360.0,
(float)440.0, (float)520.0, (float)600.0};
/* LPC analysis and quantization */
int dim_lsfCbTbl[LSF_NSPLIT] = {3, 3, 4};
int size_lsfCbTbl[LSF_NSPLIT] = {64,128,128};
float lsfmeanTbl[LPC_FILTERORDER] = {
(float)0.281738, (float)0.445801, (float)0.663330,
(float)0.962524, (float)1.251831, (float)1.533081,
(float)1.850586, (float)2.137817, (float)2.481445,
(float)2.777344};
float lsf_weightTbl_30ms[6] = {(float)(1.0/2.0), (float)1.0,
(float)(2.0/3.0),
(float)(1.0/3.0), (float)0.0, (float)0.0};
float lsf_weightTbl_20ms[4] = {(float)(3.0/4.0), (float)(2.0/4.0),
(float)(1.0/4.0), (float)(0.0)};
that's the end of this chunk, and the tables just keep going from here ๐๐
Okay bestie, we're deep in the constants now and these window tables are literally giving ๐โจ. The Hanning window ramps up and back down like a little hill ๐ฆ, the asymmetric one stays up near the top and then dips at the end ๐ , and the lag window is a tiny list of numbers that gently smooths things out ๐ซถ.
/* Hanning LPC window */
float lpc_winTbl[BLOCKL_MAX]={
(float)0.000183, (float)0.000671, (float)0.001526,
(float)0.002716, (float)0.004242, (float)0.006104,
(float)0.008301, (float)0.010834, (float)0.013702,
(float)0.016907, (float)0.020416, (float)0.024261,
(float)0.028442, (float)0.032928, (float)0.037750,
(float)0.042877, (float)0.048309, (float)0.054047,
(float)0.060089, (float)0.066437, (float)0.073090,
(float)0.080017, (float)0.087219, (float)0.094727,
(float)0.102509, (float)0.110535, (float)0.118835,
(float)0.127411, (float)0.136230, (float)0.145294,
(float)0.154602, (float)0.164154, (float)0.173920,
(float)0.183899, (float)0.194122, (float)0.204529,
(float)0.215149, (float)0.225952, (float)0.236938,
(float)0.248108, (float)0.259460, (float)0.270966,
(float)0.282654, (float)0.294464, (float)0.306396,
(float)0.318481, (float)0.330688, (float)0.343018,
(float)0.355438, (float)0.367981, (float)0.380585,
(float)0.393280, (float)0.406067, (float)0.418884,
(float)0.431763, (float)0.444702, (float)0.457672,
(float)0.470673, (float)0.483704, (float)0.496735,
(float)0.509766, (float)0.522797, (float)0.535828,
(float)0.548798, (float)0.561768, (float)0.574677,
(float)0.587524, (float)0.600342, (float)0.613068,
(float)0.625732, (float)0.638306, (float)0.650787,
(float)0.663147, (float)0.675415, (float)0.687561,
(float)0.699585, (float)0.711487, (float)0.723206,
(float)0.734802, (float)0.746216, (float)0.757477,
(float)0.768585, (float)0.779480, (float)0.790192,
(float)0.800720, (float)0.811005, (float)0.821106,
(float)0.830994, (float)0.840668, (float)0.850067,
(float)0.859253, (float)0.868225, (float)0.876892,
(float)0.885345, (float)0.893524, (float)0.901428,
(float)0.909058, (float)0.916412, (float)0.923492,
(float)0.930267, (float)0.936768, (float)0.942963,
(float)0.948853, (float)0.954437, (float)0.959717,
(float)0.964691, (float)0.969360, (float)0.973694,
(float)0.977692, (float)0.981384, (float)0.984741,
(float)0.987762, (float)0.990479, (float)0.992828,
(float)0.994873, (float)0.996552, (float)0.997925,
(float)0.998932, (float)0.999603, (float)0.999969,
(float)0.999969, (float)0.999603, (float)0.998932,
(float)0.997925, (float)0.996552, (float)0.994873,
(float)0.992828, (float)0.990479, (float)0.987762,
(float)0.984741, (float)0.981384, (float)0.977692,
(float)0.973694, (float)0.969360, (float)0.964691,
(float)0.959717, (float)0.954437, (float)0.948853,
(float)0.942963, (float)0.936768, (float)0.930267,
(float)0.923492, (float)0.916412, (float)0.909058,
(float)0.901428, (float)0.893524, (float)0.885345,
(float)0.876892, (float)0.868225, (float)0.859253,
(float)0.850067, (float)0.840668, (float)0.830994,
(float)0.821106, (float)0.811005, (float)0.800720,
(float)0.790192, (float)0.779480, (float)0.768585,
(float)0.757477, (float)0.746216, (float)0.734802,
(float)0.723206, (float)0.711487, (float)0.699585,
(float)0.687561, (float)0.675415, (float)0.663147,
(float)0.650787, (float)0.638306, (float)0.625732,
(float)0.613068, (float)0.600342, (float)0.587524,
(float)0.574677, (float)0.561768, (float)0.548798,
(float)0.535828, (float)0.522797, (float)0.509766,
(float)0.496735, (float)0.483704, (float)0.470673,
(float)0.457672, (float)0.444702, (float)0.431763,
(float)0.418884, (float)0.406067, (float)0.393280,
(float)0.380585, (float)0.367981, (float)0.355438,
(float)0.343018, (float)0.330688, (float)0.318481,
(float)0.306396, (float)0.294464, (float)0.282654,
(float)0.270966, (float)0.259460, (float)0.248108,
(float)0.236938, (float)0.225952, (float)0.215149,
(float)0.204529, (float)0.194122, (float)0.183899,
(float)0.173920, (float)0.164154, (float)0.154602,
(float)0.145294, (float)0.136230, (float)0.127411,
(float)0.118835, (float)0.110535, (float)0.102509,
(float)0.094727, (float)0.087219, (float)0.080017,
(float)0.073090, (float)0.066437, (float)0.060089,
(float)0.054047, (float)0.048309, (float)0.042877,
(float)0.037750, (float)0.032928, (float)0.028442,
(float)0.024261, (float)0.020416, (float)0.016907,
(float)0.013702, (float)0.010834, (float)0.008301,
(float)0.006104, (float)0.004242, (float)0.002716,
(float)0.001526, (float)0.000671, (float)0.000183
};
/* Asymmetric LPC window */
float lpc_asymwinTbl[BLOCKL_MAX]={
(float)0.000061, (float)0.000214, (float)0.000458,
(float)0.000824, (float)0.001282, (float)0.001831,
(float)0.002472, (float)0.003235, (float)0.004120,
(float)0.005066, (float)0.006134, (float)0.007294,
(float)0.008545, (float)0.009918, (float)0.011383,
(float)0.012939, (float)0.014587, (float)0.016357,
(float)0.018219, (float)0.020172, (float)0.022217,
(float)0.024353, (float)0.026611, (float)0.028961,
(float)0.031372, (float)0.033905, (float)0.036530,
(float)0.039276, (float)0.042084, (float)0.044983,
(float)0.047974, (float)0.051086, (float)0.054260,
(float)0.057526, (float)0.060883, (float)0.064331,
(float)0.067871, (float)0.071503, (float)0.075226,
(float)0.079010, (float)0.082916, (float)0.086884,
(float)0.090942, (float)0.095062, (float)0.099304,
(float)0.103607, (float)0.107971, (float)0.112427,
(float)0.116974, (float)0.121582, (float)0.126282,
(float)0.131073, (float)0.135895, (float)0.140839,
(float)0.145813, (float)0.150879, (float)0.156006,
(float)0.161224, (float)0.166504, (float)0.171844,
(float)0.177246, (float)0.182709, (float)0.188263,
(float)0.193848, (float)0.199524, (float)0.205231,
(float)0.211029, (float)0.216858, (float)0.222778,
(float)0.228729, (float)0.234741, (float)0.240814,
(float)0.246918, (float)0.253082, (float)0.259308,
(float)0.265564, (float)0.271881, (float)0.278259,
(float)0.284668, (float)0.291107, (float)0.297607,
(float)0.304138, (float)0.310730, (float)0.317322,
(float)0.323975, (float)0.330658, (float)0.337372,
(float)0.344147, (float)0.350922, (float)0.357727,
(float)0.364594, (float)0.371460, (float)0.378357,
(float)0.385284, (float)0.392212, (float)0.399170,
(float)0.406158, (float)0.413177, (float)0.420197,
(float)0.427246, (float)0.434296, (float)0.441376,
(float)0.448456, (float)0.455536, (float)0.462646,
(float)0.469757, (float)0.476868, (float)0.483978,
(float)0.491089, (float)0.498230, (float)0.505341,
(float)0.512451, (float)0.519592, (float)0.526703,
(float)0.533813, (float)0.540924, (float)0.548004,
(float)0.555084, (float)0.562164, (float)0.569244,
(float)0.576294, (float)0.583313, (float)0.590332,
(float)0.597321, (float)0.604309, (float)0.611267,
(float)0.618195, (float)0.625092, (float)0.631989,
(float)0.638855, (float)0.645660, (float)0.652466,
(float)0.659241, (float)0.665985, (float)0.672668,
(float)0.679352, (float)0.685974, (float)0.692566,
(float)0.699127, (float)0.705658, (float)0.712128,
(float)0.718536, (float)0.724945, (float)0.731262,
(float)0.737549, (float)0.743805, (float)0.750000,
(float)0.756134, (float)0.762238, (float)0.768280,
(float)0.774261, (float)0.780182, (float)0.786072,
(float)0.791870, (float)0.797638, (float)0.803314,
(float)0.808960, (float)0.814514, (float)0.820038,
(float)0.825470, (float)0.830841, (float)0.836151,
(float)0.841400, (float)0.846558, (float)0.851654,
(float)0.856689, (float)0.861633, (float)0.866516,
(float)0.871338, (float)0.876068, (float)0.880737,
(float)0.885315, (float)0.889801, (float)0.894226,
(float)0.898560, (float)0.902832, (float)0.907013,
(float)0.911102, (float)0.915100, (float)0.919037,
(float)0.922882, (float)0.926636, (float)0.930328,
(float)0.933899, (float)0.937408, (float)0.940796,
(float)0.944122, (float)0.947357, (float)0.950470,
(float)0.953522, (float)0.956482, (float)0.959351,
(float)0.962097, (float)0.964783, (float)0.967377,
(float)0.969849, (float)0.972229, (float)0.974518,
(float)0.976715, (float)0.978821, (float)0.980835,
(float)0.982727, (float)0.984528, (float)0.986237,
(float)0.987854, (float)0.989380, (float)0.990784,
(float)0.992096, (float)0.993317, (float)0.994415,
(float)0.995422, (float)0.996338, (float)0.997162,
(float)0.997864, (float)0.998474, (float)0.998962,
(float)0.999390, (float)0.999695, (float)0.999878,
(float)0.999969, (float)0.999969, (float)0.996918,
(float)0.987701, (float)0.972382, (float)0.951050,
(float)0.923889, (float)0.891022, (float)0.852631,
(float)0.809021, (float)0.760406, (float)0.707092,
(float)0.649445, (float)0.587799, (float)0.522491,
(float)0.453979, (float)0.382690, (float)0.309021,
(float)0.233459, (float)0.156433, (float)0.078461
};
/* Lag window for LPC */
float lpc_lagwinTbl[LPC_FILTERORDER + 1]={
(float)1.000100, (float)0.998890, (float)0.995569,
(float)0.990057, (float)0.982392,
(float)0.972623, (float)0.960816, (float)0.947047,
(float)0.931405, (float)0.913989, (float)0.894909};
okay bestie, this part is the big LSF quantization codebook, so it's all numbers, no prose ๐ข๐
/* LSF quantization*/
float lsfCbTbl[64 * 3 + 128 * 3 + 128 * 4] = {
(float)0.155396, (float)0.273193, (float)0.451172,
(float)0.390503, (float)0.648071, (float)1.002075,
(float)0.440186, (float)0.692261, (float)0.955688,
(float)0.343628, (float)0.642334, (float)1.071533,
(float)0.318359, (float)0.491577, (float)0.670532,
(float)0.193115, (float)0.375488, (float)0.725708,
(float)0.364136, (float)0.510376, (float)0.658691,
(float)0.297485, (float)0.527588, (float)0.842529,
(float)0.227173, (float)0.365967, (float)0.563110,
(float)0.244995, (float)0.396729, (float)0.636475,
(float)0.169434, (float)0.300171, (float)0.520264,
(float)0.312866, (float)0.464478, (float)0.643188,
(float)0.248535, (float)0.429932, (float)0.626099,
(float)0.236206, (float)0.491333, (float)0.817139,
(float)0.334961, (float)0.625122, (float)0.895752,
(float)0.343018, (float)0.518555, (float)0.698608,
(float)0.372803, (float)0.659790, (float)0.945435,
(float)0.176880, (float)0.316528, (float)0.581421,
(float)0.416382, (float)0.625977, (float)0.805176,
(float)0.303223, (float)0.568726, (float)0.915039,
(float)0.203613, (float)0.351440, (float)0.588135,
(float)0.221191, (float)0.375000, (float)0.614746,
(float)0.199951, (float)0.323364, (float)0.476074,
(float)0.300781, (float)0.433350, (float)0.566895,
(float)0.226196, (float)0.354004, (float)0.507568,
(float)0.300049, (float)0.508179, (float)0.711670,
(float)0.312012, (float)0.492676, (float)0.763428,
(float)0.329956, (float)0.541016, (float)0.795776,
(float)0.373779, (float)0.604614, (float)0.928833,
(float)0.210571, (float)0.452026, (float)0.755249,
(float)0.271118, (float)0.473267, (float)0.662476,
(float)0.285522, (float)0.436890, (float)0.634399,
(float)0.246704, (float)0.565552, (float)0.859009,
(float)0.270508, (float)0.406250, (float)0.553589,
(float)0.361450, (float)0.578491, (float)0.813843,
(float)0.342651, (float)0.482788, (float)0.622437,
(float)0.340332, (float)0.549438, (float)0.743164,
(float)0.200439, (float)0.336304, (float)0.540894,
(float)0.407837, (float)0.644775, (float)0.895142,
(float)0.294678, (float)0.454834, (float)0.699097,
(float)0.193115, (float)0.344482, (float)0.643188,
(float)0.275757, (float)0.420776, (float)0.598755,
(float)0.380493, (float)0.608643, (float)0.861084,
(float)0.222778, (float)0.426147, (float)0.676514,
(float)0.407471, (float)0.700195, (float)1.053101,
(float)0.218384, (float)0.377197, (float)0.669922,
(float)0.313232, (float)0.454102, (float)0.600952,
(float)0.347412, (float)0.571533, (float)0.874146,
(float)0.238037, (float)0.405396, (float)0.729492,
(float)0.223877, (float)0.412964, (float)0.822021,
(float)0.395264, (float)0.582153, (float)0.743896,
(float)0.247925, (float)0.485596, (float)0.720581,
(float)0.229126, (float)0.496582, (float)0.907715,
(float)0.260132, (float)0.566895, (float)1.012695,
(float)0.337402, (float)0.611572, (float)0.978149,
(float)0.267822, (float)0.447632, (float)0.769287,
(float)0.250610, (float)0.381714, (float)0.530029,
(float)0.430054, (float)0.805054, (float)1.221924,
(float)0.382568, (float)0.544067, (float)0.701660,
(float)0.383545, (float)0.710327, (float)1.149170,
(float)0.271362, (float)0.529053, (float)0.775513,
(float)0.246826, (float)0.393555, (float)0.588623,
(float)0.266846, (float)0.422119, (float)0.676758,
(float)0.311523, (float)0.580688, (float)0.838623,
(float)1.331177, (float)1.576782, (float)1.779541,
(float)1.160034, (float)1.401978, (float)1.768188,
(float)1.161865, (float)1.525146, (float)1.715332,
(float)0.759521, (float)0.913940, (float)1.119873,
(float)0.947144, (float)1.121338, (float)1.282471,
(float)1.015015, (float)1.557007, (float)1.804932,
(float)1.172974, (float)1.402100, (float)1.692627,
(float)1.087524, (float)1.474243, (float)1.665405,
(float)0.899536, (float)1.105225, (float)1.406250,
(float)1.148438, (float)1.484741, (float)1.796265,
(float)0.785645, (float)1.209839, (float)1.567749,
(float)0.867798, (float)1.166504, (float)1.450684,
(float)0.922485, (float)1.229858, (float)1.420898,
(float)0.791260, (float)1.123291, (float)1.409546,
(float)0.788940, (float)0.966064, (float)1.340332,
(float)1.051147, (float)1.272827, (float)1.556641,
(float)0.866821, (float)1.181152, (float)1.538818,
(float)0.906738, (float)1.373535, (float)1.607910,
(float)1.244751, (float)1.581421, (float)1.933838,
(float)0.913940, (float)1.337280, (float)1.539673,
(float)0.680542, (float)0.959229, (float)1.662720,
(float)0.887207, (float)1.430542, (float)1.800781,
(float)0.912598, (float)1.433594, (float)1.683960,
(float)0.860474, (float)1.060303, (float)1.455322,
(float)1.005127, (float)1.381104, (float)1.706909,
(float)0.800781, (float)1.363892, (float)1.829102,
(float)0.781860, (float)1.124390, (float)1.505981,
(float)1.003662, (float)1.471436, (float)1.684692,
(float)0.981323, (float)1.309570, (float)1.618042,
(float)1.228760, (float)1.554321, (float)1.756470,
(float)0.734375, (float)0.895752, (float)1.225586,
(float)0.841797, (float)1.055664, (float)1.249268,
(float)0.920166, (float)1.119385, (float)1.486206,
(float)0.894409, (float)1.539063, (float)1.828979,
(float)1.283691, (float)1.543335, (float)1.858276,
(float)0.676025, (float)0.933105, (float)1.490845,
(float)0.821289, (float)1.491821, (float)1.739868,
(float)0.923218, (float)1.144653, (float)1.580566,
(float)1.057251, (float)1.345581, (float)1.635864,
(float)0.888672, (float)1.074951, (float)1.353149,
(float)0.942749, (float)1.195435, (float)1.505493,
(float)1.492310, (float)1.788086, (float)2.039673,
(float)1.070313, (float)1.634399, (float)1.860962,
(float)1.253296, (float)1.488892, (float)1.686035,
(float)0.647095, (float)0.864014, (float)1.401855,
(float)0.866699, (float)1.254883, (float)1.453369,
(float)1.063965, (float)1.532593, (float)1.731323,
(float)1.167847, (float)1.521484, (float)1.884033,
(float)0.956055, (float)1.502075, (float)1.745605,
(float)0.928711, (float)1.288574, (float)1.479614,
(float)1.088013, (float)1.380737, (float)1.570801,
(float)0.905029, (float)1.186768, (float)1.371948,
(float)1.057861, (float)1.421021, (float)1.617432,
(float)1.108276, (float)1.312500, (float)1.501465,
(float)0.979492, (float)1.416992, (float)1.624268,
(float)1.276001, (float)1.661011, (float)2.007935,
(float)0.993042, (float)1.168579, (float)1.331665,
(float)0.778198, (float)0.944946, (float)1.235962,
(float)1.223755, (float)1.491333, (float)1.815674,
(float)0.852661, (float)1.350464, (float)1.722290,
(float)1.134766, (float)1.593140, (float)1.787354,
(float)1.051392, (float)1.339722, (float)1.531006,
(float)0.803589, (float)1.271240, (float)1.652100,
(float)0.755737, (float)1.143555, (float)1.639404,
(float)0.700928, (float)0.837280, (float)1.130371,
(float)0.942749, (float)1.197876, (float)1.669800,
(float)0.993286, (float)1.378296, (float)1.566528,
(float)0.801025, (float)1.095337, (float)1.298950,
(float)0.739990, (float)1.032959, (float)1.383667,
(float)0.845703, (float)1.072266, (float)1.543823,
(float)0.915649, (float)1.072266, (float)1.224487,
(float)1.021973, (float)1.226196, (float)1.481323,
(float)0.999878, (float)1.204102, (float)1.555908,
(float)0.722290, (float)0.913940, (float)1.340210,
(float)0.673340, (float)0.835938, (float)1.259521,
(float)0.832397, (float)1.208374, (float)1.394165,
(float)0.962158, (float)1.576172, (float)1.912842,
(float)1.166748, (float)1.370850, (float)1.556763,
(float)0.946289, (float)1.138550, (float)1.400391,
(float)1.035034, (float)1.218262, (float)1.386475,
(float)1.393799, (float)1.717773, (float)2.000244,
(float)0.972656, (float)1.260986, (float)1.760620,
(float)1.028198, (float)1.288452, (float)1.484619,
(float)0.773560, (float)1.258057, (float)1.756714,
(float)1.080322, (float)1.328003, (float)1.742676,
(float)0.823975, (float)1.450806, (float)1.917725,
(float)0.859009, (float)1.016602, (float)1.191895,
(float)0.843994, (float)1.131104, (float)1.645020,
(float)1.189697, (float)1.702759, (float)1.894409,
(float)1.346680, (float)1.763184, (float)2.066040,
(float)0.980469, (float)1.253784, (float)1.441650,
(float)1.338135, (float)1.641968, (float)1.932739,
(float)1.223267, (float)1.424194, (float)1.626465,
(float)0.765747, (float)1.004150, (float)1.579102,
(float)1.042847, (float)1.269165, (float)1.647461,
(float)0.968750, (float)1.257568, (float)1.555786,
(float)0.826294, (float)0.993408, (float)1.275146,
(float)0.742310, (float)0.950439, (float)1.430542,
(float)1.054321, (float)1.439819, (float)1.828003,
(float)1.072998, (float)1.261719, (float)1.441895,
(float)0.859375, (float)1.036377, (float)1.314819,
(float)0.895752, (float)1.267212, (float)1.605591,
(float)0.805420, (float)0.962891, (float)1.142334,
(float)0.795654, (float)1.005493, (float)1.468506,
(float)1.105347, (float)1.313843, (float)1.584839,
(float)0.792236, (float)1.221802, (float)1.465698,
(float)1.170532, (float)1.467651, (float)1.664063,
(float)0.838257, (float)1.153198, (float)1.342163,
(float)0.968018, (float)1.198242, (float)1.391235,
(float)1.250122, (float)1.623535, (float)1.823608,
(float)0.711670, (float)1.058350, (float)1.512085,
(float)1.204834, (float)1.454468, (float)1.739136,
(float)1.137451, (float)1.421753, (float)1.620117,
(float)0.820435, (float)1.322754, (float)1.578247,
(float)0.798706, (float)1.005005, (float)1.213867,
(float)0.980713, (float)1.324951, (float)1.512939,
(float)1.112305, (float)1.438843, (float)1.735596,
(float)1.135498, (float)1.356689, (float)1.635742,
(float)1.101318, (float)1.387451, (float)1.686523,
(float)0.849854, (float)1.276978, (float)1.523438,
(float)1.377930, (float)1.627563, (float)1.858154,
(float)0.884888, (float)1.095459, (float)1.287476,
(float)1.289795, (float)1.505859, (float)1.756592,
(float)0.817505, (float)1.384155, (float)1.650513,
(float)1.446655, (float)1.702148, (float)1.931885,
(float)0.835815, (float)1.023071, (float)1.385376,
(float)0.916626, (float)1.139038, (float)1.335327,
(float)0.980103, (float)1.174072, (float)1.453735,
(float)1.705688, (float)2.153809, (float)2.398315, (float)2.743408,
(float)1.797119, (float)2.016846, (float)2.445679, (float)2.701904,
(float)1.990356, (float)2.219116, (float)2.576416, (float)2.813477,
(float)1.849365, (float)2.190918, (float)2.611572, (float)2.835083,
(float)1.657959, (float)1.854370, (float)2.159058, (float)2.726196,
(float)1.437744, (float)1.897705, (float)2.253174, (float)2.655396,
(float)2.028687, (float)2.247314, (float)2.542358, (float)2.875854,
(float)1.736938, (float)1.922119, (float)2.185913, (float)2.743408,
(float)1.521606, (float)1.870972, (float)2.526855, (float)2.786987,
(float)1.841431, (float)2.050659, (float)2.463623, (float)2.857666,
(float)1.590088, (float)2.067261, (float)2.427979, (float)2.794434,
(float)1.746826, (float)2.057373, (float)2.320190, (float)2.800781,
(float)1.734619, (float)1.940552, (float)2.306030, (float)2.826416,
(float)1.786255, (float)2.204468, (float)2.457520, (float)2.795288,
(float)1.861084, (float)2.170532, (float)2.414551, (float)2.763672,
(float)2.001465, (float)2.307617, (float)2.552734, (float)2.811890,
(float)1.784424, (float)2.124146, (float)2.381592, (float)2.645508,
(float)1.888794, (float)2.135864, (float)2.418579, (float)2.861206,
(float)2.301147, (float)2.531250, (float)2.724976, (float)2.913086,
(float)1.837769, (float)2.051270, (float)2.261963, (float)2.553223,
(float)2.012939, (float)2.221191, (float)2.440186, (float)2.678101,
(float)1.429565, (float)1.858276, (float)2.582275, (float)2.845703,
(float)1.622803, (float)1.897705, (float)2.367310, (float)2.621094,
(float)1.581543, (float)1.960449, (float)2.515869, (float)2.736450,
(float)1.419434, (float)1.933960, (float)2.394653, (float)2.746704,
(float)1.721924, (float)2.059570, (float)2.421753, (float)2.769653,
(float)1.911011, (float)2.220703, (float)2.461060, (float)2.740723,
(float)1.581177, (float)1.860840, (float)2.516968, (float)2.874634,
(float)1.870361, (float)2.098755, (float)2.432373, (float)2.656494,
(float)2.059692, (float)2.279785, (float)2.495605, (float)2.729370,
(float)1.815674, (float)2.181519, (float)2.451538, (float)2.680542,
(float)1.407959, (float)1.768311, (float)2.343018, (float)2.668091,
(float)2.168701, (float)2.394653, (float)2.604736, (float)2.829346,
(float)1.636230, (float)1.865723, (float)2.329102, (float)2.824219,
(float)1.878906, (float)2.139526, (float)2.376709, (float)2.679810,
(float)1.765381, (float)1.971802, (float)2.195435, (float)2.586914,
(float)2.164795, (float)2.410889, (float)2.673706, (float)2.903198,
(float)2.071899, (float)2.331055, (float)2.645874, (float)2.907104,
(float)2.026001, (float)2.311523, (float)2.594849, (float)2.863892,
(float)1.948975, (float)2.180786, (float)2.514893, (float)2.797852,
(float)1.881836, (float)2.130859, (float)2.478149, (float)2.804199,
(float)2.238159, (float)2.452759, (float)2.652832, (float)2.868286,
(float)1.897949, (float)2.101685, (float)2.524292, (float)2.880127,
(float)1.856445, (float)2.074585, (float)2.541016, (float)2.791748,
(float)1.695557, (float)2.199097, (float)2.506226, (float)2.742676,
(float)1.612671, (float)1.877075, (float)2.435425, (float)2.732910,
(float)1.568848, (float)1.786499, (float)2.194580, (float)2.768555,
(float)1.953369, (float)2.164551, (float)2.486938, (float)2.874023,
(float)1.388306, (float)1.725342, (float)2.384521, (float)2.771851,
(float)2.115356, (float)2.337769, (float)2.592896, (float)2.864014,
(float)1.905762, (float)2.111328, (float)2.363525, (float)2.789307,
(float)1.882568, (float)2.332031, (float)2.598267, (float)2.827637,
(float)1.683594, (float)2.088745, (float)2.361938, (float)2.608643,
(float)1.874023, (float)2.182129, (float)2.536133, (float)2.766968,
(float)1.861938, (float)2.070435, (float)2.309692, (float)2.700562,
(float)1.722168, (float)2.107422, (float)2.477295, (float)2.837646,
(float)1.926880, (float)2.184692, (float)2.442627, (float)2.663818,
(float)2.123901, (float)2.337280, (float)2.553101, (float)2.777466,
(float)1.588135, (float)1.911499, (float)2.212769, (float)2.543945,
(float)2.053955, (float)2.370850, (float)2.712158, (float)2.939941,
(float)2.210449, (float)2.519653, (float)2.770386, (float)2.958618,
(float)2.199463, (float)2.474731, (float)2.718262, (float)2.919922,
(float)1.960083, (float)2.175415, (float)2.608032, (float)2.888794,
(float)1.953735, (float)2.185181, (float)2.428223, (float)2.809570,
(float)1.615234, (float)2.036499, (float)2.576538, (float)2.834595,
(float)1.621094, (float)2.028198, (float)2.431030, (float)2.664673,
(float)1.824951, (float)2.267456, (float)2.514526, (float)2.747925,
(float)1.994263, (float)2.229126, (float)2.475220, (float)2.833984,
(float)1.746338, (float)2.011353, (float)2.588257, (float)2.826904,
(float)1.562866, (float)2.135986, (float)2.471680, (float)2.687256,
(float)1.748901, (float)2.083496, (float)2.460938, (float)2.686279,
(float)1.758057, (float)2.131470, (float)2.636597, (float)2.891602,
(float)2.071289, (float)2.299072, (float)2.550781, (float)2.814331,
(float)1.839600, (float)2.094360, (float)2.496460, (float)2.723999,
(float)1.882202, (float)2.088257, (float)2.636841, (float)2.923096,
(float)1.957886, (float)2.153198, (float)2.384399, (float)2.615234,
(float)1.992920, (float)2.351196, (float)2.654419, (float)2.889771,
(float)2.012817, (float)2.262451, (float)2.643799, (float)2.903076,
(float)2.025635, (float)2.254761, (float)2.508423, (float)2.784058,
(float)2.316040, (float)2.589355, (float)2.794189, (float)2.963623,
(float)1.741211, (float)2.279541, (float)2.578491, (float)2.816284,
(float)1.845337, (float)2.055786, (float)2.348511, (float)2.822021,
(float)1.679932, (float)1.926514, (float)2.499756, (float)2.835693,
(float)1.722534, (float)1.946899, (float)2.448486, (float)2.728760,
(float)1.829834, (float)2.043213, (float)2.580444, (float)2.867676,
(float)1.676636, (float)2.071655, (float)2.322510, (float)2.704834,
(float)1.791504, (float)2.113525, (float)2.469727, (float)2.784058,
(float)1.977051, (float)2.215088, (float)2.497437, (float)2.726929,
(float)1.800171, (float)2.106689, (float)2.357788, (float)2.738892,
(float)1.827759, (float)2.170166, (float)2.525879, (float)2.852417,
(float)1.918335, (float)2.132813, (float)2.488403, (float)2.728149,
(float)1.916748, (float)2.225098, (float)2.542603, (float)2.857666,
(float)1.761230, (float)1.976074, (float)2.507446, (float)2.884521,
(float)2.053711, (float)2.367432, (float)2.608032, (float)2.837646,
(float)1.595337, (float)2.000977, (float)2.307129, (float)2.578247,
(float)1.470581, (float)2.031250, (float)2.375854, (float)2.647583,
(float)1.801392, (float)2.128052, (float)2.399780, (float)2.822876,
(float)1.853638, (float)2.066650, (float)2.429199, (float)2.751465,
(float)1.956299, (float)2.163696, (float)2.394775, (float)2.734253,
(float)1.963623, (float)2.275757, (float)2.585327, (float)2.865234,
(float)1.887451, (float)2.105469, (float)2.331787, (float)2.587402,
(float)2.120117, (float)2.443359, (float)2.733887, (float)2.941406,
(float)1.506348, (float)1.766968, (float)2.400513, (float)2.851807,
(float)1.664551, (float)1.981079, (float)2.375732, (float)2.774414,
(float)1.720703, (float)1.978882, (float)2.391479, (float)2.640991,
(float)1.483398, (float)1.814819, (float)2.434448, (float)2.722290,
(float)1.769043, (float)2.136597, (float)2.563721, (float)2.774414,
(float)1.810791, (float)2.049316, (float)2.373901, (float)2.613647,
(float)1.788330, (float)2.005981, (float)2.359131, (float)2.723145,
(float)1.785156, (float)1.993164, (float)2.399780, (float)2.832520,
(float)1.695313, (float)2.022949, (float)2.522583, (float)2.745117,
(float)1.584106, (float)1.965576, (float)2.299927, (float)2.715576,
(float)1.894897, (float)2.249878, (float)2.655884, (float)2.897705,
(float)1.720581, (float)1.995728, (float)2.299438, (float)2.557007,
(float)1.619385, (float)2.173950, (float)2.574219, (float)2.787964,
(float)1.883179, (float)2.220459, (float)2.474365, (float)2.825073,
(float)1.447632, (float)2.045044, (float)2.555542, (float)2.744873,
(float)1.502686, (float)2.156616, (float)2.653320, (float)2.846558,
(float)1.711548, (float)1.944092, (float)2.282959, (float)2.685791,
(float)1.499756, (float)1.867554, (float)2.341064, (float)2.578857,
(float)1.916870, (float)2.135132, (float)2.568237, (float)2.826050,
(float)1.498047, (float)1.711182, (float)2.223267, (float)2.755127,
(float)1.808716, (float)1.997559, (float)2.256470, (float)2.758545,
(float)2.088501, (float)2.402710, (float)2.667358, (float)2.890259,
(float)1.545044, (float)1.819214, (float)2.324097, (float)2.692993,
(float)1.796021, (float)2.012573, (float)2.505737, (float)2.784912,
(float)1.786499, (float)2.041748, (float)2.290405, (float)2.650757,
(float)1.938232, (float)2.264404, (float)2.529053, (float)2.796143
};
A.9. anaFilter.h ๐
okay this one is the header for the LP analysis filter, so it's basically just the "here's what the function looks like" card ๐ง no cap it's short but it's giving main character energy โจ
/******************************************************************
iLBC Speech Coder ANSI-C Source Code
anaFilter.h
Copyright (C) The Internet Society (2004).
All Rights Reserved.
******************************************************************/
#ifndef __iLBC_ANAFILTER_H
#define __iLBC_ANAFILTER_H
void anaFilter(
float *In, /* (i) Signal to be filtered */
float *a, /* (i) LP parameters */
int len,/* (i) Length of signal */
float *Out, /* (o) Filtered signal */
float *mem /* (i/o) Filter state */
);
#endif
A.10. anaFilter.c ๐
bestie this is the actual filter, and it's so iconic ๐ญ it goes through the signal in two passes, first using the memory from the past and then the rest where the state is already sitting right there in the input ๐ซถ the last bit updates the state vector so the next call knows where it left off ๐
/******************************************************************
iLBC Speech Coder ANSI-C Source Code
anaFilter.c
Copyright (C) The Internet Society (2004).
All Rights Reserved.
******************************************************************/
#include <string.h>
#include "iLBC_define.h"
/*----------------------------------------------------------------*
* LP analysis filter.
*---------------------------------------------------------------*/
void anaFilter(
float *In, /* (i) Signal to be filtered */
float *a, /* (i) LP parameters */
int len,/* (i) Length of signal */
float *Out, /* (o) Filtered signal */
float *mem /* (i/o) Filter state */
){
int i, j;
float *po, *pi, *pm, *pa;
po = Out;
/* Filter first part using memory from past */
for (i=0; i<LPC_FILTERORDER; i++) {
pi = &In[i];
pm = &mem[LPC_FILTERORDER-1];
pa = a;
*po=0.0;
for (j=0; j<=i; j++) {
*po+=(*pa++)*(*pi--);
}
for (j=i+1; j<LPC_FILTERORDER+1; j++) {
*po+=(*pa++)*(*pm--);
}
po++;
}
/* Filter last part where the state is entirely
in the input vector */
for (i=LPC_FILTERORDER; i<len; i++) {
pi = &In[i];
pa = a;
*po=0.0;
for (j=0; j<LPC_FILTERORDER+1; j++) {
*po+=(*pa++)*(*pi--);
}
po++;
}
/* Update state vector */
memcpy(mem, &In[len-LPC_FILTERORDER],
LPC_FILTERORDER*sizeof(float));
}
A.11. createCB.h ๐ธ
last one for this chunk, and it's the header for the codebook stuff ๐ฆ it declares three functions: one builds the filtered codebook vectors, one searches the augmented codebook, and one creates an augmented vector ๐ lowkey the searching one has the most params and it's a whole mood ๐
/******************************************************************
iLBC Speech Coder ANSI-C Source Code
createCB.h
Copyright (C) The Internet Society (2004).
All Rights Reserved.
******************************************************************/
#ifndef __iLBC_CREATECB_H
#define __iLBC_CREATECB_H
void filteredCBvecs(
float *cbvectors, /* (o) Codebook vector for the
higher section */
float *mem, /* (i) Buffer to create codebook
vectors from */
int lMem /* (i) Length of buffer */
);
void searchAugmentedCB(
int low, /* (i) Start index for the search */
int high, /* (i) End index for the search */
int stage, /* (i) Current stage */
int startIndex, /* (i) CB index for the first
augmented vector */
float *target, /* (i) Target vector for encoding */
float *buffer, /* (i) Pointer to the end of the
buffer for augmented codebook
construction */
float *max_measure, /* (i/o) Currently maximum measure */
int *best_index,/* (o) Currently the best index */
float *gain, /* (o) Currently the best gain */
float *energy, /* (o) Energy of augmented
codebook vectors */
float *invenergy/* (o) Inv energy of aug codebook
vectors */
);
void createAugmentedVec(
int index, /* (i) Index for the aug vector
to be created */
float *buffer, /* (i) Pointer to the end of the
buffer for augmented codebook
construction */
float *cbVec /* (o) The construced codebook vector */
);
#endif
A.12. createCB.c โจ
okay bestie, this file is where the codebook gets its extra section ๐ฆ It builds more codebook vectors by filtering the existing buffer, then searches those new vectors for the best match. The helpers are filteredCBvecs, searchAugmentedCB, and createAugmentedVec. ๐
/******************************************************************
iLBC Speech Coder ANSI-C Source Code
createCB.c
Copyright (C) The Internet Society (2004).
All Rights Reserved.
******************************************************************/
#include "iLBC_define.h"
#include "constants.h"
#include <string.h>
#include <math.h>
/*----------------------------------------------------------------*
* Construct an additional codebook vector by filtering the
* initial codebook buffer. This vector is then used to expand
* the codebook with an additional section.
*---------------------------------------------------------------*/
void filteredCBvecs(
float *cbvectors, /* (o) Codebook vectors for the
higher section */
float *mem, /* (i) Buffer to create codebook
vector from */
int lMem /* (i) Length of buffer */
){
int j, k;
float *pp, *pp1;
float tempbuff2[CB_MEML+CB_FILTERLEN];
float *pos;
memset(tempbuff2, 0, (CB_HALFFILTERLEN-1)*sizeof(float));
memcpy(&tempbuff2[CB_HALFFILTERLEN-1], mem, lMem*sizeof(float));
memset(&tempbuff2[lMem+CB_HALFFILTERLEN-1], 0,
(CB_HALFFILTERLEN+1)*sizeof(float));
/* Create codebook vector for higher section by filtering */
/* do filtering */
pos=cbvectors;
memset(pos, 0, lMem*sizeof(float));
for (k=0; k<lMem; k++) {
pp=&tempbuff2[k];
pp1=&cbfiltersTbl[CB_FILTERLEN-1];
for (j=0;j<CB_FILTERLEN;j++) {
(*pos)+=(*pp++)*(*pp1--);
}
pos++;
}
}
/*----------------------------------------------------------------*
* Search the augmented part of the codebook to find the best
* measure.
*----------------------------------------------------------------*/
void searchAugmentedCB(
int low, /* (i) Start index for the search */
int high, /* (i) End index for the search */
int stage, /* (i) Current stage */
int startIndex, /* (i) Codebook index for the first
aug vector */
float *target, /* (i) Target vector for encoding */
float *buffer, /* (i) Pointer to the end of the buffer for
augmented codebook construction */
float *max_measure, /* (i/o) Currently maximum measure */
int *best_index,/* (o) Currently the best index */
float *gain, /* (o) Currently the best gain */
float *energy, /* (o) Energy of augmented codebook
vectors */
float *invenergy/* (o) Inv energy of augmented codebook
vectors */
) {
int icount, ilow, j, tmpIndex;
float *pp, *ppo, *ppi, *ppe, crossDot, alfa;
float weighted, measure, nrjRecursive;
float ftmp;
/* Compute the energy for the first (low-5)
noninterpolated samples */
nrjRecursive = (float) 0.0;
pp = buffer - low + 1;
for (j=0; j<(low-5); j++) {
nrjRecursive += ( (*pp)*(*pp) );
pp++;
}
ppe = buffer - low;
for (icount=low; icount<=high; icount++) {
/* Index of the codebook vector used for retrieving
energy values */
tmpIndex = startIndex+icount-20;
ilow = icount-4;
/* Update the energy recursively to save complexity */
nrjRecursive = nrjRecursive + (*ppe)*(*ppe);
ppe--;
energy[tmpIndex] = nrjRecursive;
/* Compute cross dot product for the first (low-5)
samples */
crossDot = (float) 0.0;
pp = buffer-icount;
for (j=0; j<ilow; j++) {
crossDot += target[j]*(*pp++);
}
/* interpolation */
alfa = (float) 0.2;
ppo = buffer-4;
ppi = buffer-icount-4;
for (j=ilow; j<icount; j++) {
weighted = ((float)1.0-alfa)*(*ppo)+alfa*(*ppi);
ppo++;
ppi++;
energy[tmpIndex] += weighted*weighted;
crossDot += target[j]*weighted;
alfa += (float)0.2;
}
/* Compute energy and cross dot product for the
remaining samples */
pp = buffer - icount;
for (j=icount; j<SUBL; j++) {
energy[tmpIndex] += (*pp)*(*pp);
crossDot += target[j]*(*pp++);
}
if (energy[tmpIndex]>0.0) {
invenergy[tmpIndex]=(float)1.0/(energy[tmpIndex]+EPS);
} else {
invenergy[tmpIndex] = (float) 0.0;
}
if (stage==0) {
measure = (float)-10000000.0;
if (crossDot > 0.0) {
measure = crossDot*crossDot*invenergy[tmpIndex];
}
}
else {
measure = crossDot*crossDot*invenergy[tmpIndex];
}
/* check if measure is better */
ftmp = crossDot*invenergy[tmpIndex];
if ((measure>*max_measure) && (fabs(ftmp)<CB_MAXGAIN)) {
*best_index = tmpIndex;
*max_measure = measure;
*gain = ftmp;
}
}
}
/*----------------------------------------------------------------*
* Recreate a specific codebook vector from the augmented part.
*
*----------------------------------------------------------------*/
void createAugmentedVec(
int index, /* (i) Index for the augmented vector
to be created */
float *buffer, /* (i) Pointer to the end of the buffer for
augmented codebook construction */
float *cbVec/* (o) The construced codebook vector */
) {
int ilow, j;
float *pp, *ppo, *ppi, alfa, alfa1, weighted;
ilow = index-5;
/* copy the first noninterpolated part */
pp = buffer-index;
memcpy(cbVec,pp,sizeof(float)*index);
/* interpolation */
alfa1 = (float)0.2;
alfa = 0.0;
ppo = buffer-5;
ppi = buffer-index-5;
for (j=ilow; j<index; j++) {
weighted = ((float)1.0-alfa)*(*ppo)+alfa*(*ppi);
ppo++;
ppi++;
cbVec[j] = weighted;
alfa += alfa1;
}
/* copy the second noninterpolated part */
pp = buffer - index;
memcpy(cbVec+index,pp,sizeof(float)*(SUBL-index));
}
A.13. doCPLC.h ๐ธ
last one for this part! ๐ซถ This is the header for the packet loss concealment function doThePLC. Its parameters are the concealed residual and LP parameters as outputs, the packet loss indicator and decoded inputs, the pitch lag, and the decoder instance. Don't mind the include guard name, it really does say __iLBC_DOLPC_H ๐
/******************************************************************
iLBC Speech Coder ANSI-C Source Code
doCPLC.h
Copyright (C) The Internet Society (2004).
All Rights Reserved.
******************************************************************/
#ifndef __iLBC_DOLPC_H
#define __iLBC_DOLPC_H
void doThePLC(
float *PLCresidual, /* (o) concealed residual */
float *PLClpc, /* (o) concealed LP parameters */
int PLI, /* (i) packet loss indicator
0 - no PL, 1 = PL */
float *decresidual, /* (i) decoded residual */
float *lpc, /* (i) decoded LPC (only used for no PL) */
int inlag, /* (i) pitch lag */
iLBC_Dec_Inst_t *iLBCdec_inst
/* (i/o) decoder instance */
);
#endif
A.14. doCPLC.c โจ
okay bestie, this one is the packet loss concealment routine and it's giving main character energy ๐ ๐ฆ
/******************************************************************
iLBC Speech Coder ANSI-C Source Code
doCPLC.c
Copyright (C) The Internet Society (2004).
All Rights Reserved.
******************************************************************/
#include <math.h>
#include <string.h>
#include <stdio.h>
#include "iLBC_define.h"
/*----------------------------------------------------------------*
* Compute cross correlation and pitch gain for pitch prediction
* of last subframe at given lag.
*---------------------------------------------------------------*/
void compCorr(
float *cc, /* (o) cross correlation coefficient */
float *gc, /* (o) gain */
float *pm,
float *buffer, /* (i) signal buffer */
int lag, /* (i) pitch lag */
int bLen, /* (i) length of buffer */
int sRange /* (i) correlation search length */
){
int i;
float ftmp1, ftmp2, ftmp3;
/* Guard against getting outside buffer */
if ((bLen-sRange-lag)<0) {
sRange=bLen-lag;
}
ftmp1 = 0.0;
ftmp2 = 0.0;
ftmp3 = 0.0;
for (i=0; i<sRange; i++) {
ftmp1 += buffer[bLen-sRange+i] *
buffer[bLen-sRange+i-lag];
ftmp2 += buffer[bLen-sRange+i-lag] *
buffer[bLen-sRange+i-lag];
ftmp3 += buffer[bLen-sRange+i] *
buffer[bLen-sRange+i];
}
if (ftmp2 > 0.0) {
*cc = ftmp1*ftmp1/ftmp2;
*gc = (float)fabs(ftmp1/ftmp2);
*pm=(float)fabs(ftmp1)/
((float)sqrt(ftmp2)*(float)sqrt(ftmp3));
}
else {
*cc = 0.0;
*gc = 0.0;
*pm=0.0;
}
}
/*----------------------------------------------------------------*
* Packet loss concealment routine. Conceals a residual signal
* and LP parameters. If no packet loss, update state.
*---------------------------------------------------------------*/
void doThePLC(
float *PLCresidual, /* (o) concealed residual */
float *PLClpc, /* (o) concealed LP parameters */
int PLI, /* (i) packet loss indicator
0 - no PL, 1 = PL */
float *decresidual, /* (i) decoded residual */
float *lpc, /* (i) decoded LPC (only used for no PL) */
int inlag, /* (i) pitch lag */
iLBC_Dec_Inst_t *iLBCdec_inst
/* (i/o) decoder instance */
){
int lag=20, randlag;
float gain, maxcc;
float use_gain;
float gain_comp, maxcc_comp, per, max_per;
int i, pick, use_lag;
float ftmp, randvec[BLOCKL_MAX], pitchfact, energy;
/* Packet Loss */
if (PLI == 1) {
iLBCdec_inst->consPLICount += 1;
/* if previous frame not lost,
determine pitch pred. gain */
if (iLBCdec_inst->prevPLI != 1) {
/* Search around the previous lag to find the
best pitch period */
lag=inlag-3;
compCorr(&maxcc, &gain, &max_per,
iLBCdec_inst->prevResidual,
lag, iLBCdec_inst->blockl, 60);
for (i=inlag-2;i<=inlag+3;i++) {
compCorr(&maxcc_comp, &gain_comp, &per,
iLBCdec_inst->prevResidual,
i, iLBCdec_inst->blockl, 60);
if (maxcc_comp>maxcc) {
maxcc=maxcc_comp;
gain=gain_comp;
lag=i;
max_per=per;
}
}
}
/* previous frame lost, use recorded lag and periodicity */
else {
lag=iLBCdec_inst->prevLag;
max_per=iLBCdec_inst->per;
}
/* downscaling */
use_gain=1.0;
if (iLBCdec_inst->consPLICount*iLBCdec_inst->blockl>320)
use_gain=(float)0.9;
else if (iLBCdec_inst->consPLICount*
iLBCdec_inst->blockl>2*320)
use_gain=(float)0.7;
else if (iLBCdec_inst->consPLICount*
iLBCdec_inst->blockl>3*320)
use_gain=(float)0.5;
else if (iLBCdec_inst->consPLICount*
iLBCdec_inst->blockl>4*320)
use_gain=(float)0.0;
/* mix noise and pitch repeatition */
ftmp=(float)sqrt(max_per);
if (ftmp>(float)0.7)
pitchfact=(float)1.0;
else if (ftmp>(float)0.4)
pitchfact=(ftmp-(float)0.4)/((float)0.7-(float)0.4);
else
pitchfact=0.0;
/* avoid repetition of same pitch cycle */
use_lag=lag;
if (lag<80) {
use_lag=2*lag;
}
/* compute concealed residual */
energy = 0.0;
for (i=0; i<iLBCdec_inst->blockl; i++) {
/* noise component */
iLBCdec_inst->seed=(iLBCdec_inst->seed*69069L+1) &
(0x80000000L-1);
randlag = 50 + ((signed long) iLBCdec_inst->seed)%70;
pick = i - randlag;
if (pick < 0) {
randvec[i] =
iLBCdec_inst->prevResidual[
iLBCdec_inst->blockl+pick];
} else {
randvec[i] = randvec[pick];
}
/* pitch repeatition component */
pick = i - use_lag;
if (pick < 0) {
PLCresidual[i] =
iLBCdec_inst->prevResidual[
iLBCdec_inst->blockl+pick];
} else {
PLCresidual[i] = PLCresidual[pick];
}
/* mix random and periodicity component */
if (i<80)
PLCresidual[i] = use_gain*(pitchfact *
PLCresidual[i] +
((float)1.0 - pitchfact) * randvec[i]);
else if (i<160)
PLCresidual[i] = (float)0.95*use_gain*(pitchfact *
PLCresidual[i] +
((float)1.0 - pitchfact) * randvec[i]);
else
PLCresidual[i] = (float)0.9*use_gain*(pitchfact *
PLCresidual[i] +
((float)1.0 - pitchfact) * randvec[i]);
energy += PLCresidual[i] * PLCresidual[i];
}
/* less than 30 dB, use only noise */
if (sqrt(energy/(float)iLBCdec_inst->blockl) < 30.0) {
gain=0.0;
for (i=0; i<iLBCdec_inst->blockl; i++) {
PLCresidual[i] = randvec[i];
}
}
/* use old LPC */
memcpy(PLClpc,iLBCdec_inst->prevLpc,
(LPC_FILTERORDER+1)*sizeof(float));
}
/* no packet loss, copy input */
else {
memcpy(PLCresidual, decresidual,
iLBCdec_inst->blockl*sizeof(float));
memcpy(PLClpc, lpc, (LPC_FILTERORDER+1)*sizeof(float));
iLBCdec_inst->consPLICount = 0;
}
/* update state */
if (PLI) {
iLBCdec_inst->prevLag = lag;
iLBCdec_inst->per=max_per;
}
iLBCdec_inst->prevPLI = PLI;
memcpy(iLBCdec_inst->prevLpc, PLClpc,
(LPC_FILTERORDER+1)*sizeof(float));
memcpy(iLBCdec_inst->prevResidual, PLCresidual,
iLBCdec_inst->blockl*sizeof(float));
}
A.15. enhancer.h ๐ง
the header time, and it's a cute one ๐ธ
/******************************************************************
iLBC Speech Coder ANSI-C Source Code
enhancer.h
Copyright (C) The Internet Society (2004).
All Rights Reserved.
******************************************************************/
#ifndef __ENHANCER_H
#define __ENHANCER_H
#include "iLBC_define.h"
float xCorrCoef(
float *target, /* (i) first array */
float *regressor, /* (i) second array */
int subl /* (i) dimension arrays */
);
int enhancerInterface(
float *out, /* (o) the enhanced recidual signal */
float *in, /* (i) the recidual signal to enhance */
iLBC_Dec_Inst_t *iLBCdec_inst
/* (i/o) the decoder state structure */
);
#endif
A.16. enhancer.c ๐ฆ
ngl this file is so iconic, it's got NearestNeighbor, mycorr1, and enh_upsample all in one place ๐
/******************************************************************
iLBC Speech Coder ANSI-C Source Code
enhancer.c
Copyright (C) The Internet Society (2004).
All Rights Reserved.
******************************************************************/
#include <math.h>
#include <string.h>
#include "iLBC_define.h"
#include "constants.h"
#include "filter.h"
/*----------------------------------------------------------------*
* Find index in array such that the array element with said
* index is the element of said array closest to "value"
* according to the squared-error criterion
*---------------------------------------------------------------*/
void NearestNeighbor(
int *index, /* (o) index of array element closest
to value */
float *array, /* (i) data array */
float value,/* (i) value */
int arlength/* (i) dimension of data array */
){
int i;
float bestcrit,crit;
crit=array[0]-value;
bestcrit=crit*crit;
*index=0;
for (i=1; i<arlength; i++) {
crit=array[i]-value;
crit=crit*crit;
if (crit<bestcrit) {
bestcrit=crit;
*index=i;
}
}
}
/*----------------------------------------------------------------*
* compute cross correlation between sequences
*---------------------------------------------------------------*/
void mycorr1(
float* corr, /* (o) correlation of seq1 and seq2 */
float* seq1, /* (i) first sequence */
int dim1, /* (i) dimension first seq1 */
const float *seq2, /* (i) second sequence */
int dim2 /* (i) dimension seq2 */
){
int i,j;
for (i=0; i<=dim1-dim2; i++) {
corr[i]=0.0;
for (j=0; j<dim2; j++) {
corr[i] += seq1[i+j] * seq2[j];
}
}
}
/*----------------------------------------------------------------*
* upsample finite array assuming zeros outside bounds
*---------------------------------------------------------------*/
void enh_upsample(
float* useq1, /* (o) upsampled output sequence */
float* seq1,/* (i) unupsampled sequence */
int dim1, /* (i) dimension seq1 */
int hfl /* (i) polyphase filter length=2*hfl+1 */
){
float *pu,*ps;
int i,j,k,q,filterlength,hfl2;
const float *polyp[ENH_UPS0]; /* pointers to
polyphase columns */
const float *pp;
/* define pointers for filter */
filterlength=2*hfl+1;
if ( filterlength > dim1 ) {
hfl2=(int) (dim1/2);
for (j=0; j<ENH_UPS0; j++) {
polyp[j]=polyphaserTbl+j*filterlength+hfl-hfl2;
}
hfl=hfl2;
filterlength=2*hfl+1;
}
else {
for (j=0; j<ENH_UPS0; j++) {
polyp[j]=polyphaserTbl+j*filterlength;
}
}
/* filtering: filter overhangs left side of sequence */
okay so this is where the upsampling does its thing ๐ the first loop handles the start of the sequence, where the filter hangs off the left edge ๐ธ
pu=useq1;
for (i=hfl; i<filterlength; i++) {
for (j=0; j<ENH_UPS0; j++) {
*pu=0.0;
pp = polyp[j];
ps = seq1+i;
for (k=0; k<=i; k++) {
*pu += *ps-- * *pp++;
}
pu++;
}
}
/* filtering: simple convolution=inner products */
for (i=filterlength; i<dim1; i++) {
for (j=0;j<ENH_UPS0; j++){
*pu=0.0;
pp = polyp[j];
ps = seq1+i;
for (k=0; k<filterlength; k++) {
*pu += *ps-- * *pp++;
}
pu++;
}
}
/* filtering: filter overhangs right side of sequence */
for (q=1; q<=hfl; q++) {
for (j=0; j<ENH_UPS0; j++) {
*pu=0.0;
pp = polyp[j]+q;
ps = seq1+dim1-1;
for (k=0; k<filterlength-q; k++) {
*pu += *ps-- * *pp++;
}
pu++;
}
}
}
/*----------------------------------------------------------------*
* find segment starting near idata+estSegPos that has highest
* correlation with idata+centerStartPos through
* idata+centerStartPos+ENH_BLOCKL-1 segment is found at a
* resolution of ENH_UPSO times the original of the original
* sampling rate
*---------------------------------------------------------------*/
void refiner(
float *seg, /* (o) segment array */
float *updStartPos, /* (o) updated start point */
float* idata, /* (i) original data buffer */
int idatal, /* (i) dimension of idata */
int centerStartPos, /* (i) beginning center segment */
float estSegPos,/* (i) estimated beginning other segment */
float period /* (i) estimated pitch period */
){
int estSegPosRounded,searchSegStartPos,searchSegEndPos,corrdim;
int tloc,tloc2,i,st,en,fraction;
float vect[ENH_VECTL],corrVec[ENH_CORRDIM],maxv;
float corrVecUps[ENH_CORRDIM*ENH_UPS0];
/* defining array bounds */
estSegPosRounded=(int)(estSegPos - 0.5);
searchSegStartPos=estSegPosRounded-ENH_SLOP;
if (searchSegStartPos<0) {
searchSegStartPos=0;
}
searchSegEndPos=estSegPosRounded+ENH_SLOP;
if (searchSegEndPos+ENH_BLOCKL >= idatal) {
searchSegEndPos=idatal-ENH_BLOCKL-1;
}
corrdim=searchSegEndPos-searchSegStartPos+1;
/* compute upsampled correlation (corr33) and find
location of max */
mycorr1(corrVec,idata+searchSegStartPos,
corrdim+ENH_BLOCKL-1,idata+centerStartPos,ENH_BLOCKL);
enh_upsample(corrVecUps,corrVec,corrdim,ENH_FL0);
tloc=0; maxv=corrVecUps[0];
for (i=1; i<ENH_UPS0*corrdim; i++) {
if (corrVecUps[i]>maxv) {
tloc=i;
maxv=corrVecUps[i];
}
}
/* make vector can be upsampled without ever running outside
bounds */
*updStartPos= (float)searchSegStartPos +
(float)tloc/(float)ENH_UPS0+(float)1.0;
tloc2=(int)(tloc/ENH_UPS0);
if (tloc>tloc2*ENH_UPS0) {
tloc2++;
}
st=searchSegStartPos+tloc2-ENH_FL0;
if (st<0) {
memset(vect,0,-st*sizeof(float));
memcpy(&vect[-st],idata, (ENH_VECTL+st)*sizeof(float));
}
else {
en=st+ENH_VECTL;
if (en>idatal) {
memcpy(vect, &idata[st],
(ENH_VECTL-(en-idatal))*sizeof(float));
memset(&vect[ENH_VECTL-(en-idatal)], 0,
(en-idatal)*sizeof(float));
}
else {
memcpy(vect, &idata[st], ENH_VECTL*sizeof(float));
}
}
fraction=tloc2*ENH_UPS0-tloc;
/* compute the segment (this is actually a convolution) */
mycorr1(seg,vect,ENH_VECTL,polyphaserTbl+(2*ENH_FL0+1)*fraction,
2*ENH_FL0+1);
}
/*----------------------------------------------------------------*
* find the smoothed output data
*---------------------------------------------------------------*/
void smath(
float *odata, /* (o) smoothed output */
float *sseq,/* (i) said second sequence of waveforms */
int hl, /* (i) 2*hl+1 is sseq dimension */
float alpha0/* (i) max smoothing energy fraction */
){
int i,k;
float w00,w10,w11,A,B,C,*psseq,err,errs;
float surround[BLOCKL_MAX]; /* shape contributed by other than
current */
float wt[2*ENH_HL+1]; /* waveform weighting to get
surround shape */
float denom;
/* create shape of contribution from all waveforms except the
current one */
for (i=1; i<=2*hl+1; i++) {
wt[i-1] = (float)0.5*(1 - (float)cos(2*PI*i/(2*hl+2)));
}
wt[hl]=0.0; /* for clarity, not used */
for (i=0; i<ENH_BLOCKL; i++) {
surround[i]=sseq[i]*wt[0];
}
for (k=1; k<hl; k++) {
psseq=sseq+k*ENH_BLOCKL;
for(i=0;i<ENH_BLOCKL; i++) {
surround[i]+=psseq[i]*wt[k];
}
}
for (k=hl+1; k<=2*hl; k++) {
psseq=sseq+k*ENH_BLOCKL;
for(i=0;i<ENH_BLOCKL; i++) {
surround[i]+=psseq[i]*wt[k];
}
}
/* compute some inner products */
w00 = w10 = w11 = 0.0;
psseq=sseq+hl*ENH_BLOCKL; /* current block */
for (i=0; i<ENH_BLOCKL;i++) {
w00+=psseq[i]*psseq[i];
w11+=surround[i]*surround[i];
w10+=surround[i]*psseq[i];
}
if (fabs(w11) < 1.0) {
w11=1.0;
}
C = (float)sqrt( w00/w11);
/* first try enhancement without power-constraint */
errs=0.0;
psseq=sseq+hl*ENH_BLOCKL;
for (i=0; i<ENH_BLOCKL; i++) {
odata[i]=C*surround[i];
err=psseq[i]-odata[i];
errs+=err*err;
}
/* if constraint violated by first try, add constraint */
if (errs > alpha0 * w00) {
if ( w00 < 1) {
w00=1;
}
denom = (w11*w00-w10*w10)/(w00*w00);
if (denom > 0.0001) { /* eliminates numerical problems
for if smooth */
A = (float)sqrt( (alpha0- alpha0*alpha0/4)/denom);
B = -alpha0/2 - A * w10/w00;
B = B+1;
}
else { /* essentially no difference between cycles;
smoothing not needed */
A= 0.0;
B= 1.0;
}
/* create smoothed sequence */
psseq=sseq+hl*ENH_BLOCKL;
for (i=0; i<ENH_BLOCKL; i++) {
odata[i]=A*surround[i]+B*psseq[i];
}
}
}
/*----------------------------------------------------------------*
* get the pitch-synchronous sample sequence
*---------------------------------------------------------------*/
void getsseq(
float *sseq, /* (o) the pitch-synchronous sequence */
float *idata, /* (i) original data */
int idatal, /* (i) dimension of data */
int centerStartPos, /* (i) where current block starts */
float *period, /* (i) rough-pitch-period array */
float *plocs, /* (i) where periods of period array
are taken */
int periodl, /* (i) dimension period array */
int hl /* (i) 2*hl+1 is the number of sequences */
){
int i,centerEndPos,q;
float blockStartPos[2*ENH_HL+1];
int lagBlock[2*ENH_HL+1];
float plocs2[ENH_PLOCSL];
float *psseq;
centerEndPos=centerStartPos+ENH_BLOCKL-1;
/* present */
NearestNeighbor(lagBlock+hl,plocs,
(float)0.5*(centerStartPos+centerEndPos),periodl);
blockStartPos[hl]=(float)centerStartPos;
psseq=sseq+ENH_BLOCKL*hl;
memcpy(psseq, idata+centerStartPos, ENH_BLOCKL*sizeof(float));
/* past */
for (q=hl-1; q>=0; q--) {
blockStartPos[q]=blockStartPos[q+1]-period[lagBlock[q+1]];
NearestNeighbor(lagBlock+q,plocs,
blockStartPos[q]+
ENH_BLOCKL_HALF-period[lagBlock[q+1]], periodl);
if (blockStartPos[q]-ENH_OVERHANG>=0) {
refiner(sseq+q*ENH_BLOCKL, blockStartPos+q, idata,
idatal, centerStartPos, blockStartPos[q],
period[lagBlock[q+1]]);
} else {
psseq=sseq+q*ENH_BLOCKL;
memset(psseq, 0, ENH_BLOCKL*sizeof(float));
}
}
/* future */
for (i=0; i<periodl; i++) {
plocs2[i]=plocs[i]-period[i];
}
for (q=hl+1; q<=2*hl; q++) {
NearestNeighbor(lagBlock+q,plocs2,
blockStartPos[q-1]+ENH_BLOCKL_HALF,periodl);
blockStartPos[q]=blockStartPos[q-1]+period[lagBlock[q]];
if (blockStartPos[q]+ENH_BLOCKL+ENH_OVERHANG<idatal) {
refiner(sseq+ENH_BLOCKL*q, blockStartPos+q, idata,
idatal, centerStartPos, blockStartPos[q],
period[lagBlock[q]]);
}
else {
psseq=sseq+q*ENH_BLOCKL;
memset(psseq, 0, ENH_BLOCKL*sizeof(float));
}
}
}
/*----------------------------------------------------------------*
* perform enhancement on idata+centerStartPos through
* idata+centerStartPos+ENH_BLOCKL-1
*---------------------------------------------------------------*/
void enhancer(
float *odata, /* (o) smoothed block, dimension blockl */
float *idata, /* (i) data buffer used for enhancing */
int idatal, /* (i) dimension idata */
int centerStartPos, /* (i) first sample current block
within idata */
float alpha0, /* (i) max correction-energy-fraction
(in [0,1]) */
float *period, /* (i) pitch period array */
float *plocs, /* (i) locations where period array
values valid */
int periodl /* (i) dimension of period and plocs */
){
float sseq[(2*ENH_HL+1)*ENH_BLOCKL];
/* get said second sequence of segments */
getsseq(sseq,idata,idatal,centerStartPos,period,
plocs,periodl,ENH_HL);
/* compute the smoothed output from said second sequence */
smath(odata,sseq,ENH_HL,alpha0);
}
/*----------------------------------------------------------------*
* cross correlation
*---------------------------------------------------------------*/
float xCorrCoef(
float *target, /* (i) first array */
float *regressor, /* (i) second array */
int subl /* (i) dimension arrays */
){
int i;
float ftmp1, ftmp2;
ftmp1 = 0.0;
ftmp2 = 0.0;
for (i=0; i<subl; i++) {
ftmp1 += target[i]*regressor[i];
ftmp2 += regressor[i]*regressor[i];
}
if (ftmp1 > 0.0) {
return (float)(ftmp1*ftmp1/ftmp2);
}
else {
return (float)0.0;
}
}
/*----------------------------------------------------------------*
* interface for enhancer
*---------------------------------------------------------------*/
Okay so the enhancer's big wrap-up function is giving main character energy ๐ โจ Here's the rest of it, then we're onto filter.h ๐ฆ
int enhancerInterface(
float *out, /* (o) enhanced signal */
float *in, /* (i) unenhanced signal */
iLBC_Dec_Inst_t *iLBCdec_inst /* (i) buffers etc */
){
float *enh_buf, *enh_period;
int iblock, isample;
int lag=0, ilag, i, ioffset;
float cc, maxcc;
float ftmp1, ftmp2;
float *inPtr, *enh_bufPtr1, *enh_bufPtr2;
float plc_pred[ENH_BLOCKL];
float lpState[6], downsampled[(ENH_NBLOCKS*ENH_BLOCKL+120)/2];
int inLen=ENH_NBLOCKS*ENH_BLOCKL+120;
int start, plc_blockl, inlag;
enh_buf=iLBCdec_inst->enh_buf;
enh_period=iLBCdec_inst->enh_period;
memmove(enh_buf, &enh_buf[iLBCdec_inst->blockl],
(ENH_BUFL-iLBCdec_inst->blockl)*sizeof(float));
memcpy(&enh_buf[ENH_BUFL-iLBCdec_inst->blockl], in,
iLBCdec_inst->blockl*sizeof(float));
if (iLBCdec_inst->mode==30)
plc_blockl=ENH_BLOCKL;
else
plc_blockl=40;
/* when 20 ms frame, move processing one block */
ioffset=0;
if (iLBCdec_inst->mode==20) ioffset=1;
i=3-ioffset;
memmove(enh_period, &enh_period[i],
(ENH_NBLOCKS_TOT-i)*sizeof(float));
/* Set state information to the 6 samples right before
the samples to be downsampled. */
memcpy(lpState,
enh_buf+(ENH_NBLOCKS_EXTRA+ioffset)*ENH_BLOCKL-126,
6*sizeof(float));
/* Down sample a factor 2 to save computations */
DownSample(enh_buf+(ENH_NBLOCKS_EXTRA+ioffset)*ENH_BLOCKL-120,
lpFilt_coefsTbl, inLen-ioffset*ENH_BLOCKL,
lpState, downsampled);
/* Estimate the pitch in the down sampled domain. */
for (iblock = 0; iblock<ENH_NBLOCKS-ioffset; iblock++) {
lag = 10;
maxcc = xCorrCoef(downsampled+60+iblock*
ENH_BLOCKL_HALF, downsampled+60+iblock*
ENH_BLOCKL_HALF-lag, ENH_BLOCKL_HALF);
for (ilag=11; ilag<60; ilag++) {
cc = xCorrCoef(downsampled+60+iblock*
ENH_BLOCKL_HALF, downsampled+60+iblock*
ENH_BLOCKL_HALF-ilag, ENH_BLOCKL_HALF);
if (cc > maxcc) {
maxcc = cc;
lag = ilag;
}
}
/* Store the estimated lag in the non-downsampled domain */
enh_period[iblock+ENH_NBLOCKS_EXTRA+ioffset] = (float)lag*2;
}
/* PLC was performed on the previous packet */
if (iLBCdec_inst->prev_enh_pl==1) {
inlag=(int)enh_period[ENH_NBLOCKS_EXTRA+ioffset];
lag = inlag-1;
maxcc = xCorrCoef(in, in+lag, plc_blockl);
for (ilag=inlag; ilag<=inlag+1; ilag++) {
cc = xCorrCoef(in, in+ilag, plc_blockl);
if (cc > maxcc) {
maxcc = cc;
lag = ilag;
}
}
enh_period[ENH_NBLOCKS_EXTRA+ioffset-1]=(float)lag;
/* compute new concealed residual for the old lookahead,
mix the forward PLC with a backward PLC from
the new frame */
inPtr=&in[lag-1];
enh_bufPtr1=&plc_pred[plc_blockl-1];
if (lag>plc_blockl) {
start=plc_blockl;
} else {
start=lag;
}
for (isample = start; isample>0; isample--) {
*enh_bufPtr1-- = *inPtr--;
}
enh_bufPtr2=&enh_buf[ENH_BUFL-1-iLBCdec_inst->blockl];
for (isample = (plc_blockl-1-lag); isample>=0; isample--) {
*enh_bufPtr1-- = *enh_bufPtr2--;
}
/* limit energy change */
ftmp2=0.0;
ftmp1=0.0;
for (i=0;i<plc_blockl;i++) {
ftmp2+=enh_buf[ENH_BUFL-1-iLBCdec_inst->blockl-i]*
enh_buf[ENH_BUFL-1-iLBCdec_inst->blockl-i];
ftmp1+=plc_pred[i]*plc_pred[i];
}
ftmp1=(float)sqrt(ftmp1/(float)plc_blockl);
ftmp2=(float)sqrt(ftmp2/(float)plc_blockl);
if (ftmp1>(float)2.0*ftmp2 && ftmp1>0.0) {
for (i=0;i<plc_blockl-10;i++) {
plc_pred[i]*=(float)2.0*ftmp2/ftmp1;
}
for (i=plc_blockl-10;i<plc_blockl;i++) {
plc_pred[i]*=(float)(i-plc_blockl+10)*
((float)1.0-(float)2.0*ftmp2/ftmp1)/(float)(10)+
(float)2.0*ftmp2/ftmp1;
}
}
enh_bufPtr1=&enh_buf[ENH_BUFL-1-iLBCdec_inst->blockl];
for (i=0; i<plc_blockl; i++) {
ftmp1 = (float) (i+1) / (float) (plc_blockl+1);
*enh_bufPtr1 *= ftmp1;
*enh_bufPtr1 += ((float)1.0-ftmp1)*
plc_pred[plc_blockl-1-i];
enh_bufPtr1--;
}
}
if (iLBCdec_inst->mode==20) {
/* Enhancer with 40 samples delay */
for (iblock = 0; iblock<2; iblock++) {
enhancer(out+iblock*ENH_BLOCKL, enh_buf,
ENH_BUFL, (5+iblock)*ENH_BLOCKL+40,
ENH_ALPHA0, enh_period, enh_plocsTbl,
ENH_NBLOCKS_TOT);
}
} else if (iLBCdec_inst->mode==30) {
/* Enhancer with 80 samples delay */
for (iblock = 0; iblock<3; iblock++) {
enhancer(out+iblock*ENH_BLOCKL, enh_buf,
ENH_BUFL, (4+iblock)*ENH_BLOCKL,
ENH_ALPHA0, enh_period, enh_plocsTbl,
ENH_NBLOCKS_TOT);
}
}
return (lag*2);
}
A.17. filter.h ๐
Last up in this chunk is the filter header, and it's giving a clean little lineup of function prototypes. Each one comes with its own little comment explaining what goes in and out, so you can see exactly how the state and the samples are passed around ๐ง
/******************************************************************
iLBC Speech Coder ANSI-C Source Code
filter.h
Copyright (C) The Internet Society (2004).
All Rights Reserved.
******************************************************************/
#ifndef __iLBC_FILTER_H
#define __iLBC_FILTER_H
void AllPoleFilter(
float *InOut, /* (i/o) on entrance InOut[-orderCoef] to
InOut[-1] contain the state of the
filter (delayed samples). InOut[0] to
InOut[lengthInOut-1] contain the filter
input, on en exit InOut[-orderCoef] to
InOut[-1] is unchanged and InOut[0] to
InOut[lengthInOut-1] contain filtered
samples */
float *Coef,/* (i) filter coefficients, Coef[0] is assumed
to be 1.0 */
int lengthInOut,/* (i) number of input/output samples */
int orderCoef /* (i) number of filter coefficients */
);
void AllZeroFilter(
float *In, /* (i) In[0] to In[lengthInOut-1] contain
filter input samples */
float *Coef,/* (i) filter coefficients (Coef[0] is assumed
to be 1.0) */
int lengthInOut,/* (i) number of input/output samples */
int orderCoef, /* (i) number of filter coefficients */
float *Out /* (i/o) on entrance Out[-orderCoef] to Out[-1]
contain the filter state, on exit Out[0]
to Out[lengthInOut-1] contain filtered
samples */
);
void ZeroPoleFilter(
float *In, /* (i) In[0] to In[lengthInOut-1] contain filter
input samples In[-orderCoef] to In[-1]
contain state of all-zero section */
float *ZeroCoef,/* (i) filter coefficients for all-zero
section (ZeroCoef[0] is assumed to
be 1.0) */
float *PoleCoef,/* (i) filter coefficients for all-pole section
(ZeroCoef[0] is assumed to be 1.0) */
int lengthInOut,/* (i) number of input/output samples */
int orderCoef, /* (i) number of filter coefficients */
float *Out /* (i/o) on entrance Out[-orderCoef] to Out[-1]
contain state of all-pole section. On
exit Out[0] to Out[lengthInOut-1]
contain filtered samples */
);
void DownSample (
float *In, /* (i) input samples */
float *Coef, /* (i) filter coefficients */
int lengthIn, /* (i) number of input samples */
float *state, /* (i) filter state */
float *Out /* (o) downsampled output */
);
#endif
A.18. filter.c ๐
Okay bestie, this one is the filter toolbox ๐งฐโจ It has all-pole, all-zero, and pole-zero filters plus the downsampler (low-pass filter and decimation in one go). Lowkey the DownSample loop is my favourite. ๐
/******************************************************************
iLBC Speech Coder ANSI-C Source Code
filter.c
Copyright (C) The Internet Society (2004).
All Rights Reserved.
******************************************************************/
#include "iLBC_define.h"
/*----------------------------------------------------------------*
* all-pole filter
*---------------------------------------------------------------*/
void AllPoleFilter(
float *InOut, /* (i/o) on entrance InOut[-orderCoef] to
InOut[-1] contain the state of the
filter (delayed samples). InOut[0] to
InOut[lengthInOut-1] contain the filter
input, on en exit InOut[-orderCoef] to
InOut[-1] is unchanged and InOut[0] to
InOut[lengthInOut-1] contain filtered
samples */
float *Coef,/* (i) filter coefficients, Coef[0] is assumed
to be 1.0 */
int lengthInOut,/* (i) number of input/output samples */
int orderCoef /* (i) number of filter coefficients */
){
int n,k;
for(n=0;n<lengthInOut;n++){
for(k=1;k<=orderCoef;k++){
*InOut -= Coef[k]*InOut[-k];
}
InOut++;
}
}
/*----------------------------------------------------------------*
* all-zero filter
*---------------------------------------------------------------*/
void AllZeroFilter(
float *In, /* (i) In[0] to In[lengthInOut-1] contain
filter input samples */
float *Coef,/* (i) filter coefficients (Coef[0] is assumed
to be 1.0) */
int lengthInOut,/* (i) number of input/output samples */
int orderCoef, /* (i) number of filter coefficients */
float *Out /* (i/o) on entrance Out[-orderCoef] to Out[-1]
contain the filter state, on exit Out[0]
to Out[lengthInOut-1] contain filtered
samples */
){
int n,k;
for(n=0;n<lengthInOut;n++){
*Out = Coef[0]*In[0];
for(k=1;k<=orderCoef;k++){
*Out += Coef[k]*In[-k];
}
Out++;
In++;
}
}
/*----------------------------------------------------------------*
* pole-zero filter
*---------------------------------------------------------------*/
void ZeroPoleFilter(
float *In, /* (i) In[0] to In[lengthInOut-1] contain
filter input samples In[-orderCoef] to
In[-1] contain state of all-zero
section */
float *ZeroCoef,/* (i) filter coefficients for all-zero
section (ZeroCoef[0] is assumed to
be 1.0) */
float *PoleCoef,/* (i) filter coefficients for all-pole section
(ZeroCoef[0] is assumed to be 1.0) */
int lengthInOut,/* (i) number of input/output samples */
int orderCoef, /* (i) number of filter coefficients */
float *Out /* (i/o) on entrance Out[-orderCoef] to Out[-1]
contain state of all-pole section. On
exit Out[0] to Out[lengthInOut-1]
contain filtered samples */
){
AllZeroFilter(In,ZeroCoef,lengthInOut,orderCoef,Out);
AllPoleFilter(Out,PoleCoef,lengthInOut,orderCoef);
}
/*----------------------------------------------------------------*
* downsample (LP filter and decimation)
*---------------------------------------------------------------*/
void DownSample (
float *In, /* (i) input samples */
float *Coef, /* (i) filter coefficients */
int lengthIn, /* (i) number of input samples */
float *state, /* (i) filter state */
float *Out /* (o) downsampled output */
){
float o;
float *Out_ptr = Out;
float *Coef_ptr, *In_ptr;
float *state_ptr;
int i, j, stop;
/* LP filter and decimate at the same time */
for (i = DELAY_DS; i < lengthIn; i+=FACTOR_DS)
{
Coef_ptr = &Coef[0];
In_ptr = &In[i];
state_ptr = &state[FILTERORDER_DS-2];
o = (float)0.0;
stop = (i < FILTERORDER_DS) ? i + 1 : FILTERORDER_DS;
for (j = 0; j < stop; j++)
{
o += *Coef_ptr++ * (*In_ptr--);
}
for (j = i + 1; j < FILTERORDER_DS; j++)
{
o += *Coef_ptr++ * (*state_ptr--);
}
*Out_ptr++ = o;
}
/* Get the last part (use zeros as input for the future) */
for (i=(lengthIn+FACTOR_DS); i<(lengthIn+DELAY_DS);
i+=FACTOR_DS) {
o=(float)0.0;
if (i<lengthIn) {
Coef_ptr = &Coef[0];
In_ptr = &In[i];
for (j=0; j<FILTERORDER_DS; j++) {
o += *Coef_ptr++ * (*Out_ptr--);
}
} else {
Coef_ptr = &Coef[i-lengthIn];
In_ptr = &In[lengthIn-1];
for (j=0; j<FILTERORDER_DS-(i-lengthIn); j++) {
o += *Coef_ptr++ * (*In_ptr--);
}
}
*Out_ptr++ = o;
}
}
A.19. FrameClassify.h ๐ฆ
Next up is the header for FrameClassify. It basically says "hey, give me the index of the loudest sub-frame" ๐ฅ The encoder state and the LPC residual go in, and an int comes out.
/******************************************************************
iLBC Speech Coder ANSI-C Source Code
FrameClassify.h
Copyright (C) The Internet Society (2004).
All Rights Reserved.
******************************************************************/
#ifndef __iLBC_FRAMECLASSIFY_H
#define __iLBC_FRAMECLASSIFY_H
int FrameClassify( /* index to the max-energy sub-frame */
iLBC_Enc_Inst_t *iLBCenc_inst,
/* (i/o) the encoder state structure */
float *residual /* (i) lpc residual signal */
);
#endif
A.20. FrameClassify.c ๐ธ
Okay this is the actual implementation of FrameClassify, and it's giving energy math ๐งฎ๐ It works out the front and back energies of each sub-frame, applies those little weighting windows (ssqEn_win and sampEn_win), and then picks whichever weighted 80-sample chunk has the most energy. Iconic. ๐
/******************************************************************
iLBC Speech Coder ANSI-C Source Code
FrameClassify.c
Copyright (C) The Internet Society (2004).
All Rights Reserved.
******************************************************************/
#include "iLBC_define.h"
/*---------------------------------------------------------------*
* Classification of subframes to localize start state
*--------------------------------------------------------------*/
int FrameClassify( /* index to the max-energy sub-frame */
iLBC_Enc_Inst_t *iLBCenc_inst,
/* (i/o) the encoder state structure */
float *residual /* (i) lpc residual signal */
) {
float max_ssqEn, fssqEn[NSUB_MAX], bssqEn[NSUB_MAX], *pp;
int n, l, max_ssqEn_n;
const float ssqEn_win[NSUB_MAX-1]={(float)0.8,(float)0.9,
(float)1.0,(float)0.9,(float)0.8};
const float sampEn_win[5]={(float)1.0/(float)6.0,
(float)2.0/(float)6.0, (float)3.0/(float)6.0,
(float)4.0/(float)6.0, (float)5.0/(float)6.0};
/* init the front and back energies to zero */
memset(fssqEn, 0, NSUB_MAX*sizeof(float));
memset(bssqEn, 0, NSUB_MAX*sizeof(float));
/* Calculate front of first seqence */
n=0;
pp=residual;
for (l=0; l<5; l++) {
fssqEn[n] += sampEn_win[l] * (*pp) * (*pp);
pp++;
}
for (l=5; l<SUBL; l++) {
fssqEn[n] += (*pp) * (*pp);
pp++;
}
/* Calculate front and back of all middle sequences */
for (n=1; n<iLBCenc_inst->nsub-1; n++) {
pp=residual+n*SUBL;
for (l=0; l<5; l++) {
fssqEn[n] += sampEn_win[l] * (*pp) * (*pp);
bssqEn[n] += (*pp) * (*pp);
pp++;
}
for (l=5; l<SUBL-5; l++) {
fssqEn[n] += (*pp) * (*pp);
bssqEn[n] += (*pp) * (*pp);
pp++;
}
for (l=SUBL-5; l<SUBL; l++) {
fssqEn[n] += (*pp) * (*pp);
bssqEn[n] += sampEn_win[SUBL-l-1] * (*pp) * (*pp);
pp++;
}
}
/* Calculate back of last seqence */
n=iLBCenc_inst->nsub-1;
pp=residual+n*SUBL;
for (l=0; l<SUBL-5; l++) {
bssqEn[n] += (*pp) * (*pp);
pp++;
}
for (l=SUBL-5; l<SUBL; l++) {
bssqEn[n] += sampEn_win[SUBL-l-1] * (*pp) * (*pp);
pp++;
}
/* find the index to the weighted 80 sample with
most energy */
if (iLBCenc_inst->mode==20) l=1;
else l=0;
max_ssqEn=(fssqEn[0]+bssqEn[1])*ssqEn_win[l];
max_ssqEn_n=1;
for (n=2; n<iLBCenc_inst->nsub; n++) {
l++;
if ((fssqEn[n-1]+bssqEn[n])*ssqEn_win[l] > max_ssqEn) {
max_ssqEn=(fssqEn[n-1]+bssqEn[n]) *
ssqEn_win[l];
max_ssqEn_n=n;
}
}
return max_ssqEn_n;
}
A.21. gainquant.h โจ
Last one for this part! The gainquant header declares two functions: gainquant, which quantizes a gain value into an index, and gaindequant, which turns that index back into a gain value ๐ Bestie, it's the encode and decode duo. ๐ซถ
/******************************************************************
iLBC Speech Coder ANSI-C Source Code
gainquant.h
Copyright (C) The Internet Society (2004).
All Rights Reserved.
******************************************************************/
#ifndef __iLBC_GAINQUANT_H
#define __iLBC_GAINQUANT_H
float gainquant(/* (o) quantized gain value */
float in, /* (i) gain value */
float maxIn,/* (i) maximum of gain value */
int cblen, /* (i) number of quantization indices */
int *index /* (o) quantization index */
);
float gaindequant( /* (o) quantized gain value */
int index, /* (i) quantization index */
float maxIn,/* (i) maximum of unquantized gain */
int cblen /* (i) number of quantization indices */
);
#endif
A.22. gainquant.c โจ
okay so this one is the gain quantizer for the gain-shape coding of the residual ๐ it's got two functions, gainquant() to pick the closest entry in a gain table and gaindequant() to turn the index back into a gain on the decoder side ๐
/******************************************************************
iLBC Speech Coder ANSI-C Source Code
gainquant.c
Copyright (C) The Internet Society (2004).
All Rights Reserved.
******************************************************************/
#include <string.h>
#include <math.h>
#include "constants.h"
#include "filter.h"
/*----------------------------------------------------------------*
* quantizer for the gain in the gain-shape coding of residual
*---------------------------------------------------------------*/
float gainquant(/* (o) quantized gain value */
float in, /* (i) gain value */
float maxIn,/* (i) maximum of gain value */
int cblen, /* (i) number of quantization indices */
int *index /* (o) quantization index */
){
int i, tindex;
float minmeasure,measure, *cb, scale;
/* ensure a lower bound on the scaling factor */
scale=maxIn;
if (scale<0.1) {
scale=(float)0.1;
}
/* select the quantization table */
if (cblen == 8) {
cb = gain_sq3Tbl;
} else if (cblen == 16) {
cb = gain_sq4Tbl;
} else {
cb = gain_sq5Tbl;
}
/* select the best index in the quantization table */
minmeasure=10000000.0;
tindex=0;
for (i=0; i<cblen; i++) {
measure=(in-scale*cb[i])*(in-scale*cb[i]);
if (measure<minmeasure) {
tindex=i;
minmeasure=measure;
}
}
*index=tindex;
/* return the quantized value */
return scale*cb[tindex];
}
/*----------------------------------------------------------------*
* decoder for quantized gains in the gain-shape coding of
* residual
*---------------------------------------------------------------*/
float gaindequant( /* (o) quantized gain value */
int index, /* (i) quantization index */
float maxIn,/* (i) maximum of unquantized gain */
int cblen /* (i) number of quantization indices */
){
float scale;
/* obtain correct scale factor */
scale=(float)fabs(maxIn);
if (scale<0.1) {
scale=(float)0.1;
}
/* select the quantization table and return the decoded value */
if (cblen==8) {
return scale*gain_sq3Tbl[index];
} else if (cblen==16) {
return scale*gain_sq4Tbl[index];
}
else if (cblen==32) {
return scale*gain_sq5Tbl[index];
}
return 0.0;
}
A.23. getCBvec.h ๐ฆ
this header is the little doorway for getCBvec(), the function that builds a codebook vector from a given index ๐ง it's giving tidy and minimal, just the prototype and the include guard
/******************************************************************
iLBC Speech Coder ANSI-C Source Code
getCBvec.h
Copyright (C) The Internet Society (2004).
All Rights Reserved.
******************************************************************/
#ifndef __iLBC_GETCBVEC_H
#define __iLBC_GETCBVEC_H
void getCBvec(
float *cbvec, /* (o) Constructed codebook vector */
float *mem, /* (i) Codebook buffer */
int index, /* (i) Codebook index */
int lMem, /* (i) Length of codebook buffer */
int cbveclen/* (i) Codebook vector length */
);
#endif
A.24. getCBvec.c ๐ธ
okay this is the big one bestie ๐ญ getCBvec() figures out how the codebook is split into sections and builds the vector differently depending on which section the index lands in. there's a plain copy part, an interpolation part, and a filtering part for the higher sections, and it's honestly so iconic how it all fits together ๐
/******************************************************************
iLBC Speech Coder ANSI-C Source Code
getCBvec.c
Copyright (C) The Internet Society (2004).
All Rights Reserved.
******************************************************************/
#include "iLBC_define.h"
#include "constants.h"
#include <string.h>
/*----------------------------------------------------------------*
* Construct codebook vector for given index.
*---------------------------------------------------------------*/
void getCBvec(
float *cbvec, /* (o) Constructed codebook vector */
float *mem, /* (i) Codebook buffer */
int index, /* (i) Codebook index */
int lMem, /* (i) Length of codebook buffer */
int cbveclen/* (i) Codebook vector length */
){
int j, k, n, memInd, sFilt;
float tmpbuf[CB_MEML];
int base_size;
int ilow, ihigh;
float alfa, alfa1;
/* Determine size of codebook sections */
base_size=lMem-cbveclen+1;
if (cbveclen==SUBL) {
base_size+=cbveclen/2;
}
/* No filter -> First codebook section */
if (index<lMem-cbveclen+1) {
/* first non-interpolated vectors */
k=index+cbveclen;
/* get vector */
memcpy(cbvec, mem+lMem-k, cbveclen*sizeof(float));
} else if (index < base_size) {
k=2*(index-(lMem-cbveclen+1))+cbveclen;
ihigh=k/2;
ilow=ihigh-5;
/* Copy first noninterpolated part */
memcpy(cbvec, mem+lMem-k/2, ilow*sizeof(float));
/* interpolation */
alfa1=(float)0.2;
alfa=0.0;
for (j=ilow; j<ihigh; j++) {
cbvec[j]=((float)1.0-alfa)*mem[lMem-k/2+j]+
alfa*mem[lMem-k+j];
alfa+=alfa1;
}
/* Copy second noninterpolated part */
memcpy(cbvec+ihigh, mem+lMem-k+ihigh,
(cbveclen-ihigh)*sizeof(float));
}
/* Higher codebook section based on filtering */
else {
/* first non-interpolated vectors */
if (index-base_size<lMem-cbveclen+1) {
float tempbuff2[CB_MEML+CB_FILTERLEN+1];
float *pos;
float *pp, *pp1;
memset(tempbuff2, 0,
CB_HALFFILTERLEN*sizeof(float));
memcpy(&tempbuff2[CB_HALFFILTERLEN], mem,
lMem*sizeof(float));
memset(&tempbuff2[lMem+CB_HALFFILTERLEN], 0,
(CB_HALFFILTERLEN+1)*sizeof(float));
k=index-base_size+cbveclen;
sFilt=lMem-k;
memInd=sFilt+1-CB_HALFFILTERLEN;
/* do filtering */
pos=cbvec;
memset(pos, 0, cbveclen*sizeof(float));
for (n=0; n<cbveclen; n++) {
pp=&tempbuff2[memInd+n+CB_HALFFILTERLEN];
pp1=&cbfiltersTbl[CB_FILTERLEN-1];
for (j=0; j<CB_FILTERLEN; j++) {
(*pos)+=(*pp++)*(*pp1--);
}
pos++;
}
}
/* interpolated vectors */
else {
float tempbuff2[CB_MEML+CB_FILTERLEN+1];
float *pos;
float *pp, *pp1;
int i;
memset(tempbuff2, 0,
CB_HALFFILTERLEN*sizeof(float));
memcpy(&tempbuff2[CB_HALFFILTERLEN], mem,
lMem*sizeof(float));
memset(&tempbuff2[lMem+CB_HALFFILTERLEN], 0,
(CB_HALFFILTERLEN+1)*sizeof(float));
k=2*(index-base_size-
(lMem-cbveclen+1))+cbveclen;
sFilt=lMem-k;
memInd=sFilt+1-CB_HALFFILTERLEN;
/* do filtering */
pos=&tmpbuf[sFilt];
memset(pos, 0, k*sizeof(float));
for (i=0; i<k; i++) {
pp=&tempbuff2[memInd+i+CB_HALFFILTERLEN];
pp1=&cbfiltersTbl[CB_FILTERLEN-1];
for (j=0; j<CB_FILTERLEN; j++) {
(*pos)+=(*pp++)*(*pp1--);
}
pos++;
}
ihigh=k/2;
ilow=ihigh-5;
/* Copy first noninterpolated part */
memcpy(cbvec, tmpbuf+lMem-k/2,
ilow*sizeof(float));
/* interpolation */
alfa1=(float)0.2;
alfa=0.0;
for (j=ilow; j<ihigh; j++) {
cbvec[j]=((float)1.0-alfa)*
tmpbuf[lMem-k/2+j]+alfa*tmpbuf[lMem-k+j];
alfa+=alfa1;
}
/* Copy second noninterpolated part */
memcpy(cbvec+ihigh, tmpbuf+lMem-k+ihigh,
(cbveclen-ihigh)*sizeof(float));
}
}
}
A.25. helpfun.h ๐
last one in this batch! helpfun.h is the header for the grab bag of helper functions the codec leans on, like autocorrelation, windowing, Levinson-Durbin, interpolation, bandwidth expansion, vector quantization, and the LSF stability check ๐ every prototype comes with its own little comment about what goes in and out, so it's super easy to follow along โจ
/******************************************************************
iLBC Speech Coder ANSI-C Source Code
helpfun.h
Copyright (C) The Internet Society (2004).
All Rights Reserved.
******************************************************************/
#ifndef __iLBC_HELPFUN_H
#define __iLBC_HELPFUN_H
void autocorr(
float *r, /* (o) autocorrelation vector */
const float *x, /* (i) data vector */
int N, /* (i) length of data vector */
int order /* largest lag for calculated
autocorrelations */
);
void window(
float *z, /* (o) the windowed data */
const float *x, /* (i) the original data vector */
const float *y, /* (i) the window */
int N /* (i) length of all vectors */
);
void levdurb(
float *a, /* (o) lpc coefficient vector starting
with 1.0 */
float *k, /* (o) reflection coefficients */
float *r, /* (i) autocorrelation vector */
int order /* (i) order of lpc filter */
);
void interpolate(
float *out, /* (o) the interpolated vector */
float *in1, /* (i) the first vector for the
interpolation */
float *in2, /* (i) the second vector for the
interpolation */
float coef, /* (i) interpolation weights */
int length /* (i) length of all vectors */
);
void bwexpand(
float *out, /* (o) the bandwidth expanded lpc
coefficients */
float *in, /* (i) the lpc coefficients before bandwidth
expansion */
float coef, /* (i) the bandwidth expansion factor */
int length /* (i) the length of lpc coefficient vectors */
);
void vq(
float *Xq, /* (o) the quantized vector */
int *index, /* (o) the quantization index */
const float *CB,/* (i) the vector quantization codebook */
float *X, /* (i) the vector to quantize */
int n_cb, /* (i) the number of vectors in the codebook */
int dim /* (i) the dimension of all vectors */
);
void SplitVQ(
float *qX, /* (o) the quantized vector */
int *index, /* (o) a vector of indexes for all vector
codebooks in the split */
float *X, /* (i) the vector to quantize */
const float *CB,/* (i) the quantizer codebook */
int nsplit, /* the number of vector splits */
const int *dim, /* the dimension of X and qX */
const int *cbsize /* the number of vectors in the codebook */
);
void sort_sq(
float *xq, /* (o) the quantized value */
int *index, /* (o) the quantization index */
float x, /* (i) the value to quantize */
const float *cb,/* (i) the quantization codebook */
int cb_size /* (i) the size of the quantization codebook */
);
int LSF_check( /* (o) 1 for stable lsf vectors and 0 for
nonstable ones */
float *lsf, /* (i) a table of lsf vectors */
int dim, /* (i) the dimension of each lsf vector */
int NoAn /* (i) the number of lsf vectors in the
table */
);
#endif
A.26. helpfun.c โจ
okay so helpfun.c is literally the grab bag of helper functions ๐ง autocorrelation, windowing, levinson-durbin, interpolation, bandwidth expansion, vector quantization, and the LSF stability check. here's the whole file, unchanged:
/******************************************************************
iLBC Speech Coder ANSI-C Source Code
helpfun.c
Copyright (C) The Internet Society (2004).
All Rights Reserved.
******************************************************************/
#include <math.h>
#include "iLBC_define.h"
#include "constants.h"
/*----------------------------------------------------------------*
* calculation of auto correlation
*---------------------------------------------------------------*/
void autocorr(
float *r, /* (o) autocorrelation vector */
const float *x, /* (i) data vector */
int N, /* (i) length of data vector */
int order /* largest lag for calculated
autocorrelations */
){
int lag, n;
float sum;
for (lag = 0; lag <= order; lag++) {
sum = 0;
for (n = 0; n < N - lag; n++) {
sum += x[n] * x[n+lag];
}
r[lag] = sum;
}
}
/*----------------------------------------------------------------*
* window multiplication
*---------------------------------------------------------------*/
void window(
float *z, /* (o) the windowed data */
const float *x, /* (i) the original data vector */
const float *y, /* (i) the window */
int N /* (i) length of all vectors */
){
int i;
for (i = 0; i < N; i++) {
z[i] = x[i] * y[i];
}
}
/*----------------------------------------------------------------*
* levinson-durbin solution for lpc coefficients
*---------------------------------------------------------------*/
void levdurb(
float *a, /* (o) lpc coefficient vector starting
with 1.0 */
float *k, /* (o) reflection coefficients */
float *r, /* (i) autocorrelation vector */
int order /* (i) order of lpc filter */
){
float sum, alpha;
int m, m_h, i;
a[0] = 1.0;
if (r[0] < EPS) { /* if r[0] <= 0, set LPC coeff. to zero */
for (i = 0; i < order; i++) {
k[i] = 0;
a[i+1] = 0;
}
} else {
a[1] = k[0] = -r[1]/r[0];
alpha = r[0] + r[1] * k[0];
for (m = 1; m < order; m++){
sum = r[m + 1];
for (i = 0; i < m; i++){
sum += a[i+1] * r[m - i];
}
k[m] = -sum / alpha;
alpha += k[m] * sum;
m_h = (m + 1) >> 1;
for (i = 0; i < m_h; i++){
sum = a[i+1] + k[m] * a[m - i];
a[m - i] += k[m] * a[i+1];
a[i+1] = sum;
}
a[m+1] = k[m];
}
}
}
/*----------------------------------------------------------------*
* interpolation between vectors
*---------------------------------------------------------------*/
void interpolate(
float *out, /* (o) the interpolated vector */
float *in1, /* (i) the first vector for the
interpolation */
float *in2, /* (i) the second vector for the
interpolation */
float coef, /* (i) interpolation weights */
int length /* (i) length of all vectors */
){
int i;
float invcoef;
invcoef = (float)1.0 - coef;
for (i = 0; i < length; i++) {
out[i] = coef * in1[i] + invcoef * in2[i];
}
}
/*----------------------------------------------------------------*
* lpc bandwidth expansion
*---------------------------------------------------------------*/
void bwexpand(
float *out, /* (o) the bandwidth expanded lpc
coefficients */
float *in, /* (i) the lpc coefficients before bandwidth
expansion */
float coef, /* (i) the bandwidth expansion factor */
int length /* (i) the length of lpc coefficient vectors */
){
int i;
float chirp;
chirp = coef;
out[0] = in[0];
for (i = 1; i < length; i++) {
out[i] = chirp * in[i];
chirp *= coef;
}
}
/*----------------------------------------------------------------*
* vector quantization
*---------------------------------------------------------------*/
void vq(
float *Xq, /* (o) the quantized vector */
int *index, /* (o) the quantization index */
const float *CB,/* (i) the vector quantization codebook */
float *X, /* (i) the vector to quantize */
int n_cb, /* (i) the number of vectors in the codebook */
int dim /* (i) the dimension of all vectors */
){
int i, j;
int pos, minindex;
float dist, tmp, mindist;
pos = 0;
mindist = FLOAT_MAX;
minindex = 0;
for (j = 0; j < n_cb; j++) {
dist = X[0] - CB[pos];
dist *= dist;
for (i = 1; i < dim; i++) {
tmp = X[i] - CB[pos + i];
dist += tmp*tmp;
}
if (dist < mindist) {
mindist = dist;
minindex = j;
}
pos += dim;
}
for (i = 0; i < dim; i++) {
Xq[i] = CB[minindex*dim + i];
}
*index = minindex;
}
/*----------------------------------------------------------------*
* split vector quantization
*---------------------------------------------------------------*/
void SplitVQ(
float *qX, /* (o) the quantized vector */
int *index, /* (o) a vector of indexes for all vector
codebooks in the split */
float *X, /* (i) the vector to quantize */
const float *CB,/* (i) the quantizer codebook */
int nsplit, /* the number of vector splits */
const int *dim, /* the dimension of X and qX */
const int *cbsize /* the number of vectors in the codebook */
){
int cb_pos, X_pos, i;
cb_pos = 0;
X_pos= 0;
for (i = 0; i < nsplit; i++) {
vq(qX + X_pos, index + i, CB + cb_pos, X + X_pos,
cbsize[i], dim[i]);
X_pos += dim[i];
cb_pos += dim[i] * cbsize[i];
}
}
/*----------------------------------------------------------------*
* scalar quantization
*---------------------------------------------------------------*/
void sort_sq(
float *xq, /* (o) the quantized value */
int *index, /* (o) the quantization index */
float x, /* (i) the value to quantize */
const float *cb,/* (i) the quantization codebook */
int cb_size /* (i) the size of the quantization codebook */
){
int i;
if (x <= cb[0]) {
*index = 0;
*xq = cb[0];
} else {
i = 0;
while ((x > cb[i]) && i < cb_size - 1) {
i++;
}
if (x > ((cb[i] + cb[i - 1])/2)) {
*index = i;
*xq = cb[i];
} else {
*index = i - 1;
*xq = cb[i - 1];
}
}
}
/*----------------------------------------------------------------*
* check for stability of lsf coefficients
*---------------------------------------------------------------*/
int LSF_check( /* (o) 1 for stable lsf vectors and 0 for
nonstable ones */
float *lsf, /* (i) a table of lsf vectors */
int dim, /* (i) the dimension of each lsf vector */
int NoAn /* (i) the number of lsf vectors in the
table */
){
int k,n,m, Nit=2, change=0,pos;
float tmp;
static float eps=(float)0.039; /* 50 Hz */
static float eps2=(float)0.0195;
static float maxlsf=(float)3.14; /* 4000 Hz */
static float minlsf=(float)0.01; /* 0 Hz */
/* LSF separation check*/
for (n=0; n<Nit; n++) { /* Run through a couple of times */
for (m=0; m<NoAn; m++) { /* Number of analyses per frame */
for (k=0; k<(dim-1); k++) {
pos=m*dim+k;
if ((lsf[pos+1]-lsf[pos])<eps) {
if (lsf[pos+1]<lsf[pos]) {
tmp=lsf[pos+1];
lsf[pos+1]= lsf[pos]+eps2;
lsf[pos]= lsf[pos+1]-eps2;
} else {
lsf[pos]-=eps2;
lsf[pos+1]+=eps2;
}
change=1;
}
if (lsf[pos]<minlsf) {
lsf[pos]=minlsf;
change=1;
}
if (lsf[pos]>maxlsf) {
lsf[pos]=maxlsf;
change=1;
}
}
}
}
return change;
}
A.27. hpInput.h ๐
a tiny header, super cute ๐
/******************************************************************
iLBC Speech Coder ANSI-C Source Code
hpInput.h
Copyright (C) The Internet Society (2004).
All Rights Reserved.
******************************************************************/
#ifndef __iLBC_HPINPUT_H
#define __iLBC_HPINPUT_H
void hpInput(
float *In, /* (i) vector to filter */
int len, /* (i) length of vector to filter */
float *Out, /* (o) the resulting filtered vector */
float *mem /* (i/o) the filter state */
);
#endif
A.28. hpInput.c ๐ธ
this is the input high-pass filter, and it has two sections: an all-zero part and an all-pole part ๐ซถ
/******************************************************************
iLBC Speech Coder ANSI-C Source Code
hpInput.c
Copyright (C) The Internet Society (2004).
All Rights Reserved.
******************************************************************/
#include "constants.h"
/*----------------------------------------------------------------*
* Input high-pass filter
*---------------------------------------------------------------*/
void hpInput(
float *In, /* (i) vector to filter */
int len, /* (i) length of vector to filter */
float *Out, /* (o) the resulting filtered vector */
float *mem /* (i/o) the filter state */
){
int i;
float *pi, *po;
/* all-zero section*/
pi = &In[0];
po = &Out[0];
for (i=0; i<len; i++) {
*po = hpi_zero_coefsTbl[0] * (*pi);
*po += hpi_zero_coefsTbl[1] * mem[0];
*po += hpi_zero_coefsTbl[2] * mem[1];
mem[1] = mem[0];
mem[0] = *pi;
po++;
pi++;
}
/* all-pole section*/
po = &Out[0];
for (i=0; i<len; i++) {
*po -= hpi_pole_coefsTbl[1] * mem[2];
*po -= hpi_pole_coefsTbl[2] * mem[3];
mem[3] = mem[2];
mem[2] = *po;
po++;
}
}
A.29. hpOutput.h ๐ฆ
last one in this part, and it's the output twin of hpInput.h ๐
/******************************************************************
iLBC Speech Coder ANSI-C Source Code
hpOutput.h
Copyright (C) The Internet Society (2004).
All Rights Reserved.
******************************************************************/
#ifndef __iLBC_HPOUTPUT_H
#define __iLBC_HPOUTPUT_H
void hpOutput(
float *In, /* (i) vector to filter */
int len,/* (i) length of vector to filter */
float *Out, /* (o) the resulting filtered vector */
float *mem /* (i/o) the filter state */
);
#endif
A.30. hpOutput.c ๐
/******************************************************************
iLBC Speech Coder ANSI-C Source Code
hpOutput.c
Copyright (C) The Internet Society (2004).
All Rights Reserved.
******************************************************************/
#include "constants.h"
/*----------------------------------------------------------------*
* Output high-pass filter
*---------------------------------------------------------------*/
void hpOutput(
float *In, /* (i) vector to filter */
int len,/* (i) length of vector to filter */
float *Out, /* (o) the resulting filtered vector */
float *mem /* (i/o) the filter state */
){
int i;
float *pi, *po;
/* all-zero section*/
pi = &In[0];
po = &Out[0];
for (i=0; i<len; i++) {
*po = hpo_zero_coefsTbl[0] * (*pi);
*po += hpo_zero_coefsTbl[1] * mem[0];
*po += hpo_zero_coefsTbl[2] * mem[1];
mem[1] = mem[0];
mem[0] = *pi;
po++;
pi++;
}
/* all-pole section*/
po = &Out[0];
for (i=0; i<len; i++) {
*po -= hpo_pole_coefsTbl[1] * mem[2];
*po -= hpo_pole_coefsTbl[2] * mem[3];
mem[3] = mem[2];
mem[2] = *po;
po++;
}
}
A.31. iCBConstruct.h ๐
/******************************************************************
iLBC Speech Coder ANSI-C Source Code
iCBConstruct.h
Copyright (C) The Internet Society (2004).
All Rights Reserved.
******************************************************************/
#ifndef __iLBC_ICBCONSTRUCT_H
#define __iLBC_ICBCONSTRUCT_H
void index_conv_enc(
int *index /* (i/o) Codebook indexes */
);
void index_conv_dec(
int *index /* (i/o) Codebook indexes */
);
void iCBConstruct(
float *decvector, /* (o) Decoded vector */
int *index, /* (i) Codebook indices */
int *gain_index,/* (i) Gain quantization indices */
float *mem, /* (i) Buffer for codevector construction */
int lMem, /* (i) Length of buffer */
int veclen, /* (i) Length of vector */
int nStages /* (i) Number of codebook stages */
);
#endif
A.32. iCBConstruct.c ๐ฆ
/******************************************************************
iLBC Speech Coder ANSI-C Source Code
iCBConstruct.c
Copyright (C) The Internet Society (2004).
All Rights Reserved.
******************************************************************/
#include <math.h>
#include "iLBC_define.h"
#include "gainquant.h"
#include "getCBvec.h"
/*----------------------------------------------------------------*
* Convert the codebook indexes to make the search easier
*---------------------------------------------------------------*/
void index_conv_enc(
int *index /* (i/o) Codebook indexes */
){
int k;
for (k=1; k<CB_NSTAGES; k++) {
if ((index[k]>=108)&&(index[k]<172)) {
index[k]-=64;
} else if (index[k]>=236) {
index[k]-=128;
} else {
/* ERROR */
}
}
}
void index_conv_dec(
int *index /* (i/o) Codebook indexes */
){
int k;
for (k=1; k<CB_NSTAGES; k++) {
if ((index[k]>=44)&&(index[k]<108)) {
index[k]+=64;
} else if ((index[k]>=108)&&(index[k]<128)) {
index[k]+=128;
} else {
/* ERROR */
}
}
}
/*----------------------------------------------------------------*
* Construct decoded vector from codebook and gains.
*---------------------------------------------------------------*/
void iCBConstruct(
float *decvector, /* (o) Decoded vector */
int *index, /* (i) Codebook indices */
int *gain_index,/* (i) Gain quantization indices */
float *mem, /* (i) Buffer for codevector construction */
int lMem, /* (i) Length of buffer */
int veclen, /* (i) Length of vector */
int nStages /* (i) Number of codebook stages */
){
int j,k;
float gain[CB_NSTAGES];
float cbvec[SUBL];
/* gain de-quantization */
gain[0] = gaindequant(gain_index[0], 1.0, 32);
if (nStages > 1) {
gain[1] = gaindequant(gain_index[1],
(float)fabs(gain[0]), 16);
}
if (nStages > 2) {
gain[2] = gaindequant(gain_index[2],
(float)fabs(gain[1]), 8);
}
/* codebook vector construction and construction of
total vector */
getCBvec(cbvec, mem, index[0], lMem, veclen);
for (j=0;j<veclen;j++){
decvector[j] = gain[0]*cbvec[j];
}
if (nStages > 1) {
for (k=1; k<nStages; k++) {
getCBvec(cbvec, mem, index[k], lMem, veclen);
for (j=0;j<veclen;j++) {
decvector[j] += gain[k]*cbvec[j];
}
}
}
}
A.33. iCBSearch.h โจ
/******************************************************************
iLBC Speech Coder ANSI-C Source Code
iCBSearch.h
Copyright (C) The Internet Society (2004).
All Rights Reserved.
******************************************************************/
#ifndef __iLBC_ICBSEARCH_H
#define __iLBC_ICBSEARCH_H
void iCBSearch(
iLBC_Enc_Inst_t *iLBCenc_inst,
/* (i) the encoder state structure */
int *index, /* (o) Codebook indices */
int *gain_index,/* (o) Gain quantization indices */
float *intarget,/* (i) Target vector for encoding */
float *mem, /* (i) Buffer for codebook construction */
int lMem, /* (i) Length of buffer */
int lTarget, /* (i) Length of vector */
int nStages, /* (i) Number of codebook stages */
float *weightDenum, /* (i) weighting filter coefficients */
float *weightState, /* (i) weighting filter state */
int block /* (i) the sub-block number */
);
#endif
A.34. iCBSearch.c ๐
/******************************************************************
iLBC Speech Coder ANSI-C Source Code
iCBSearch.c
Copyright (C) The Internet Society (2004).
All Rights Reserved.
******************************************************************/
#include <math.h>
#include <string.h>
#include "iLBC_define.h"
#include "gainquant.h"
#include "createCB.h"
#include "filter.h"
#include "constants.h"
/*----------------------------------------------------------------*
* Search routine for codebook encoding and gain quantization.
*---------------------------------------------------------------*/
void iCBSearch(
iLBC_Enc_Inst_t *iLBCenc_inst,
/* (i) the encoder state structure */
int *index, /* (o) Codebook indices */
int *gain_index,/* (o) Gain quantization indices */
float *intarget,/* (i) Target vector for encoding */
float *mem, /* (i) Buffer for codebook construction */
int lMem, /* (i) Length of buffer */
int lTarget, /* (i) Length of vector */
int nStages, /* (i) Number of codebook stages */
float *weightDenum, /* (i) weighting filter coefficients */
float *weightState, /* (i) weighting filter state */
int block /* (i) the sub-block number */
){
int i, j, icount, stage, best_index, range, counter;
float max_measure, gain, measure, crossDot, ftmp;
float gains[CB_NSTAGES];
float target[SUBL];
int base_index, sInd, eInd, base_size;
int sIndAug=0, eIndAug=0;
float buf[CB_MEML+SUBL+2*LPC_FILTERORDER];
float invenergy[CB_EXPAND*128], energy[CB_EXPAND*128];
float *pp, *ppi=0, *ppo=0, *ppe=0;
float cbvectors[CB_MEML];
float tene, cene, cvec[SUBL];
float aug_vec[SUBL];
memset(cvec,0,SUBL*sizeof(float));
/* Determine size of codebook sections */
base_size=lMem-lTarget+1;
if (lTarget==SUBL) {
base_size=lMem-lTarget+1+lTarget/2;
}
/* setup buffer for weighting */
memcpy(buf,weightState,sizeof(float)*LPC_FILTERORDER);
memcpy(buf+LPC_FILTERORDER,mem,lMem*sizeof(float));
memcpy(buf+LPC_FILTERORDER+lMem,intarget,lTarget*sizeof(float));
/* weighting */
AllPoleFilter(buf+LPC_FILTERORDER, weightDenum,
lMem+lTarget, LPC_FILTERORDER);
/* Construct the codebook and target needed */
memcpy(target, buf+LPC_FILTERORDER+lMem, lTarget*sizeof(float));
tene=0.0;
for (i=0; i<lTarget; i++) {
tene+=target[i]*target[i];
}
/* Prepare search over one more codebook section. This section
is created by filtering the original buffer with a filter. */
filteredCBvecs(cbvectors, buf+LPC_FILTERORDER, lMem);
/* The Main Loop over stages */
for (stage=0; stage<nStages; stage++) {
range = search_rangeTbl[block][stage];
/* initialize search measure */
max_measure = (float)-10000000.0;
gain = (float)0.0;
best_index = 0;
/* Compute cross dot product between the target
and the CB memory */
crossDot=0.0;
pp=buf+LPC_FILTERORDER+lMem-lTarget;
for (j=0; j<lTarget; j++) {
crossDot += target[j]*(*pp++);
}
if (stage==0) {
/* Calculate energy in the first block of
'lTarget' samples. */
ppe = energy;
ppi = buf+LPC_FILTERORDER+lMem-lTarget-1;
ppo = buf+LPC_FILTERORDER+lMem-1;
*ppe=0.0;
pp=buf+LPC_FILTERORDER+lMem-lTarget;
for (j=0; j<lTarget; j++) {
*ppe+=(*pp)*(*pp++);
}
if (*ppe>0.0) {
invenergy[0] = (float) 1.0 / (*ppe + EPS);
} else {
invenergy[0] = (float) 0.0;
}
ppe++;
measure=(float)-10000000.0;
if (crossDot > 0.0) {
measure = crossDot*crossDot*invenergy[0];
}
}
else {
measure = crossDot*crossDot*invenergy[0];
}
/* check if measure is better */
ftmp = crossDot*invenergy[0];
if ((measure>max_measure) && (fabs(ftmp)<CB_MAXGAIN)) {
best_index = 0;
max_measure = measure;
gain = ftmp;
}
/* loop over the main first codebook section,
full search */
for (icount=1; icount<range; icount++) {
/* calculate measure */
crossDot=0.0;
pp = buf+LPC_FILTERORDER+lMem-lTarget-icount;
for (j=0; j<lTarget; j++) {
crossDot += target[j]*(*pp++);
}
if (stage==0) {
*ppe++ = energy[icount-1] + (*ppi)*(*ppi) -
(*ppo)*(*ppo);
ppo--;
ppi--;
if (energy[icount]>0.0) {
invenergy[icount] =
(float)1.0/(energy[icount]+EPS);
} else {
invenergy[icount] = (float) 0.0;
}
measure=(float)-10000000.0;
if (crossDot > 0.0) {
measure = crossDot*crossDot*invenergy[icount];
}
}
else {
measure = crossDot*crossDot*invenergy[icount];
}
/* check if measure is better */
ftmp = crossDot*invenergy[icount];
if ((measure>max_measure) && (fabs(ftmp)<CB_MAXGAIN)) {
best_index = icount;
max_measure = measure;
gain = ftmp;
}
}
/* Loop over augmented part in the first codebook
* section, full search.
* The vectors are interpolated.
*/
if (lTarget==SUBL) {
/* Search for best possible cb vector and
compute the CB-vectors' energy. */
searchAugmentedCB(20, 39, stage, base_size-lTarget/2,
target, buf+LPC_FILTERORDER+lMem,
&max_measure, &best_index, &gain, energy,
invenergy);
}
/* set search range for following codebook sections */
base_index=best_index;
/* unrestricted search */
if (CB_RESRANGE == -1) {
sInd=0;
eInd=range-1;
sIndAug=20;
eIndAug=39;
}
/* restricted search around best index from first
codebook section */
else {
/* Initialize search indices */
sIndAug=0;
eIndAug=0;
sInd=base_index-CB_RESRANGE/2;
eInd=sInd+CB_RESRANGE;
if (lTarget==SUBL) {
if (sInd<0) {
sIndAug = 40 + sInd;
eIndAug = 39;
sInd=0;
} else if ( base_index < (base_size-20) ) {
if (eInd > range) {
sInd -= (eInd-range);
eInd = range;
}
} else { /* base_index >= (base_size-20) */
if (sInd < (base_size-20)) {
sIndAug = 20;
sInd = 0;
eInd = 0;
eIndAug = 19 + CB_RESRANGE;
if(eIndAug > 39) {
eInd = eIndAug-39;
eIndAug = 39;
}
} else {
sIndAug = 20 + sInd - (base_size-20);
eIndAug = 39;
sInd = 0;
eInd = CB_RESRANGE - (eIndAug-sIndAug+1);
}
}
} else { /* lTarget = 22 or 23 */
if (sInd < 0) {
eInd -= sInd;
sInd = 0;
}
if(eInd > range) {
sInd -= (eInd - range);
eInd = range;
}
}
}
/* search of higher codebook section */
/* index search range */
counter = sInd;
sInd += base_size;
eInd += base_size;
if (stage==0) {
ppe = energy+base_size;
*ppe=0.0;
pp=cbvectors+lMem-lTarget;
for (j=0; j<lTarget; j++) {
*ppe+=(*pp)*(*pp++);
}
ppi = cbvectors + lMem - 1 - lTarget;
ppo = cbvectors + lMem - 1;
for (j=0; j<(range-1); j++) {
*(ppe+1) = *ppe + (*ppi)*(*ppi) - (*ppo)*(*ppo);
ppo--;
ppi--;
ppe++;
}
}
/* loop over search range */
for (icount=sInd; icount<eInd; icount++) {
/* calculate measure */
crossDot=0.0;
pp=cbvectors + lMem - (counter++) - lTarget;
for (j=0;j<lTarget;j++) {
crossDot += target[j]*(*pp++);
}
if (energy[icount]>0.0) {
invenergy[icount] =(float)1.0/(energy[icount]+EPS);
} else {
invenergy[icount] =(float)0.0;
}
if (stage==0) {
measure=(float)-10000000.0;
if (crossDot > 0.0) {
measure = crossDot*crossDot*
invenergy[icount];
}
}
else {
measure = crossDot*crossDot*invenergy[icount];
}
/* check if measure is better */
ftmp = crossDot*invenergy[icount];
if ((measure>max_measure) && (fabs(ftmp)<CB_MAXGAIN)) {
best_index = icount;
max_measure = measure;
gain = ftmp;
}
}
/* Search the augmented CB inside the limited range. */
if ((lTarget==SUBL)&&(sIndAug!=0)) {
searchAugmentedCB(sIndAug, eIndAug, stage,
2*base_size-20, target, cbvectors+lMem,
&max_measure, &best_index, &gain, energy,
invenergy);
}
/* record best index */
index[stage] = best_index;
/* gain quantization */
if (stage==0){
if (gain<0.0){
gain = 0.0;
}
if (gain>CB_MAXGAIN) {
gain = (float)CB_MAXGAIN;
}
gain = gainquant(gain, 1.0, 32, &gain_index[stage]);
}
else {
if (stage==1) {
gain = gainquant(gain, (float)fabs(gains[stage-1]),
16, &gain_index[stage]);
} else {
gain = gainquant(gain, (float)fabs(gains[stage-1]),
8, &gain_index[stage]);
}
}
/* Extract the best (according to measure)
codebook vector */
if (lTarget==(STATE_LEN-iLBCenc_inst->state_short_len)) {
if (index[stage]<base_size) {
pp=buf+LPC_FILTERORDER+lMem-lTarget-index[stage];
} else {
pp=cbvectors+lMem-lTarget-
index[stage]+base_size;
}
} else {
if (index[stage]<base_size) {
if (index[stage]<(base_size-20)) {
pp=buf+LPC_FILTERORDER+lMem-
lTarget-index[stage];
} else {
createAugmentedVec(index[stage]-base_size+40,
buf+LPC_FILTERORDER+lMem,aug_vec);
pp=aug_vec;
}
} else {
int filterno, position;
filterno=index[stage]/base_size;
position=index[stage]-filterno*base_size;
if (position<(base_size-20)) {
pp=cbvectors+filterno*lMem-lTarget-
index[stage]+filterno*base_size;
} else {
createAugmentedVec(
index[stage]-(filterno+1)*base_size+40,
cbvectors+filterno*lMem,aug_vec);
pp=aug_vec;
}
}
}
/* Subtract the best codebook vector, according
to measure, from the target vector */
for (j=0;j<lTarget;j++) {
cvec[j] += gain*(*pp);
target[j] -= gain*(*pp++);
}
/* record quantized gain */
gains[stage]=gain;
}/* end of Main Loop. for (stage=0;... */
/* Gain adjustment for energy matching */
cene=0.0;
for (i=0; i<lTarget; i++) {
cene+=cvec[i]*cvec[i];
}
j=gain_index[0];
for (i=gain_index[0]; i<32; i++) {
ftmp=cene*gain_sq5Tbl[i]*gain_sq5Tbl[i];
if ((ftmp<(tene*gains[0]*gains[0])) &&
(gain_sq5Tbl[j]<(2.0*gains[0]))) {
j=i;
}
}
gain_index[0]=j;
}
A.35. LPCdecode.h
Okay bestie, this is the header for the LPC decoding bits ๐ It's basically the little list of function promises that the decoder side makes, so the rest of the code knows what to call and what to hand over. Iconic organization, honestly ๐ง
/******************************************************************
iLBC Speech Coder ANSI-C Source Code
LPC_decode.h
Copyright (C) The Internet Society (2004).
All Rights Reserved.
******************************************************************/
#ifndef __iLBC_LPC_DECODE_H
#define __iLBC_LPC_DECODE_H
void LSFinterpolate2a_dec(
float *a, /* (o) lpc coefficients for a sub-frame */
float *lsf1, /* (i) first lsf coefficient vector */
float *lsf2, /* (i) second lsf coefficient vector */
float coef, /* (i) interpolation weight */
int length /* (i) length of lsf vectors */
);
void SimplelsfDEQ(
float *lsfdeq, /* (o) dequantized lsf coefficients */
int *index, /* (i) quantization index */
int lpc_n /* (i) number of LPCs */
);
void DecoderInterpolateLSF(
float *syntdenum, /* (o) synthesis filter coefficients */
float *weightdenum, /* (o) weighting denumerator
coefficients */
float *lsfdeq, /* (i) dequantized lsf coefficients */
int length, /* (i) length of lsf coefficient vector */
iLBC_Dec_Inst_t *iLBCdec_inst
/* (i) the decoder state structure */
);
#endif
A.36. LPCdecode.c
okay bestie, this file is where the decoder turns the quantized LSF stuff back into usable filter coefficients ๐โจ here's the whole thing, no cap ๐
/******************************************************************
iLBC Speech Coder ANSI-C Source Code
LPC_decode.c
Copyright (C) The Internet Society (2004).
All Rights Reserved.
******************************************************************/
#include <math.h>
#include <string.h>
#include "helpfun.h"
#include "lsf.h"
#include "iLBC_define.h"
#include "constants.h"
/*---------------------------------------------------------------*
* interpolation of lsf coefficients for the decoder
*--------------------------------------------------------------*/
void LSFinterpolate2a_dec(
float *a, /* (o) lpc coefficients for a sub-frame */
float *lsf1, /* (i) first lsf coefficient vector */
float *lsf2, /* (i) second lsf coefficient vector */
float coef, /* (i) interpolation weight */
int length /* (i) length of lsf vectors */
){
float lsftmp[LPC_FILTERORDER];
interpolate(lsftmp, lsf1, lsf2, coef, length);
lsf2a(a, lsftmp);
}
/*---------------------------------------------------------------*
* obtain dequantized lsf coefficients from quantization index
*--------------------------------------------------------------*/
void SimplelsfDEQ(
float *lsfdeq, /* (o) dequantized lsf coefficients */
int *index, /* (i) quantization index */
int lpc_n /* (i) number of LPCs */
){
int i, j, pos, cb_pos;
/* decode first LSF */
pos = 0;
cb_pos = 0;
for (i = 0; i < LSF_NSPLIT; i++) {
for (j = 0; j < dim_lsfCbTbl[i]; j++) {
lsfdeq[pos + j] = lsfCbTbl[cb_pos +
(long)(index[i])*dim_lsfCbTbl[i] + j];
}
pos += dim_lsfCbTbl[i];
cb_pos += size_lsfCbTbl[i]*dim_lsfCbTbl[i];
}
if (lpc_n>1) {
/* decode last LSF */
pos = 0;
cb_pos = 0;
for (i = 0; i < LSF_NSPLIT; i++) {
for (j = 0; j < dim_lsfCbTbl[i]; j++) {
lsfdeq[LPC_FILTERORDER + pos + j] =
lsfCbTbl[cb_pos +
(long)(index[LSF_NSPLIT + i])*
dim_lsfCbTbl[i] + j];
}
pos += dim_lsfCbTbl[i];
cb_pos += size_lsfCbTbl[i]*dim_lsfCbTbl[i];
}
}
}
/*----------------------------------------------------------------*
* obtain synthesis and weighting filters form lsf coefficients
*---------------------------------------------------------------*/
void DecoderInterpolateLSF(
float *syntdenum, /* (o) synthesis filter coefficients */
float *weightdenum, /* (o) weighting denumerator
coefficients */
float *lsfdeq, /* (i) dequantized lsf coefficients */
int length, /* (i) length of lsf coefficient vector */
iLBC_Dec_Inst_t *iLBCdec_inst
/* (i) the decoder state structure */
){
int i, pos, lp_length;
float lp[LPC_FILTERORDER + 1], *lsfdeq2;
lsfdeq2 = lsfdeq + length;
lp_length = length + 1;
if (iLBCdec_inst->mode==30) {
/* sub-frame 1: Interpolation between old and first */
LSFinterpolate2a_dec(lp, iLBCdec_inst->lsfdeqold, lsfdeq,
lsf_weightTbl_30ms[0], length);
memcpy(syntdenum,lp,lp_length*sizeof(float));
bwexpand(weightdenum, lp, LPC_CHIRP_WEIGHTDENUM,
lp_length);
/* sub-frames 2 to 6: interpolation between first
and last LSF */
pos = lp_length;
for (i = 1; i < 6; i++) {
LSFinterpolate2a_dec(lp, lsfdeq, lsfdeq2,
lsf_weightTbl_30ms[i], length);
memcpy(syntdenum + pos,lp,lp_length*sizeof(float));
bwexpand(weightdenum + pos, lp,
LPC_CHIRP_WEIGHTDENUM, lp_length);
pos += lp_length;
}
}
else {
pos = 0;
for (i = 0; i < iLBCdec_inst->nsub; i++) {
LSFinterpolate2a_dec(lp, iLBCdec_inst->lsfdeqold,
lsfdeq, lsf_weightTbl_20ms[i], length);
memcpy(syntdenum+pos,lp,lp_length*sizeof(float));
bwexpand(weightdenum+pos, lp, LPC_CHIRP_WEIGHTDENUM,
lp_length);
pos += lp_length;
}
}
/* update memory */
if (iLBCdec_inst->mode==30)
memcpy(iLBCdec_inst->lsfdeqold, lsfdeq2,
length*sizeof(float));
else
memcpy(iLBCdec_inst->lsfdeqold, lsfdeq,
length*sizeof(float));
}
A.37. LPCencode.h
this one's the header for the encoder's LPC function, so it's just the prototype and the include guard ๐ฆ
/******************************************************************
iLBC Speech Coder ANSI-C Source Code
LPCencode.h
Copyright (C) The Internet Society (2004).
All Rights Reserved.
******************************************************************/
#ifndef __iLBC_LPCENCOD_H
#define __iLBC_LPCENCOD_H
void LPCencode(
float *syntdenum, /* (i/o) synthesis filter coefficients
before/after encoding */
float *weightdenum, /* (i/o) weighting denumerator coefficients
before/after encoding */
int *lsf_index, /* (o) lsf quantization index */
float *data, /* (i) lsf coefficients to quantize */
iLBC_Enc_Inst_t *iLBCenc_inst
/* (i/o) the encoder state structure */
);
#endif
A.38. LPCencode.c ๐
okay bestie, this is the encoder side of the LPC stuff, and the whole file is literally one big vibe check ๐ฆ
/******************************************************************
iLBC Speech Coder ANSI-C Source Code
LPCencode.c
Copyright (C) The Internet Society (2004).
All Rights Reserved.
******************************************************************/
#include <string.h>
#include "iLBC_define.h"
#include "helpfun.h"
#include "lsf.h"
#include "constants.h"
/*----------------------------------------------------------------*
* lpc analysis (subrutine to LPCencode)
*---------------------------------------------------------------*/
void SimpleAnalysis(
float *lsf, /* (o) lsf coefficients */
float *data, /* (i) new data vector */
iLBC_Enc_Inst_t *iLBCenc_inst
/* (i/o) the encoder state structure */
){
int k, is;
float temp[BLOCKL_MAX], lp[LPC_FILTERORDER + 1];
float lp2[LPC_FILTERORDER + 1];
float r[LPC_FILTERORDER + 1];
is=LPC_LOOKBACK+BLOCKL_MAX-iLBCenc_inst->blockl;
memcpy(iLBCenc_inst->lpc_buffer+is,data,
iLBCenc_inst->blockl*sizeof(float));
/* No lookahead, last window is asymmetric */
for (k = 0; k < iLBCenc_inst->lpc_n; k++) {
is = LPC_LOOKBACK;
if (k < (iLBCenc_inst->lpc_n - 1)) {
window(temp, lpc_winTbl,
iLBCenc_inst->lpc_buffer, BLOCKL_MAX);
} else {
window(temp, lpc_asymwinTbl,
iLBCenc_inst->lpc_buffer + is, BLOCKL_MAX);
}
autocorr(r, temp, BLOCKL_MAX, LPC_FILTERORDER);
window(r, r, lpc_lagwinTbl, LPC_FILTERORDER + 1);
levdurb(lp, temp, r, LPC_FILTERORDER);
bwexpand(lp2, lp, LPC_CHIRP_SYNTDENUM, LPC_FILTERORDER+1);
a2lsf(lsf + k*LPC_FILTERORDER, lp2);
}
is=LPC_LOOKBACK+BLOCKL_MAX-iLBCenc_inst->blockl;
memmove(iLBCenc_inst->lpc_buffer,
iLBCenc_inst->lpc_buffer+LPC_LOOKBACK+BLOCKL_MAX-is,
is*sizeof(float));
}
/*----------------------------------------------------------------*
* lsf interpolator and conversion from lsf to a coefficients
* (subrutine to SimpleInterpolateLSF)
*---------------------------------------------------------------*/
void LSFinterpolate2a_enc(
float *a, /* (o) lpc coefficients */
float *lsf1,/* (i) first set of lsf coefficients */
float *lsf2,/* (i) second set of lsf coefficients */
float coef, /* (i) weighting coefficient to use between
lsf1 and lsf2 */
long length /* (i) length of coefficient vectors */
){
float lsftmp[LPC_FILTERORDER];
interpolate(lsftmp, lsf1, lsf2, coef, length);
lsf2a(a, lsftmp);
}
/*----------------------------------------------------------------*
* lsf interpolator (subrutine to LPCencode)
*---------------------------------------------------------------*/
void SimpleInterpolateLSF(
float *syntdenum, /* (o) the synthesis filter denominator
resulting from the quantized
interpolated lsf */
float *weightdenum, /* (o) the weighting filter denominator
resulting from the unquantized
interpolated lsf */
float *lsf, /* (i) the unquantized lsf coefficients */
float *lsfdeq, /* (i) the dequantized lsf coefficients */
float *lsfold, /* (i) the unquantized lsf coefficients of
the previous signal frame */
float *lsfdeqold, /* (i) the dequantized lsf coefficients of
the previous signal frame */
int length, /* (i) should equate LPC_FILTERORDER */
iLBC_Enc_Inst_t *iLBCenc_inst
/* (i/o) the encoder state structure */
){
int i, pos, lp_length;
float lp[LPC_FILTERORDER + 1], *lsf2, *lsfdeq2;
lsf2 = lsf + length;
lsfdeq2 = lsfdeq + length;
lp_length = length + 1;
if (iLBCenc_inst->mode==30) {
/* sub-frame 1: Interpolation between old and first
set of lsf coefficients */
LSFinterpolate2a_enc(lp, lsfdeqold, lsfdeq,
lsf_weightTbl_30ms[0], length);
memcpy(syntdenum,lp,lp_length*sizeof(float));
LSFinterpolate2a_enc(lp, lsfold, lsf,
lsf_weightTbl_30ms[0], length);
bwexpand(weightdenum, lp, LPC_CHIRP_WEIGHTDENUM, lp_length);
/* sub-frame 2 to 6: Interpolation between first
and second set of lsf coefficients */
pos = lp_length;
for (i = 1; i < iLBCenc_inst->nsub; i++) {
LSFinterpolate2a_enc(lp, lsfdeq, lsfdeq2,
lsf_weightTbl_30ms[i], length);
memcpy(syntdenum + pos,lp,lp_length*sizeof(float));
LSFinterpolate2a_enc(lp, lsf, lsf2,
lsf_weightTbl_30ms[i], length);
bwexpand(weightdenum + pos, lp,
LPC_CHIRP_WEIGHTDENUM, lp_length);
pos += lp_length;
}
}
else {
pos = 0;
for (i = 0; i < iLBCenc_inst->nsub; i++) {
LSFinterpolate2a_enc(lp, lsfdeqold, lsfdeq,
lsf_weightTbl_20ms[i], length);
memcpy(syntdenum+pos,lp,lp_length*sizeof(float));
LSFinterpolate2a_enc(lp, lsfold, lsf,
lsf_weightTbl_20ms[i], length);
bwexpand(weightdenum+pos, lp,
LPC_CHIRP_WEIGHTDENUM, lp_length);
pos += lp_length;
}
}
/* update memory */
if (iLBCenc_inst->mode==30) {
memcpy(lsfold, lsf2, length*sizeof(float));
memcpy(lsfdeqold, lsfdeq2, length*sizeof(float));
}
else {
memcpy(lsfold, lsf, length*sizeof(float));
memcpy(lsfdeqold, lsfdeq, length*sizeof(float));
}
}
/*----------------------------------------------------------------*
* lsf quantizer (subrutine to LPCencode)
*---------------------------------------------------------------*/
void SimplelsfQ(
float *lsfdeq, /* (o) dequantized lsf coefficients
(dimension FILTERORDER) */
int *index, /* (o) quantization index */
float *lsf, /* (i) the lsf coefficient vector to be
quantized (dimension FILTERORDER ) */
int lpc_n /* (i) number of lsf sets to quantize */
){
/* Quantize first LSF with memoryless split VQ */
SplitVQ(lsfdeq, index, lsf, lsfCbTbl, LSF_NSPLIT,
dim_lsfCbTbl, size_lsfCbTbl);
if (lpc_n==2) {
/* Quantize second LSF with memoryless split VQ */
SplitVQ(lsfdeq + LPC_FILTERORDER, index + LSF_NSPLIT,
lsf + LPC_FILTERORDER, lsfCbTbl, LSF_NSPLIT,
dim_lsfCbTbl, size_lsfCbTbl);
}
}
/*----------------------------------------------------------------*
* lpc encoder
*---------------------------------------------------------------*/
void LPCencode(
float *syntdenum, /* (i/o) synthesis filter coefficients
before/after encoding */
float *weightdenum, /* (i/o) weighting denumerator
coefficients before/after
encoding */
int *lsf_index, /* (o) lsf quantization index */
float *data, /* (i) lsf coefficients to quantize */
iLBC_Enc_Inst_t *iLBCenc_inst
/* (i/o) the encoder state structure */
){
float lsf[LPC_FILTERORDER * LPC_N_MAX];
float lsfdeq[LPC_FILTERORDER * LPC_N_MAX];
int change=0;
SimpleAnalysis(lsf, data, iLBCenc_inst);
SimplelsfQ(lsfdeq, lsf_index, lsf, iLBCenc_inst->lpc_n);
change=LSF_check(lsfdeq, LPC_FILTERORDER, iLBCenc_inst->lpc_n);
SimpleInterpolateLSF(syntdenum, weightdenum,
lsf, lsfdeq, iLBCenc_inst->lsfold,
iLBCenc_inst->lsfdeqold, LPC_FILTERORDER, iLBCenc_inst);
}
A.39. lsf.h ๐ฆ
this one is the little header that declares the two LSF conversion helpers, a2lsf and lsf2a ๐ง
/******************************************************************
iLBC Speech Coder ANSI-C Source Code
lsf.h
Copyright (C) The Internet Society (2004).
All Rights Reserved.
******************************************************************/
#ifndef __iLBC_LSF_H
#define __iLBC_LSF_H
void a2lsf(
float *freq,/* (o) lsf coefficients */
float *a /* (i) lpc coefficients */
);
void lsf2a(
float *a_coef, /* (o) lpc coefficients */
float *freq /* (i) lsf coefficients */
);
#endif
A.40. lsf.c โจ
okay so this one is the LSF conversion file, and it's giving math bestie energy ๐งฎ๐
it has two big functions: a2lsf goes from LPC coefficients to LSF coefficients, and lsf2a goes the other way. the code is below, verbatim, so every single bracket is exactly where the spec put it ๐ซถ
/******************************************************************
iLBC Speech Coder ANSI-C Source Code
lsf.c
Copyright (C) The Internet Society (2004).
All Rights Reserved.
******************************************************************/
#include <string.h>
#include <math.h>
#include "iLBC_define.h"
/*----------------------------------------------------------------*
* conversion from lpc coefficients to lsf coefficients
*---------------------------------------------------------------*/
void a2lsf(
float *freq,/* (o) lsf coefficients */
float *a /* (i) lpc coefficients */
){
float steps[LSF_NUMBER_OF_STEPS] =
{(float)0.00635, (float)0.003175, (float)0.0015875,
(float)0.00079375};
float step;
int step_idx;
int lsp_index;
float p[LPC_HALFORDER];
float q[LPC_HALFORDER];
float p_pre[LPC_HALFORDER];
float q_pre[LPC_HALFORDER];
float old_p, old_q, *old;
float *pq_coef;
float omega, old_omega;
int i;
float hlp, hlp1, hlp2, hlp3, hlp4, hlp5;
for (i=0; i<LPC_HALFORDER; i++) {
p[i] = (float)-1.0 * (a[i + 1] + a[LPC_FILTERORDER - i]);
q[i] = a[LPC_FILTERORDER - i] - a[i + 1];
}
p_pre[0] = (float)-1.0 - p[0];
p_pre[1] = - p_pre[0] - p[1];
p_pre[2] = - p_pre[1] - p[2];
p_pre[3] = - p_pre[2] - p[3];
p_pre[4] = - p_pre[3] - p[4];
p_pre[4] = p_pre[4] / 2;
q_pre[0] = (float)1.0 - q[0];
q_pre[1] = q_pre[0] - q[1];
q_pre[2] = q_pre[1] - q[2];
q_pre[3] = q_pre[2] - q[3];
q_pre[4] = q_pre[3] - q[4];
q_pre[4] = q_pre[4] / 2;
omega = 0.0;
old_omega = 0.0;
old_p = FLOAT_MAX;
old_q = FLOAT_MAX;
/* Here we loop through lsp_index to find all the
LPC_FILTERORDER roots for omega. */
for (lsp_index = 0; lsp_index<LPC_FILTERORDER; lsp_index++) {
/* Depending on lsp_index being even or odd, we
alternatively solve the roots for the two LSP equations. */
if ((lsp_index & 0x1) == 0) {
pq_coef = p_pre;
old = &old_p;
} else {
pq_coef = q_pre;
old = &old_q;
}
/* Start with low resolution grid */
for (step_idx = 0, step = steps[step_idx];
step_idx < LSF_NUMBER_OF_STEPS;){
/* cos(10piw) + pq(0)cos(8piw) + pq(1)cos(6piw) +
pq(2)cos(4piw) + pq(3)cod(2piw) + pq(4) */
hlp = (float)cos(omega * TWO_PI);
hlp1 = (float)2.0 * hlp + pq_coef[0];
hlp2 = (float)2.0 * hlp * hlp1 - (float)1.0 +
pq_coef[1];
hlp3 = (float)2.0 * hlp * hlp2 - hlp1 + pq_coef[2];
hlp4 = (float)2.0 * hlp * hlp3 - hlp2 + pq_coef[3];
hlp5 = hlp * hlp4 - hlp3 + pq_coef[4];
if (((hlp5 * (*old)) <= 0.0) || (omega >= 0.5)){
if (step_idx == (LSF_NUMBER_OF_STEPS - 1)){
if (fabs(hlp5) >= fabs(*old)) {
freq[lsp_index] = omega - step;
} else {
freq[lsp_index] = omega;
}
if ((*old) >= 0.0){
*old = (float)-1.0 * FLOAT_MAX;
} else {
*old = FLOAT_MAX;
}
omega = old_omega;
step_idx = 0;
step_idx = LSF_NUMBER_OF_STEPS;
} else {
if (step_idx == 0) {
old_omega = omega;
}
step_idx++;
omega -= steps[step_idx];
/* Go back one grid step */
step = steps[step_idx];
}
} else {
/* increment omega until they are of different sign,
and we know there is at least one root between omega
and old_omega */
*old = hlp5;
omega += step;
}
}
}
for (i = 0; i<LPC_FILTERORDER; i++) {
freq[i] = freq[i] * TWO_PI;
}
}
/*----------------------------------------------------------------*
* conversion from lsf coefficients to lpc coefficients
*---------------------------------------------------------------*/
void lsf2a(
float *a_coef, /* (o) lpc coefficients */
float *freq /* (i) lsf coefficients */
){
int i, j;
float hlp;
float p[LPC_HALFORDER], q[LPC_HALFORDER];
float a[LPC_HALFORDER + 1], a1[LPC_HALFORDER],
a2[LPC_HALFORDER];
float b[LPC_HALFORDER + 1], b1[LPC_HALFORDER],
b2[LPC_HALFORDER];
for (i=0; i<LPC_FILTERORDER; i++) {
freq[i] = freq[i] * PI2;
}
/* Check input for ill-conditioned cases. This part is not
found in the TIA standard. It involves the following 2 IF
blocks. If "freq" is judged ill-conditioned, then we first
modify freq[0] and freq[LPC_HALFORDER-1] (normally
LPC_HALFORDER = 10 for LPC applications), then we adjust
the other "freq" values slightly */
if ((freq[0] <= 0.0) || (freq[LPC_FILTERORDER - 1] >= 0.5)){
if (freq[0] <= 0.0) {
freq[0] = (float)0.022;
}
if (freq[LPC_FILTERORDER - 1] >= 0.5) {
freq[LPC_FILTERORDER - 1] = (float)0.499;
}
hlp = (freq[LPC_FILTERORDER - 1] - freq[0]) /
(float) (LPC_FILTERORDER - 1);
for (i=1; i<LPC_FILTERORDER; i++) {
freq[i] = freq[i - 1] + hlp;
}
}
memset(a1, 0, LPC_HALFORDER*sizeof(float));
memset(a2, 0, LPC_HALFORDER*sizeof(float));
memset(b1, 0, LPC_HALFORDER*sizeof(float));
memset(b2, 0, LPC_HALFORDER*sizeof(float));
memset(a, 0, (LPC_HALFORDER+1)*sizeof(float));
memset(b, 0, (LPC_HALFORDER+1)*sizeof(float));
/* p[i] and q[i] compute cos(2*pi*omega_{2j}) and
cos(2*pi*omega_{2j-1} in eqs. 4.2.2.2-1 and 4.2.2.2-2.
Note that for this code p[i] specifies the coefficients
used in .Q_A(z) while q[i] specifies the coefficients used
in .P_A(z) */
for (i=0; i<LPC_HALFORDER; i++) {
p[i] = (float)cos(TWO_PI * freq[2 * i]);
q[i] = (float)cos(TWO_PI * freq[2 * i + 1]);
}
a[0] = 0.25;
b[0] = 0.25;
for (i= 0; i<LPC_HALFORDER; i++) {
a[i + 1] = a[i] - 2 * p[i] * a1[i] + a2[i];
b[i + 1] = b[i] - 2 * q[i] * b1[i] + b2[i];
a2[i] = a1[i];
a1[i] = a[i];
b2[i] = b1[i];
b1[i] = b[i];
}
for (j=0; j<LPC_FILTERORDER; j++) {
if (j == 0) {
a[0] = 0.25;
b[0] = -0.25;
} else {
a[0] = b[0] = 0.0;
}
for (i=0; i<LPC_HALFORDER; i++) {
a[i + 1] = a[i] - 2 * p[i] * a1[i] + a2[i];
b[i + 1] = b[i] - 2 * q[i] * b1[i] + b2[i];
a2[i] = a1[i];
a1[i] = a[i];
b2[i] = b1[i];
b1[i] = b[i];
}
a_coef[j + 1] = 2 * (a[LPC_HALFORDER] + b[LPC_HALFORDER]);
}
a_coef[0] = 1.0;
}
A.41. packing.h ๐
last one in this batch, and it's the header for the packing helpers ๐ฆ it declares packsplit, packcombine, dopack and unpack, all with their parameter comments kept exactly as written. bestie, don't touch a single character ๐
/******************************************************************
iLBC Speech Coder ANSI-C Source Code
packing.h
Copyright (C) The Internet Society (2004).
All Rights Reserved.
******************************************************************/
#ifndef __PACKING_H
#define __PACKING_H
void packsplit(
int *index, /* (i) the value to split */
int *firstpart, /* (o) the value specified by most
significant bits */
int *rest, /* (o) the value specified by least
significant bits */
int bitno_firstpart, /* (i) number of bits in most
significant part */
int bitno_total /* (i) number of bits in full range
of value */
);
void packcombine(
int *index, /* (i/o) the msb value in the
combined value out */
int rest, /* (i) the lsb value */
int bitno_rest /* (i) the number of bits in the
lsb part */
);
void dopack(
unsigned char **bitstream, /* (i/o) on entrance pointer to
place in bitstream to pack
new data, on exit pointer
to place in bitstream to
pack future data */
int index, /* (i) the value to pack */
int bitno, /* (i) the number of bits that the
value will fit within */
int *pos /* (i/o) write position in the
current byte */
);
void unpack(
unsigned char **bitstream, /* (i/o) on entrance pointer to
place in bitstream to
unpack new data from, on
exit pointer to place in
bitstream to unpack future
data from */
int *index, /* (o) resulting value */
int bitno, /* (i) number of bits used to
represent the value */
int *pos /* (i/o) read position in the
current byte */
);
#endif
A.42. packing.c ๐
okay bestie, this is where the bit-juggling happens ๐ง packsplit and packcombine handle the MSB and LSB parts, and dopack and unpack move bits in and out of the byte stream ๐
/******************************************************************
iLBC Speech Coder ANSI-C Source Code
packing.c
Copyright (C) The Internet Society (2004).
All Rights Reserved.
******************************************************************/
#include <math.h>
#include <stdlib.h>
#include "iLBC_define.h"
#include "constants.h"
#include "helpfun.h"
#include "string.h"
/*----------------------------------------------------------------*
* splitting an integer into first most significant bits and
* remaining least significant bits
*---------------------------------------------------------------*/
void packsplit(
int *index, /* (i) the value to split */
int *firstpart, /* (o) the value specified by most
significant bits */
int *rest, /* (o) the value specified by least
significant bits */
int bitno_firstpart, /* (i) number of bits in most
significant part */
int bitno_total /* (i) number of bits in full range
of value */
){
int bitno_rest = bitno_total-bitno_firstpart;
*firstpart = *index>>(bitno_rest);
*rest = *index-(*firstpart<<(bitno_rest));
}
/*----------------------------------------------------------------*
* combining a value corresponding to msb's with a value
* corresponding to lsb's
*---------------------------------------------------------------*/
void packcombine(
int *index, /* (i/o) the msb value in the
combined value out */
int rest, /* (i) the lsb value */
int bitno_rest /* (i) the number of bits in the
lsb part */
){
*index = *index<<bitno_rest;
*index += rest;
}
/*----------------------------------------------------------------*
* packing of bits into bitstream, i.e., vector of bytes
*---------------------------------------------------------------*/
void dopack(
unsigned char **bitstream, /* (i/o) on entrance pointer to
place in bitstream to pack
new data, on exit pointer
to place in bitstream to
pack future data */
int index, /* (i) the value to pack */
int bitno, /* (i) the number of bits that the
value will fit within */
int *pos /* (i/o) write position in the
current byte */
){
int posLeft;
/* Clear the bits before starting in a new byte */
if ((*pos)==0) {
**bitstream=0;
}
while (bitno>0) {
/* Jump to the next byte if end of this byte is reached*/
if (*pos==8) {
*pos=0;
(*bitstream)++;
**bitstream=0;
}
posLeft=8-(*pos);
/* Insert index into the bitstream */
if (bitno <= posLeft) {
**bitstream |= (unsigned char)(index<<(posLeft-bitno));
*pos+=bitno;
bitno=0;
} else {
**bitstream |= (unsigned char)(index>>(bitno-posLeft));
*pos=8;
index-=((index>>(bitno-posLeft))<<(bitno-posLeft));
bitno-=posLeft;
}
}
}
/*----------------------------------------------------------------*
* unpacking of bits from bitstream, i.e., vector of bytes
*---------------------------------------------------------------*/
void unpack(
unsigned char **bitstream, /* (i/o) on entrance pointer to
place in bitstream to
unpack new data from, on
exit pointer to place in
bitstream to unpack future
data from */
int *index, /* (o) resulting value */
int bitno, /* (i) number of bits used to
represent the value */
int *pos /* (i/o) read position in the
current byte */
){
int BitsLeft;
*index=0;
while (bitno>0) {
/* move forward in bitstream when the end of the
byte is reached */
if (*pos==8) {
*pos=0;
(*bitstream)++;
}
BitsLeft=8-(*pos);
/* Extract bits to index */
if (BitsLeft>=bitno) {
*index+=((((**bitstream)<<(*pos)) & 0xFF)>>(8-bitno));
*pos+=bitno;
bitno=0;
} else {
if ((8-bitno)>0) {
*index+=((((**bitstream)<<(*pos)) & 0xFF)>>
(8-bitno));
*pos=8;
} else {
*index+=(((int)(((**bitstream)<<(*pos)) & 0xFF))<<
(bitno-8));
*pos=8;
}
bitno-=BitsLeft;
}
}
}
A.43. StateConstructW.h ๐ฆ
next up is the header for the state constructor, all about the 6-bit max amplitude index and the decoded state vector ๐ธ
/******************************************************************
iLBC Speech Coder ANSI-C Source Code
StateConstructW.h
Copyright (C) The Internet Society (2004).
All Rights Reserved.
******************************************************************/
#ifndef __iLBC_STATECONSTRUCTW_H
#define __iLBC_STATECONSTRUCTW_H
void StateConstructW(
int idxForMax, /* (i) 6-bit index for the quantization of
max amplitude */
int *idxVec, /* (i) vector of quantization indexes */
float *syntDenum, /* (i) synthesis filter denumerator */
float *out, /* (o) the decoded state vector */
int len /* (i) length of a state vector */
);
#endif
A.44. StateConstructW.c โจ
and here's the real thing, the decoding of the start state, complete with the circular convolution with the all-pass filter ๐ฅ it's giving full signal-processing main character energy ๐
/******************************************************************
iLBC Speech Coder ANSI-C Source Code
StateConstructW.c
Copyright (C) The Internet Society (2004).
All Rights Reserved.
******************************************************************/
#include <math.h>
#include <string.h>
#include "iLBC_define.h"
#include "constants.h"
#include "filter.h"
/*----------------------------------------------------------------*
* decoding of the start state
*---------------------------------------------------------------*/
void StateConstructW(
int idxForMax, /* (i) 6-bit index for the quantization of
max amplitude */
int *idxVec, /* (i) vector of quantization indexes */
float *syntDenum, /* (i) synthesis filter denumerator */
float *out, /* (o) the decoded state vector */
int len /* (i) length of a state vector */
){
float maxVal, tmpbuf[LPC_FILTERORDER+2*STATE_LEN], *tmp,
numerator[LPC_FILTERORDER+1];
float foutbuf[LPC_FILTERORDER+2*STATE_LEN], *fout;
int k,tmpi;
/* decoding of the maximum value */
maxVal = state_frgqTbl[idxForMax];
maxVal = (float)pow(10,maxVal)/(float)4.5;
/* initialization of buffers and coefficients */
memset(tmpbuf, 0, LPC_FILTERORDER*sizeof(float));
memset(foutbuf, 0, LPC_FILTERORDER*sizeof(float));
for (k=0; k<LPC_FILTERORDER; k++) {
numerator[k]=syntDenum[LPC_FILTERORDER-k];
}
numerator[LPC_FILTERORDER]=syntDenum[0];
tmp = &tmpbuf[LPC_FILTERORDER];
fout = &foutbuf[LPC_FILTERORDER];
/* decoding of the sample values */
for (k=0; k<len; k++) {
tmpi = len-1-k;
/* maxVal = 1/scal */
tmp[k] = maxVal*state_sq3Tbl[idxVec[tmpi]];
}
/* circular convolution with all-pass filter */
memset(tmp+len, 0, len*sizeof(float));
ZeroPoleFilter(tmp, numerator, syntDenum, 2*len,
LPC_FILTERORDER, fout);
for (k=0;k<len;k++) {
out[k] = fout[len-1-k]+fout[2*len-1-k];
}
}
A.45. StateSearchW.h ๐
last one in this batch: the header for the state search, with AbsQuantW and StateSearchW both getting their parameter lists, encoder instance and all ๐ง๐ซถ
/******************************************************************
iLBC Speech Coder ANSI-C Source Code
StateSearchW.h
Copyright (C) The Internet Society (2004).
All Rights Reserved.
******************************************************************/
#ifndef __iLBC_STATESEARCHW_H
#define __iLBC_STATESEARCHW_H
void AbsQuantW(
iLBC_Enc_Inst_t *iLBCenc_inst,
/* (i) Encoder instance */
float *in, /* (i) vector to encode */
float *syntDenum, /* (i) denominator of synthesis filter */
float *weightDenum, /* (i) denominator of weighting filter */
int *out, /* (o) vector of quantizer indexes */
int len, /* (i) length of vector to encode and
vector of quantizer indexes */
int state_first /* (i) position of start state in the
80 vec */
);
void StateSearchW(
iLBC_Enc_Inst_t *iLBCenc_inst,
/* (i) Encoder instance */
float *residual,/* (i) target residual vector */
float *syntDenum, /* (i) lpc synthesis filter */
float *weightDenum, /* (i) weighting filter denuminator */
int *idxForMax, /* (o) quantizer index for maximum
amplitude */
int *idxVec, /* (o) vector of quantization indexes */
int len, /* (i) length of all vectors */
int state_first /* (i) position of start state in the
80 vec */
);
#endif
A.46. StateSearchW.c ๐
okay so this is the big one for the start state ๐ฅ AbsQuantW does the actual predictive noise shaping encoding, and StateSearchW sets everything up and then hands off to it ๐
/******************************************************************
iLBC Speech Coder ANSI-C Source Code
StateSearchW.c
Copyright (C) The Internet Society (2004).
All Rights Reserved.
******************************************************************/
#include <math.h>
#include <string.h>
#include "iLBC_define.h"
#include "constants.h"
#include "filter.h"
#include "helpfun.h"
/*----------------------------------------------------------------*
* predictive noise shaping encoding of scaled start state
* (subrutine for StateSearchW)
*---------------------------------------------------------------*/
void AbsQuantW(
iLBC_Enc_Inst_t *iLBCenc_inst,
/* (i) Encoder instance */
float *in, /* (i) vector to encode */
float *syntDenum, /* (i) denominator of synthesis filter */
float *weightDenum, /* (i) denominator of weighting filter */
int *out, /* (o) vector of quantizer indexes */
int len, /* (i) length of vector to encode and
vector of quantizer indexes */
int state_first /* (i) position of start state in the
80 vec */
){
float *syntOut;
float syntOutBuf[LPC_FILTERORDER+STATE_SHORT_LEN_30MS];
float toQ, xq;
int n;
int index;
/* initialization of buffer for filtering */
memset(syntOutBuf, 0, LPC_FILTERORDER*sizeof(float));
/* initialization of pointer for filtering */
syntOut = &syntOutBuf[LPC_FILTERORDER];
/* synthesis and weighting filters on input */
if (state_first) {
AllPoleFilter (in, weightDenum, SUBL, LPC_FILTERORDER);
} else {
AllPoleFilter (in, weightDenum,
iLBCenc_inst->state_short_len-SUBL,
LPC_FILTERORDER);
}
/* encoding loop */
for (n=0; n<len; n++) {
/* time update of filter coefficients */
if ((state_first)&&(n==SUBL)){
syntDenum += (LPC_FILTERORDER+1);
weightDenum += (LPC_FILTERORDER+1);
/* synthesis and weighting filters on input */
AllPoleFilter (&in[n], weightDenum, len-n,
LPC_FILTERORDER);
} else if ((state_first==0)&&
(n==(iLBCenc_inst->state_short_len-SUBL))) {
syntDenum += (LPC_FILTERORDER+1);
weightDenum += (LPC_FILTERORDER+1);
/* synthesis and weighting filters on input */
AllPoleFilter (&in[n], weightDenum, len-n,
LPC_FILTERORDER);
}
/* prediction of synthesized and weighted input */
syntOut[n] = 0.0;
AllPoleFilter (&syntOut[n], weightDenum, 1,
LPC_FILTERORDER);
/* quantization */
toQ = in[n]-syntOut[n];
sort_sq(&xq, &index, toQ, state_sq3Tbl, 8);
out[n]=index;
syntOut[n] = state_sq3Tbl[out[n]];
/* update of the prediction filter */
AllPoleFilter(&syntOut[n], weightDenum, 1,
LPC_FILTERORDER);
}
}
/*----------------------------------------------------------------*
* encoding of start state
*---------------------------------------------------------------*/
void StateSearchW(
iLBC_Enc_Inst_t *iLBCenc_inst,
/* (i) Encoder instance */
float *residual,/* (i) target residual vector */
float *syntDenum, /* (i) lpc synthesis filter */
float *weightDenum, /* (i) weighting filter denuminator */
int *idxForMax, /* (o) quantizer index for maximum
amplitude */
int *idxVec, /* (o) vector of quantization indexes */
int len, /* (i) length of all vectors */
int state_first /* (i) position of start state in the
80 vec */
){
float dtmp, maxVal;
float tmpbuf[LPC_FILTERORDER+2*STATE_SHORT_LEN_30MS];
float *tmp, numerator[1+LPC_FILTERORDER];
float foutbuf[LPC_FILTERORDER+2*STATE_SHORT_LEN_30MS], *fout;
int k;
float qmax, scal;
/* initialization of buffers and filter coefficients */
memset(tmpbuf, 0, LPC_FILTERORDER*sizeof(float));
memset(foutbuf, 0, LPC_FILTERORDER*sizeof(float));
for (k=0; k<LPC_FILTERORDER; k++) {
numerator[k]=syntDenum[LPC_FILTERORDER-k];
}
numerator[LPC_FILTERORDER]=syntDenum[0];
tmp = &tmpbuf[LPC_FILTERORDER];
fout = &foutbuf[LPC_FILTERORDER];
/* circular convolution with the all-pass filter */
memcpy(tmp, residual, len*sizeof(float));
memset(tmp+len, 0, len*sizeof(float));
ZeroPoleFilter(tmp, numerator, syntDenum, 2*len,
LPC_FILTERORDER, fout);
for (k=0; k<len; k++) {
fout[k] += fout[k+len];
}
/* identification of the maximum amplitude value */
maxVal = fout[0];
for (k=1; k<len; k++) {
if (fout[k]*fout[k] > maxVal*maxVal){
maxVal = fout[k];
}
}
maxVal=(float)fabs(maxVal);
/* encoding of the maximum amplitude value */
if (maxVal < 10.0) {
maxVal = 10.0;
}
maxVal = (float)log10(maxVal);
sort_sq(&dtmp, idxForMax, maxVal, state_frgqTbl, 64);
/* decoding of the maximum amplitude representation value,
and corresponding scaling of start state */
maxVal=state_frgqTbl[*idxForMax];
qmax = (float)pow(10,maxVal);
scal = (float)(4.5)/qmax;
for (k=0; k<len; k++){
fout[k] *= scal;
}
/* predictive noise shaping encoding of scaled start state */
AbsQuantW(iLBCenc_inst, fout,syntDenum,
weightDenum,idxVec, len, state_first);
}
A.47. syntFilter.h ๐ธ
this one's a tiny header, just the function prototype for the LP synthesis filter, wrapped in the usual include guard ๐
/******************************************************************
iLBC Speech Coder ANSI-C Source Code
syntFilter.h
Copyright (C) The Internet Society (2004).
All Rights Reserved.
******************************************************************/
#ifndef __iLBC_SYNTFILTER_H
#define __iLBC_SYNTFILTER_H
void syntFilter(
float *Out, /* (i/o) Signal to be filtered */
float *a, /* (i) LP parameters */
int len, /* (i) Length of signal */
float *mem /* (i/o) Filter state */
);
#endif
A.48. syntFilter.c ๐ฆ
and here's the actual LP synthesis filter ๐ง it filters the first part using the memory from past frames, then the rest of the signal where the state is entirely inside the output vector, and finally it saves the state for next time ๐ซถ
/******************************************************************
iLBC Speech Coder ANSI-C Source Code
syntFilter.c
Copyright (C) The Internet Society (2004).
All Rights Reserved.
******************************************************************/
#include "iLBC_define.h"
/*----------------------------------------------------------------*
* LP synthesis filter.
*---------------------------------------------------------------*/
void syntFilter(
float *Out, /* (i/o) Signal to be filtered */
float *a, /* (i) LP parameters */
int len, /* (i) Length of signal */
float *mem /* (i/o) Filter state */
){
int i, j;
float *po, *pi, *pa, *pm;
po=Out;
/* Filter first part using memory from past */
for (i=0; i<LPC_FILTERORDER; i++) {
pi=&Out[i-1];
pa=&a[1];
pm=&mem[LPC_FILTERORDER-1];
for (j=1; j<=i; j++) {
*po-=(*pa++)*(*pi--);
}
for (j=i+1; j<LPC_FILTERORDER+1; j++) {
*po-=(*pa++)*(*pm--);
}
po++;
}
/* Filter last part where the state is entirely in
the output vector */
for (i=LPC_FILTERORDER; i<len; i++) {
pi=&Out[i-1];
pa=&a[1];
for (j=1; j<LPC_FILTERORDER+1; j++) {
*po-=(*pa++)*(*pi--);
}
po++;
}
/* Update state vector */
memcpy(mem, &Out[len-LPC_FILTERORDER],
LPC_FILTERORDER*sizeof(float));
}
Authors' Addresses ๐
ok these are the people who wrote the whole thing, so here's where to find them, with their phone numbers and emails ๐
Soren Vang Andersen
Department of Communication Technology
Aalborg University
Fredrik Bajers Vej 7A
9200 Aalborg
Denmark
Phone: ++45 9 6358627
EMail: sva@kom.auc.dk
Alan Duric
Telio AS
Stoperigt. 2
Oslo, N-0250
Norway
Phone: +47 21673555
EMail: alan.duric@telio.no
Henrik Astrom
Global IP Sound AB
Olandsgatan 42
Stockholm, S-11663
Sweden
Phone: +46 8 54553040
EMail: henrik.astrom@globalipsound.com
Roar Hagen
Global IP Sound AB
Olandsgatan 42
Stockholm, S-11663
Sweden
Phone: +46 8 54553040
EMail: roar.hagen@globalipsound.com
W. Bastiaan Kleijn
Global IP Sound AB
Olandsgatan 42
Stockholm, S-11663
Sweden
Phone: +46 8 54553040
EMail: bastiaan.kleijn@globalipsound.com
Jan Linden
Global IP Sound Inc.
900 Kearny Street, suite 500
San Francisco, CA-94133
USA
Phone: +1 415 397 2555
EMail: jan.linden@globalipsound.com
Full Copyright Statement ๐
the legal bit, but make it cute: it's the Internet Society's copyright, the authors keep their rights under BCP 78, and the whole thing is provided with a big "as is" disclaimer ๐งพ
Copyright (C) The Internet Society (2004).
This document is subject to the rights, licenses and restrictions contained in BCP 78, and except as set forth therein, the authors retain all their rights.
The exact disclaimer wording matters, so here it is verbatim:
This document and the information contained herein are provided on an "AS IS" basis and THE CONTRIBUTOR, THE ORGANIZATION HE/SHE REPRESENTS OR IS SPONSORED BY (IF ANY), THE INTERNET SOCIETY AND THE INTERNET ENGINEERING TASK FORCE DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
Intellectual Property ๐ฅ
this part is about who owns what ๐ the IETF doesn't pick a side on whether any patents or rights apply to the tech in here, and it doesn't claim it checked for any either. if you want the IPR paperwork, look at BCP 78 and BCP 79 for the rules, and the IPR disclosures live in the IETF repository at http://www.ietf.org/ipr, where you can also find any licence assurances that have been filed ๐
if you think something might be covered by a copyright, patent, patent application, or other proprietary right that the implementation would need, the IETF wants you to tell them about it, sending that info to ietf-ipr@ietf.org ๐
Acknowledgement ๐
funding for the RFC Editor function is currently coming from the Internet Society, so big thanks to them ๐ซถโจ
๐ฆ where this page comes from
- ietf: rfc3951 2004-12
- ๐ girlified by claude-haiku-5-5 on : a language model's rewrite of the original, under its terms