/* ------------------------------------------------------------------
 * Copyright (C) 1998-2009 PacketVideo
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 * -------------------------------------------------------------------
 */
/****************************************************************************************
Portions of this file are derived from the following 3GPP standard:

    3GPP TS 26.173
    ANSI-C code for the Adaptive Multi-Rate - Wideband (AMR-WB) speech codec
    Available from http://www.3gpp.org

(C) 2007, 3GPP Organizational Partners (ARIB, ATIS, CCSA, ETSI, TTA, TTC)
Permission to distribute, modify and use this file under the standard license
terms listed above has been obtained from the copyright holder.
****************************************************************************************/
/*
------------------------------------------------------------------------------



 Filename: dtx_decoder_amr_wb.cpp

     Date: 05/08/2007

------------------------------------------------------------------------------
 REVISION HISTORY


 Description:

------------------------------------------------------------------------------
 INPUT AND OUTPUT DEFINITIONS


------------------------------------------------------------------------------
 FUNCTION DESCRIPTION

    DTX functions

------------------------------------------------------------------------------
 REQUIREMENTS


------------------------------------------------------------------------------
 REFERENCES

------------------------------------------------------------------------------
 PSEUDO-CODE

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


/*----------------------------------------------------------------------------
; INCLUDES
----------------------------------------------------------------------------*/

#include "pv_amr_wb_type_defs.h"
#include "pvamrwbdecoder_basic_op.h"
#include "pvamrwb_math_op.h"
#include "pvamrwbdecoder_cnst.h"
#include "pvamrwbdecoder_acelp.h"  /* prototype of functions    */
#include "get_amr_wb_bits.h"
#include "dtx.h"

/*----------------------------------------------------------------------------
; MACROS
; Define module specific macros here
----------------------------------------------------------------------------*/


/*----------------------------------------------------------------------------
; DEFINES
; Include all pre-processor statements here. Include conditional
; compile variables also.
----------------------------------------------------------------------------*/

/*----------------------------------------------------------------------------
; LOCAL FUNCTION DEFINITIONS
; Function Prototype declaration
----------------------------------------------------------------------------*/

/*----------------------------------------------------------------------------
; LOCAL STORE/BUFFER/POINTER DEFINITIONS
; Variable declaration - defined here and used outside this module
----------------------------------------------------------------------------*/

/*----------------------------------------------------------------------------
; EXTERNAL FUNCTION REFERENCES
; Declare functions defined elsewhere and referenced in this module
----------------------------------------------------------------------------*/

/*----------------------------------------------------------------------------
; EXTERNAL GLOBAL STORE/BUFFER/POINTER REFERENCES
; Declare variables used in this module but defined elsewhere
----------------------------------------------------------------------------*/

/*----------------------------------------------------------------------------
; FUNCTION CODE
----------------------------------------------------------------------------*/
/*
 * Function    : dtx_dec_amr_wb_reset
 */
int16 dtx_dec_amr_wb_reset(dtx_decState * st, const int16 isf_init[])
{
    int16 i;


    if (st == (dtx_decState *) NULL)
    {
        /* dtx_dec_amr_wb_reset invalid parameter */
        return (-1);
    }
    st->since_last_sid = 0;
    st->true_sid_period_inv = (1 << 13);      /* 0.25 in Q15 */

    st->log_en = 3500;
    st->old_log_en = 3500;
    /* low level noise for better performance in  DTX handover cases */

    st->cng_seed = RANDOM_INITSEED;

    st->hist_ptr = 0;

    /* Init isf_hist[] and decoder log frame energy */
    pv_memcpy((void *)st->isf, (void *)isf_init, M*sizeof(*isf_init));

    pv_memcpy((void *)st->isf_old, (void *)isf_init, M*sizeof(*isf_init));

    for (i = 0; i < DTX_HIST_SIZE; i++)
    {
        pv_memcpy((void *)&st->isf_hist[i * M], (void *)isf_init, M*sizeof(*isf_init));
        st->log_en_hist[i] = st->log_en;
    }

    st->dtxHangoverCount = DTX_HANG_CONST;
    st->decAnaElapsedCount = 32767;

    st->sid_frame = 0;
    st->valid_data = 0;
    st->dtxHangoverAdded = 0;

    st->dtxGlobalState = SPEECH;
    st->data_updated = 0;

    st->dither_seed = RANDOM_INITSEED;
    st->CN_dith = 0;

    return 0;
}


