/*---------------------------------------------------------------------------*/
/* g711plc.cpp                                                               */
/* copyright (c) innovaphone 2012                                            */
/*                                                                           */
/* Packet loss concealment based on correlation analysis                     */
/*---------------------------------------------------------------------------*/

#define G711PLC_CPP_

#include "platform.h"
#include "sdsputil.h"
#include "g711plc.h"

#define FILE_ "g711plc.cpp"

NAMESPACE_BEGIN

/*----------------------------------------------------------------------------*/

#define G711PLC_VOCAL_PERIOD_RANGE      (G711PLC_VOCAL_PERIOD_MAX - G711PLC_VOCAL_PERIOD_MIN)
#define G711PLC_CORRELATION_DECIMATION  2
#define G711PLC_CORRELATION_DEC_EXP     1
#define G711PLC_CORRELATION_LEN_EXP     8       /* max carryover on correlation */
#define G711PLC_CORRELATION_LEN_DEC_EXP (G711PLC_CORRELATION_LEN_EXP - G711PLC_CORRELATION_DEC_EXP)
#define G711PLC_CORRELATION_LEN_QUOT    (0x8000 / G711PLC_CORRELATION_LENGTH)
#define G711PLC_INTERPOLATION_DIVS_EXP  6       /* 64 fractional divisions */
#define G711PLC_INTERPOLATION_DIVS      (1 << G711PLC_INTERPOLATION_DIVS_EXP)
#define G711PLC_MERGE_GAIN_INC          (0x8000 / 80)
#define G711PLC_CONCEAL_GAIN_INC        (0x8000 / 80)
#define G711PLC_CONCEAL_GAIN_DEC        (0x8000 / 800)
#define G711PLC_CONCEAL_GAIN_INIT       (0x7fff + 800 * G711PLC_CONCEAL_GAIN_DEC)
#define G711PLC_VOCAL_QUALITY           0x4000

