/*
 *  Copyright (c) 2011 The WebRTC project authors. All Rights Reserved.
 *
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the LICENSE file in the root of the source
 *  tree. An additional intellectual property rights grant can be found
 *  in the file PATENTS.  All contributing project authors may
 *  be found in the AUTHORS file in the root of the source tree.
 */

/*
 * Signal the MCU that data is available and ask for a RecOut decision.
 */

#include "mcu.h"

#include <string.h>

#include "signal_processing_library.h"

#include "automode.h"
#include "dtmf_buffer.h"
#include "mcu_dsp_common.h"
#include "neteq_error_codes.h"

#ifdef NETEQ_DELAY_LOGGING
#include "delay_logging.h"
#include <stdio.h>

extern FILE *delay_fid2; /* file pointer to delay log file */
#endif


/*
 * Signals the MCU that DSP status data is available.
 */
int WebRtcNetEQ_SignalMcu(MCUInst_t *inst)
{

    int i_bufferpos, i_res;
    WebRtc_UWord16 uw16_instr;
    DSP2MCU_info_t dspInfo;
    WebRtc_Word16 *blockPtr, blockLen;
    WebRtc_UWord32 uw32_availableTS;
    RTPPacket_t temp_pkt;
    WebRtc_Word32 w32_bufsize, w32_tmp;
    WebRtc_Word16 payloadType = -1;
    WebRtc_Word16 wantedNoOfTimeStamps;
    WebRtc_Word32 totalTS;
    WebRtc_Word16 oldPT, latePacketExist = 0;
    WebRtc_UWord32 oldTS, prevTS, uw32_tmp;
    WebRtc_UWord16 prevSeqNo;
    WebRtc_Word16 nextSeqNoAvail;
    WebRtc_Word16 fs_mult, w16_tmp;
    WebRtc_Word16 lastModeBGNonly = 0;
#ifdef NETEQ_DELAY_LOGGING
    int temp_var;
#endif
    int playDtmf = 0;

    fs_mult = WebRtcSpl_DivW32W16ResW16(inst->fs, 8000);

    /* Increment counter since last statistics report */
    inst->lastReportTS += inst->timestampsPerCall;

    /* Read info from DSP so we now current status */

    WEBRTC_SPL_MEMCPY_W8(&dspInfo,inst->pw16_readAddress,sizeof(DSP2MCU_info_t));

    /* Set blockPtr to first payload block */
    blockPtr = &inst->pw16_writeAddress[3];

    /* Clear instruction word and number of lost samples (2*WebRtc_Word16) */
    inst->pw16_writeAddress[0] = 0;
    inst->pw16_writeAddress[1] = 0;
    inst->pw16_writeAddress[2] = 0;

    if ((dspInfo.lastMode & MODE_AWAITING_CODEC_PTR) != 0)
    {
        /*
         * Make sure state is adjusted so that a codec update is
         * performed when first packet arrives.
         */
        if (inst->new_codec != 1)
        {
            inst->current_Codec = -1;
        }
        dspInfo.lastMode = (dspInfo.lastMode ^ MODE_AWAITING_CODEC_PTR);
    }

#ifdef NETEQ_STEREO
    if ((dspInfo.lastMode & MODE_MASTER_DTMF_SIGNAL) != 0)
    {
        playDtmf = 1; /* force DTMF decision */
        dspInfo.lastMode = (dspInfo.lastMode ^ MODE_MASTER_DTMF_SIGNAL);
    }

    if ((dspInfo.lastMode & MODE_USING_STEREO) != 0)
    {
        if (inst->usingStereo == 0)
        {
            /* stereo mode changed; reset automode instance to re-synchronize statistics */
            WebRtcNetEQ_ResetAutomode(&(inst->BufferStat_inst.Automode_inst),
                inst->PacketBuffer_inst.maxInsertPositions);
        }
        inst->usingStereo = 1;
        dspInfo.lastMode = (dspInfo.lastMode ^ MODE_USING_STEREO);
    }
    else
    {
        inst->usingStereo = 0;
    }
#endif

    /* detect if BGN_ONLY flag is set in lastMode */
    if ((dspInfo.lastMode & MODE_BGN_ONLY) != 0)
    {
        lastModeBGNonly = 1; /* remember flag */
        dspInfo.lastMode ^= MODE_BGN_ONLY; /* clear the flag */
    }

    if ((dspInfo.lastMode == MODE_RFC3389CNG) || (dspInfo.lastMode == MODE_CODEC_INTERNAL_CNG)
        || (dspInfo.lastMode == MODE_EXPAND))
    {
        /*
         * If last mode was CNG (or Expand, since this could be covering up for a lost CNG
         * packet), increase the CNGplayedTS counter.
         */
        inst->BufferStat_inst.uw32_CNGplayedTS += inst->timestampsPerCall;

        if (dspInfo.lastMode == MODE_RFC3389CNG)
        {
            /* remember that RFC3389CNG is on (needed if CNG is interrupted by DTMF) */
            inst->BufferStat_inst.w16_cngOn = CNG_RFC3389_ON;
        }
        else if (dspInfo.lastMode == MODE_CODEC_INTERNAL_CNG)
        {
            /* remember that internal CNG is on (needed if CNG is interrupted by DTMF) */
            inst->BufferStat_inst.w16_cngOn = CNG_INTERNAL_ON;
        }

    }

    /* Update packet size from previously decoded packet */
    if (dspInfo.frameLen > 0)
    {
        inst->PacketBuffer_inst.packSizeSamples = dspInfo.frameLen;
    }

    /* Look for late packet (unless codec has changed) */
    if (inst->new_codec != 1)
    {
        if (WebRtcNetEQ_DbIsMDCodec((enum WebRtcNetEQDecoder) inst->current_Codec))
        {
            WebRtcNetEQ_PacketBufferFindLowestTimestamp(&inst->PacketBuffer_inst,
                inst->timeStamp, &uw32_availableTS, &i_bufferpos, 1, &payloadType);
            if ((inst->new_codec != 1) && (inst->timeStamp == uw32_availableTS)
                && (inst->timeStamp < dspInfo.playedOutTS) && (i_bufferpos != -1)
                && (WebRtcNetEQ_DbGetPayload(&(inst->codec_DB_inst),
                    (enum WebRtcNetEQDecoder) inst->current_Codec) == payloadType))
            {
                temp_pkt.payload = blockPtr + 1;
                i_res = WebRtcNetEQ_PacketBufferExtract(&inst->PacketBuffer_inst, &temp_pkt,
                    i_bufferpos);
                if (i_res < 0)
                { /* error returned */
                    return i_res;
                }
                *blockPtr = temp_pkt.payloadLen;
                /* set the flag if this is a redundant payload */
                if (temp_pkt.rcuPlCntr > 0)
                {
                    *blockPtr = (*blockPtr) | (DSP_CODEC_RED_FLAG);
                }
                blockPtr += ((temp_pkt.payloadLen + 1) >> 1) + 1;

                /*
                 * Close the data with a zero size block, in case we will not write any
                 * more data.
                 */
                *blockPtr = 0;
                inst->pw16_writeAddress[0] = (inst->pw16_writeAddress[0] & 0xf0ff)
                        | DSP_CODEC_ADD_LATE_PKT;
                latePacketExist = 1;
            }
        }
    }

    i_res = WebRtcNetEQ_PacketBufferFindLowestTimestamp(&inst->PacketBuffer_inst,
        dspInfo.playedOutTS, &uw32_availableTS, &i_bufferpos, (inst->new_codec == 0),
        &payloadType);
    if (i_res < 0)
    { /* error returned */
        return i_res;
    }

    if (inst->BufferStat_inst.w16_cngOn == CNG_RFC3389_ON)
    {
        /*
         * Because of timestamp peculiarities, we have to "manually" disallow using a CNG
         * packet with the same timestamp as the one that was last played. This can happen
         * when using redundancy and will cause the timing to shift.
         */
        while (i_bufferpos != -1 && WebRtcNetEQ_DbIsCNGPayload(&inst->codec_DB_inst,
            payloadType) && dspInfo.playedOutTS >= uw32_availableTS)
        {

            /* Don't use this packet, discard it */
            inst->PacketBuffer_inst.payloadType[i_bufferpos] = -1;
            inst->PacketBuffer_inst.payloadLengthBytes[i_bufferpos] = 0;
            inst->PacketBuffer_inst.numPacketsInBuffer--;

            /* Check buffer again */
            WebRtcNetEQ_PacketBufferFindLowestTimestamp(&inst->PacketBuffer_inst,
                dspInfo.playedOutTS, &uw32_availableTS, &i_bufferpos, (inst->new_codec == 0),
                &payloadType);
        }
    }

    /* Check packet buffer */
    w32_bufsize = WebRtcNetEQ_PacketBufferGetSize(&inst->PacketBuffer_inst);

    if (dspInfo.lastMode == MODE_SUCCESS_ACCELERATE || dspInfo.lastMode
        == MODE_LOWEN_ACCELERATE || dspInfo.lastMode == MODE_SUCCESS_PREEMPTIVE
        || dspInfo.lastMode == MODE_LOWEN_PREEMPTIVE)
    {
        /* Subtract (dspInfo.samplesLeft + inst->timestampsPerCall) from sampleMemory */
        inst->BufferStat_inst.Automode_inst.sampleMemory -= dspInfo.samplesLeft
            + inst->timestampsPerCall;
        /* Update post-call statistics */
        inst->statInst.jbChangeCount++;
    }

    /* calculate total current buffer size (in ms*8), including sync buffer */
    w32_bufsize = WebRtcSpl_DivW32W16((w32_bufsize + dspInfo.samplesLeft), fs_mult);

    if (((WebRtc_UWord32) w32_bufsize >> 3) < inst->statInst.jbMinSize)
    {
        /* new all-time low */
        inst->statInst.jbMinSize = ((WebRtc_UWord32) w32_bufsize >> 3); /* shift to ms */
    }
    if (((WebRtc_UWord32) w32_bufsize >> 3) > inst->statInst.jbMaxSize)
    {
        /* new all-time high */
        inst->statInst.jbMaxSize = ((WebRtc_UWord32) w32_bufsize >> 3); /* shift to ms */
    }

    /* Update avg bufsize:
     * jbAvgSize = (jbAvgCount * jbAvgSize + w32_bufsize/8)/(jbAvgCount+1)
     * with proper rounding
     */
    {
        WebRtc_Word32 avgTmp;

        /* Simplify the above formula to:
         * jbAvgSizeQ16 =
         *    jbAvgSizeQ16 + ( (w32_bufsize/8 << 16) - jbAvgSizeQ16 + d ) / (jbAvgCount+1)
         * where d = jbAvgCount/2 for proper rounding.
         */

        avgTmp = (((WebRtc_UWord32) w32_bufsize >> 3) << 16) - inst->statInst.jbAvgSizeQ16;
        avgTmp = WEBRTC_SPL_DIV( avgTmp + (inst->statInst.jbAvgCount>>1),
            inst->statInst.jbAvgCount + 1 );
        inst->statInst.jbAvgSizeQ16 += avgTmp;

        if (inst->statInst.jbAvgCount < (0xFFFF - 1))
        {
            inst->statInst.jbAvgCount++;
        }
    }

#ifdef NETEQ_ATEVENT_DECODE
    /* DTMF data will affect the decision */
    if (WebRtcNetEQ_DtmfDecode(&inst->DTMF_inst, blockPtr + 1, blockPtr + 2,
        dspInfo.playedOutTS + inst->BufferStat_inst.uw32_CNGplayedTS) > 0)
    {
        playDtmf = 1;

        /* Flag DTMF payload */
        inst->pw16_writeAddress[0] = inst->pw16_writeAddress[0] | DSP_DTMF_PAYLOAD;

        /* Block Length in bytes */
        blockPtr[0] = 4;
        /* Advance to next payload position */
        blockPtr += 3;
    }
#endif

    /* Update statistics and make decision */
    uw16_instr = WebRtcNetEQ_BufstatsDecision(&inst->BufferStat_inst,
        inst->PacketBuffer_inst.packSizeSamples, w32_bufsize, dspInfo.playedOutTS,
        uw32_availableTS, i_bufferpos == -1,
        WebRtcNetEQ_DbIsCNGPayload(&inst->codec_DB_inst, payloadType), dspInfo.lastMode,
        inst->NetEqPlayoutMode, inst->timestampsPerCall, inst->NoOfExpandCalls, fs_mult,
        lastModeBGNonly, playDtmf);

    /* Check if time to reset loss counter */
    if (inst->lastReportTS > WEBRTC_SPL_UMUL(inst->fs, MAX_LOSS_REPORT_PERIOD))
    {
        /* reset loss counter */
        WebRtcNetEQ_ResetMcuInCallStats(inst);
    }

    /* Check sync buffer size */
    if ((dspInfo.samplesLeft >= inst->timestampsPerCall) && (uw16_instr
        != BUFSTATS_DO_ACCELERATE) && (uw16_instr != BUFSTATS_DO_MERGE) && (uw16_instr
            != BUFSTATS_DO_PREEMPTIVE_EXPAND))
    {
        *blockPtr = 0;
        inst->pw16_writeAddress[0] = (inst->pw16_writeAddress[0] & 0x0fff) | DSP_INSTR_NORMAL;
        return 0;
    }

    if (uw16_instr == BUFSTATS_DO_EXPAND)
    {
        inst->NoOfExpandCalls++;
    }
    else
    {
        /* update post-call statistics */
        WebRtc_UWord32
        expandTime =
                WEBRTC_SPL_UDIV(WEBRTC_SPL_UMUL_32_16(
                        WEBRTC_SPL_UMUL_32_16((WebRtc_UWord32) inst->NoOfExpandCalls,
                            (WebRtc_UWord16) 1000),
                        inst->timestampsPerCall), inst->fs); /* expand time in ms */

        if (expandTime > 2000)
        {
            inst->statInst.countExpandMoreThan2000ms++;
        }
        else if (expandTime > 500)
        {
            inst->statInst.countExpandMoreThan500ms++;
        }
        else if (expandTime > 250)
        {
            inst->statInst.countExpandMoreThan250ms++;
        }
        else if (expandTime > 120)
        {
            inst->statInst.countExpandMoreThan120ms++;
        }

        if (expandTime > inst->statInst.longestExpandDurationMs)
        {
            inst->statInst.longestExpandDurationMs = expandTime;
        }

        /* reset counter */
        inst->NoOfExpandCalls = 0;
    }

    /* New codec or big change in packet number? */
    if (((inst->new_codec) || (uw16_instr == BUFSTAT_REINIT)) && (uw16_instr
        != BUFSTATS_DO_EXPAND))
    {
        CodecFuncInst_t cinst;

        /* Clear other instructions */
        blockPtr = &inst->pw16_writeAddress[3];
        /* Clear instruction word */
        inst->pw16_writeAddress[0] = 0;

        inst->timeStamp = uw32_availableTS;
        dspInfo.playedOutTS = uw32_availableTS;
        if (inst->current_Codec != -1)
        {
            i_res = WebRtcNetEQ_DbGetPtrs(&inst->codec_DB_inst,
                (enum WebRtcNetEQDecoder) inst->current_Codec, &cinst);
            if (i_res < 0)
            { /* error returned */
                return i_res;
            }
        }
        else
        {
            /* The main codec has not been initialized yet (first packets are DTMF or CNG). */
            if (WebRtcNetEQ_DbIsCNGPayload(&inst->codec_DB_inst, payloadType))
            {
                /* The currently extracted packet is CNG; get CNG fs */
                WebRtc_UWord16 tempFs;

                tempFs = WebRtcNetEQ_DbGetSampleRate(&inst->codec_DB_inst, payloadType);
                if (tempFs > 0)
                {
                    inst->fs = tempFs;
                }
            }
            WebRtcSpl_MemSetW16((WebRtc_Word16*) &cinst, 0,
                                sizeof(CodecFuncInst_t) / sizeof(WebRtc_Word16));
            cinst.codec_fs = inst->fs;
        }
        cinst.timeStamp = inst->timeStamp;
        blockLen = (sizeof(CodecFuncInst_t)) >> (sizeof(WebRtc_Word16) - 1); /* in Word16 */
        *blockPtr = blockLen * 2;
        blockPtr++;
        WEBRTC_SPL_MEMCPY_W8(blockPtr,&cinst,sizeof(CodecFuncInst_t));
        blockPtr += blockLen;
        inst->new_codec = 0;

        /* Reinitialize the MCU fs */
        i_res = WebRtcNetEQ_McuSetFs(inst, cinst.codec_fs);
        if (i_res < 0)
        { /* error returned */
            return i_res;
        }

        /* Set the packet size by guessing */
        inst->PacketBuffer_inst.packSizeSamples = inst->timestampsPerCall * 3;

        WebRtcNetEQ_ResetAutomode(&(inst->BufferStat_inst.Automode_inst),
                                  inst->PacketBuffer_inst.maxInsertPositions);

#ifdef NETEQ_CNG_CODEC
        /* Also insert CNG state as this might be needed by DSP */
        i_res = WebRtcNetEQ_DbGetPtrs(&inst->codec_DB_inst, kDecoderCNG, &cinst);
        if ((i_res < 0) && (i_res != CODEC_DB_NOT_EXIST1))
        {
            /* other error returned */
            /* (CODEC_DB_NOT_EXIST1 simply indicates that CNG is not used */
            return i_res;
        }
        else
        {
            /* CNG exists */
            blockLen = (sizeof(cinst.codec_state)) >> (sizeof(WebRtc_Word16) - 1);
            *blockPtr = blockLen * 2;
            blockPtr++;
            WEBRTC_SPL_MEMCPY_W8(blockPtr,&cinst.codec_state,sizeof(cinst.codec_state));
            blockPtr += blockLen;
        }
#endif

        inst->pw16_writeAddress[0] = (inst->pw16_writeAddress[0] & 0xf0ff)
                | DSP_CODEC_NEW_CODEC;

        if (uw16_instr == BUFSTATS_DO_RFC3389CNG_NOPACKET)
        {
            /*
             * Change decision to CNG packet, since we do have a CNG packet, but it was
             * considered too early to use. Now, use it anyway.
             */
            uw16_instr = BUFSTATS_DO_RFC3389CNG_PACKET;
        }
        else if (uw16_instr != BUFSTATS_DO_RFC3389CNG_PACKET)
        {
            uw16_instr = BUFSTATS_DO_NORMAL;
        }

        /* reset loss counter */
        WebRtcNetEQ_ResetMcuInCallStats(inst);
    }

    /* Should we just reset the decoder? */
    if (uw16_instr == BUFSTAT_REINIT_DECODER)
    {
        /* Change decision to normal and flag decoder reset */
        uw16_instr = BUFSTATS_DO_NORMAL;
        inst->pw16_writeAddress[0] = (inst->pw16_writeAddress[0] & 0xf0ff) | DSP_CODEC_RESET;
    }

    /* Expand requires no new packet */
    if (uw16_instr == BUFSTATS_DO_EXPAND)
    {

        inst->timeStamp = dspInfo.playedOutTS;

        /* Have we got one descriptor left? */
        if (WebRtcNetEQ_DbIsMDCodec((enum WebRtcNetEQDecoder) inst->current_Codec)
            && (dspInfo.MD || latePacketExist))
        {

            if (dspInfo.lastMode != MODE_ONE_DESCRIPTOR)
            {
                /* this is the first "consecutive" one-descriptor decoding; reset counter */
                inst->one_desc = 0;
            }
            if (inst->one_desc < MAX_ONE_DESC)
            {
                /* use that one descriptor */
                inst->one_desc++; /* increase counter */
                inst->pw16_writeAddress[0] = (inst->pw16_writeAddress[0] & 0x0fff)
                        | DSP_INSTR_NORMAL_ONE_DESC;

                /* decrease counter since we did no Expand */
                inst->NoOfExpandCalls = WEBRTC_SPL_MAX(inst->NoOfExpandCalls - 1, 0);
                return 0;
            }
            else
            {
                /* too many consecutive one-descriptor decodings; do expand instead */
                inst->one_desc = 0; /* reset counter */
            }

        }

        inst->pw16_writeAddress[0] = (inst->pw16_writeAddress[0] & 0x0fff) | DSP_INSTR_EXPAND;
        return 0;
    }

    /* Merge is not needed if we still have a descriptor */
    if ((uw16_instr == BUFSTATS_DO_MERGE) && (dspInfo.MD != 0))
    {
        inst->pw16_writeAddress[0] = (inst->pw16_writeAddress[0] & 0x0fff)
                | DSP_INSTR_NORMAL_ONE_DESC;
        *blockPtr = 0;
        return 0;
    }

    /* Do CNG without trying to extract any packets from buffer */
    if (uw16_instr == BUFSTATS_DO_RFC3389CNG_NOPACKET)
    {
        inst->pw16_writeAddress[0] = (inst->pw16_writeAddress[0] & 0x0fff)
                | DSP_INSTR_DO_RFC3389CNG;
        *blockPtr = 0;
        return 0;
    }

    /* Do built-in CNG without extracting any new packets from buffer */
    if (uw16_instr == BUFSTATS_DO_INTERNAL_CNG_NOPACKET)
    {
        inst->pw16_writeAddress[0] = (inst->pw16_writeAddress[0] & 0x0fff)
                | DSP_INSTR_DO_CODEC_INTERNAL_CNG;
        *blockPtr = 0;
        return 0;
    }

    /* Do DTMF without extracting any new packets from buffer */
    if (uw16_instr == BUFSTATS_DO_DTMF_ONLY)
    {
        WebRtc_UWord32 timeStampJump = 0;

        /* Update timestamp */
        if ((inst->BufferStat_inst.uw32_CNGplayedTS > 0) && (dspInfo.lastMode != MODE_DTMF))
        {
            /* Jump in timestamps if needed */
            timeStampJump = inst->BufferStat_inst.uw32_CNGplayedTS;
            inst->pw16_writeAddress[1] = (WebRtc_UWord16) (timeStampJump >> 16);
            inst->pw16_writeAddress[2] = (WebRtc_UWord16) (timeStampJump & 0xFFFF);
        }

        inst->timeStamp = dspInfo.playedOutTS + timeStampJump;

        /* update post-call statistics (since we will reset the CNG counter) */
        inst->statInst.generatedSilentMs
        += WEBRTC_SPL_UDIV(
            WEBRTC_SPL_UMUL_32_16(inst->BufferStat_inst.uw32_CNGplayedTS, (WebRtc_UWord16) 1000),
            inst->fs);

        inst->BufferStat_inst.uw32_CNGplayedTS = 0;
        inst->NoOfExpandCalls = 0;

        inst->pw16_writeAddress[0] = (inst->pw16_writeAddress[0] & 0x0fff)
                | DSP_INSTR_DTMF_GENERATE;
        *blockPtr = 0;
        return 0;
    }

    if (uw16_instr == BUFSTATS_DO_ACCELERATE)
    {
        /* In order to do a Accelerate we need at least 30 ms of data */
        if (dspInfo.samplesLeft >= (3 * 80 * fs_mult))
        {
            /* Already have enough data, so we do not need to extract any more */
            inst->pw16_writeAddress[0] = (inst->pw16_writeAddress[0] & 0x0fff)
                    | DSP_INSTR_ACCELERATE;
            *blockPtr = 0;
            inst->BufferStat_inst.Automode_inst.sampleMemory
            = (WebRtc_Word32) dspInfo.samplesLeft;
            inst->BufferStat_inst.Automode_inst.prevTimeScale = 1;
            return 0;
        }
        else if ((dspInfo.samplesLeft >= (1 * 80 * fs_mult))
            && (inst->PacketBuffer_inst.packSizeSamples >= (240 * fs_mult)))
        {
            /* Avoid decoding more data as it might overflow playout buffer */
            inst->pw16_writeAddress[0] = (inst->pw16_writeAddress[0] & 0x0fff)
                    | DSP_INSTR_NORMAL;
            *blockPtr = 0;
            return 0;
        }
        else if ((dspInfo.samplesLeft < (1 * 80 * fs_mult))
            && (inst->PacketBuffer_inst.packSizeSamples >= (240 * fs_mult)))
        {
            /* For >= 30ms allow Accelerate with a decoding to avoid overflow in playout buffer */
            wantedNoOfTimeStamps = inst->timestampsPerCall;
        }
        else if (dspInfo.samplesLeft >= (2 * 80 * fs_mult))
        {
            /* We need to decode another 10 ms in order to do an Accelerate */
            wantedNoOfTimeStamps = inst->timestampsPerCall;
        }
        else
        {
            /*
             * Build up decoded data by decoding at least 20 ms of data.
             * Do not perform Accelerate yet, but wait until we only need to do one decoding.
             */
            wantedNoOfTimeStamps = 2 * inst->timestampsPerCall;
            uw16_instr = BUFSTATS_DO_NORMAL;
        }
    }
    else if (uw16_instr == BUFSTATS_DO_PREEMPTIVE_EXPAND)
    {
        /* In order to do a Preemptive Expand we need at least 30 ms of data */
        if (dspInfo.samplesLeft >= (3 * 80 * fs_mult))
        {
            /* Already have enough data, so we do not need to extract any more */
            inst->pw16_writeAddress[0] = (inst->pw16_writeAddress[0] & 0x0fff)
                    | DSP_INSTR_PREEMPTIVE_EXPAND;
            *blockPtr = 0;
            inst->BufferStat_inst.Automode_inst.sampleMemory
            = (WebRtc_Word32) dspInfo.samplesLeft;
            inst->BufferStat_inst.Automode_inst.prevTimeScale = 1;
            return 0;
        }
        else if ((dspInfo.samplesLeft >= (1 * 80 * fs_mult))
            && (inst->PacketBuffer_inst.packSizeSamples >= (240 * fs_mult)))
        {
            /*
             * Avoid decoding more data as it might overflow playout buffer;
             * still try Preemptive Expand though.
             */
            inst->pw16_writeAddress[0] = (inst->pw16_writeAddress[0] & 0x0fff)
                    | DSP_INSTR_PREEMPTIVE_EXPAND;
            *blockPtr = 0;
            inst->BufferStat_inst.Automode_inst.sampleMemory
            = (WebRtc_Word32) dspInfo.samplesLeft;
            inst->BufferStat_inst.Automode_inst.prevTimeScale = 1;
            return 0;
        }
        else if ((dspInfo.samplesLeft < (1 * 80 * fs_mult))
            && (inst->PacketBuffer_inst.packSizeSamples >= (240 * fs_mult)))
        {
            /*
             * For >= 30ms allow Preemptive Expand with a decoding to avoid overflow in
             * playout buffer
             */
            wantedNoOfTimeStamps = inst->timestampsPerCall;
        }
        else if (dspInfo.samplesLeft >= (2 * 80 * fs_mult))
        {
            /* We need to decode another 10 ms in order to do an Preemptive Expand */
            wantedNoOfTimeStamps = inst->timestampsPerCall;
        }
        else
        {
            /*
             * Build up decoded data by decoding at least 20 ms of data,
             * Still try to perform Preemptive Expand.
             */
            wantedNoOfTimeStamps = 2 * inst->timestampsPerCall;
        }
    }
    else
    {
        wantedNoOfTimeStamps = inst->timestampsPerCall;
    }

    /* Otherwise get data from buffer, try to get at least 10ms */
    totalTS = 0;
    oldTS = uw32_availableTS;
    if ((i_bufferpos > -1) && (uw16_instr != BUFSTATS_DO_ALTERNATIVE_PLC) && (uw16_instr
        != BUFSTATS_DO_ALTERNATIVE_PLC_INC_TS) && (uw16_instr != BUFSTATS_DO_AUDIO_REPETITION)
        && (uw16_instr != BUFSTATS_DO_AUDIO_REPETITION_INC_TS))
    {
        uw32_tmp = (uw32_availableTS - dspInfo.playedOutTS);
        inst->pw16_writeAddress[1] = (WebRtc_UWord16) (uw32_tmp >> 16);
        inst->pw16_writeAddress[2] = (WebRtc_UWord16) (uw32_tmp & 0xFFFF);
        if (inst->BufferStat_inst.w16_cngOn == CNG_OFF)
        {
            /*
             * Adjustment of TS only corresponds to an actual packet loss
             * if comfort noise is not played. If comfort noise was just played,
             * this adjustment of TS is only done to get back in sync with the
             * stream TS; no loss to report.
             */
            inst->lostTS += uw32_tmp;
        }

        if (uw16_instr != BUFSTATS_DO_RFC3389CNG_PACKET)
        {
            /* We are about to decode and use a non-CNG packet => CNG period is ended */
            inst->BufferStat_inst.w16_cngOn = CNG_OFF;
        }

        /* update post-call statistics */
        inst->statInst.generatedSilentMs
        += WEBRTC_SPL_UDIV(
            WEBRTC_SPL_UMUL_32_16(inst->BufferStat_inst.uw32_CNGplayedTS, (WebRtc_UWord16) 1000),
            inst->fs);

        /*
         * Reset CNG timestamp as a new packet will be delivered.
         * (Also if CNG packet, since playedOutTS is updated.)
         */
        inst->BufferStat_inst.uw32_CNGplayedTS = 0;

        prevSeqNo = inst->PacketBuffer_inst.seqNumber[i_bufferpos];
        prevTS = inst->PacketBuffer_inst.timeStamp[i_bufferpos];
        oldPT = inst->PacketBuffer_inst.payloadType[i_bufferpos];

        /* clear flag bits */
        inst->pw16_writeAddress[0] = inst->pw16_writeAddress[0] & 0xFF3F;
        do
        {
            inst->timeStamp = uw32_availableTS;
            /* Write directly to shared memory */
            temp_pkt.payload = blockPtr + 1;
            i_res = WebRtcNetEQ_PacketBufferExtract(&inst->PacketBuffer_inst, &temp_pkt,
                i_bufferpos);

            if (i_res < 0)
            {
                /* error returned */
                return i_res;
            }

#ifdef NETEQ_DELAY_LOGGING
            temp_var = NETEQ_DELAY_LOGGING_SIGNAL_DECODE;
            fwrite(&temp_var,sizeof(int),1,delay_fid2);
            fwrite(&temp_pkt.timeStamp,sizeof(WebRtc_UWord32),1,delay_fid2);
            fwrite(&dspInfo.samplesLeft, sizeof(WebRtc_UWord16), 1, delay_fid2);
#endif

            *blockPtr = temp_pkt.payloadLen;
            /* set the flag if this is a redundant payload */
            if (temp_pkt.rcuPlCntr > 0)
            {
                *blockPtr = (*blockPtr) | (DSP_CODEC_RED_FLAG);
            }
            blockPtr += ((temp_pkt.payloadLen + 1) >> 1) + 1;

            if (i_bufferpos > -1)
            {
                /*
                 * Store number of TS extracted (last extracted is assumed to be of
                 * packSizeSamples).
                 */
                totalTS = uw32_availableTS - oldTS + inst->PacketBuffer_inst.packSizeSamples;
            }
            /* Check what next packet is available */
            WebRtcNetEQ_PacketBufferFindLowestTimestamp(&inst->PacketBuffer_inst,
                inst->timeStamp, &uw32_availableTS, &i_bufferpos, 0, &payloadType);

            nextSeqNoAvail = 0;
            if ((i_bufferpos > -1) && (oldPT
                == inst->PacketBuffer_inst.payloadType[i_bufferpos]))
            {
                w16_tmp = inst->PacketBuffer_inst.seqNumber[i_bufferpos] - prevSeqNo;
                w32_tmp = inst->PacketBuffer_inst.timeStamp[i_bufferpos] - prevTS;
                if ((w16_tmp == 1) || /* Next packet */
                    ((w16_tmp == 0) && (w32_tmp == inst->PacketBuffer_inst.packSizeSamples)))
                { /* or packet split into frames */
                    nextSeqNoAvail = 1;
                }
                prevSeqNo = inst->PacketBuffer_inst.seqNumber[i_bufferpos];
            }

        }
        while ((totalTS < wantedNoOfTimeStamps) && (nextSeqNoAvail == 1));
    }

    if ((uw16_instr == BUFSTATS_DO_ACCELERATE)
        || (uw16_instr == BUFSTATS_DO_PREEMPTIVE_EXPAND))
    {
        /* Check that we have enough data (30ms) to do the Accelearate */
        if ((totalTS + dspInfo.samplesLeft) < WEBRTC_SPL_MUL(3,inst->timestampsPerCall)
            && (uw16_instr == BUFSTATS_DO_ACCELERATE))
        {
            /* Not enough, do normal operation instead */
            uw16_instr = BUFSTATS_DO_NORMAL;
        }
        else
        {
            inst->BufferStat_inst.Automode_inst.sampleMemory
            = (WebRtc_Word32) dspInfo.samplesLeft + totalTS;
            inst->BufferStat_inst.Automode_inst.prevTimeScale = 1;
        }
    }

    /* Close the data with a zero size block */
    *blockPtr = 0;

    /* Write data to DSP */
    switch (uw16_instr)
    {
        case BUFSTATS_DO_NORMAL:
            /* Normal with decoding included */
            inst->pw16_writeAddress[0] = (inst->pw16_writeAddress[0] & 0x0fff)
            | DSP_INSTR_NORMAL;
            break;
        case BUFSTATS_DO_ACCELERATE:
            inst->pw16_writeAddress[0] = (inst->pw16_writeAddress[0] & 0x0fff)
            | DSP_INSTR_ACCELERATE;
            break;
        case BUFSTATS_DO_MERGE:
            inst->pw16_writeAddress[0] = (inst->pw16_writeAddress[0] & 0x0fff)
            | DSP_INSTR_MERGE;
            break;
        case BUFSTATS_DO_RFC3389CNG_PACKET:
            inst->pw16_writeAddress[0] = (inst->pw16_writeAddress[0] & 0x0fff)
            | DSP_INSTR_DO_RFC3389CNG;
            break;
        case BUFSTATS_DO_ALTERNATIVE_PLC:
            inst->pw16_writeAddress[1] = 0;
            inst->pw16_writeAddress[0] = (inst->pw16_writeAddress[0] & 0x0fff)
                    | DSP_INSTR_DO_ALTERNATIVE_PLC;
            break;
        case BUFSTATS_DO_ALTERNATIVE_PLC_INC_TS:
            inst->pw16_writeAddress[1] = 0;
            inst->pw16_writeAddress[0] = (inst->pw16_writeAddress[0] & 0x0fff)
                    | DSP_INSTR_DO_ALTERNATIVE_PLC_INC_TS;
            break;
        case BUFSTATS_DO_AUDIO_REPETITION:
            inst->pw16_writeAddress[1] = 0;
            inst->pw16_writeAddress[0] = (inst->pw16_writeAddress[0] & 0x0fff)
                    | DSP_INSTR_DO_AUDIO_REPETITION;
            break;
        case BUFSTATS_DO_AUDIO_REPETITION_INC_TS:
            inst->pw16_writeAddress[1] = 0;
            inst->pw16_writeAddress[0] = (inst->pw16_writeAddress[0] & 0x0fff)
                    | DSP_INSTR_DO_AUDIO_REPETITION_INC_TS;
            break;
        case BUFSTATS_DO_PREEMPTIVE_EXPAND:
            inst->pw16_writeAddress[0] = (inst->pw16_writeAddress[0] & 0x0fff)
            | DSP_INSTR_PREEMPTIVE_EXPAND;
            break;
        default:
            return UNKNOWN_BUFSTAT_DECISION;
    }

    inst->timeStamp = dspInfo.playedOutTS;
    return 0;

}