/*
     Table of new SPD synthesis states

                           |     previous SPD_synthesis_state
     Incoming              |
     frame_type            | SPEECH       | DTX           | DTX_MUTE
     ---------------------------------------------------------------
     RX_SPEECH_GOOD ,      |              |               |
     RX_SPEECH_PR_DEGRADED | SPEECH       | SPEECH        | SPEECH
     ----------------------------------------------------------------
     RX_SPEECH_BAD,        | SPEECH       | DTX           | DTX_MUTE
     ----------------------------------------------------------------
     RX_SID_FIRST,         | DTX          | DTX/(DTX_MUTE)| DTX_MUTE
     ----------------------------------------------------------------
     RX_SID_UPDATE,        | DTX          | DTX           | DTX
     ----------------------------------------------------------------
     RX_SID_BAD,           | DTX          | DTX/(DTX_MUTE)| DTX_MUTE
     ----------------------------------------------------------------
     RX_NO_DATA,           | SPEECH       | DTX/(DTX_MUTE)| DTX_MUTE
     RX_SPARE              |(class2 garb.)|               |
     ----------------------------------------------------------------
*/

/*----------------------------------------------------------------------------
; FUNCTION CODE
----------------------------------------------------------------------------*/

/*
 * Function    : dtx_dec_amr_wb
 */