static word interpolation_filters[G711PLC_INTERPOLATION_DIVS][G711PLC_INTERPOLATION_TAPS] =
{
{
/*0xFFC7,*/0x0085, 0xFEA7, 0x02E9, 0xFAD5, 0x07CF, 0xF5B0, 0x0C1D,
  0x7137, 0x0C1D, 0xF5B0, 0x07CF, 0xFAD5, 0x02E9, 0xFEA7, 0x0085,
  0xFFC7
},
{
  0xFFCB, 0x0082, 0xFEA9, 0x02EF, 0xFAB8, 0x081D, 0xF4F9, 0x0DF5,
  0x712D, 0x0A4F, 0xF669, 0x077D, 0xFAF4, 0x02E1, 0xFEA6, 0x0088
},
{
  0xFFCE, 0x007E, 0xFEAB, 0x02F4, 0xFA9E, 0x0869, 0xF443, 0x0FD5,
  0x7110, 0x088B, 0xF723, 0x072A, 0xFB15, 0x02D8, 0xFEA6, 0x008A
},
{
  0xFFD2, 0x007A, 0xFEAE, 0x02F8, 0xFA85, 0x08B2, 0xF390, 0x11BF,
  0x70E0, 0x06D1, 0xF7DE, 0x06D4, 0xFB38, 0x02CE, 0xFEA6, 0x008C
},
{
  0xFFD6, 0x0076, 0xFEB2, 0x02F9, 0xFA70, 0x08F8, 0xF2E0, 0x13B0,
  0x709D, 0x0521, 0xF899, 0x067C, 0xFB5D, 0x02C2, 0xFEA8, 0x008E
},
{
  0xFFDA, 0x0072, 0xFEB7, 0x02FA, 0xFA5C, 0x093A, 0xF233, 0x15A9,
  0x7046, 0x037D, 0xF954, 0x0622, 0xFB84, 0x02B6, 0xFEAA, 0x008F
},
{
  0xFFDE, 0x006D, 0xFEBD, 0x02F8, 0xFA4C, 0x0979, 0xF189, 0x17A9,
  0x6FDD, 0x01E3, 0xFA10, 0x05C7, 0xFBAC, 0x02A8, 0xFEAC, 0x0091
},
{
  0xFFE3, 0x0067, 0xFEC3, 0x02F5, 0xFA3E, 0x09B3, 0xF0E4, 0x19B0,
  0x6F60, 0x0055, 0xFACA, 0x056A, 0xFBD6, 0x0299, 0xFEB0, 0x0092
},
{
  0xFFE7, 0x0062, 0xFECA, 0x02F1, 0xFA32, 0x09EA, 0xF042, 0x1BBD,
  0x6ED1, 0xFED4, 0xFB84, 0x050C, 0xFC01, 0x0288, 0xFEB4, 0x0092
},
{
  0xFFEB, 0x005C, 0xFED3, 0x02EA, 0xFA2A, 0x0A1D, 0xEFA6, 0x1DD0,
  0x6E2F, 0xFD5E, 0xFC3C, 0x04AE, 0xFC2D, 0x0277, 0xFEB8, 0x0092
},
{
  0xFFF0, 0x0056, 0xFEDC, 0x02E3, 0xFA25, 0x0A4B, 0xEF0E, 0x1FE8,
  0x6D7B, 0xFBF4, 0xFCF2, 0x044E, 0xFC5B, 0x0265, 0xFEBD, 0x0092
},
{
  0xFFF5, 0x004F, 0xFEE5, 0x02D9, 0xFA22, 0x0A75, 0xEE7C, 0x2205,
  0x6CB5, 0xFA97, 0xFDA7, 0x03EE, 0xFC89, 0x0252, 0xFEC3, 0x0092
},
{
  0xFFF9, 0x0048, 0xFEF0, 0x02CE, 0xFA23, 0x0A9A, 0xEDF0, 0x2425,
  0x6BDD, 0xF947, 0xFE59, 0x038E, 0xFCB9, 0x023E, 0xFEC9, 0x0091
},
{
  0xFFFE, 0x0041, 0xFEFB, 0x02C0, 0xFA26, 0x0ABA, 0xED6B, 0x264A,
  0x6AF4, 0xF804, 0xFF09, 0x032D, 0xFCE9, 0x022A, 0xFED0, 0x0091
},
{
  0x0003, 0x003A, 0xFF08, 0x02B2, 0xFA2D, 0x0AD5, 0xECEC, 0x2871,
  0x69F9, 0xF6CE, 0xFFB5, 0x02CD, 0xFD1A, 0x0215, 0xFED7, 0x008F
},
{
  0x0007, 0x0032, 0xFF15, 0x02A1, 0xFA37, 0x0AEB, 0xEC74, 0x2A9A,
  0x68ED, 0xF5A6, 0x005F, 0x026D, 0xFD4C, 0x01FF, 0xFEDE, 0x008E
},
{
  0x000C, 0x002A, 0xFF23, 0x028F, 0xFA44, 0x0AFC, 0xEC04, 0x2CC6,
  0x67D0, 0xF48B, 0x0105, 0x020D, 0xFD7E, 0x01E8, 0xFEE6, 0x008C
},
{
  0x0011, 0x0021, 0xFF31, 0x027B, 0xFA55, 0x0B07, 0xEB9C, 0x2EF2,
  0x66A4, 0xF37D, 0x01A8, 0x01AE, 0xFDB1, 0x01D1, 0xFEEF, 0x008A
},
{
  0x0016, 0x0019, 0xFF41, 0x0265, 0xFA69, 0x0B0D, 0xEB3C, 0x311F,
  0x6567, 0xF27D, 0x0246, 0x014F, 0xFDE4, 0x01BA, 0xFEF8, 0x0088
},
{
  0x001B, 0x0010, 0xFF51, 0x024D, 0xFA80, 0x0B0D, 0xEAE4, 0x334C,
  0x641A, 0xF18B, 0x02E1, 0x00F2, 0xFE17, 0x01A2, 0xFF01, 0x0086
},
{
  0x001F, 0x0006, 0xFF62, 0x0234, 0xFA9A, 0x0B07, 0xEA96, 0x3579,
  0x62BF, 0xF0A7, 0x0377, 0x0096, 0xFE4A, 0x0189, 0xFF0A, 0x0084
},
{
  0x0024, 0xFFFD, 0xFF74, 0x0219, 0xFAB8, 0x0AFC, 0xEA51, 0x37A4,
  0x6154, 0xEFD0, 0x0409, 0x003B, 0xFE7C, 0x0171, 0xFF14, 0x0081
},
{
  0x0029, 0xFFF3, 0xFF86, 0x01FD, 0xFAD9, 0x0AEA, 0xEA15, 0x39CD,
  0x5FDB, 0xEF07, 0x0495, 0xFFE1, 0xFEAF, 0x0158, 0xFF1E, 0x007E
},
{
  0x002E, 0xFFE9, 0xFF99, 0x01DF, 0xFAFD, 0x0AD3, 0xE9E4, 0x3BF4,
  0x5E55, 0xEE4C, 0x051D, 0xFF8A, 0xFEE2, 0x013F, 0xFF28, 0x007B
},
{
  0x0033, 0xFFDF, 0xFFAD, 0x01BF, 0xFB25, 0x0AB5, 0xE9BD, 0x3E18,
  0x5CC1, 0xED9F, 0x05A0, 0xFF34, 0xFF14, 0x0126, 0xFF32, 0x0078
},
{
  0x0038, 0xFFD5, 0xFFC1, 0x019E, 0xFB50, 0x0A92, 0xE9A0, 0x4038,
  0x5B20, 0xECFF, 0x061D, 0xFEE0, 0xFF46, 0x010D, 0xFF3D, 0x0074
},
{
  0x003D, 0xFFCB, 0xFFD6, 0x017B, 0xFB7E, 0x0A68, 0xE98F, 0x4253,
  0x5973, 0xEC6D, 0x0695, 0xFE8E, 0xFF77, 0x00F4, 0xFF48, 0x0070
},
{
  0x0041, 0xFFC0, 0xFFEC, 0x0157, 0xFBAF, 0x0A38, 0xE988, 0x446A,
  0x57BA, 0xEBE9, 0x0707, 0xFE3E, 0xFFA7, 0x00DA, 0xFF52, 0x006D
},
{
  0x0046, 0xFFB5, 0x0002, 0x0131, 0xFBE3, 0x0A02, 0xE98E, 0x467B,
  0x55F5, 0xEB72, 0x0774, 0xFDF0, 0xFFD7, 0x00C1, 0xFF5D, 0x0069
},
{
  0x004B, 0xFFAA, 0x0019, 0x010A, 0xFC1B, 0x09C6, 0xE99F, 0x4887,
  0x5426, 0xEB09, 0x07DB, 0xFDA5, 0x0006, 0x00A8, 0xFF68, 0x0065
},
{
  0x004F, 0xFFA0, 0x0030, 0x00E1, 0xFC55, 0x0983, 0xE9BC, 0x4A8B,
  0x524C, 0xEAAC, 0x083C, 0xFD5C, 0x0034, 0x0090, 0xFF73, 0x0061
},
{
  0x0054, 0xFF95, 0x0047, 0x00B8, 0xFC93, 0x093B, 0xE9E5, 0x4C88,
  0x5069, 0xEA5D, 0x0897, 0xFD16, 0x0061, 0x0077, 0xFF7E, 0x005C
},
{
  0x0058, 0xFF89, 0x005F, 0x008D, 0xFCD3, 0x08EC, 0xEA1B, 0x4E7D,
  0x4E7D, 0xEA1B, 0x08EC, 0xFCD3, 0x008D, 0x005F, 0xFF89, 0x0058
},
{
  0x005C, 0xFF7E, 0x0077, 0x0061, 0xFD16, 0x0897, 0xEA5D, 0x5069,
  0x4C88, 0xE9E5, 0x093B, 0xFC93, 0x00B8, 0x0047, 0xFF95, 0x0054
},
{
  0x0061, 0xFF73, 0x0090, 0x0034, 0xFD5C, 0x083C, 0xEAAC, 0x524C,
  0x4A8B, 0xE9BC, 0x0983, 0xFC55, 0x00E1, 0x0030, 0xFFA0, 0x004F
},
{
  0x0065, 0xFF68, 0x00A8, 0x0006, 0xFDA5, 0x07DB, 0xEB09, 0x5426,
  0x4887, 0xE99F, 0x09C6, 0xFC1B, 0x010A, 0x0019, 0xFFAA, 0x004B
},
{
  0x0069, 0xFF5D, 0x00C1, 0xFFD7, 0xFDF0, 0x0774, 0xEB72, 0x55F5,
  0x467B, 0xE98E, 0x0A02, 0xFBE3, 0x0131, 0x0002, 0xFFB5, 0x0046
},
{
  0x006D, 0xFF52, 0x00DA, 0xFFA7, 0xFE3E, 0x0707, 0xEBE9, 0x57BA,
  0x446A, 0xE988, 0x0A38, 0xFBAF, 0x0157, 0xFFEC, 0xFFC0, 0x0041
},
{
  0x0070, 0xFF48, 0x00F4, 0xFF77, 0xFE8E, 0x0695, 0xEC6D, 0x5973,
  0x4253, 0xE98F, 0x0A68, 0xFB7E, 0x017B, 0xFFD6, 0xFFCB, 0x003D
},
{
  0x0074, 0xFF3D, 0x010D, 0xFF46, 0xFEE0, 0x061D, 0xECFF, 0x5B20,
  0x4038, 0xE9A0, 0x0A92, 0xFB50, 0x019E, 0xFFC1, 0xFFD5, 0x0038
},
{
  0x0078, 0xFF32, 0x0126, 0xFF14, 0xFF34, 0x05A0, 0xED9F, 0x5CC1,
  0x3E18, 0xE9BD, 0x0AB5, 0xFB25, 0x01BF, 0xFFAD, 0xFFDF, 0x0033
},
{
  0x007B, 0xFF28, 0x013F, 0xFEE2, 0xFF8A, 0x051D, 0xEE4C, 0x5E55,
  0x3BF4, 0xE9E4, 0x0AD3, 0xFAFD, 0x01DF, 0xFF99, 0xFFE9, 0x002E
},
{
  0x007E, 0xFF1E, 0x0158, 0xFEAF, 0xFFE1, 0x0495, 0xEF07, 0x5FDB,
  0x39CD, 0xEA15, 0x0AEA, 0xFAD9, 0x01FD, 0xFF86, 0xFFF3, 0x0029
},
{
  0x0081, 0xFF14, 0x0171, 0xFE7C, 0x003B, 0x0409, 0xEFD0, 0x6154,
  0x37A4, 0xEA51, 0x0AFC, 0xFAB8, 0x0219, 0xFF74, 0xFFFD, 0x0024
},
{
  0x0084, 0xFF0A, 0x0189, 0xFE4A, 0x0096, 0x0377, 0xF0A7, 0x62BF,
  0x3579, 0xEA96, 0x0B07, 0xFA9A, 0x0234, 0xFF62, 0x0006, 0x001F
},
{
  0x0086, 0xFF01, 0x01A2, 0xFE17, 0x00F2, 0x02E1, 0xF18B, 0x641A,
  0x334C, 0xEAE4, 0x0B0D, 0xFA80, 0x024D, 0xFF51, 0x0010, 0x001B
},
{
  0x0088, 0xFEF8, 0x01BA, 0xFDE4, 0x014F, 0x0246, 0xF27D, 0x6567,
  0x311F, 0xEB3C, 0x0B0D, 0xFA69, 0x0265, 0xFF41, 0x0019, 0x0016
},
{
  0x008A, 0xFEEF, 0x01D1, 0xFDB1, 0x01AE, 0x01A8, 0xF37D, 0x66A4,
  0x2EF2, 0xEB9C, 0x0B07, 0xFA55, 0x027B, 0xFF31, 0x0021, 0x0011
},
{
  0x008C, 0xFEE6, 0x01E8, 0xFD7E, 0x020D, 0x0105, 0xF48B, 0x67D0,
  0x2CC6, 0xEC04, 0x0AFC, 0xFA44, 0x028F, 0xFF23, 0x002A, 0x000C
},
{
  0x008E, 0xFEDE, 0x01FF, 0xFD4C, 0x026D, 0x005F, 0xF5A6, 0x68ED,
  0x2A9A, 0xEC74, 0x0AEB, 0xFA37, 0x02A1, 0xFF15, 0x0032, 0x0007
},
{
  0x008F, 0xFED7, 0x0215, 0xFD1A, 0x02CD, 0xFFB5, 0xF6CE, 0x69F9,
  0x2871, 0xECEC, 0x0AD5, 0xFA2D, 0x02B2, 0xFF08, 0x003A, 0x0003
},
{
  0x0091, 0xFED0, 0x022A, 0xFCE9, 0x032D, 0xFF09, 0xF804, 0x6AF4,
  0x264A, 0xED6B, 0x0ABA, 0xFA26, 0x02C0, 0xFEFB, 0x0041, 0xFFFE
},
{
  0x0091, 0xFEC9, 0x023E, 0xFCB9, 0x038E, 0xFE59, 0xF947, 0x6BDD,
  0x2425, 0xEDF0, 0x0A9A, 0xFA23, 0x02CE, 0xFEF0, 0x0048, 0xFFF9
},
{
  0x0092, 0xFEC3, 0x0252, 0xFC89, 0x03EE, 0xFDA7, 0xFA97, 0x6CB5,
  0x2205, 0xEE7C, 0x0A75, 0xFA22, 0x02D9, 0xFEE5, 0x004F, 0xFFF5
},
{
  0x0092, 0xFEBD, 0x0265, 0xFC5B, 0x044E, 0xFCF2, 0xFBF4, 0x6D7B,
  0x1FE8, 0xEF0E, 0x0A4B, 0xFA25, 0x02E3, 0xFEDC, 0x0056, 0xFFF0
},
{
  0x0092, 0xFEB8, 0x0277, 0xFC2D, 0x04AE, 0xFC3C, 0xFD5E, 0x6E2F,
  0x1DD0, 0xEFA6, 0x0A1D, 0xFA2A, 0x02EA, 0xFED3, 0x005C, 0xFFEB
},
{
  0x0092, 0xFEB4, 0x0288, 0xFC01, 0x050C, 0xFB84, 0xFED4, 0x6ED1,
  0x1BBD, 0xF042, 0x09EA, 0xFA32, 0x02F1, 0xFECA, 0x0062, 0xFFE7
},
{
  0x0092, 0xFEB0, 0x0299, 0xFBD6, 0x056A, 0xFACA, 0x0055, 0x6F60,
  0x19B0, 0xF0E4, 0x09B3, 0xFA3E, 0x02F5, 0xFEC3, 0x0067, 0xFFE3
},
{
  0x0091, 0xFEAC, 0x02A8, 0xFBAC, 0x05C7, 0xFA10, 0x01E3, 0x6FDD,
  0x17A9, 0xF189, 0x0979, 0xFA4C, 0x02F8, 0xFEBD, 0x006D, 0xFFDE
},
{
  0x008F, 0xFEAA, 0x02B6, 0xFB84, 0x0622, 0xF954, 0x037D, 0x7046,
  0x15A9, 0xF233, 0x093A, 0xFA5C, 0x02FA, 0xFEB7, 0x0072, 0xFFDA
},
{
  0x008E, 0xFEA8, 0x02C2, 0xFB5D, 0x067C, 0xF899, 0x0521, 0x709D,
  0x13B0, 0xF2E0, 0x08F8, 0xFA70, 0x02F9, 0xFEB2, 0x0076, 0xFFD6
},
{
  0x008C, 0xFEA6, 0x02CE, 0xFB38, 0x06D4, 0xF7DE, 0x06D1, 0x70E0,
  0x11BF, 0xF390, 0x08B2, 0xFA85, 0x02F8, 0xFEAE, 0x007A, 0xFFD2
},
{
  0x008A, 0xFEA6, 0x02D8, 0xFB15, 0x072A, 0xF723, 0x088B, 0x7110,
  0x0FD5, 0xF443, 0x0869, 0xFA9E, 0x02F4, 0xFEAB, 0x007E, 0xFFCE
},
{
  0x0088, 0xFEA6, 0x02E1, 0xFAF4, 0x077D, 0xF669, 0x0A4F, 0x712D,
  0x0DF5, 0xF4F9, 0x081D, 0xFAB8, 0x02EF, 0xFEA9, 0x0082, 0xFFCB
},
};


