RFC 3951

Internet Low Bit Rate Codec (iLBC)

๐Ÿ“… published December 2004, from ietf

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 (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 ๐Ÿ“‹

  1. Pre-process speech with a HP filter, if needed (section 3.1).
  2. Compute LPC parameters, quantize, and interpolate (section 3.2).
  3. Use analysis filters on speech to compute residual (section 3.3).
  4. Select position of 57/58-sample start state (section 3.5).
  5. Quantize the 57/58-sample start state with scalar quantization (section 3.5).
  6. 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).
  7. 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:

  1. Quantize the first three LSF coefficients (1 - 3) with a VQ codebook of size 64.
  2. Quantize the next three LSF coefficients 4 - 6 with VQ a codebook of size 128.
  3. 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 (and qlsf1) to lsf2 (and qlsf2). lsf1 is used in sub-block two, and lsf2 in sub-block five.
  • In the last sub-block, lsf2 is 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.

  1. Decode the part of the residual that has been encoded so far, using the codebook without perceptual weighting ๐ŸŒˆ
  2. 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) ๐Ÿ”„
  3. Filter the memory + target with the perceptual weighting filter (section 3.6.2) โœจ
  4. 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) ๐Ÿ”
  5. 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 ๐Ÿ”
  6. 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 ๐Ÿ”ฅ.


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:

  1. Compute the measure (target*cbvec)^2 / ||cbvec||^2 for 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).
  2. 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
  1. 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.

  1. Extract the parameters from the bitstream ๐Ÿ“ฆ.
  2. Decode the LPC and interpolate (section 4.1).
  3. Construct the 57/58-sample start state (section 4.2).
  4. 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 ๐Ÿ”.
  5. 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.
  6. Enhance the residual with the post filter (section 4.6).
  7. Synthesis of the residual (section 4.7).
  8. 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 ๐Ÿ”ฅ:

  1. form a set of samples (by table lookup) from the index stream idxVec[n]
  2. multiply the set with 1/scal=(10^qmax)/4.5
  3. time reverse the 57/58 samples
  4. 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 ๐ŸŒธ
  5. 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 ๐Ÿฆ‹:

  1. Pitch estimation of each of the two or three new 80-sample blocks.
  2. 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.
  3. Calculate the smoothed residual generated by the six pitch-period-synchronous sequences from the prior step.
  4. Check if the smoothed residual satisfies the criterion (section 4.6.4).
  5. Use the constraint to calculate the mixing factor (section 4.6.5).
  6. 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:

  1. 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 ๐Ÿ™ˆ.
  2. 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 โœจ.
  3. 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}
  1. 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

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