예제 #1
0
파일: Stereo.cs 프로젝트: leelu611/NSpeex
        /// <summary>
        /// Callback handler for intensity stereo info
        /// </summary>
        /// <param name="bits">
        /// Speex bits buffer.
        /// </param>
        public void init(Bits bits)
        {
            float sign = 1;
            int   tmp;

            if (bits.UnPack(1) != 0)
            {
                sign = -1;
            }
            tmp     = bits.UnPack(5);
            balance = (float)Math.Exp(sign * .25f * tmp);
            tmp     = bits.UnPack(2);
            e_ratio = e_ratio_quant[tmp];
        }
예제 #2
0
 /// <summary>
 /// User in-band request (submode=13).
 /// </summary>
 /// <param name="bits"></param>
 public void userInbandRequest(Bits bits)
 {
     try
     {
         int req_size = bits.UnPack(4);
         bits.Advance(5 + 8 * req_size);
     }
     catch (Exception e)
     {
         throw new SpeexException(e.Message);
     }
 }
예제 #3
0
        /// <summary>
        /// Decode the given input bits.
        /// </summary>
        /// <param name="bits">Speex bits buffer.</param>
        /// <param name="vout">the decoded mono audio frame.</param>
        /// <returns>
        /// 1 if a terminator was found, 0 if not.
        /// </returns>
        public int Decode(Bits bits, float[] vout)
        {
            int i, sub, wideband, ret;

            float[] low_pi_gain, low_exc, low_innov;

            /* Decode the low-band */
            ret = lowdec.Decode(bits, x0d);
            if (ret != 0)
            {
                return(ret);
            }
            bool dtx = lowdec.Dtx;

            if (bits == null)
            {
                decodeLost(vout, dtx);
                return(0);
            }
            /* Check "wideband bit" */
            wideband = bits.Peek();
            if (wideband != 0)
            {
                /*Regular wideband frame, read the submode*/
                wideband  = bits.UnPack(1);
                submodeID = bits.UnPack(3);
            }
            else
            {
                /* was a narrowband frame, set "null submode"*/
                submodeID = 0;
            }
            for (i = 0; i < frameSize; i++)
            {
                excBuf[i] = 0;
            }
            if (submodes[submodeID] == null)
            {
                if (dtx)
                {
                    decodeLost(vout, true);
                    return(0);
                }
                for (i = 0; i < frameSize; i++)
                {
                    excBuf[i] = VERY_SMALL;
                }

                first = 1;
                Filters.iir_mem2(excBuf, excIdx, interp_qlpc, high, 0, frameSize,
                                 lpcSize, mem_sp);
                filters.fir_mem_up(x0d, Codebook.h0, y0, fullFrameSize, QMF_ORDER, g0_mem);
                filters.fir_mem_up(high, Codebook.h1, y1, fullFrameSize, QMF_ORDER, g1_mem);
                for (i = 0; i < fullFrameSize; i++)
                {
                    vout[i] = 2 * (y0[i] - y1[i]);
                }
                return(0);
            }
            low_pi_gain = lowdec.PitchGain;
            low_exc     = lowdec.Excitation;
            low_innov   = lowdec.Innovation;
            submodes[submodeID].lsqQuant.unquant(qlsp, lpcSize, bits);
            if (first != 0)
            {
                for (i = 0; i < lpcSize; i++)
                {
                    old_qlsp[i] = qlsp[i];
                }
            }
            for (sub = 0; sub < nbSubframes; sub++)
            {
                float tmp, filter_ratio, el = 0.0f, rl = 0.0f, rh = 0.0f;
                int   subIdx = subframeSize * sub;

                /* LSP interpolation */
                tmp = (1.0f + sub) / nbSubframes;
                for (i = 0; i < lpcSize; i++)
                {
                    interp_qlsp[i] = (1 - tmp) * old_qlsp[i] + tmp * qlsp[i];
                }
                Lsp.enforce_margin(interp_qlsp, lpcSize, .05f);
                /* LSPs to x-domain */
                for (i = 0; i < lpcSize; i++)
                {
                    interp_qlsp[i] = (float)Math.Cos(interp_qlsp[i]);
                }
                /* LSP to LPC */
                m_lsp.lsp2lpc(interp_qlsp, interp_qlpc, lpcSize);
                if (enhanced)
                {
                    float k1, k2, k3;
                    k1 = submodes[submodeID].lpc_enh_k1;
                    k2 = submodes[submodeID].lpc_enh_k2;
                    k3 = k1 - k2;
                    Filters.bw_lpc(k1, interp_qlpc, awk1, lpcSize);
                    Filters.bw_lpc(k2, interp_qlpc, awk2, lpcSize);
                    Filters.bw_lpc(k3, interp_qlpc, awk3, lpcSize);
                }
                /* Calculate reponse ratio between low & high filter in band middle (4000 Hz) */
                tmp          = 1;
                pi_gain[sub] = 0;
                for (i = 0; i <= lpcSize; i++)
                {
                    rh           += tmp * interp_qlpc[i];
                    tmp           = -tmp;
                    pi_gain[sub] += interp_qlpc[i];
                }
                rl           = low_pi_gain[sub];
                rl           = 1 / (Math.Abs(rl) + .01f);
                rh           = 1 / (Math.Abs(rh) + .01f);
                filter_ratio = Math.Abs(.01f + rh) / (.01f + Math.Abs(rl));
                /* reset excitation buffer */
                for (i = subIdx; i < subIdx + subframeSize; i++)
                {
                    excBuf[i] = 0;
                }
                if (submodes[submodeID].innovation == null)
                {
                    float g;
                    int   quant;

                    quant = bits.UnPack(5);
                    g     = (float)Math.Exp(((double)quant - 10) / 8.0);
                    g    /= filter_ratio;

                    /* High-band excitation using the low-band excitation and a gain */
                    for (i = subIdx; i < subIdx + subframeSize; i++)
                    {
                        excBuf[i] = foldingGain * g * low_innov[i];
                    }
                }
                else
                {
                    float gc, scale;
                    int   qgc = bits.UnPack(4);

                    for (i = subIdx; i < subIdx + subframeSize; i++)
                    {
                        el += low_exc[i] * low_exc[i];
                    }

                    gc    = (float)Math.Exp((1 / 3.7f) * qgc - 2);
                    scale = gc * (float)Math.Sqrt(1 + el) / filter_ratio;
                    submodes[submodeID].innovation.UnQuant(excBuf, subIdx, subframeSize, bits);

                    for (i = subIdx; i < subIdx + subframeSize; i++)
                    {
                        excBuf[i] *= scale;
                    }
                    if (submodes[submodeID].double_codebook != 0)
                    {
                        for (i = 0; i < subframeSize; i++)
                        {
                            innov2[i] = 0;
                        }
                        submodes[submodeID].innovation.UnQuant(innov2, 0, subframeSize, bits);
                        for (i = 0; i < subframeSize; i++)
                        {
                            innov2[i] *= scale * (1 / 2.5f);
                        }
                        for (i = 0; i < subframeSize; i++)
                        {
                            excBuf[subIdx + i] += innov2[i];
                        }
                    }
                }
                for (i = subIdx; i < subIdx + subframeSize; i++)
                {
                    high[i] = excBuf[i];
                }
                if (enhanced)
                {
                    /* Use enhanced LPC filter */
                    Filters.filter_mem2(high, subIdx, awk2, awk1, subframeSize,
                                        lpcSize, mem_sp, lpcSize);
                    Filters.filter_mem2(high, subIdx, awk3, interp_qlpc, subframeSize,
                                        lpcSize, mem_sp, 0);
                }
                else
                {
                    /* Use regular filter */
                    for (i = 0; i < lpcSize; i++)
                    {
                        mem_sp[lpcSize + i] = 0;
                    }
                    Filters.iir_mem2(high, subIdx, interp_qlpc, high, subIdx,
                                     subframeSize, lpcSize, mem_sp);
                }
            }
            filters.fir_mem_up(x0d, Codebook.h0, y0, fullFrameSize, QMF_ORDER, g0_mem);
            filters.fir_mem_up(high, Codebook.h1, y1, fullFrameSize, QMF_ORDER, g1_mem);

            for (i = 0; i < fullFrameSize; i++)
            {
                vout[i] = 2 * (y0[i] - y1[i]);
            }

            for (i = 0; i < lpcSize; i++)
            {
                old_qlsp[i] = qlsp[i];
            }

            first = 0;
            return(0);
        }