//static dword sample_time = 0;

void g711plc_init(t_g711plc *g711plc)
{
    g711plc->sample_pos = 0;
    memset(g711plc->samples, 0, G711PLC_CORRELATION_BUF_LEN * sizeof(short));
    memset(g711plc->samples_odd, 0, G711PLC_CORRELATION_BUF_LEN * sizeof(short));
    g711plc->merging = false;
    g711plc->concealing = false;
    g711plc->conceal_gain = G711PLC_CONCEAL_GAIN_INIT;
    g711plc->random_number_seed = 0;
}


static int32 g711plc_correlation_norm(dword lo, signed char hi, word quot)
{
    return (((((int32) hi) * ((int32) quot)) << 17) + ((((dword)(lo >> 16)) * ((dword) quot)) << 1) + (((dword)(((dword)(lo & 0xffff)) * ((dword) quot))) >> 15));
}

static dword g711plc_find_vocal_period(const short *corr_buf, word *vocal_gain_dec, word *noise_level)
{
    unsigned i, j, k;
    word  bestmatch_dec, bestmatch, bestmatch_quot, n, dec;
    int32 corr[3], bestcorr[3], x0, y0, l;
    dword corr_low, d, e;
    signed char corr_high;
    const short *p;
    short corr_dec_buf[G711PLC_CORRELATION_BUF_LEN / G711PLC_CORRELATION_DECIMATION];
    dword period_energies[2];
    word period_levels[2];

    j = 0;
    for (i = 0; i < G711PLC_CORRELATION_BUF_LEN / G711PLC_CORRELATION_DECIMATION; i++) {
        corr_dec_buf[i] = corr_buf[j] >> (G711PLC_CORRELATION_LEN_DEC_EXP / 2);
        j += G711PLC_CORRELATION_DECIMATION;
    }
    p = &(corr_dec_buf[(G711PLC_CORRELATION_BUF_LEN - G711PLC_CORRELATION_LENGTH) / G711PLC_CORRELATION_DECIMATION]);

    bestcorr[1] = sdsp_correlation_32 (corr_dec_buf, p, G711PLC_CORRELATION_LENGTH / G711PLC_CORRELATION_DECIMATION);
    bestmatch_dec = 0;
    for (j = 1; j <= G711PLC_VOCAL_PERIOD_RANGE / G711PLC_CORRELATION_DECIMATION; j++) {
        corr[1] = sdsp_correlation_32 (&(corr_dec_buf[j]), p, G711PLC_CORRELATION_LENGTH / G711PLC_CORRELATION_DECIMATION);
        if (corr[1] > bestcorr[1])
        {
            bestcorr[1] = corr[1];
            bestmatch_dec = j;
        }
    }

    bestmatch_dec = G711PLC_VOCAL_PERIOD_MAX - (bestmatch_dec * G711PLC_CORRELATION_DECIMATION);
    bestmatch_quot = 0x8000 / bestmatch_dec;
    i = G711PLC_CORRELATION_BUF_LEN - (G711PLC_CORRELATION_DECIMATION - 1) - 2 * bestmatch_dec;
    k = G711PLC_CORRELATION_BUF_LEN + (G711PLC_CORRELATION_DECIMATION - 1) - 2 * bestmatch_dec;
    p = &(corr_buf[G711PLC_CORRELATION_BUF_LEN - bestmatch_dec]);

    j = i;
    bestcorr[0] = 0;
    bestcorr[1] = corr[0] = (sdsp_correlation (&(corr_buf[j]), p, bestmatch_dec, &corr_low, &corr_high), g711plc_correlation_norm(corr_low, corr_high, bestmatch_quot));
    bestcorr[2] = corr[1] = (sdsp_correlation (&(corr_buf[j + 1]), p, bestmatch_dec, &corr_low, &corr_high), g711plc_correlation_norm(corr_low, corr_high, bestmatch_quot));
    bestmatch = j;
    j++;
    while (j < k) {
        corr[2] = (sdsp_correlation (&(corr_buf[j + 1]), p, bestmatch_dec, &corr_low, &corr_high), g711plc_correlation_norm(corr_low, corr_high, bestmatch_quot));
        if (corr[1] > bestcorr[1])
        {
            bestcorr[0] = corr[0];
            bestcorr[1] = corr[1];
            bestcorr[2] = corr[2];
            bestmatch = j;
        }
        corr[0] = corr[1];
        corr[1] = corr[2];
        j++;
    }
    if (corr[1] > bestcorr[1]) {
        bestcorr[0] = corr[0];
        bestcorr[1] = corr[1];
        bestcorr[2] = (sdsp_correlation (&(corr_buf[j + 1]), p, bestmatch_dec, &corr_low, &corr_high), g711plc_correlation_norm(corr_low, corr_high, bestmatch_quot));
        if (j == G711PLC_CORRELATION_BUF_LEN + (G711PLC_CORRELATION_DECIMATION - 1) - 2 * G711PLC_VOCAL_PERIOD_MIN) {
            if (bestcorr[2] > bestcorr[0]) {
                bestcorr[2] = bestcorr[0];
            }
        }
        else {
            if (bestcorr[2] > bestcorr[1]) {
                bestcorr[2] = bestcorr[1];
            }
        }
        bestmatch = j;
    }
    else if (bestmatch == i) {
        bestcorr[0] = (sdsp_correlation (&(corr_buf[i - 1]), p, bestmatch_dec, &corr_low, &corr_high), g711plc_correlation_norm(corr_low, corr_high, bestmatch_quot));
        if (i == G711PLC_CORRELATION_BUF_LEN - (G711PLC_CORRELATION_DECIMATION - 1) - 2 * G711PLC_VOCAL_PERIOD_MAX) {
            if (bestcorr[0] > bestcorr[2]) {
                bestcorr[0] = bestcorr[2];
            }
        }
        else {
            if (bestcorr[0] > bestcorr[1]) {
                bestcorr[0] = bestcorr[1];
            }
        }
    }
    if (bestcorr[0] < 0) {
        bestcorr[0] = 0;
    }
    if (bestcorr[1] <= 0) {
        bestcorr[1] = 1;
    }
    if (bestcorr[2] < 0) {
        bestcorr[2] = 0;
    }
    i = sdsp_dword_leading_zeroes (bestcorr[1]) - 1;
    n = ((i < 2) ? 4 * (bestcorr[1] >> (2 - i)) - 2 * (bestcorr[0] >> (2 - i)) - 2 * (bestcorr[2] >> (2 - i)) : (4 * bestcorr[1] - 2 * bestcorr[0] - 2 * bestcorr[2]) << (i - 2)) >> 16;
    x0 = ((int32)((i < 2) ? (bestcorr[2] >> (2 - i)) - (bestcorr[0] >> (2 - i)) : (bestcorr[2] - bestcorr[0]) << (i - 2))) / ((int32)((n != 0) ? n : 1));
    l = (bestcorr[2] >> 2) - (bestcorr[0] >> 2);
    y0 = bestcorr[1] + ((((int32)(l >> 16)) * ((int32)(x0 >> 1))) << 1) + (((int32)(((int32)(l & 0xffff)) * ((int32)(x0 >> 1)))) >> 15);
    bestmatch = G711PLC_CORRELATION_BUF_LEN - bestmatch_dec - bestmatch;
    for (i = 0; i < 2; i++) {
        p = &(corr_buf[G711PLC_CORRELATION_BUF_LEN - bestmatch_dec - i * bestmatch]);
        period_energies[i] = (sdsp_correlation (p, p, bestmatch_dec, &corr_low, &corr_high), g711plc_correlation_norm(corr_low, corr_high, bestmatch_quot));
        period_levels[i] = sdsp_sqrt (period_energies[i]);
    }
    e = (((dword)(period_levels[0])) * ((dword)(period_levels[1]))) << 1;
    d = (y0 >= 0) ? y0 : 0;
    *noise_level = (e <= d) ? 0 : sdsp_sqrt ((d <= e - d) ? d : e - d);
    *vocal_gain_dec = (period_levels[0] >= period_levels[1]) ? 0 : ((word)((((dword)(period_levels[1] - period_levels[0])) << 15) / (((dword)(period_levels[1])) * ((dword) bestmatch))));
    n = (e <= d) ? 0 : sdsp_sqrt (e) - sdsp_sqrt (d);
    dec = (word)((period_levels[0] <= n) ? ((dword) 0x8000) / ((dword) bestmatch) : (((dword) n) << 15) / (((dword)(period_levels[0])) * ((dword) bestmatch)));
    if (dec > *vocal_gain_dec) {
        *vocal_gain_dec = dec;
    }
//    printf("%08lx %08lx %08lx %6i %04x %04x %08lx %08lx %04x ", bestcorr[0], bestcorr[1], bestcorr[2], x0, period_levels[0], period_levels[1], e, d, n);
    return ((bestmatch << 16) - x0);
}


