FreeCalypso > hg > gsm-codec-lib
view libgsmhr1/dtx_rxfe.c @ 581:e2d5cad04cbf
libgsmhr1 RxFE: store CN R0+LPC separately from speech
In the original GSM 06.06 code the ECU for speech mode is entirely
separate from the CN generator, maintaining separate state. (The
main intertie between them is the speech vs CN state variable,
distinguishing between speech and CN BFIs, in addition to the
CN-specific function of distinguishing between initial and update
SIDs.)
In the present RxFE implementation I initially thought that we could
use the same saved_frame buffer for both ECU and CN, overwriting
just the first 4 params (R0 and LPC) when a valid SID comes in.
However, I now realize it was a bad idea: the original code has a
corner case (long sequence of speech-mode BFIs to put the ECU in
state 6, then SID and CN-mode BFIs, then a good speech frame) that
would be broken by that buffer reuse approach. We could eliminate
this corner case by resetting the ECU state when passing through
a CN insertion period, but doing so would needlessly increase
the behavioral diffs between GSM 06.06 and our version.
Solution: use a separate CN-specific buffer for CN R0+LPC parameters,
and match the behavior of GSM 06.06 code in this regard.
author | Mychaela Falconia <falcon@freecalypso.org> |
---|---|
date | Thu, 13 Feb 2025 10:02:45 +0000 |
parents | 7756b23b78cd |
children |
line wrap: on
line source
/* * Here we implement the subset of DTX functions that are used by the Rx * front end, i.e., the part of libgsmhr1 that is common between the full * decoder and our TFO transform implementation. Note that the same DTX * functions will also be used by the speech encoder. */ #include "typedefs.h" #include "namespace.h" #include "dtx_rxfe.h" #include "mathhalf.h" #include "mathdp31.h" /* relevant definitions from original dtx.c */ #define PN_XOR_REG (Longword)0x00000005L #define PN_XOR_ADD (Longword)0x40000000L #define OH_SHIFT 3 /* shift corresponding to OVERHANG */ /* Values of GS for voicing state 0, all values shifted down by 2 shifts */ const LongwordRom ppLr_gsTable[4][32] = { { 0x000011ab, 0x000038d2, 0x0000773e, 0x000144ef, 0x00035675, 0x000648c5, 0x000c3d65, 0x0017ae17, 0x002a3dbb, 0x005238e7, 0x00695c1a, 0x00a60d45, 0x00e4cc68, 0x01c3ba6a, 0x019e3c96, 0x02d1fbac, 0x030453ec, 0x0549a998, 0x05190298, 0x08258920, 0x08daff30, 0x0c3150e0, 0x0e45d850, 0x14c111a0, 0x0ff7e1c0, 0x18a06860, 0x13810400, 0x1abc9ee0, 0x28500940, 0x41f22800, 0x22fc5040, 0x2cd90180 }, { 0x00003ede, 0x00021fc9, 0x0013f0c3, 0x003a7be2, 0x007a6663, 0x00fe3773, 0x012fabf4, 0x02275cd0, 0x01c0ef14, 0x02c0b1d8, 0x0350fc70, 0x05505078, 0x04175f30, 0x052c1098, 0x08ed3310, 0x0a63b470, 0x05417870, 0x08995ee0, 0x07bbe018, 0x0a19fa10, 0x0b5818c0, 0x0fd96ea0, 0x0e5cad10, 0x13b40d40, 0x12d45840, 0x14577320, 0x2b2e5e00, 0x333e9640, 0x194c35c0, 0x1c30f8c0, 0x2d16db00, 0x2cc970ff }, { 0x002f18e7, 0x00a47be0, 0x01222efe, 0x01c42df8, 0x024be794, 0x03424c40, 0x036950fc, 0x04973108, 0x038405b4, 0x05d8c8f0, 0x05063e08, 0x070cdea0, 0x05812be8, 0x06da5fc8, 0x088fcd60, 0x0a013cb0, 0x0909a460, 0x09e6cf40, 0x0ee581d0, 0x0ec99f20, 0x0b4e7470, 0x0c730e80, 0x0ff39d20, 0x105d0d80, 0x158b0b00, 0x172babe0, 0x14576460, 0x181a6720, 0x26126e80, 0x1f590180, 0x1fdaad60, 0x2e0e8000 }, { 0x00c7f603, 0x01260cda, 0x01b3926a, 0x026d82bc, 0x0228fba0, 0x036ec5b0, 0x034bf4cc, 0x043a55d0, 0x044f9c20, 0x05c66f50, 0x0515f890, 0x06065300, 0x0665dc00, 0x0802b630, 0x0737a1c0, 0x087294e0, 0x09253fc0, 0x0a619760, 0x097bd060, 0x0a6d4e50, 0x0d19e520, 0x0e15c420, 0x0c4e4eb0, 0x0e8880e0, 0x11cdf480, 0x12c85800, 0x10f4c0a0, 0x13e51b00, 0x189dbaa0, 0x18a6bb60, 0x22e31500, 0x21615240 } }; /************************************************************************* * * FUNCTION NAME: avgGsHistQntz * * PURPOSE: * * Average gs history, where history is of length OVERHANG-1 * frames. The last frame's (i.e. this frame) gs values are not * available since quantization would have occured only after the * VAD decision is made. * * INPUTS: * * pL_GsHistory[(OVERHANG-1)*N_SUB] - the GS of the past * OVERHANG-1 frames. The GS values are stored shifted down by 2 * shifts to avoid overflow (the largest GS is greater than 2.0). * * * OUTPUTS: * * *pL_GsAvgd - the average of pL_GsHistory[], also shifted down * by two shifts. * * RETURN VALUE: * * none. * * *************************************************************************/ void avgGsHistQntz(const Longword pL_GsHistory[], Longword *pL_GsAvgd) { /*_________________________________________________________________________ | | | Automatic Variables | |_________________________________________________________________________| */ int i; Longword L_avg; /*_________________________________________________________________________ | | | Executable Code | |_________________________________________________________________________| */ L_avg = L_shift_r(pL_GsHistory[0], -(OH_SHIFT + 2)); for (i = 1; i < N_SUB * (OVERHANG - 1); i++) L_avg = L_add(L_shift_r(pL_GsHistory[i], -(OH_SHIFT + 2)), L_avg); /* avg number x/32 not x/28 */ *pL_GsAvgd = L_add(L_avg, L_mpy_ls(L_avg, 0x1249)); /* L_avg *= 32/28 */ } /************************************************************************* * * FUNCTION NAME: gsQuant * * PURPOSE: * * Quantize a value of gs in any of the voicing modes. Input GS * is a 32 bit number. The GSP0 index is returned. * * INPUTS: * * L_GsIn - 32 bit GS value, shifted down by 2 shifts. * * swVoicingMode - voicing level * * ppLr_gsTable[4][32] - Rom GS Table. (global), all GS values * have been shifted down by 2 from their true value. * * OUTPUTS: * * none * * RETURN VALUE: * * * GSP0 Index closest to the input value of GS. * * *************************************************************************/ Shortword gsQuant(Longword L_GsIn, Shortword swVoicingMode) { /*_________________________________________________________________________ | | | Automatic Variables | |_________________________________________________________________________| */ Shortword swGsIndex, swBestGs; Longword L_diff, L_min = LW_MAX; /*_________________________________________________________________________ | | | Executable Code | |_________________________________________________________________________| */ for (swGsIndex = 0; swGsIndex < 32; swGsIndex++) { L_diff = L_abs(L_sub(L_GsIn, ppLr_gsTable[swVoicingMode][swGsIndex])); if (L_sub(L_diff, L_min) < 0) { /* new minimum */ /* ----------- */ swBestGs = swGsIndex; L_min = L_diff; } } return (swBestGs); } /************************************************************************* * * FUNCTION NAME: getPnBits * * PURPOSE: * * Generate iBits pseudo-random bits using *pL_PNSeed as the * pn-generators seed. * * INPUTS: * * iBits - integer indicating how many random bits to return. * range [0,15], 0 yields 1 bit output * * *pL_PNSeed - 32 bit seed (changed by function) * * OUTPUTS: * * *pL_PNSeed - 32 bit seed, modified. * * RETURN VALUE: * * random bits in iBits LSB's. * * * IMPLEMENTATION: * * implementation of x**31 + x**3 + 1 == PN_XOR_REG | PN_XOR_ADD a * PN sequence generator using Longwords generating a 2**31 -1 * length pn-sequence. * *************************************************************************/ Shortword getPnBits(int iBits, Longword *pL_PNSeed) { /*_________________________________________________________________________ | | | Automatic Variables | |_________________________________________________________________________| */ Shortword swPnBits = 0; Longword L_Taps, L_FeedBack; int i; /*_________________________________________________________________________ | | | Executable Code | |_________________________________________________________________________| */ for (i = 0; i < iBits; i++) { /* update the state */ /* ---------------- */ L_Taps = *pL_PNSeed & PN_XOR_REG; L_FeedBack = L_Taps; /* Xor tap bits to yield * feedback bit */ L_Taps = L_shr(L_Taps, 1); while (L_Taps) { L_FeedBack = L_FeedBack ^ L_Taps; L_Taps = L_shr(L_Taps, 1); } /* LSB of L_FeedBack is next MSB of PN register */ *pL_PNSeed = L_shr(*pL_PNSeed, 1); if (L_FeedBack & 1) *pL_PNSeed = *pL_PNSeed | PN_XOR_ADD; /* State update complete. Get the output bit from the state, add/or it * into output */ swPnBits = shl(swPnBits, 1); swPnBits = swPnBits | (extract_l(*pL_PNSeed) & 0x0001); } return (swPnBits); }