예제 #4
0
        /// <summary>
        /// Speex in-band request (submode=14).
        /// </summary>
        /// <param name="bits"></param>
        public void speexInbandRequest(Bits bits)
        {
            int code = bits.UnPack(4);

            switch (code)
            {
            case 0:     // asks the decoder to set perceptual enhancment off (0) or on (1)
                bits.Advance(1);
                break;

            case 1:     // asks (if 1) the encoder to be less "aggressive" due to high packet loss
                bits.Advance(1);
                break;

            case 2:     // asks the encoder to switch to mode N
                bits.Advance(4);
                break;

            case 3:     // asks the encoder to switch to mode N for low-band
                bits.Advance(4);
                break;

            case 4:     // asks the encoder to switch to mode N for high-band
                bits.Advance(4);
                break;

            case 5:     // asks the encoder to switch to quality N for VBR
                bits.Advance(4);
                break;

            case 6:     // request acknowledgement (0=no, 1=all, 2=only for inband data)
                bits.Advance(4);
                break;

            case 7:     // asks the encoder to set CBR(0), VAD(1), DTX(3), VBR(5), VBR+DTX(7)
                bits.Advance(4);
                break;

            case 8:     // transmit (8-bit) character to the other end
                bits.Advance(8);
                break;

            case 9:                 // intensity stereo information
                // setup the stereo decoder; to skip: tmp = bits.unpack(8); break;
                _stereo.init(bits); // read 8 bits
                break;

            case 10:     // announce maximum bit-rate acceptable (N in byets/second)
                bits.Advance(16);
                break;

            case 11:     // reserved
                bits.Advance(16);
                break;

            case 12:     // Acknowledge receiving packet N
                bits.Advance(32);
                break;

            case 13:     // reserved
                bits.Advance(32);
                break;

            case 14:     // reserved
                bits.Advance(64);
                break;

            case 15:     // reserved
                bits.Advance(64);
                break;

            default:
                break;
            }
        }