short g711plc_put_get_sample_cng (t_g711plc *g711plc, short input_sample, short input_sample_odd,
    short cng_sample, short cng_sample_odd, short *p_result_sample_odd, byte *p_tail)
{
    short result_sample, result_sample_odd;
    short noise[2];
    int32 l;

    if (g711plc->merging) {
        if (g711plc->vocal) {
            if (g711plc->vocal_pos == g711plc->vocal_period) {
                g711plc->vocal_pos_low += g711plc->vocal_period_low;
                if (g711plc->vocal_pos_low >= g711plc->vocal_period_low) {
                    g711plc->vocal_wave = g711plc->vocal_wave_s;
                    result_sample = g711plc->vocal_wave[0];
                    g711plc->vocal_wave_odd = g711plc->vocal_wave_s_odd;
                    result_sample_odd = g711plc->vocal_wave_odd[0];
                    g711plc->vocal_pos = 1;
                }
                else {
                    g711plc->vocal_wave = g711plc->vocal_wave_l;
                    result_sample = g711plc->vocal_wave_pad;
                    g711plc->vocal_wave_pad = g711plc->vocal_wave_pad_next;
                    g711plc->vocal_wave_odd = g711plc->vocal_wave_l_odd;
                    result_sample_odd = g711plc->vocal_wave_pad_odd;
                    g711plc->vocal_wave_pad_odd = g711plc->vocal_wave_pad_next_odd;
                    g711plc->vocal_pos = 0;
                }
                g711plc->vocal_wave_s = g711plc->vocal_wave_short;
                g711plc->vocal_wave_l = g711plc->vocal_wave_long;
                g711plc->vocal_wave_s_odd = g711plc->vocal_wave_short_odd;
                g711plc->vocal_wave_l_odd = g711plc->vocal_wave_long_odd;
            }
            else {
                result_sample = g711plc->vocal_wave[g711plc->vocal_pos];
                result_sample_odd = g711plc->vocal_wave_odd[g711plc->vocal_pos];
                (g711plc->vocal_pos)++;
            }
            result_sample = (short)(((int32)(((int32) result_sample) * ((int32)(g711plc->vocal_gain)))) >> 15);
            result_sample_odd = (short)(((int32)(((int32) result_sample_odd) * ((int32)(g711plc->vocal_gain)))) >> 15);
            if (g711plc->vocal_gain >= g711plc->vocal_gain_dec) {
                g711plc->vocal_gain -= g711plc->vocal_gain_dec;
            }
            else {
                g711plc->vocal = false;
            }
        }
        else {
            result_sample = 0;
            result_sample_odd = 0;
        }
        sdsp_fill_gaussian_noise (noise, 2, g711plc->noise_level, &(g711plc->random_number_seed));
        l = result_sample + noise[0];
        result_sample = (l > 32767) ? 32767 : ((l < -32768) ? -32758 : (short) l);
        l = result_sample_odd + noise[1];
        result_sample_odd = (l > 32767) ? 32767 : ((l < -32768) ? -32758 : (short) l);

        result_sample = (short)((((int32) result_sample) * ((int32)(0x8000 - g711plc->merge_gain)) + ((int32) input_sample) * ((int32)(g711plc->merge_gain))) >> 15);
        result_sample_odd = (short)((((int32) result_sample_odd) * ((int32)(0x8000 - g711plc->merge_gain)) + ((int32) input_sample_odd) * ((int32)(g711plc->merge_gain))) >> 15);
        if (g711plc->merge_gain < 0x8000 - G711PLC_MERGE_GAIN_INC) {
            g711plc->merge_gain += G711PLC_MERGE_GAIN_INC;
        }
        else {
            g711plc->merging = false;
        }
    }
    else {
        result_sample = input_sample;
        result_sample_odd = input_sample_odd;
    }
    g711plc->samples[g711plc->sample_pos] = result_sample;
    g711plc->samples_odd[g711plc->sample_pos] = result_sample_odd;
    g711plc->sample_pos = (g711plc->sample_pos < G711PLC_CORRELATION_BUF_LEN - 1) ? g711plc->sample_pos + 1 : 0;

    if (g711plc->concealing) {
        result_sample = (short)(((int32)(((int32) result_sample) * ((int32)((g711plc->conceal_gain <= 0x8000) ? g711plc->conceal_gain : 0x8000)))) >> 15);
        result_sample_odd = (short)(((int32)(((int32) result_sample_odd) * ((int32)((g711plc->conceal_gain <= 0x8000) ? g711plc->conceal_gain : 0x8000)))) >> 15);
        if (g711plc->conceal_gain < 0x8000 - G711PLC_CONCEAL_GAIN_INC) {
            g711plc->conceal_gain += G711PLC_CONCEAL_GAIN_INC;
        }
        else {
            g711plc->conceal_gain = G711PLC_CONCEAL_GAIN_INIT;
            g711plc->concealing = false;
        }
    }
//    sample_time++;
    *p_result_sample_odd = result_sample_odd;
    return result_sample;
}