int16 dtx_dec_amr_wb(
    dtx_decState * st,                    /* i/o : State struct         */
    int16 * exc2,                        /* o   : CN excitation        */
    int16 new_state,                     /* i   : New DTX state        */
    int16 isf[],                         /* o   : CN ISF vector        */
    int16 ** prms
)
{
    int16 log_en_index;
    int16 ind[7];
    int16 i, j;
    int16 int_fac;
    int16 gain;

    int32 L_isf[M], L_log_en_int, level32, ener32;
    int16 ptr;
    int16 tmp_int_length;
    int16 tmp, exp, exp0, log_en_int_e, log_en_int_m, level;

    /* This function is called if synthesis state is not SPEECH the globally passed  inputs to this function
     * are st->sid_frame st->valid_data st->dtxHangoverAdded new_state  (SPEECH, DTX, DTX_MUTE) */

    if ((st->dtxHangoverAdded != 0) &&
            (st->sid_frame != 0))
    {
        /* sid_first after dtx hangover period */
        /* or sid_upd after dtxhangover        */

        /* consider  twice the last frame */
        ptr = st->hist_ptr + 1;

        if (ptr == DTX_HIST_SIZE)
            ptr = 0;

        pv_memcpy((void *)&st->isf_hist[ptr * M], (void *)&st->isf_hist[st->hist_ptr * M], M*sizeof(*st->isf_hist));

        st->log_en_hist[ptr] = st->log_en_hist[st->hist_ptr];

        /* compute mean log energy and isf from decoded signal (SID_FIRST) */
        st->log_en = 0;
        for (i = 0; i < M; i++)
        {
            L_isf[i] = 0;
        }

        /* average energy and isf */
        for (i = 0; i < DTX_HIST_SIZE; i++)
        {
            /* Division by DTX_HIST_SIZE = 8 has been done in dtx_buffer log_en is in Q10 */
            st->log_en = add_int16(st->log_en, st->log_en_hist[i]);

            for (j = 0; j < M; j++)
            {
                L_isf[j] = add_int32(L_isf[j], (int32)(st->isf_hist[i * M + j]));
            }
        }

        /* st->log_en in Q9 */
        st->log_en >>=  1;

        /* Add 2 in Q9, in order to have only positive values for Pow2 */
        /* this value is subtracted back after Pow2 function */
        st->log_en += 1024;

        if (st->log_en < 0)
            st->log_en = 0;

        for (j = 0; j < M; j++)
        {
            st->isf[j] = (int16)(L_isf[j] >> 3);  /* divide by 8 */
        }

    }

    if (st->sid_frame != 0)
    {
        /* Set old SID parameters, always shift */
        /* even if there is no new valid_data   */

        pv_memcpy((void *)st->isf_old, (void *)st->isf, M*sizeof(*st->isf));

        st->old_log_en = st->log_en;

        if (st->valid_data != 0)           /* new data available (no CRC) */
        {
            /* st->true_sid_period_inv = 1.0f/st->since_last_sid; */
            /* Compute interpolation factor, since the division only works * for values of since_last_sid <
             * 32 we have to limit the      * interpolation to 32 frames                                  */
            tmp_int_length = st->since_last_sid;


            if (tmp_int_length > 32)
            {
                tmp_int_length = 32;
            }

            if (tmp_int_length >= 2)
            {
                st->true_sid_period_inv = div_16by16(1 << 10, shl_int16(tmp_int_length, 10));
            }
            else
            {
                st->true_sid_period_inv = 1 << 14;      /* 0.5 it Q15 */
            }

            ind[0] = Serial_parm(6, prms);
            ind[1] = Serial_parm(6, prms);
            ind[2] = Serial_parm(6, prms);
            ind[3] = Serial_parm(5, prms);
            ind[4] = Serial_parm(5, prms);

            Disf_ns(ind, st->isf);

            log_en_index = Serial_parm(6, prms);

            /* read background noise stationarity information */
            st->CN_dith = Serial_parm_1bit(prms);

            /* st->log_en = (float)log_en_index / 2.625 - 2.0;  */
            /* log2(E) in Q9 (log2(E) lies in between -2:22) */
            st->log_en = shl_int16(log_en_index, 15 - 6);

            /* Divide by 2.625  */
            st->log_en = mult_int16(st->log_en, 12483);
            /* Subtract 2 in Q9 is done later, after Pow2 function  */

            /* no interpolation at startup after coder reset        */
            /* or when SID_UPD has been received right after SPEECH */

            if ((st->data_updated == 0) || (st->dtxGlobalState == SPEECH))
            {
                pv_memcpy((void *)st->isf_old, (void *)st->isf, M*sizeof(*st->isf));

                st->old_log_en = st->log_en;
            }
        }                                  /* endif valid_data */
    }                                      /* endif sid_frame */


    if ((st->sid_frame != 0) && (st->valid_data != 0))
    {
        st->since_last_sid = 0;
    }
    /* Interpolate SID info */
    int_fac = shl_int16(st->since_last_sid, 10); /* Q10 */
    int_fac = mult_int16(int_fac, st->true_sid_period_inv);   /* Q10 * Q15 -> Q10 */

    /* Maximize to 1.0 in Q10 */

    if (int_fac > 1024)
    {
        int_fac = 1024;
    }
    int_fac = shl_int16(int_fac, 4);             /* Q10 -> Q14 */

    L_log_en_int = mul_16by16_to_int32(int_fac, st->log_en); /* Q14 * Q9 -> Q24 */

    for (i = 0; i < M; i++)
    {
        isf[i] = mult_int16(int_fac, st->isf[i]);/* Q14 * Q15 -> Q14 */
    }

    int_fac = 16384 - int_fac;         /* 1-k in Q14 */

    /* ( Q14 * Q9 -> Q24 ) + Q24 -> Q24 */
    L_log_en_int = mac_16by16_to_int32(L_log_en_int, int_fac, st->old_log_en);

    for (i = 0; i < M; i++)
    {
        /* Q14 + (Q14 * Q15 -> Q14) -> Q14 */
        isf[i] = add_int16(isf[i], mult_int16(int_fac, st->isf_old[i]));
        isf[i] = shl_int16(isf[i], 1);           /* Q14 -> Q15 */
    }

    /* If background noise is non-stationary, insert comfort noise dithering */
    if (st->CN_dith != 0)
    {
        CN_dithering(isf, &L_log_en_int, &st->dither_seed);
    }
    /* L_log_en_int corresponds to log2(E)+2 in Q24, i.e log2(gain)+1 in Q25 */
    /* Q25 -> Q16 */
    L_log_en_int >>= 9;

    /* Find integer part  */
    log_en_int_e = extract_h(L_log_en_int);

    /* Find fractional part */
    log_en_int_m = (int16)(sub_int32(L_log_en_int, L_deposit_h(log_en_int_e)) >> 1);

    /* Subtract 2 from L_log_en_int in Q9, i.e divide the gain by 2 (energy by 4) */
    /* Add 16 in order to have the result of pow2 in Q16 */
    log_en_int_e += 15;

    /* level = (float)( pow( 2.0f, log_en ) );  */
    level32 = power_of_2(log_en_int_e, log_en_int_m); /* Q16 */

    exp0 = normalize_amr_wb(level32);
    level32 <<= exp0;        /* level in Q31 */
    exp0 = 15 - exp0;
    level = (int16)(level32 >> 16);          /* level in Q15 */

    /* generate white noise vector */
    for (i = 0; i < L_FRAME; i++)
    {
        exc2[i] = noise_gen_amrwb(&(st->cng_seed)) >> 4;
    }

    /* gain = level / sqrt(ener) * sqrt(L_FRAME) */

    /* energy of generated excitation */
    ener32 = Dot_product12(exc2, exc2, L_FRAME, &exp);

    one_ov_sqrt_norm(&ener32, &exp);

    gain = extract_h(ener32);

    gain = mult_int16(level, gain);              /* gain in Q15 */

    exp += exp0;

    /* Multiply by sqrt(L_FRAME)=16, i.e. shift left by 4 */
    exp += 4;

    for (i = 0; i < L_FRAME; i++)
    {
        tmp = mult_int16(exc2[i], gain);         /* Q0 * Q15 */
        exc2[i] = shl_int16(tmp, exp);
    }


    if (new_state == DTX_MUTE)
    {
        /* mute comfort noise as it has been quite a long time since last SID update  was performed                            */

        tmp_int_length = st->since_last_sid;

        if (tmp_int_length > 32)
        {
            tmp_int_length = 32;
        }

        st->true_sid_period_inv = div_16by16(1 << 10, shl_int16(tmp_int_length, 10));

        st->since_last_sid = 0;
        st->old_log_en = st->log_en;
        /* subtract 1/8 in Q9 (energy), i.e -3/8 dB */
        st->log_en -= 64;
    }
    /* reset interpolation length timer if data has been updated.        */

    if ((st->sid_frame != 0) &&
            ((st->valid_data != 0) ||
             ((st->valid_data == 0) && (st->dtxHangoverAdded) != 0)))
    {
        st->since_last_sid = 0;
        st->data_updated = 1;
    }
    return 0;
}