예제 #5
0
        /// <summary>
        /// Decode the given input bits.
        /// </summary>
        /// <param name="bits">Speex bits buffer.</param>
        /// <param name="vout">the decoded mono audio frame.</param>
        /// <returns>1 if a terminator was found, 0 if not.</returns>
        public int Decode(Bits bits, float[] vout)
        {
            int i, sub, pitch, ol_pitch = 0, m;

            float[] pitch_gain = new float[3];
            float   ol_gain = 0.0f, ol_pitch_coef = 0.0f;
            int     best_pitch      = 40;
            float   best_pitch_gain = 0;
            float   pitch_average   = 0;

            if (bits == null && dtx_enabled != 0)
            {
                submodeID = 0;
            }
            else
            {
                if (bits == null)
                {
                    decodeLost(vout);
                    return(0);
                }
                do
                {
                    if (bits.UnPack(1) != 0)
                    {
                        m = bits.UnPack(SbCodec.SB_SUBMODE_BITS);
                        int advance = SbCodec.SB_FRAME_SIZE[m];
                        if (advance < 0)
                        {
                            throw new Exception("Invalid sideband mode encountered (1st sideband): " + m);
                        }
                        advance -= (SbCodec.SB_SUBMODE_BITS + 1);
                        bits.Advance(advance);
                        if (bits.UnPack(1) != 0)
                        { /* Skip ultra-wideband block (for compatibility) */
                            /* Get the sub-mode that was used */
                            m       = bits.UnPack(SbCodec.SB_SUBMODE_BITS);
                            advance = SbCodec.SB_FRAME_SIZE[m];
                            if (advance < 0)
                            {
                                throw new Exception("Invalid sideband mode encountered. (2nd sideband): " + m);
                                //return -2;
                            }
                            advance -= (SbCodec.SB_SUBMODE_BITS + 1);
                            bits.Advance(advance);
                            if (bits.UnPack(1) != 0)
                            { /* Sanity check */
                                throw new Exception("More than two sideband layers found");
                                //return -2;
                            }
                        }
                    }

                    m = bits.UnPack(NB_SUBMODE_BITS);
                    if (m == 15)
                    { /* We found a terminator */
                        return(1);
                    }
                    else if (m == 14)
                    { /* Speex in-band request */
                        inband.speexInbandRequest(bits);
                    }
                    else if (m == 13)
                    { /* User in-band request */
                        inband.userInbandRequest(bits);
                    }
                    else if (m > 8)
                    { /* Invalid mode */
                        throw new Exception("Invalid mode encountered: " + m);
                        //return -2;
                    }
                }while (m > 8);
                submodeID = m;
            }
            /* Shift all buffers by one frame */
            System.Array.Copy(frmBuf, frameSize, frmBuf, 0, bufSize - frameSize);
            System.Array.Copy(excBuf, frameSize, excBuf, 0, bufSize - frameSize);
            if (submodes[submodeID] == null)
            {
                Filters.bw_lpc(.93f, interp_qlpc, lpc, 10);
                float innov_gain = 0;
                for (i = 0; i < frameSize; i++)
                {
                    innov_gain += innov[i] * innov[i];
                }
                innov_gain = (float)Math.Sqrt(innov_gain / frameSize);
                for (i = excIdx; i < excIdx + frameSize; i++)
                {
                    excBuf[i] = 3 * innov_gain * ((float)random.NextDouble() - .5f);
                }
                first = 1;
                /* Final signal synthesis from excitation */
                Filters.iir_mem2(excBuf, excIdx, lpc, frmBuf, frmIdx, frameSize, lpcSize, mem_sp);
                vout[0] = frmBuf[frmIdx] + preemph * pre_mem;
                for (i = 1; i < frameSize; i++)
                {
                    vout[i] = frmBuf[frmIdx + i] + preemph * vout[i - 1];
                }
                pre_mem    = vout[frameSize - 1];
                count_lost = 0;
                return(0);
            }
            /* Unquantize LSPs */
            submodes[submodeID].lsqQuant.unquant(qlsp, lpcSize, bits);
            if (count_lost != 0)
            {
                float lsp_dist = 0, fact;
                for (i = 0; i < lpcSize; i++)
                {
                    lsp_dist += Math.Abs(old_qlsp[i] - qlsp[i]);
                }
                fact = (float)(.6 * Math.Exp(-.2 * lsp_dist));
                for (i = 0; i < 2 * lpcSize; i++)
                {
                    mem_sp[i] *= fact;
                }
            }
            /* Handle first frame and lost-packet case */
            if (first != 0 || count_lost != 0)
            {
                for (i = 0; i < lpcSize; i++)
                {
                    old_qlsp[i] = qlsp[i];
                }
            }

            /* Get open-loop pitch estimation for low bit-rate pitch coding */
            if (submodes[submodeID].lbr_pitch != -1)
            {
                ol_pitch = min_pitch + bits.UnPack(7);
            }

            if (submodes[submodeID].forced_pitch_gain != 0)
            {
                int quant = bits.UnPack(4);
                ol_pitch_coef = 0.066667f * quant;
            }
            /* Get global excitation gain */
            int qe = bits.UnPack(5);

            ol_gain = (float)Math.Exp(qe / 3.5);

            /* unpacks unused dtx bits */
            if (submodeID == 1)
            {
                int extra = bits.UnPack(4);
                if (extra == 15)
                {
                    dtx_enabled = 1;
                }
                else
                {
                    dtx_enabled = 0;
                }
            }
            if (submodeID > 1)
            {
                dtx_enabled = 0;
            }
            /*Loop on subframes */
            for (sub = 0; sub < nbSubframes; sub++)
            {
                int   offset, spIdx, extIdx;
                float tmp;
                /* Offset relative to start of frame */
                offset = subframeSize * sub;
                /* Original signal */
                spIdx = frmIdx + offset;
                /* Excitation */
                extIdx = excIdx + offset;

                /* LSP interpolation (quantized and unquantized) */
                tmp = (1.0f + sub) / nbSubframes;
                for (i = 0; i < lpcSize; i++)
                {
                    interp_qlsp[i] = (1 - tmp) * old_qlsp[i] + tmp * qlsp[i];
                }

                /* Make sure the LSP's are stable */
                Lsp.enforce_margin(interp_qlsp, lpcSize, .002f);

                /* Compute interpolated LPCs (unquantized) */
                for (i = 0; i < lpcSize; i++)
                {
                    interp_qlsp[i] = (float)Math.Cos(interp_qlsp[i]);
                }
                m_lsp.lsp2lpc(interp_qlsp, interp_qlpc, lpcSize);
                /* Compute enhanced synthesis filter */
                if (enhanced)
                {
                    float r = .9f;
                    float k1, k2, k3;

                    k1 = submodes[submodeID].lpc_enh_k1;
                    k2 = submodes[submodeID].lpc_enh_k2;
                    k3 = (1 - (1 - r * k1) / (1 - r * k2)) / r;
                    Filters.bw_lpc(k1, interp_qlpc, awk1, lpcSize);
                    Filters.bw_lpc(k2, interp_qlpc, awk2, lpcSize);
                    Filters.bw_lpc(k3, interp_qlpc, awk3, lpcSize);
                }
                /* Compute analysis filter at w=pi */
                tmp          = 1;
                pi_gain[sub] = 0;
                for (i = 0; i <= lpcSize; i++)
                {
                    pi_gain[sub] += tmp * interp_qlpc[i];
                    tmp           = -tmp;
                }
                /* Reset excitation */
                for (i = 0; i < subframeSize; i++)
                {
                    excBuf[extIdx + i] = 0;
                }

                /*Adaptive codebook contribution*/
                int pit_min, pit_max;
                /* Handle pitch constraints if any */
                if (submodes[submodeID].lbr_pitch != -1)
                {
                    int margin = submodes[submodeID].lbr_pitch;
                    if (margin != 0)
                    {
                        pit_min = ol_pitch - margin + 1;
                        if (pit_min < min_pitch)
                        {
                            pit_min = min_pitch;
                        }
                        pit_max = ol_pitch + margin;
                        if (pit_max > max_pitch)
                        {
                            pit_max = max_pitch;
                        }
                    }
                    else
                    {
                        pit_min = pit_max = ol_pitch;
                    }
                }
                else
                {
                    pit_min = min_pitch;
                    pit_max = max_pitch;
                }
                /* Pitch synthesis */
                pitch = submodes[submodeID].ltp.UnQuant(excBuf, extIdx, pit_min, ol_pitch_coef,
                                                        subframeSize, pitch_gain, bits,
                                                        count_lost, offset, last_pitch_gain);
                /* If we had lost frames, check energy of last received frame */
                if (count_lost != 0 && ol_gain < last_ol_gain)
                {
                    float fact = ol_gain / (last_ol_gain + 1);
                    for (i = 0; i < subframeSize; i++)
                    {
                        excBuf[excIdx + i] *= fact;
                    }
                }
                tmp = Math.Abs(pitch_gain[0] + pitch_gain[1] + pitch_gain[2]);
                tmp = Math.Abs(pitch_gain[1]);
                if (pitch_gain[0] > 0)
                {
                    tmp += pitch_gain[0];
                }
                else
                {
                    tmp -= .5f * pitch_gain[0];
                }
                if (pitch_gain[2] > 0)
                {
                    tmp += pitch_gain[2];
                }
                else
                {
                    tmp -= .5f * pitch_gain[0];
                }

                pitch_average += tmp;
                if (tmp > best_pitch_gain)
                {
                    best_pitch      = pitch;
                    best_pitch_gain = tmp;
                }
                /* Unquantize the innovation */
                int   q_energy, ivi = sub * subframeSize;
                float ener;

                for (i = ivi; i < ivi + subframeSize; i++)
                {
                    innov[i] = 0.0f;
                }
                /* Decode sub-frame gain correction */
                if (submodes[submodeID].have_subframe_gain == 3)
                {
                    q_energy = bits.UnPack(3);
                    ener     = (float)(ol_gain * Math.Exp(exc_gain_quant_scal3[q_energy]));
                }
                else if (submodes[submodeID].have_subframe_gain == 1)
                {
                    q_energy = bits.UnPack(1);
                    ener     = (float)(ol_gain * Math.Exp(exc_gain_quant_scal1[q_energy]));
                }
                else
                {
                    ener = ol_gain;
                }
                if (submodes[submodeID].innovation != null)
                {
                    /* Fixed codebook contribution */
                    submodes[submodeID].innovation.UnQuant(innov, ivi, subframeSize, bits);
                }
                /* De-normalize innovation and update excitation */
                for (i = ivi; i < ivi + subframeSize; i++)
                {
                    innov[i] *= ener;
                }
                /*  Vocoder mode */
                if (submodeID == 1)
                {
                    float g = ol_pitch_coef;

                    for (i = 0; i < subframeSize; i++)
                    {
                        excBuf[extIdx + i] = 0;
                    }
                    while (voc_offset < subframeSize)
                    {
                        if (voc_offset >= 0)
                        {
                            excBuf[extIdx + voc_offset] = (float)Math.Sqrt(1.0f * ol_pitch);
                        }
                        voc_offset += ol_pitch;
                    }
                    voc_offset -= subframeSize;

                    g = .5f + 2 * (g - .6f);
                    if (g < 0)
                    {
                        g = 0;
                    }
                    if (g > 1)
                    {
                        g = 1;
                    }
                    for (i = 0; i < subframeSize; i++)
                    {
                        float itmp = excBuf[extIdx + i];
                        excBuf[extIdx + i]  = .8f * g * excBuf[extIdx + i] * ol_gain + .6f * g * voc_m1 * ol_gain + .5f * g * innov[ivi + i] - .5f * g * voc_m2 + (1 - g) * innov[ivi + i];
                        voc_m1              = itmp;
                        voc_m2              = innov[ivi + i];
                        voc_mean            = .95f * voc_mean + .05f * excBuf[extIdx + i];
                        excBuf[extIdx + i] -= voc_mean;
                    }
                }
                else
                {
                    for (i = 0; i < subframeSize; i++)
                    {
                        excBuf[extIdx + i] += innov[ivi + i];
                    }
                }
                /* Decode second codebook (only for some modes) */
                if (submodes[submodeID].double_codebook != 0)
                {
                    for (i = 0; i < subframeSize; i++)
                    {
                        innov2[i] = 0;
                    }
                    submodes[submodeID].innovation.UnQuant(innov2, 0, subframeSize, bits);
                    for (i = 0; i < subframeSize; i++)
                    {
                        innov2[i] *= ener * (1f / 2.2f);
                    }
                    for (i = 0; i < subframeSize; i++)
                    {
                        excBuf[extIdx + i] += innov2[i];
                    }
                }
                for (i = 0; i < subframeSize; i++)
                {
                    frmBuf[spIdx + i] = excBuf[extIdx + i];
                }
                /* Signal synthesis */
                if (enhanced && submodes[submodeID].comb_gain > 0)
                {
                    filters.comb_filter(excBuf, extIdx, frmBuf, spIdx, subframeSize,
                                        pitch, pitch_gain, submodes[submodeID].comb_gain);
                }
                if (enhanced)
                {
                    /* Use enhanced LPC filter */
                    Filters.filter_mem2(frmBuf, spIdx, awk2, awk1, subframeSize, lpcSize, mem_sp, lpcSize);
                    Filters.filter_mem2(frmBuf, spIdx, awk3, interp_qlpc, subframeSize, lpcSize, mem_sp, 0);
                }
                else
                {
                    /* Use regular filter */
                    for (i = 0; i < lpcSize; i++)
                    {
                        mem_sp[lpcSize + i] = 0;
                    }
                    Filters.iir_mem2(frmBuf, spIdx, interp_qlpc, frmBuf, spIdx, subframeSize, lpcSize, mem_sp);
                }
            }
            /*Copy output signal*/
            vout[0] = frmBuf[frmIdx] + preemph * pre_mem;
            for (i = 1; i < frameSize; i++)
            {
                vout[i] = frmBuf[frmIdx + i] + preemph * vout[i - 1];
            }
            pre_mem = vout[frameSize - 1];

            /* Store the LSPs for interpolation in the next frame */
            for (i = 0; i < lpcSize; i++)
            {
                old_qlsp[i] = qlsp[i];
            }
            /* The next frame will not be the first (Duh!) */
            first           = 0;
            count_lost      = 0;
            last_pitch      = best_pitch;
            last_pitch_gain = .25f * pitch_average;
            pitch_gain_buf[pitch_gain_buf_idx++] = last_pitch_gain;
            if (pitch_gain_buf_idx > 2) /* rollover */
            {
                pitch_gain_buf_idx = 0;
            }
            last_ol_gain = ol_gain;
            return(0);
        }