short g711plc_get_plc_sample_cng (t_g711plc *g711plc, short cng_sample, short cng_sample_odd, short *p_result_sample_odd)
{
    short corr_buf[G711PLC_CORRELATION_BUF_LEN + 1 + G711PLC_VOCAL_PERIOD_MAX + 1];
    short corr_buf_odd[G711PLC_CORRELATION_BUF_LEN + 1 + G711PLC_VOCAL_PERIOD_MAX + 1];
    dword vocal_period;
    word gain, gain_inc;
    short s, t, result_sample, result_sample_odd, noise[2];
    int32 l;
    unsigned i, n, k;

    if (!g711plc->merging) {
        memcpy(corr_buf, &(g711plc->samples[g711plc->sample_pos]), (G711PLC_CORRELATION_BUF_LEN - g711plc->sample_pos) * sizeof(short));
        memcpy(corr_buf_odd, &(g711plc->samples_odd[g711plc->sample_pos]), (G711PLC_CORRELATION_BUF_LEN - g711plc->sample_pos) * sizeof(short));
        if (g711plc->sample_pos != 0) {
            memcpy(&(corr_buf[G711PLC_CORRELATION_BUF_LEN - g711plc->sample_pos]), g711plc->samples, g711plc->sample_pos * sizeof(short));
            memcpy(&(corr_buf_odd[G711PLC_CORRELATION_BUF_LEN - g711plc->sample_pos]), g711plc->samples_odd, g711plc->sample_pos * sizeof(short));
        }
        vocal_period = g711plc_find_vocal_period(corr_buf, &(g711plc->vocal_gain_dec), &(g711plc->noise_level));
        g711plc->vocal_period_low = (word)(vocal_period & 0xffff);
        g711plc->vocal_period = (word)(vocal_period >> 16);
/*
        printf("%u.%03u dec %04x noise %04x period %u.%03u ms %u.%03u\n", sample_time >> 3, ((sample_time & 0x7) * 1000) >> 3,
            g711plc->vocal_gain_dec, g711plc->noise_level,
            vocal_period >> 16, ((vocal_period & 0xffff) * 1000) >> 16, vocal_period >> 19, ((vocal_period & 0x7ffff) * 1000) >> 19);
*/
        gain = 0x8000;
        i = G711PLC_CORRELATION_BUF_LEN;
        do {
            i--;
            if (gain >= g711plc->vocal_gain_dec) {
                gain -= g711plc->vocal_gain_dec;
            }
            else {
                gain = 0;
            }
            corr_buf[i] = (short)(((int32)(((int32)(corr_buf[i])) * ((int32) gain))) >> 15);
            corr_buf_odd[i] = (short)(((int32)(((int32)(corr_buf_odd[i])) * ((int32) gain))) >> 15);
        }
        while (i > ((unsigned)(G711PLC_CORRELATION_BUF_LEN - (G711PLC_INTERPOLATION_TAPS_HALF + 1) - 2 * g711plc->vocal_period)));
        g711plc->vocal_overlap = g711plc->vocal_period;
        g711plc->vocal_wave = g711plc->vocal_wave_first;
        g711plc->vocal_wave_s = g711plc->vocal_wave_second;
        g711plc->vocal_wave_l = g711plc->vocal_wave_second;
        g711plc->vocal_wave_odd = g711plc->vocal_wave_first_odd;
        g711plc->vocal_wave_s_odd = g711plc->vocal_wave_second_odd;
        g711plc->vocal_wave_l_odd = g711plc->vocal_wave_second_odd;
        g711plc->vocal_pos_low = 0x8000 + g711plc->vocal_period_low;
        gain_inc = (word)(((dword) 0x80000000) / (((dword) g711plc->vocal_overlap) << 16));
        k = -(g711plc->vocal_period_low >> (16 - G711PLC_INTERPOLATION_DIVS_EXP)) & (G711PLC_INTERPOLATION_DIVS - 1);
        gain = 0;
        if (g711plc->vocal_pos_low >= g711plc->vocal_period_low) {
            n = g711plc->vocal_period_low >> (16 - G711PLC_INTERPOLATION_DIVS_EXP);
            if (n > g711plc->vocal_overlap) {
                n = g711plc->vocal_overlap;
            }
            for (i = 0; i < n; i++) {
                s = sdsp_fir ((const short *)(interpolation_filters[k]), &(corr_buf[G711PLC_CORRELATION_BUF_LEN - (G711PLC_INTERPOLATION_TAPS_HALF - 1) - g711plc->vocal_period + i]), 0, G711PLC_INTERPOLATION_TAPS);
                t = corr_buf[G711PLC_CORRELATION_BUF_LEN - g711plc->vocal_period + i];
                corr_buf[G711PLC_CORRELATION_BUF_LEN + i] = (short)((((int32) s) * ((int32)(0x8000 - gain)) + ((int32) t) * ((int32) gain)) >> 15);
                s = sdsp_fir ((const short *)(interpolation_filters[k]), &(corr_buf_odd[G711PLC_CORRELATION_BUF_LEN - (G711PLC_INTERPOLATION_TAPS_HALF - 1) - g711plc->vocal_period + i]), 0, G711PLC_INTERPOLATION_TAPS);
                t = corr_buf_odd[G711PLC_CORRELATION_BUF_LEN - g711plc->vocal_period + i];
                corr_buf_odd[G711PLC_CORRELATION_BUF_LEN + i] = (short)((((int32) s) * ((int32)(0x8000 - gain)) + ((int32) t) * ((int32) gain)) >> 15);
                gain += gain_inc;
                k++;
            }
            memmove(&(corr_buf[G711PLC_CORRELATION_BUF_LEN + i]), &(corr_buf[G711PLC_CORRELATION_BUF_LEN - (g711plc->vocal_period - i)]), (g711plc->vocal_period - i + 1) * sizeof(short));
            memmove(&(corr_buf_odd[G711PLC_CORRELATION_BUF_LEN + i]), &(corr_buf_odd[G711PLC_CORRELATION_BUF_LEN - (g711plc->vocal_period - i)]), (g711plc->vocal_period - i + 1) * sizeof(short));
            g711plc->vocal_pos = g711plc->vocal_overlap;
        }
        else {
            corr_buf[G711PLC_CORRELATION_BUF_LEN] = sdsp_fir ((const short *)(interpolation_filters[k]), &(corr_buf[G711PLC_CORRELATION_BUF_LEN - (G711PLC_INTERPOLATION_TAPS_HALF + 1) - g711plc->vocal_period]), 0, G711PLC_INTERPOLATION_TAPS);
            corr_buf_odd[G711PLC_CORRELATION_BUF_LEN] = sdsp_fir ((const short *)(interpolation_filters[k]), &(corr_buf_odd[G711PLC_CORRELATION_BUF_LEN - (G711PLC_INTERPOLATION_TAPS_HALF + 1) - g711plc->vocal_period]), 0, G711PLC_INTERPOLATION_TAPS);
            n = k - 1;
            if (n > g711plc->vocal_overlap) {
                n = g711plc->vocal_overlap;
            }
            for (i = 0; i < n; i++) {
                k--;
                s = sdsp_fir ((const short *)(interpolation_filters[k]), &(corr_buf[G711PLC_CORRELATION_BUF_LEN + 1 - (G711PLC_INTERPOLATION_TAPS_HALF + 1) - g711plc->vocal_period + i]), 0, G711PLC_INTERPOLATION_TAPS);
                t = corr_buf[G711PLC_CORRELATION_BUF_LEN - g711plc->vocal_period + i];
                corr_buf[G711PLC_CORRELATION_BUF_LEN + 1 + i] = (short)((((int32) s) * ((int32)(0x8000 - gain)) + ((int32) t) * ((int32) gain)) >> 15);
                s = sdsp_fir ((const short *)(interpolation_filters[k]), &(corr_buf_odd[G711PLC_CORRELATION_BUF_LEN + 1 - (G711PLC_INTERPOLATION_TAPS_HALF + 1) - g711plc->vocal_period + i]), 0, G711PLC_INTERPOLATION_TAPS);
                t = corr_buf_odd[G711PLC_CORRELATION_BUF_LEN - g711plc->vocal_period + i];
                corr_buf_odd[G711PLC_CORRELATION_BUF_LEN + 1 + i] = (short)((((int32) s) * ((int32)(0x8000 - gain)) + ((int32) t) * ((int32) gain)) >> 15);
                gain += gain_inc;
            }
            memmove(&(corr_buf[G711PLC_CORRELATION_BUF_LEN + 1 + i]), &(corr_buf[G711PLC_CORRELATION_BUF_LEN - (g711plc->vocal_period - i)]), (g711plc->vocal_period - i + 1) * sizeof(short));
            memmove(&(corr_buf_odd[G711PLC_CORRELATION_BUF_LEN + 1 + i]), &(corr_buf_odd[G711PLC_CORRELATION_BUF_LEN - (g711plc->vocal_period - i)]), (g711plc->vocal_period - i + 1) * sizeof(short));
            g711plc->vocal_pos = g711plc->vocal_overlap - 1;
        }
        if (g711plc->vocal_pos != g711plc->vocal_period) {
            memcpy(&(g711plc->vocal_wave_first[g711plc->vocal_pos]), &(corr_buf[G711PLC_CORRELATION_BUF_LEN]), (g711plc->vocal_period - g711plc->vocal_pos) * sizeof(short));
            memcpy(&(g711plc->vocal_wave_first_odd[g711plc->vocal_pos]), &(corr_buf_odd[G711PLC_CORRELATION_BUF_LEN]), (g711plc->vocal_period - g711plc->vocal_pos) * sizeof(short));
        }
        gain = 0;
        if (((word)(g711plc->vocal_pos_low + g711plc->vocal_period_low)) >= g711plc->vocal_period_low) {
            g711plc->vocal_wave_pad_next = g711plc->vocal_wave_pad = corr_buf[G711PLC_CORRELATION_BUF_LEN - g711plc->vocal_overlap];;
            g711plc->vocal_wave_pad_next_odd = g711plc->vocal_wave_pad_odd = corr_buf_odd[G711PLC_CORRELATION_BUF_LEN - g711plc->vocal_overlap];;
            for (i = 0; i < g711plc->vocal_overlap; i++) {
                s = corr_buf[G711PLC_CORRELATION_BUF_LEN + g711plc->vocal_period - g711plc->vocal_pos + i];
                t = corr_buf[G711PLC_CORRELATION_BUF_LEN - g711plc->vocal_period - g711plc->vocal_overlap + i];
                g711plc->vocal_wave_second[i] = (short)((((int32) s) * ((int32)(0x8000 - gain)) + ((int32) t) * ((int32) gain)) >> 15);
                s = corr_buf[G711PLC_CORRELATION_BUF_LEN - g711plc->vocal_overlap + i];
                g711plc->vocal_wave_short[i] = (short)((((int32) s) * ((int32)(0x8000 - gain)) + ((int32) t) * ((int32) gain)) >> 15);
                s = corr_buf[G711PLC_CORRELATION_BUF_LEN + 1 - g711plc->vocal_overlap + i];
                g711plc->vocal_wave_long[i] = (short)((((int32) s) * ((int32)(0x8000 - gain)) + ((int32) t) * ((int32) gain)) >> 15);
                s = corr_buf_odd[G711PLC_CORRELATION_BUF_LEN + g711plc->vocal_period - g711plc->vocal_pos + i];
                t = corr_buf_odd[G711PLC_CORRELATION_BUF_LEN - g711plc->vocal_period - g711plc->vocal_overlap + i];
                g711plc->vocal_wave_second_odd[i] = (short)((((int32) s) * ((int32)(0x8000 - gain)) + ((int32) t) * ((int32) gain)) >> 15);
                s = corr_buf_odd[G711PLC_CORRELATION_BUF_LEN - g711plc->vocal_overlap + i];
                g711plc->vocal_wave_short_odd[i] = (short)((((int32) s) * ((int32)(0x8000 - gain)) + ((int32) t) * ((int32) gain)) >> 15);
                s = corr_buf_odd[G711PLC_CORRELATION_BUF_LEN + 1 - g711plc->vocal_overlap + i];
                g711plc->vocal_wave_long_odd[i] = (short)((((int32) s) * ((int32)(0x8000 - gain)) + ((int32) t) * ((int32) gain)) >> 15);
                gain += gain_inc;
            }
        }
        else {
            g711plc->vocal_wave_pad = corr_buf[G711PLC_CORRELATION_BUF_LEN + g711plc->vocal_period - g711plc->vocal_pos];
            g711plc->vocal_wave_pad_next = corr_buf[G711PLC_CORRELATION_BUF_LEN - g711plc->vocal_overlap];
            g711plc->vocal_wave_pad_odd = corr_buf_odd[G711PLC_CORRELATION_BUF_LEN + g711plc->vocal_period - g711plc->vocal_pos];
            g711plc->vocal_wave_pad_next_odd = corr_buf_odd[G711PLC_CORRELATION_BUF_LEN - g711plc->vocal_overlap];
            for (i = 0; i < g711plc->vocal_overlap; i++) {
                s = corr_buf[G711PLC_CORRELATION_BUF_LEN + 1 + g711plc->vocal_period - g711plc->vocal_pos + i];
                t = corr_buf[G711PLC_CORRELATION_BUF_LEN - g711plc->vocal_period - g711plc->vocal_overlap + i];
                g711plc->vocal_wave_second[i] = (short)((((int32) s) * ((int32)(0x8000 - gain)) + ((int32) t) * ((int32) gain)) >> 15);
                s = corr_buf[G711PLC_CORRELATION_BUF_LEN - g711plc->vocal_overlap + i];
                g711plc->vocal_wave_short[i] = (short)((((int32) s) * ((int32)(0x8000 - gain)) + ((int32) t) * ((int32) gain)) >> 15);
                s = corr_buf[G711PLC_CORRELATION_BUF_LEN + 1 - g711plc->vocal_overlap + i];
                g711plc->vocal_wave_long[i] = (short)((((int32) s) * ((int32)(0x8000 - gain)) + ((int32) t) * ((int32) gain)) >> 15);
                s = corr_buf_odd[G711PLC_CORRELATION_BUF_LEN + 1 + g711plc->vocal_period - g711plc->vocal_pos + i];
                t = corr_buf_odd[G711PLC_CORRELATION_BUF_LEN - g711plc->vocal_period - g711plc->vocal_overlap + i];
                g711plc->vocal_wave_second_odd[i] = (short)((((int32) s) * ((int32)(0x8000 - gain)) + ((int32) t) * ((int32) gain)) >> 15);
                s = corr_buf_odd[G711PLC_CORRELATION_BUF_LEN - g711plc->vocal_overlap + i];
                g711plc->vocal_wave_short_odd[i] = (short)((((int32) s) * ((int32)(0x8000 - gain)) + ((int32) t) * ((int32) gain)) >> 15);
                s = corr_buf_odd[G711PLC_CORRELATION_BUF_LEN + 1 - g711plc->vocal_overlap + i];
                g711plc->vocal_wave_long_odd[i] = (short)((((int32) s) * ((int32)(0x8000 - gain)) + ((int32) t) * ((int32) gain)) >> 15);
                gain += gain_inc;
            }
        }
        if (g711plc->vocal_overlap != g711plc->vocal_period) {
            memcpy(&(g711plc->vocal_wave_second[g711plc->vocal_overlap]), &(corr_buf[G711PLC_CORRELATION_BUF_LEN - g711plc->vocal_period]), (g711plc->vocal_period - g711plc->vocal_overlap) * sizeof(short));
            memcpy(&(g711plc->vocal_wave_short[g711plc->vocal_overlap]), &(corr_buf[G711PLC_CORRELATION_BUF_LEN - g711plc->vocal_period]), (g711plc->vocal_period - g711plc->vocal_overlap) * sizeof(short));
            memcpy(&(g711plc->vocal_wave_long[g711plc->vocal_overlap]), &(corr_buf[G711PLC_CORRELATION_BUF_LEN - g711plc->vocal_period]), (g711plc->vocal_period - g711plc->vocal_overlap) * sizeof(short));
            memcpy(&(g711plc->vocal_wave_second_odd[g711plc->vocal_overlap]), &(corr_buf_odd[G711PLC_CORRELATION_BUF_LEN - g711plc->vocal_period]), (g711plc->vocal_period - g711plc->vocal_overlap) * sizeof(short));
            memcpy(&(g711plc->vocal_wave_short_odd[g711plc->vocal_overlap]), &(corr_buf_odd[G711PLC_CORRELATION_BUF_LEN - g711plc->vocal_period]), (g711plc->vocal_period - g711plc->vocal_overlap) * sizeof(short));
            memcpy(&(g711plc->vocal_wave_long_odd[g711plc->vocal_overlap]), &(corr_buf_odd[G711PLC_CORRELATION_BUF_LEN - g711plc->vocal_period]), (g711plc->vocal_period - g711plc->vocal_overlap) * sizeof(short));
        }
        g711plc->vocal = true;
        g711plc->vocal_gain = 0x8000;
        g711plc->merging = true;
        g711plc->merge_gain = 0;
        g711plc->concealing = true;
    }
    if (g711plc->vocal) {
        if (g711plc->vocal_pos == g711plc->vocal_period) {
            g711plc->vocal_pos_low += g711plc->vocal_period_low;
            if (g711plc->vocal_pos_low >= g711plc->vocal_period_low) {
                g711plc->vocal_wave = g711plc->vocal_wave_s;
                result_sample = g711plc->vocal_wave[0];
                g711plc->vocal_wave_odd = g711plc->vocal_wave_s_odd;
                result_sample_odd = g711plc->vocal_wave_odd[0];
                g711plc->vocal_pos = 1;
            }
            else {
                g711plc->vocal_wave = g711plc->vocal_wave_l;
                result_sample = g711plc->vocal_wave_pad;
                g711plc->vocal_wave_pad = g711plc->vocal_wave_pad_next;
                g711plc->vocal_wave_odd = g711plc->vocal_wave_l_odd;
                result_sample_odd = g711plc->vocal_wave_pad_odd;
                g711plc->vocal_wave_pad_odd = g711plc->vocal_wave_pad_next_odd;
                g711plc->vocal_pos = 0;
            }
            g711plc->vocal_wave_s = g711plc->vocal_wave_short;
            g711plc->vocal_wave_l = g711plc->vocal_wave_long;
            g711plc->vocal_wave_s_odd = g711plc->vocal_wave_short_odd;
            g711plc->vocal_wave_l_odd = g711plc->vocal_wave_long_odd;
        }
        else {
            result_sample = g711plc->vocal_wave[g711plc->vocal_pos];
            result_sample_odd = g711plc->vocal_wave_odd[g711plc->vocal_pos];
            (g711plc->vocal_pos)++;
        }
        result_sample = (short)(((int32)(((int32) result_sample) * ((int32)(g711plc->vocal_gain)))) >> 15);
        result_sample_odd = (short)(((int32)(((int32) result_sample_odd) * ((int32)(g711plc->vocal_gain)))) >> 15);
        if (g711plc->vocal_gain >= g711plc->vocal_gain_dec) {
            g711plc->vocal_gain -= g711plc->vocal_gain_dec;
        }
        else {
            g711plc->vocal = false;
        }
    }
    else {
        result_sample = 0;
        result_sample_odd = 0;
    }
    sdsp_fill_gaussian_noise (noise, 2, g711plc->noise_level, &(g711plc->random_number_seed));
    l = result_sample + noise[0];
    result_sample = (l > 32767) ? 32767 : ((l < -32768) ? -32758 : (short) l);
    l = result_sample_odd + noise[1];
    result_sample_odd = (l > 32767) ? 32767 : ((l < -32768) ? -32758 : (short) l);

    g711plc->samples[g711plc->sample_pos] = result_sample;
    g711plc->samples_odd[g711plc->sample_pos] = result_sample_odd;
    g711plc->sample_pos = (g711plc->sample_pos < G711PLC_CORRELATION_BUF_LEN - 1) ? g711plc->sample_pos + 1 : 0;
    result_sample = (short)(((int32)(((int32) result_sample) * ((int32)((g711plc->conceal_gain <= 0x8000) ? g711plc->conceal_gain : 0x8000)))) >> 15);
    result_sample_odd = (short)(((int32)(((int32) result_sample_odd) * ((int32)((g711plc->conceal_gain <= 0x8000) ? g711plc->conceal_gain : 0x8000)))) >> 15);
    g711plc->conceal_gain = (g711plc->conceal_gain > G711PLC_CONCEAL_GAIN_DEC) ? g711plc->conceal_gain - G711PLC_CONCEAL_GAIN_DEC : 0;

//    sample_time++;
    *p_result_sample_odd = result_sample_odd;
    return result_sample;
}

NAMESPACE_END

/*----------------------------------------------------------------------------*/