/*----------------------------------------------------------------------------
; FUNCTION CODE
----------------------------------------------------------------------------*/

void dtx_dec_amr_wb_activity_update(
    dtx_decState * st,
    int16 isf[],
    int16 exc[])
{
    int16 i;

    int32 L_frame_en;
    int16 log_en_e, log_en_m, log_en;


    st->hist_ptr++;

    if (st->hist_ptr == DTX_HIST_SIZE)
    {
        st->hist_ptr = 0;
    }
    pv_memcpy((void *)&st->isf_hist[st->hist_ptr * M], (void *)isf, M*sizeof(*isf));


    /* compute log energy based on excitation frame energy in Q0 */
    L_frame_en = 0;
    for (i = 0; i < L_FRAME; i++)
    {
        L_frame_en = mac_16by16_to_int32(L_frame_en, exc[i], exc[i]);
    }
    L_frame_en >>= 1;

    /* log_en = (float)log10(L_frame_en/(float)L_FRAME)/(float)log10(2.0f); */
    amrwb_log_2(L_frame_en, &log_en_e, &log_en_m);

    /* convert exponent and mantissa to int16 Q7. Q7 is used to simplify averaging in dtx_enc */
    log_en = shl_int16(log_en_e, 7);             /* Q7 */
    log_en += log_en_m >> 8;

    /* Divide by L_FRAME = 256, i.e subtract 8 in Q7 = 1024 */
    log_en -= 1024;

    /* insert into log energy buffer */
    st->log_en_hist[st->hist_ptr] = log_en;

    return;
}