예제 #6
0
        /// <summary>
        /// Long Term Prediction Unquantification (3Tap).
        /// </summary>
        /// <param name="exc"> Excitation</param>
        /// <param name="es">Excitation offset</param>
        /// <param name="start">Smallest pitch value allowed</param>
        /// <param name="pitch_coef"> Voicing (pitch) coefficient</param>
        /// <param name="nsf">Number of samples in subframe</param>
        /// <param name="gain_val"></param>
        /// <param name="bits">Speex bits buffer.</param>
        /// <param name="count_lost"></param>
        /// <param name="subframe_offset"></param>
        /// <param name="last_pitch_gain"></param>
        /// <returns>pitch</returns>
        public override sealed int UnQuant(float[] exc, int es, int start, float pitch_coef, int nsf, float[] gain_val, Bits bits, int count_lost, int subframe_offset, float last_pitch_gain)
        {
            int i, pitch, gain_index;

            pitch      = bits.UnPack(pitch_bits);
            pitch     += start;
            gain_index = bits.UnPack(gain_bits);

            gain[0] = 0.015625f * (float)gain_cdbk[gain_index * 3] + .5f;
            gain[1] = 0.015625f * (float)gain_cdbk[gain_index * 3 + 1] + .5f;
            gain[2] = 0.015625f * (float)gain_cdbk[gain_index * 3 + 2] + .5f;
            if (count_lost != 0 && pitch > subframe_offset)
            {
                float gain_sum = Math.Abs(gain[1]);
                float tmp      = count_lost < 4 ? last_pitch_gain : 0.4f * last_pitch_gain;
                if (tmp > .95f)
                {
                    tmp = .95f;
                }
                if (gain[0] > 0)
                {
                    gain_sum += gain[0];
                }
                else
                {
                    gain_sum -= .5f * gain[0];
                }
                if (gain[2] > 0)
                {
                    gain_sum += gain[2];
                }
                else
                {
                    gain_sum -= .5f * gain[0];
                }
                if (gain_sum > tmp)
                {
                    float fact = tmp / gain_sum;
                    for (i = 0; i < 3; i++)
                    {
                        gain[i] *= fact;
                    }
                }
            }
            gain_val[0] = gain[0];
            gain_val[1] = gain[1];
            gain_val[2] = gain[2];

            for (i = 0; i < 3; i++)
            {
                int j, tmp1, tmp2, pp = pitch + 1 - i;

                tmp1 = nsf;
                if (tmp1 > pp)
                {
                    tmp1 = pp;
                }
                tmp2 = nsf;
                if (tmp2 > pp + pitch)
                {
                    tmp2 = pp + pitch;
                }

                for (j = 0; j < tmp1; j++)
                {
                    e[i][j] = exc[es + j - pp];
                }
                for (j = tmp1; j < tmp2; j++)
                {
                    e[i][j] = exc[es + j - pp - pitch];
                }
                for (j = tmp2; j < nsf; j++)
                {
                    e[i][j] = 0;
                }
            }
            for (i = 0; i < nsf; i++)
            {
                exc[es + i] = gain[0] * e[2][i] + gain[1] * e[1][i] + gain[2] * e[0][i];
            }

            return(pitch);
        }