/*
 *  Copyright (c) 2012 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


/*
 * Update the frame size, if we can.
 */
static int WebRtcNetEQ_UpdatePackSizeSamples(MCUInst_t* inst, int buffer_pos,
                                             int payload_type,
                                             int pack_size_samples) {
  if (buffer_pos >= 0) {
    int codec_pos;
    codec_pos = WebRtcNetEQ_DbGetCodec(&inst->codec_DB_inst, payload_type);
    if (codec_pos >= 0) {
      codec_pos = inst->codec_DB_inst.position[codec_pos];
      if (codec_pos >= 0) {
        return WebRtcNetEQ_PacketBufferGetPacketSize(
          &inst->PacketBuffer_inst, buffer_pos,
          &inst->codec_DB_inst, codec_pos, pack_size_samples);
      }
    }
  }
  return pack_size_samples;
}

/*
 * 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;

    /* Increment waiting time for all packets. */
    WebRtcNetEQ_IncrementWaitingTimes(&inst->PacketBuffer_inst);

    /* 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))
            {
                int waitingTime;
                temp_pkt.payload = blockPtr + 1;
                i_res = WebRtcNetEQ_PacketBufferExtract(&inst->PacketBuffer_inst, &temp_pkt,
                    i_bufferpos, &waitingTime);
                if (i_res < 0)
                { /* error returned */
                    return i_res;
                }
                WebRtcNetEQ_StoreWaitingTime(inst, waitingTime);
                *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,
        &inst->codec_DB_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;
    }

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

#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 the frame size, if we can. */
    inst->PacketBuffer_inst.packSizeSamples =
        WebRtcNetEQ_UpdatePackSizeSamples(inst, i_bufferpos, payloadType,
            inst->PacketBuffer_inst.packSizeSamples);
    /* 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
    {
        /* reset counter */
        inst->NoOfExpandCalls = 0;
    }

    /* New codec or big change in packet number? */
    if ((inst->new_codec) || (uw16_instr == BUFSTAT_REINIT))
    {
        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);
                /* TODO(tlegrand): Remove this limitation once ACM has full
                 * 48 kHz support. */
                if (tempFs > 32000)
                {
                    inst->fs = 32000;
                }
                else 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 =
            WebRtcNetEQ_UpdatePackSizeSamples(inst, i_bufferpos, payloadType,
                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;

        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;
        }

        /*
         * 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
        {
            int waitingTime;
            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, &waitingTime);

            if (i_res < 0)
            {
                /* error returned */
                return i_res;
            }
            WebRtcNetEQ_StoreWaitingTime(inst, waitingTime);

#ifdef NETEQ_DELAY_LOGGING
            temp_var = NETEQ_DELAY_LOGGING_SIGNAL_DECODE;
            if ((fwrite(&temp_var, sizeof(int),
                        1, delay_fid2) != 1) ||
                (fwrite(&temp_pkt.timeStamp, sizeof(WebRtc_UWord32),
                        1, delay_fid2) != 1) ||
                (fwrite(&dspInfo.samplesLeft, sizeof(WebRtc_UWord16),
                        1, delay_fid2) != 1)) {
              return -1;
            }
#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];
            }
            /* Update the frame size, if we can. */
            inst->PacketBuffer_inst.packSizeSamples =
                WebRtcNetEQ_UpdatePackSizeSamples(inst, i_bufferpos,
                    payloadType, inst->PacketBuffer_inst.packSizeSamples);
        }
        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;

}