/*
     Table of new SPD synthesis states

                           |     previous SPD_synthesis_state
     Incoming              |
     frame_type            | SPEECH       | DTX           | DTX_MUTE
     ---------------------------------------------------------------
     RX_SPEECH_GOOD ,      |              |               |
     RX_SPEECH_PR_DEGRADED | SPEECH       | SPEECH        | SPEECH
     ----------------------------------------------------------------
     RX_SPEECH_BAD,        | SPEECH       | DTX           | DTX_MUTE
     ----------------------------------------------------------------
     RX_SID_FIRST,         | DTX          | DTX/(DTX_MUTE)| DTX_MUTE
     ----------------------------------------------------------------
     RX_SID_UPDATE,        | DTX          | DTX           | DTX
     ----------------------------------------------------------------
     RX_SID_BAD,           | DTX          | DTX/(DTX_MUTE)| DTX_MUTE
     ----------------------------------------------------------------
     RX_NO_DATA,           | SPEECH       | DTX/(DTX_MUTE)| DTX_MUTE
     RX_SPARE              |(class2 garb.)|               |
     ----------------------------------------------------------------
*/


/*----------------------------------------------------------------------------
; FUNCTION CODE
----------------------------------------------------------------------------*/

int16 rx_amr_wb_dtx_handler(
    dtx_decState * st,                    /* i/o : State struct     */
    int16 frame_type                     /* i   : Frame type       */
)
{
    int16 newState;
    int16 encState;

    /* DTX if SID frame or previously in DTX{_MUTE} and (NO_RX OR BAD_SPEECH) */



    if ((frame_type == RX_SID_FIRST)  ||
            (frame_type == RX_SID_UPDATE) ||
            (frame_type == RX_SID_BAD)    ||
            (((st->dtxGlobalState == DTX) ||
              (st->dtxGlobalState == DTX_MUTE)) &&
             ((frame_type == RX_NO_DATA)    ||
              (frame_type == RX_SPEECH_BAD) ||
              (frame_type == RX_SPEECH_LOST))))
    {
        newState = DTX;

        /* stay in mute for these input types */

        if ((st->dtxGlobalState == DTX_MUTE) &&
                ((frame_type == RX_SID_BAD) ||
                 (frame_type == RX_SID_FIRST) ||
                 (frame_type == RX_SPEECH_LOST) ||
                 (frame_type == RX_NO_DATA)))
        {
            newState = DTX_MUTE;
        }
        /* evaluate if noise parameters are too old                     */
        /* since_last_sid is reset when CN parameters have been updated */
        st->since_last_sid = add_int16(st->since_last_sid, 1);

        /* no update of sid parameters in DTX for a long while */

        if (st->since_last_sid > DTX_MAX_EMPTY_THRESH)
        {
            newState = DTX_MUTE;
        }
    }
    else
    {
        newState = SPEECH;
        st->since_last_sid = 0;
    }

    /* reset the decAnaElapsed Counter when receiving CNI data the first time, to robustify counter missmatch
     * after handover this might delay the bwd CNI analysis in the new decoder slightly. */

    if ((st->data_updated == 0) &&
            (frame_type == RX_SID_UPDATE))
    {
        st->decAnaElapsedCount = 0;
    }
    /* update the SPE-SPD DTX hangover synchronization */
    /* to know when SPE has added dtx hangover         */
    st->decAnaElapsedCount = add_int16(st->decAnaElapsedCount, 1);
    st->dtxHangoverAdded = 0;


    if ((frame_type == RX_SID_FIRST) ||
            (frame_type == RX_SID_UPDATE) ||
            (frame_type == RX_SID_BAD) ||
            (frame_type == RX_NO_DATA))
    {
        encState = DTX;
    }
    else
    {
        encState = SPEECH;
    }


    if (encState == SPEECH)
    {
        st->dtxHangoverCount = DTX_HANG_CONST;
    }
    else
    {

        if (st->decAnaElapsedCount > DTX_ELAPSED_FRAMES_THRESH)
        {
            st->dtxHangoverAdded = 1;
            st->decAnaElapsedCount = 0;
            st->dtxHangoverCount = 0;
        }
        else if (st->dtxHangoverCount == 0)
        {
            st->decAnaElapsedCount = 0;
        }
        else
        {
            st->dtxHangoverCount--;
        }
    }

    if (newState != SPEECH)
    {
        /* DTX or DTX_MUTE CN data is not in a first SID, first SIDs are marked as SID_BAD but will do
         * backwards analysis if a hangover period has been added according to the state machine above */

        st->sid_frame = 0;
        st->valid_data = 0;


        if (frame_type == RX_SID_FIRST)
        {
            st->sid_frame = 1;
        }
        else if (frame_type == RX_SID_UPDATE)
        {
            st->sid_frame = 1;
            st->valid_data = 1;
        }
        else if (frame_type == RX_SID_BAD)
        {
            st->sid_frame = 1;
            st->dtxHangoverAdded = 0;      /* use old data */
        }
    }
    return newState;
    /* newState is used by both SPEECH AND DTX synthesis routines */
}


/*----------------------------------------------------------------------------
; FUNCTION CODE
----------------------------------------------------------------------------*/

void aver_isf_history(
    int16 isf_old[],
    int16 indices[],
    int32 isf_aver[]
)
{
    int16 i, j, k;
    int16 isf_tmp[2 * M];
    int32 L_tmp;

    /* Memorize in isf_tmp[][] the ISF vectors to be replaced by */
    /* the median ISF vector prior to the averaging               */
    for (k = 0; k < 2; k++)
    {

        if (indices[k] + 1 != 0)
        {
            for (i = 0; i < M; i++)
            {
                isf_tmp[k * M + i] = isf_old[indices[k] * M + i];
                isf_old[indices[k] * M + i] = isf_old[indices[2] * M + i];
            }
        }
    }

    /* Perform the ISF averaging */
    for (j = 0; j < M; j++)
    {
        L_tmp = 0;

        for (i = 0; i < DTX_HIST_SIZE; i++)
        {
            L_tmp = add_int32(L_tmp, (int32)(isf_old[i * M + j]));
        }
        isf_aver[j] = L_tmp;
    }

    /* Retrieve from isf_tmp[][] the ISF vectors saved prior to averaging */
    for (k = 0; k < 2; k++)
    {

        if (indices[k] + 1 != 0)
        {
            for (i = 0; i < M; i++)
            {
                isf_old[indices[k] * M + i] = isf_tmp[k * M + i];
            }
        }
    }

    return;
}


/*----------------------------------------------------------------------------
; FUNCTION CODE
----------------------------------------------------------------------------*/

void find_frame_indices(
    int16 isf_old_tx[],
    int16 indices[],
    dtx_encState * st
)
{
    int32 L_tmp, summin, summax, summax2nd;
    int16 i, j, tmp;
    int16 ptr;

    /* Remove the effect of the oldest frame from the column */
    /* sum sumD[0..DTX_HIST_SIZE-1]. sumD[DTX_HIST_SIZE] is    */
    /* not updated since it will be removed later.           */

    tmp = DTX_HIST_SIZE_MIN_ONE;
    j = -1;
    for (i = 0; i < DTX_HIST_SIZE_MIN_ONE; i++)
    {
        j += tmp;
        st->sumD[i] = sub_int32(st->sumD[i], st->D[j]);
        tmp--;
    }

    /* Shift the column sum sumD. The element sumD[DTX_HIST_SIZE-1]    */
    /* corresponding to the oldest frame is removed. The sum of     */
    /* the distances between the latest isf and other isfs, */
    /* i.e. the element sumD[0], will be computed during this call. */
    /* Hence this element is initialized to zero.                   */

    for (i = DTX_HIST_SIZE_MIN_ONE; i > 0; i--)
    {
        st->sumD[i] = st->sumD[i - 1];
    }
    st->sumD[0] = 0;

    /* Remove the oldest frame from the distance matrix.           */
    /* Note that the distance matrix is replaced by a one-         */
    /* dimensional array to save static memory.                    */

    tmp = 0;
    for (i = 27; i >= 12; i -= tmp)
    {
        tmp++;
        for (j = tmp; j > 0; j--)
        {
            st->D[i - j + 1] = st->D[i - j - tmp];
        }
    }

    /* Compute the first column of the distance matrix D            */
    /* (squared Euclidean distances from isf1[] to isf_old_tx[][]). */

    ptr = st->hist_ptr;
    for (i = 1; i < DTX_HIST_SIZE; i++)
    {
        /* Compute the distance between the latest isf and the other isfs. */
        ptr--;

        if (ptr < 0)
        {
            ptr = DTX_HIST_SIZE_MIN_ONE;
        }
        L_tmp = 0;
        for (j = 0; j < M; j++)
        {
            tmp = sub_int16(isf_old_tx[st->hist_ptr * M + j], isf_old_tx[ptr * M + j]);
            L_tmp = mac_16by16_to_int32(L_tmp, tmp, tmp);
        }
        st->D[i - 1] = L_tmp;

        /* Update also the column sums. */
        st->sumD[0] = add_int32(st->sumD[0], st->D[i - 1]);
        st->sumD[i] = add_int32(st->sumD[i], st->D[i - 1]);
    }

    /* Find the minimum and maximum distances */
    summax = st->sumD[0];
    summin = st->sumD[0];
    indices[0] = 0;
    indices[2] = 0;
    for (i = 1; i < DTX_HIST_SIZE; i++)
    {

        if (st->sumD[i] > summax)
        {
            indices[0] = i;
            summax = st->sumD[i];
        }

        if (st->sumD[i] < summin)
        {
            indices[2] = i;
            summin = st->sumD[i];
        }
    }

    /* Find the second largest distance */
    summax2nd = -2147483647L;
    indices[1] = -1;
    for (i = 0; i < DTX_HIST_SIZE; i++)
    {

        if ((st->sumD[i] > summax2nd) && (i != indices[0]))
        {
            indices[1] = i;
            summax2nd = st->sumD[i];
        }
    }

    for (i = 0; i < 3; i++)
    {
        indices[i] = sub_int16(st->hist_ptr, indices[i]);

        if (indices[i] < 0)
        {
            indices[i] = add_int16(indices[i], DTX_HIST_SIZE);
        }
    }

    /* If maximum distance/MED_THRESH is smaller than minimum distance */
    /* then the median ISF vector replacement is not performed         */
    tmp = normalize_amr_wb(summax);
    summax <<= tmp;
    summin <<= tmp;
    L_tmp = mul_16by16_to_int32(amr_wb_round(summax), INV_MED_THRESH);

    if (L_tmp <= summin)
    {
        indices[0] = -1;
    }
    /* If second largest distance/MED_THRESH is smaller than     */
    /* minimum distance then the median ISF vector replacement is    */
    /* not performed                                                 */
    summax2nd = shl_int32(summax2nd, tmp);
    L_tmp = mul_16by16_to_int32(amr_wb_round(summax2nd), INV_MED_THRESH);

    if (L_tmp <= summin)
    {
        indices[1] = -1;
    }
    return;
}


/*----------------------------------------------------------------------------
; FUNCTION CODE
----------------------------------------------------------------------------*/

int16 dithering_control(dtx_encState * st)
{
    int16 i, tmp, mean, CN_dith, gain_diff;
    int32 ISF_diff;

    /* determine how stationary the spectrum of background noise is */
    ISF_diff = 0;
    for (i = 0; i < 8; i++)
    {
        ISF_diff = add_int32(ISF_diff, st->sumD[i]);
    }
    if ((ISF_diff >> 26) > 0)
    {
        CN_dith = 1;
    }
    else
    {
        CN_dith = 0;
    }

    /* determine how stationary the energy of background noise is */
    mean = 0;
    for (i = 0; i < DTX_HIST_SIZE; i++)
    {
        mean = add_int16(mean, st->log_en_hist[i]);
    }
    mean >>= 3;
    gain_diff = 0;
    for (i = 0; i < DTX_HIST_SIZE; i++)
    {
        tmp = sub_int16(st->log_en_hist[i], mean);
        tmp = tmp - (tmp < 0);

        gain_diff += tmp ^(tmp >> 15);    /*  tmp ^sign(tmp)  */;
    }
    if (gain_diff > GAIN_THR)
    {
        CN_dith = 1;
    }
    return CN_dith;
}


/*----------------------------------------------------------------------------
; FUNCTION CODE
----------------------------------------------------------------------------*/

void CN_dithering(
    int16 isf[M],
    int32 * L_log_en_int,
    int16 * dither_seed
)
{
    int16 temp, temp1, i, dither_fac, rand_dith;
    int16 rand_dith2;

    /* Insert comfort noise dithering for energy parameter */
    rand_dith = noise_gen_amrwb(dither_seed) >> 1;
    rand_dith2 = noise_gen_amrwb(dither_seed) >> 1;
    rand_dith += rand_dith2;
    *L_log_en_int = add_int32(*L_log_en_int, mul_16by16_to_int32(rand_dith, GAIN_FACTOR));

    if (*L_log_en_int < 0)
    {
        *L_log_en_int = 0;
    }
    /* Insert comfort noise dithering for spectral parameters (ISF-vector) */
    dither_fac = ISF_FACTOR_LOW;

    rand_dith = noise_gen_amrwb(dither_seed) >> 1;
    rand_dith2 = noise_gen_amrwb(dither_seed) >> 1;
    rand_dith +=  rand_dith2;
    temp = add_int16(isf[0], mult_int16_r(rand_dith, dither_fac));

    /* Make sure that isf[0] will not get negative values */
    if (temp < ISF_GAP)
    {
        isf[0] = ISF_GAP;
    }
    else
    {
        isf[0] = temp;
    }

    for (i = 1; i < M - 1; i++)
    {
        dither_fac = add_int16(dither_fac, ISF_FACTOR_STEP);

        rand_dith = noise_gen_amrwb(dither_seed) >> 1;
        rand_dith2 = noise_gen_amrwb(dither_seed) >> 1;
        rand_dith +=  rand_dith2;
        temp = add_int16(isf[i], mult_int16_r(rand_dith, dither_fac));
        temp1 = sub_int16(temp, isf[i - 1]);

        /* Make sure that isf spacing remains at least ISF_DITH_GAP Hz */
        if (temp1 < ISF_DITH_GAP)
        {
            isf[i] = isf[i - 1] + ISF_DITH_GAP;
        }
        else
        {
            isf[i] = temp;
        }
    }

    /* Make sure that isf[M-2] will not get values above 16384 */
    if (isf[M - 2] > 16384)
    {
        isf[M - 2] = 16384;
    }
    return;
}
