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

/*
 * Implementation of RecOut function, which is the main function for the audio output
 * process. This function must be called (through the NetEQ API) once every 10 ms.
 */

#include "dsp.h"

#include <assert.h>
#include <string.h> /* to define NULL */

#include "signal_processing_library.h"

#include "dsp_helpfunctions.h"
#include "neteq_error_codes.h"
#include "neteq_defines.h"
#include "mcu_dsp_common.h"

/* Audio types */
#define TYPE_SPEECH 1
#define TYPE_CNG 2

#ifdef NETEQ_DELAY_LOGGING
#include "delay_logging.h"
#include <stdio.h>
#pragma message("*******************************************************************")
#pragma message("You have specified to use NETEQ_DELAY_LOGGING in the NetEQ library.")
#pragma message("Make sure that your test application supports this.")
#pragma message("*******************************************************************")
#endif

/* Scratch usage:

 Type           Name                            size             startpos      endpos
 WebRtc_Word16  pw16_NetEqAlgorithm_buffer      600*fs/8000      0             600*fs/8000-1
 struct         dspInfo                         6                600*fs/8000   605*fs/8000

 func           WebRtcNetEQ_Normal              40+495*fs/8000   0             39+495*fs/8000
 func           WebRtcNetEQ_Merge               40+496*fs/8000   0             39+496*fs/8000
 func           WebRtcNetEQ_Expand              40+370*fs/8000   126*fs/800    39+496*fs/8000
 func           WebRtcNetEQ_Accelerate          210              240*fs/8000   209+240*fs/8000
 func           WebRtcNetEQ_BGNUpdate           69               480*fs/8000   68+480*fs/8000

 Total:  605*fs/8000
 */

#define SCRATCH_ALGORITHM_BUFFER            0
#define SCRATCH_NETEQ_NORMAL                0
#define SCRATCH_NETEQ_MERGE                 0

#if (defined(NETEQ_48KHZ_WIDEBAND)) 
#define SCRATCH_DSP_INFO                     3600
#define SCRATCH_NETEQ_ACCELERATE            1440
#define SCRATCH_NETEQ_BGN_UPDATE            2880
#define SCRATCH_NETEQ_EXPAND                756
#elif (defined(NETEQ_32KHZ_WIDEBAND)) 
#define SCRATCH_DSP_INFO                     2400
#define SCRATCH_NETEQ_ACCELERATE            960
#define SCRATCH_NETEQ_BGN_UPDATE            1920
#define SCRATCH_NETEQ_EXPAND                504
#elif (defined(NETEQ_WIDEBAND)) 
#define SCRATCH_DSP_INFO                     1200
#define SCRATCH_NETEQ_ACCELERATE            480
#define SCRATCH_NETEQ_BGN_UPDATE            960
#define SCRATCH_NETEQ_EXPAND                252
#else    /* NB */
#define SCRATCH_DSP_INFO                     600
#define SCRATCH_NETEQ_ACCELERATE            240
#define SCRATCH_NETEQ_BGN_UPDATE            480
#define SCRATCH_NETEQ_EXPAND                126
#endif

#if (defined(NETEQ_48KHZ_WIDEBAND)) 
#define SIZE_SCRATCH_BUFFER                 3636
#elif (defined(NETEQ_32KHZ_WIDEBAND)) 
#define SIZE_SCRATCH_BUFFER                 2424
#elif (defined(NETEQ_WIDEBAND)) 
#define SIZE_SCRATCH_BUFFER                 1212
#else    /* NB */
#define SIZE_SCRATCH_BUFFER                 606
#endif

#ifdef NETEQ_DELAY_LOGGING
extern FILE *delay_fid2; /* file pointer to delay log file */
extern WebRtc_UWord32 tot_received_packets;
#endif


int WebRtcNetEQ_RecOutInternal(DSPInst_t *inst, WebRtc_Word16 *pw16_outData,
                               WebRtc_Word16 *pw16_len, WebRtc_Word16 BGNonly)
{

    WebRtc_Word16 blockLen, payloadLen, len = 0, pos;
    WebRtc_Word16 w16_tmp1, w16_tmp2, w16_tmp3, DataEnough;
    WebRtc_Word16 *blockPtr;
    WebRtc_Word16 MD = 0;

    WebRtc_Word16 speechType = TYPE_SPEECH;
    WebRtc_UWord16 instr;
    WebRtc_UWord16 uw16_tmp;
#ifdef SCRATCH
    char pw8_ScratchBuffer[((SIZE_SCRATCH_BUFFER + 1) * 2)];
    WebRtc_Word16 *pw16_scratchPtr = (WebRtc_Word16*) pw8_ScratchBuffer;
    WebRtc_Word16 pw16_decoded_buffer[NETEQ_MAX_FRAME_SIZE];
    WebRtc_Word16 *pw16_NetEqAlgorithm_buffer = pw16_scratchPtr
        + SCRATCH_ALGORITHM_BUFFER;
    DSP2MCU_info_t *dspInfo = (DSP2MCU_info_t*) (pw16_scratchPtr + SCRATCH_DSP_INFO);
#else
    WebRtc_Word16 pw16_decoded_buffer[NETEQ_MAX_FRAME_SIZE];
    WebRtc_Word16 pw16_NetEqAlgorithm_buffer[NETEQ_MAX_OUTPUT_SIZE];
    DSP2MCU_info_t dspInfoStruct;
    DSP2MCU_info_t *dspInfo = &dspInfoStruct;
#endif
    WebRtc_Word16 fs_mult;
    int borrowedSamples;
    int oldBorrowedSamples;
    int return_value = 0;
    WebRtc_Word16 lastModeBGNonly = (inst->w16_mode & MODE_BGN_ONLY) != 0; /* check BGN flag */
    void *mainInstBackup = inst->main_inst;

#ifdef NETEQ_DELAY_LOGGING
    int temp_var;
#endif
    WebRtc_Word16 dtmfValue = -1;
    WebRtc_Word16 dtmfVolume = -1;
    int playDtmf = 0;
#ifdef NETEQ_ATEVENT_DECODE
    int dtmfSwitch = 0;
#endif
#ifdef NETEQ_STEREO
    MasterSlaveInfo *msInfo = inst->msInfo;
#endif
    WebRtc_Word16 *sharedMem = pw16_NetEqAlgorithm_buffer; /* Reuse memory SHARED_MEM_SIZE size */
    inst->pw16_readAddress = sharedMem;
    inst->pw16_writeAddress = sharedMem;

    /* Get information about if there is one descriptor left */
    if (inst->codec_ptr_inst.funcGetMDinfo != NULL)
    {
        MD = inst->codec_ptr_inst.funcGetMDinfo(inst->codec_ptr_inst.codec_state);
        if (MD > 0)
            MD = 1;
        else
            MD = 0;
    }

#ifdef NETEQ_STEREO
    if ((msInfo->msMode == NETEQ_SLAVE) && (inst->codec_ptr_inst.funcDecode != NULL))
    {
        /*
         * Valid function pointers indicate that we have decoded something,
         * and that the timestamp information is correct.
         */

        /* Get the information from master to correct synchronization */
        WebRtc_UWord32 currentMasterTimestamp;
        WebRtc_UWord32 currentSlaveTimestamp;

        currentMasterTimestamp = msInfo->endTimestamp - msInfo->samplesLeftWithOverlap;
        currentSlaveTimestamp = inst->endTimestamp - (inst->endPosition - inst->curPosition);

        if (currentSlaveTimestamp < currentMasterTimestamp)
        {
            /* brute-force discard a number of samples to catch up */
            inst->curPosition += currentMasterTimestamp - currentSlaveTimestamp;

            /* make sure we have at least "overlap" samples left */
            inst->curPosition = WEBRTC_SPL_MIN(inst->curPosition,
                inst->endPosition - inst->ExpandInst.w16_overlap);
        }
        else if (currentSlaveTimestamp > currentMasterTimestamp)
        {
            /* back off current position to slow down */
            inst->curPosition -= currentSlaveTimestamp - currentMasterTimestamp;

            /* make sure we do not end up outside the speech history */
            inst->curPosition = WEBRTC_SPL_MAX(inst->curPosition, 0);
        }
    }
#endif

    /* Write status data to shared memory */
    dspInfo->playedOutTS = inst->endTimestamp;
    dspInfo->samplesLeft = inst->endPosition - inst->curPosition
        - inst->ExpandInst.w16_overlap;
    dspInfo->MD = MD;
    dspInfo->lastMode = inst->w16_mode;
    dspInfo->frameLen = inst->w16_frameLen;

    /* Force update of codec if codec function is NULL */
    if (inst->codec_ptr_inst.funcDecode == NULL)
    {
        dspInfo->lastMode |= MODE_AWAITING_CODEC_PTR;
    }

#ifdef NETEQ_STEREO
    if (msInfo->msMode == NETEQ_SLAVE && (msInfo->extraInfo == DTMF_OVERDUB
        || msInfo->extraInfo == DTMF_ONLY))
    {
        /* Signal that the master instance generated DTMF tones */
        dspInfo->lastMode |= MODE_MASTER_DTMF_SIGNAL;
    }

    if (msInfo->msMode != NETEQ_MONO)
    {
        /* We are using stereo mode; signal this to MCU side */
        dspInfo->lastMode |= MODE_USING_STEREO;
    }
#endif

    WEBRTC_SPL_MEMCPY_W8(inst->pw16_writeAddress,dspInfo,sizeof(DSP2MCU_info_t));

    /* Signal MCU with "interrupt" call to main inst*/
#ifdef NETEQ_STEREO
    assert(msInfo != NULL);
    if (msInfo->msMode == NETEQ_MASTER)
    {
        /* clear info to slave */
        WebRtcSpl_MemSetW16((WebRtc_Word16 *) msInfo, 0,
            sizeof(MasterSlaveInfo) / sizeof(WebRtc_Word16));
        /* re-set mode */
        msInfo->msMode = NETEQ_MASTER;

        /* Store some information to slave */
        msInfo->endTimestamp = inst->endTimestamp;
        msInfo->samplesLeftWithOverlap = inst->endPosition - inst->curPosition;
    }
#endif

    /*
     * This call will trigger the MCU side to make a decision based on buffer contents and
     * decision history. Instructions, encoded data and function pointers will be written
     * to the shared memory.
     */
    return_value = WebRtcNetEQ_DSP2MCUinterrupt((MainInst_t *) inst->main_inst, sharedMem);

    /* Read MCU data and instructions */
    instr = (WebRtc_UWord16) (inst->pw16_readAddress[0] & 0xf000);

#ifdef NETEQ_STEREO
    if (msInfo->msMode == NETEQ_MASTER)
    {
        msInfo->instruction = instr;
    }
    else if (msInfo->msMode == NETEQ_SLAVE)
    {
        /* Nothing to do */
    }
#endif

    /* check for error returned from MCU side, if so, return error */
    if (return_value < 0)
    {
        inst->w16_mode = MODE_ERROR;
        dspInfo->lastMode = MODE_ERROR;
        return return_value;
    }

    blockPtr = &((inst->pw16_readAddress)[3]);

    /* Check for DTMF payload flag */
    if ((inst->pw16_readAddress[0] & DSP_DTMF_PAYLOAD) != 0)
    {
        playDtmf = 1;
        dtmfValue = blockPtr[1];
        dtmfVolume = blockPtr[2];
        blockPtr += 3;

#ifdef NETEQ_STEREO
        if (msInfo->msMode == NETEQ_MASTER)
        {
            /* signal to slave that master is using DTMF */
            msInfo->extraInfo = DTMF_OVERDUB;
        }
#endif
    }

    blockLen = (((*blockPtr) & DSP_CODEC_MASK_RED_FLAG) + 1) >> 1; /* In # of WebRtc_Word16 */
    payloadLen = ((*blockPtr) & DSP_CODEC_MASK_RED_FLAG);
    blockPtr++;

    /* Do we have to change our decoder? */
    if ((inst->pw16_readAddress[0] & 0x0f00) == DSP_CODEC_NEW_CODEC)
    {
        WEBRTC_SPL_MEMCPY_W16(&inst->codec_ptr_inst,blockPtr,(payloadLen+1)>>1);
        if (inst->codec_ptr_inst.codec_fs != 0)
        {
            return_value = WebRtcNetEQ_DSPInit(inst, inst->codec_ptr_inst.codec_fs);
            if (return_value != 0)
            { /* error returned */
                instr = DSP_INSTR_FADE_TO_BGN; /* emergency instruction */
            }
#ifdef NETEQ_DELAY_LOGGING
            temp_var = NETEQ_DELAY_LOGGING_SIGNAL_CHANGE_FS;
            fwrite(&temp_var, sizeof(int), 1, delay_fid2);
            fwrite(&inst->fs, sizeof(WebRtc_UWord16), 1, delay_fid2);
#endif
        }

        /* Copy it again since the init destroys this part */

        WEBRTC_SPL_MEMCPY_W16(&inst->codec_ptr_inst,blockPtr,(payloadLen+1)>>1);
        inst->endTimestamp = inst->codec_ptr_inst.timeStamp;
        inst->videoSyncTimestamp = inst->codec_ptr_inst.timeStamp;
        blockPtr += blockLen;
        blockLen = (((*blockPtr) & DSP_CODEC_MASK_RED_FLAG) + 1) >> 1;
        payloadLen = ((*blockPtr) & DSP_CODEC_MASK_RED_FLAG);
        blockPtr++;
        if (inst->codec_ptr_inst.funcDecodeInit != NULL)
        {
            inst->codec_ptr_inst.funcDecodeInit(inst->codec_ptr_inst.codec_state);
        }

#ifdef NETEQ_CNG_CODEC

        /* Also update the CNG state as this might be uninitialized */

        WEBRTC_SPL_MEMCPY_W16(&inst->CNG_Codec_inst,blockPtr,(payloadLen+1)>>1);
        blockPtr += blockLen;
        blockLen = (((*blockPtr) & DSP_CODEC_MASK_RED_FLAG) + 1) >> 1;
        payloadLen = ((*blockPtr) & DSP_CODEC_MASK_RED_FLAG);
        blockPtr++;
        if (inst->CNG_Codec_inst != NULL)
        {
            WebRtcCng_InitDec(inst->CNG_Codec_inst);
        }
#endif
    }
    else if ((inst->pw16_readAddress[0] & 0x0f00) == DSP_CODEC_RESET)
    {
        /* Reset the current codec (but not DSP struct) */
        if (inst->codec_ptr_inst.funcDecodeInit != NULL)
        {
            inst->codec_ptr_inst.funcDecodeInit(inst->codec_ptr_inst.codec_state);
        }

#ifdef NETEQ_CNG_CODEC
        /* And reset CNG */
        if (inst->CNG_Codec_inst != NULL)
        {
            WebRtcCng_InitDec(inst->CNG_Codec_inst);
        }
#endif /*NETEQ_CNG_CODEC*/
    }

    fs_mult = WebRtcNetEQ_CalcFsMult(inst->fs);

    /* Add late packet? */
    if ((inst->pw16_readAddress[0] & 0x0f00) == DSP_CODEC_ADD_LATE_PKT)
    {
        if (inst->codec_ptr_inst.funcAddLatePkt != NULL)
        {
            /* Only do this if the codec has support for Add Late Pkt */
            inst->codec_ptr_inst.funcAddLatePkt(inst->codec_ptr_inst.codec_state, blockPtr,
                payloadLen);
        }
        blockPtr += blockLen;
        blockLen = (((*blockPtr) & DSP_CODEC_MASK_RED_FLAG) + 1) >> 1; /* In # of Word16 */
        payloadLen = ((*blockPtr) & DSP_CODEC_MASK_RED_FLAG);
        blockPtr++;
    }

    /* Do we have to decode data? */
    if ((instr == DSP_INSTR_NORMAL) || (instr == DSP_INSTR_ACCELERATE) || (instr
        == DSP_INSTR_MERGE) || (instr == DSP_INSTR_PREEMPTIVE_EXPAND))
    {
        /* Do we need to update codec-internal PLC state? */
        if ((instr == DSP_INSTR_MERGE) && (inst->codec_ptr_inst.funcDecodePLC != NULL))
        {
            len = 0;
            len = inst->codec_ptr_inst.funcDecodePLC(inst->codec_ptr_inst.codec_state,
                &pw16_decoded_buffer[len], 1);
        }
        len = 0;

        /* Do decoding */
        while ((blockLen > 0) && (len < (240 * fs_mult))) /* Guard somewhat against overflow */
        {
            if (inst->codec_ptr_inst.funcDecode != NULL)
            {
                WebRtc_Word16 dec_Len;
                if (!BGNonly)
                {
                    /* Do decoding as normal
                     *
                     * blockPtr is pointing to payload, at this point,
                     * the most significant bit of *(blockPtr - 1) is a flag if set to 1
                     * indicates that the following payload is the redundant payload.
                     */
                    if (((*(blockPtr - 1) & DSP_CODEC_RED_FLAG) != 0)
                        && (inst->codec_ptr_inst.funcDecodeRCU != NULL))
                    {
                        dec_Len = inst->codec_ptr_inst.funcDecodeRCU(
                            inst->codec_ptr_inst.codec_state, blockPtr, payloadLen,
                            &pw16_decoded_buffer[len], &speechType);
                    }
                    else
                    {
                        dec_Len = inst->codec_ptr_inst.funcDecode(
                            inst->codec_ptr_inst.codec_state, blockPtr, payloadLen,
                            &pw16_decoded_buffer[len], &speechType);
                    }
                }
                else
                {
                    /*
                     * Background noise mode: don't decode, just produce the same length BGN.
                     * Don't call Expand for BGN here, since Expand uses the memory where the
                     * bitstreams are stored (sharemem).
                     */
                    dec_Len = inst->w16_frameLen;
                }

                if (dec_Len > 0)
                {
                    len += dec_Len;
                    /* Update frameLen */
                    inst->w16_frameLen = dec_Len;
                }
                else if (dec_Len < 0)
                {
                    /* Error */
                    len = -1;
                    break;
                }
                /*
                 * Sanity check (although we might still write outside memory when this
                 * happens...)
                 */
                if (len > NETEQ_MAX_FRAME_SIZE)
                {
                    WebRtcSpl_MemSetW16(pw16_outData, 0, inst->timestampsPerCall);
                    *pw16_len = inst->timestampsPerCall;
                    inst->w16_mode = MODE_ERROR;
                    dspInfo->lastMode = MODE_ERROR;
                    return RECOUT_ERROR_DECODED_TOO_MUCH;
                }

                /* Verify that instance was not corrupted by decoder */
                if (mainInstBackup != inst->main_inst)
                {
                    /* Instance is corrupt */
                    return CORRUPT_INSTANCE;
                }

            }
            blockPtr += blockLen;
            blockLen = (((*blockPtr) & DSP_CODEC_MASK_RED_FLAG) + 1) >> 1; /* In # of Word16 */
            payloadLen = ((*blockPtr) & DSP_CODEC_MASK_RED_FLAG);
            blockPtr++;
        }

        if (len < 0)
        {
            len = 0;
            inst->endTimestamp += inst->w16_frameLen; /* advance one frame */
            if (inst->codec_ptr_inst.funcGetErrorCode != NULL)
            {
                return_value = -inst->codec_ptr_inst.funcGetErrorCode(
                    inst->codec_ptr_inst.codec_state);
            }
            else
            {
                return_value = RECOUT_ERROR_DECODING;
            }
            instr = DSP_INSTR_FADE_TO_BGN;
        }
        if (speechType != TYPE_CNG)
        {
            /*
             * Don't increment timestamp if codec returned CNG speech type
             * since in this case, the MCU side will increment the CNGplayedTS counter.
             */
            inst->endTimestamp += len;
        }
    }
    else if (instr == DSP_INSTR_NORMAL_ONE_DESC)
    {
        if (inst->codec_ptr_inst.funcDecode != NULL)
        {
            len = inst->codec_ptr_inst.funcDecode(inst->codec_ptr_inst.codec_state, NULL, 0,
                pw16_decoded_buffer, &speechType);
#ifdef NETEQ_DELAY_LOGGING
            temp_var = NETEQ_DELAY_LOGGING_SIGNAL_DECODE_ONE_DESC;
            fwrite(&temp_var, sizeof(int), 1, delay_fid2);
            fwrite(&inst->endTimestamp, sizeof(WebRtc_UWord32), 1, delay_fid2);
            fwrite(&dspInfo->samplesLeft, sizeof(WebRtc_UWord16), 1, delay_fid2);
            tot_received_packets++;
#endif
        }
        if (speechType != TYPE_CNG)
        {
            /*
             * Don't increment timestamp if codec returned CNG speech type
             * since in this case, the MCU side will increment the CNGplayedTS counter.
             */
            inst->endTimestamp += len;
        }

        /* Verify that instance was not corrupted by decoder */
        if (mainInstBackup != inst->main_inst)
        {
            /* Instance is corrupt */
            return CORRUPT_INSTANCE;
        }

        if (len <= 0)
        {
            len = 0;
            if (inst->codec_ptr_inst.funcGetErrorCode != NULL)
            {
                return_value = -inst->codec_ptr_inst.funcGetErrorCode(
                    inst->codec_ptr_inst.codec_state);
            }
            else
            {
                return_value = RECOUT_ERROR_DECODING;
            }
            if ((inst->codec_ptr_inst.funcDecodeInit != NULL)
                && (inst->codec_ptr_inst.codec_state != NULL))
            {
                /* Reinitialize codec state as something is obviously wrong */
                inst->codec_ptr_inst.funcDecodeInit(inst->codec_ptr_inst.codec_state);
            }
            inst->endTimestamp += inst->w16_frameLen; /* advance one frame */
            instr = DSP_INSTR_FADE_TO_BGN;
        }
    }

    if (len == 0 && lastModeBGNonly) /* no new data */
    {
        BGNonly = 1; /* force BGN this time too */
    }

#ifdef NETEQ_VAD
    if ((speechType == TYPE_CNG) /* decoder responded with codec-internal CNG */
    || ((instr == DSP_INSTR_DO_RFC3389CNG) && (blockLen > 0)) /* ... or, SID frame */
    || (inst->fs > 16000)) /* ... or, if not NB or WB */
    {
        /* disable post-decode VAD upon first sign of send-side DTX/VAD active, or if SWB */
        inst->VADInst.VADEnabled = 0;
        inst->VADInst.VADDecision = 1; /* set to always active, just to be on the safe side */
        inst->VADInst.SIDintervalCounter = 0; /* reset SID interval counter */
    }
    else if (!inst->VADInst.VADEnabled) /* VAD disabled and no SID/CNG data observed this time */
    {
        inst->VADInst.SIDintervalCounter++; /* increase counter */
    }

    /* check for re-enabling the VAD */
    if (inst->VADInst.SIDintervalCounter >= POST_DECODE_VAD_AUTO_ENABLE)
    {
        /*
         * It's been a while since the last CNG/SID frame was observed => re-enable VAD.
         * (Do not care to look for a VAD instance, since this is done inside the init
         * function)
         */
        WebRtcNetEQ_InitVAD(&inst->VADInst, inst->fs);
    }

    if (len > 0 /* if we decoded any data */
    && inst->VADInst.VADEnabled /* and VAD enabled */
    && inst->fs <= 16000) /* can only do VAD for NB and WB */
    {
        int VADframeSize; /* VAD frame size in ms */
        int VADSamplePtr = 0;

        inst->VADInst.VADDecision = 0;

        if (inst->VADInst.VADFunction != NULL) /* make sure that VAD function is provided */
        {
            /* divide the data into groups, as large as possible */
            for (VADframeSize = 30; VADframeSize >= 10; VADframeSize -= 10)
            {
                /* loop through 30, 20, 10 */

                while (inst->VADInst.VADDecision == 0
                    && len - VADSamplePtr >= VADframeSize * fs_mult * 8)
                {
                    /*
                     * Only continue until first active speech found, and as long as there is
                     * one VADframeSize left.
                     */

                    /* call VAD with new decoded data */
                    inst->VADInst.VADDecision |= inst->VADInst.VADFunction(
                        inst->VADInst.VADState, (WebRtc_Word16) inst->fs,
                        (WebRtc_Word16 *) &pw16_decoded_buffer[VADSamplePtr],
                        (WebRtc_Word16) (VADframeSize * fs_mult * 8));

                    VADSamplePtr += VADframeSize * fs_mult * 8; /* increment sample counter */
                }
            }
        }
        else
        { /* VAD function is NULL */
            inst->VADInst.VADDecision = 1; /* set decision to active */
            inst->VADInst.VADEnabled = 0; /* disable VAD since we have no VAD function */
        }

    }
#endif /* NETEQ_VAD */

    /* Adjust timestamp if needed */
    uw16_tmp = (WebRtc_UWord16) inst->pw16_readAddress[1];
    inst->endTimestamp += (((WebRtc_UWord32) uw16_tmp) << 16);
    uw16_tmp = (WebRtc_UWord16) inst->pw16_readAddress[2];
    inst->endTimestamp += uw16_tmp;

    if (BGNonly && len > 0)
    {
        /*
         * If BGN mode, we did not produce any data at decoding.
         * Do it now instead.
         */

        WebRtcNetEQ_GenerateBGN(inst,
#ifdef SCRATCH
            pw16_scratchPtr + SCRATCH_NETEQ_EXPAND,
#endif
            pw16_decoded_buffer, len);
    }

    /* Switch on the instruction received from the MCU side. */
    switch (instr)
    {
        case DSP_INSTR_NORMAL:

            /* Allow for signal processing to apply gain-back etc */
            WebRtcNetEQ_Normal(inst,
#ifdef SCRATCH
                pw16_scratchPtr + SCRATCH_NETEQ_NORMAL,
#endif
                pw16_decoded_buffer, len, pw16_NetEqAlgorithm_buffer, &len);

            /* If last packet was decoded as a inband CNG set mode to CNG instead */
            if ((speechType == TYPE_CNG) || ((inst->w16_mode == MODE_CODEC_INTERNAL_CNG)
                && (len == 0)))
            {
                inst->w16_mode = MODE_CODEC_INTERNAL_CNG;
            }

#ifdef NETEQ_ATEVENT_DECODE
            if (playDtmf == 0)
            {
                inst->DTMFInst.reinit = 1;
            }
#endif
            break;
        case DSP_INSTR_NORMAL_ONE_DESC:

            /* Allow for signal processing to apply gain-back etc */
            WebRtcNetEQ_Normal(inst,
#ifdef SCRATCH
                pw16_scratchPtr + SCRATCH_NETEQ_NORMAL,
#endif
                pw16_decoded_buffer, len, pw16_NetEqAlgorithm_buffer, &len);
#ifdef NETEQ_ATEVENT_DECODE
            if (playDtmf == 0)
            {
                inst->DTMFInst.reinit = 1;
            }
#endif
            inst->w16_mode = MODE_ONE_DESCRIPTOR;
            break;
        case DSP_INSTR_MERGE:
#ifdef NETEQ_DELAY_LOGGING
            temp_var = NETEQ_DELAY_LOGGING_SIGNAL_MERGE_INFO;
            fwrite(&temp_var, sizeof(int), 1, delay_fid2);
            temp_var = -len;
#endif
            /* Call Merge with history*/
            return_value = WebRtcNetEQ_Merge(inst,
#ifdef SCRATCH
                pw16_scratchPtr + SCRATCH_NETEQ_MERGE,
#endif
                pw16_decoded_buffer, len, pw16_NetEqAlgorithm_buffer, &len);

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

#ifdef NETEQ_DELAY_LOGGING
            temp_var += len;
            fwrite(&temp_var, sizeof(int), 1, delay_fid2);
#endif
            /* If last packet was decoded as a inband CNG set mode to CNG instead */
            if (speechType == TYPE_CNG) inst->w16_mode = MODE_CODEC_INTERNAL_CNG;
#ifdef NETEQ_ATEVENT_DECODE
            if (playDtmf == 0)
            {
                inst->DTMFInst.reinit = 1;
            }
#endif
            break;

        case DSP_INSTR_EXPAND:
            len = 0;
            pos = 0;
            while ((inst->endPosition - inst->curPosition - inst->ExpandInst.w16_overlap + pos)
                < (inst->timestampsPerCall))
            {
                return_value = WebRtcNetEQ_Expand(inst,
#ifdef SCRATCH
                    pw16_scratchPtr + SCRATCH_NETEQ_EXPAND,
#endif
                    pw16_NetEqAlgorithm_buffer, &len, BGNonly);
                if (return_value < 0)
                {
                    /* error */
                    return return_value;
                }

                /*
                 * Update buffer, but only end part (otherwise expand state is destroyed
                 * since it reuses speechBuffer[] memory
                 */

                WEBRTC_SPL_MEMMOVE_W16(inst->pw16_speechHistory,
                                       inst->pw16_speechHistory + len,
                                       (inst->w16_speechHistoryLen-len));
                WEBRTC_SPL_MEMCPY_W16(&inst->pw16_speechHistory[inst->w16_speechHistoryLen-len],
                                      pw16_NetEqAlgorithm_buffer, len);

                inst->curPosition -= len;

                /* Update variables for VQmon */
                inst->w16_concealedTS += len;
#ifdef NETEQ_DELAY_LOGGING
                temp_var = NETEQ_DELAY_LOGGING_SIGNAL_EXPAND_INFO;
                fwrite(&temp_var, sizeof(int), 1, delay_fid2);
                temp_var = len;
                fwrite(&temp_var, sizeof(int), 1, delay_fid2);
#endif
                len = 0; /* already written the data, so do not write it again further down. */
            }
#ifdef NETEQ_ATEVENT_DECODE
            if (playDtmf == 0)
            {
                inst->DTMFInst.reinit = 1;
            }
#endif
            break;

        case DSP_INSTR_ACCELERATE:
            if (len < 3 * 80 * fs_mult)
            {
                /* We need to move data from the speechBuffer[] in order to get 30 ms */
                borrowedSamples = 3 * 80 * fs_mult - len;

                WEBRTC_SPL_MEMMOVE_W16(&pw16_decoded_buffer[borrowedSamples],
                                       pw16_decoded_buffer, len);
                WEBRTC_SPL_MEMCPY_W16(pw16_decoded_buffer,
                                      &(inst->speechBuffer[inst->endPosition-borrowedSamples]),
                                      borrowedSamples);

                return_value = WebRtcNetEQ_Accelerate(inst,
#ifdef SCRATCH
                    pw16_scratchPtr + SCRATCH_NETEQ_ACCELERATE,
#endif
                    pw16_decoded_buffer, 3 * inst->timestampsPerCall,
                    pw16_NetEqAlgorithm_buffer, &len, BGNonly);

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

                /* Copy back samples to the buffer */
                if (len < borrowedSamples)
                {
                    /*
                     * This destroys the beginning of the buffer, but will not cause any
                     * problems
                     */

                    WEBRTC_SPL_MEMCPY_W16(&inst->speechBuffer[inst->endPosition-borrowedSamples],
                        pw16_NetEqAlgorithm_buffer, len);
                    WEBRTC_SPL_MEMMOVE_W16(&inst->speechBuffer[borrowedSamples-len],
                                           inst->speechBuffer,
                                           (inst->endPosition-(borrowedSamples-len)));

                    inst->curPosition += (borrowedSamples - len);
#ifdef NETEQ_DELAY_LOGGING
                    temp_var = NETEQ_DELAY_LOGGING_SIGNAL_ACCELERATE_INFO;
                    fwrite(&temp_var, sizeof(int), 1, delay_fid2);
                    temp_var = 3 * inst->timestampsPerCall - len;
                    fwrite(&temp_var, sizeof(int), 1, delay_fid2);
#endif
                    len = 0;
                }
                else
                {
                    WEBRTC_SPL_MEMCPY_W16(&inst->speechBuffer[inst->endPosition-borrowedSamples],
                        pw16_NetEqAlgorithm_buffer, borrowedSamples);
                    WEBRTC_SPL_MEMMOVE_W16(pw16_NetEqAlgorithm_buffer,
                                           &pw16_NetEqAlgorithm_buffer[borrowedSamples],
                                           (len-borrowedSamples));
#ifdef NETEQ_DELAY_LOGGING
                    temp_var = NETEQ_DELAY_LOGGING_SIGNAL_ACCELERATE_INFO;
                    fwrite(&temp_var, sizeof(int), 1, delay_fid2);
                    temp_var = 3 * inst->timestampsPerCall - len;
                    fwrite(&temp_var, sizeof(int), 1, delay_fid2);
#endif
                    len = len - borrowedSamples;
                }

            }
            else
            {
#ifdef NETEQ_DELAY_LOGGING
                temp_var = NETEQ_DELAY_LOGGING_SIGNAL_ACCELERATE_INFO;
                fwrite(&temp_var, sizeof(int), 1, delay_fid2);
                temp_var = len;
#endif
                return_value = WebRtcNetEQ_Accelerate(inst,
#ifdef SCRATCH
                    pw16_scratchPtr + SCRATCH_NETEQ_ACCELERATE,
#endif
                    pw16_decoded_buffer, len, pw16_NetEqAlgorithm_buffer, &len, BGNonly);

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

#ifdef NETEQ_DELAY_LOGGING
                temp_var -= len;
                fwrite(&temp_var, sizeof(int), 1, delay_fid2);
#endif
            }
            /* If last packet was decoded as a inband CNG set mode to CNG instead */
            if (speechType == TYPE_CNG) inst->w16_mode = MODE_CODEC_INTERNAL_CNG;
#ifdef NETEQ_ATEVENT_DECODE
            if (playDtmf == 0)
            {
                inst->DTMFInst.reinit = 1;
            }
#endif
            break;

        case DSP_INSTR_DO_RFC3389CNG:
#ifdef NETEQ_CNG_CODEC
            if (blockLen > 0)
            {
                if (WebRtcCng_UpdateSid(inst->CNG_Codec_inst, (WebRtc_UWord8*) blockPtr,
                    payloadLen) < 0)
                {
                    /* error returned from CNG function */
                    return_value = -WebRtcCng_GetErrorCodeDec(inst->CNG_Codec_inst);
                    len = inst->timestampsPerCall;
                    WebRtcSpl_MemSetW16(pw16_NetEqAlgorithm_buffer, 0, len);
                    break;
                }
            }

            if (BGNonly)
            {
                /* Get data from BGN function instead of CNG */
                len = WebRtcNetEQ_GenerateBGN(inst,
#ifdef SCRATCH
                    pw16_scratchPtr + SCRATCH_NETEQ_EXPAND,
#endif
                    pw16_NetEqAlgorithm_buffer, inst->timestampsPerCall);
                if (len != inst->timestampsPerCall)
                {
                    /* this is not good, treat this as an error */
                    return_value = -1;
                }
            }
            else
            {
                return_value = WebRtcNetEQ_Cng(inst, pw16_NetEqAlgorithm_buffer,
                    inst->timestampsPerCall);
            }
            len = inst->timestampsPerCall;
            inst->ExpandInst.w16_consecExp = 0;
            inst->w16_mode = MODE_RFC3389CNG;
#ifdef NETEQ_ATEVENT_DECODE
            if (playDtmf == 0)
            {
                inst->DTMFInst.reinit = 1;
            }
#endif

            if (return_value < 0)
            {
                /* error returned */
                WebRtcSpl_MemSetW16(pw16_NetEqAlgorithm_buffer, 0, len);
            }

            break;
#else
            return FAULTY_INSTRUCTION;
#endif
        case DSP_INSTR_DO_CODEC_INTERNAL_CNG:
            /*
             * This represents the case when there is no transmission and the decoder should
             * do internal CNG.
             */
            len = 0;
            if (inst->codec_ptr_inst.funcDecode != NULL && !BGNonly)
            {
                len = inst->codec_ptr_inst.funcDecode(inst->codec_ptr_inst.codec_state,
                    blockPtr, 0, pw16_decoded_buffer, &speechType);
            }
            else
            {
                /* get BGN data */
                len = WebRtcNetEQ_GenerateBGN(inst,
#ifdef SCRATCH
                    pw16_scratchPtr + SCRATCH_NETEQ_EXPAND,
#endif
                    pw16_decoded_buffer, inst->timestampsPerCall);
            }
            WebRtcNetEQ_Normal(inst,
#ifdef SCRATCH
                pw16_scratchPtr + SCRATCH_NETEQ_NORMAL,
#endif
                pw16_decoded_buffer, len, pw16_NetEqAlgorithm_buffer, &len);
            inst->w16_mode = MODE_CODEC_INTERNAL_CNG;
            inst->ExpandInst.w16_consecExp = 0;
            break;

        case DSP_INSTR_DTMF_GENERATE:
#ifdef NETEQ_ATEVENT_DECODE
            dtmfSwitch = 0;
            if ((inst->w16_mode != MODE_DTMF) && (inst->DTMFInst.reinit == 0))
            {
                /* Special case; see below.
                 * We must catch this before calling DTMFGenerate,
                 * since reinit is set to 0 in that call.
                 */
                dtmfSwitch = 1;
            }

            len = WebRtcNetEQ_DTMFGenerate(&inst->DTMFInst, dtmfValue, dtmfVolume,
                pw16_NetEqAlgorithm_buffer, inst->fs, -1);
            if (len < 0)
            {
                /* error occurred */
                return_value = len;
                len = inst->timestampsPerCall;
                WebRtcSpl_MemSetW16(pw16_NetEqAlgorithm_buffer, 0, len);
            }

            if (dtmfSwitch == 1)
            {
                /*
                 * This is the special case where the previous operation was DTMF overdub.
                 * but the current instruction is "regular" DTMF. We must make sure that the
                 * DTMF does not have any discontinuities. The first DTMF sample that we
                 * generate now must be played out immediately, wherefore it must be copied to
                 * the speech buffer.
                 */

                /*
                 * Generate extra DTMF data to fill the space between
                 * curPosition and endPosition
                 */
                WebRtc_Word16 tempLen;

                tempLen = WebRtcNetEQ_DTMFGenerate(&inst->DTMFInst, dtmfValue, dtmfVolume,
                    &pw16_NetEqAlgorithm_buffer[len], inst->fs,
                    inst->endPosition - inst->curPosition);
                if (tempLen < 0)
                {
                    /* error occurred */
                    return_value = tempLen;
                    len = inst->endPosition - inst->curPosition;
                    WebRtcSpl_MemSetW16(pw16_NetEqAlgorithm_buffer, 0,
                        inst->endPosition - inst->curPosition);
                }

                /* Add to total length */
                len += tempLen;

                /* Overwrite the "future" part of the speech buffer with the new DTMF data */

                WEBRTC_SPL_MEMCPY_W16(&inst->speechBuffer[inst->curPosition],
                                      pw16_NetEqAlgorithm_buffer,
                                      inst->endPosition - inst->curPosition);

                /* Shuffle the remaining data to the beginning of algorithm buffer */
                len -= (inst->endPosition - inst->curPosition);
                WEBRTC_SPL_MEMMOVE_W16(pw16_NetEqAlgorithm_buffer,
                    &pw16_NetEqAlgorithm_buffer[inst->endPosition - inst->curPosition],
                    len);
            }

            inst->endTimestamp += inst->timestampsPerCall;
            inst->DTMFInst.reinit = 0;
            inst->ExpandInst.w16_consecExp = 0;
            inst->w16_mode = MODE_DTMF;
            BGNonly = 0; /* override BGN only and let DTMF through */

            playDtmf = 0; /* set to zero because the DTMF is already in the Algorithm buffer */
            /*
             * If playDtmf is 1, an extra DTMF vector will be generated and overdubbed
             * on the output.
             */

#ifdef NETEQ_STEREO
            if (msInfo->msMode == NETEQ_MASTER)
            {
                /* signal to slave that master is using DTMF only */
                msInfo->extraInfo = DTMF_ONLY;
            }
#endif

            break;
#else
            inst->w16_mode = MODE_ERROR;
            dspInfo->lastMode = MODE_ERROR;
            return FAULTY_INSTRUCTION;
#endif

        case DSP_INSTR_DO_ALTERNATIVE_PLC:
            if (inst->codec_ptr_inst.funcDecodePLC != 0)
            {
                len = inst->codec_ptr_inst.funcDecodePLC(inst->codec_ptr_inst.codec_state,
                    pw16_NetEqAlgorithm_buffer, 1);
            }
            else
            {
                len = inst->timestampsPerCall;
                /* ZeroStuffing... */
                WebRtcSpl_MemSetW16(pw16_NetEqAlgorithm_buffer, 0, len);
            }
            inst->ExpandInst.w16_consecExp = 0;
            break;
        case DSP_INSTR_DO_ALTERNATIVE_PLC_INC_TS:
            if (inst->codec_ptr_inst.funcDecodePLC != 0)
            {
                len = inst->codec_ptr_inst.funcDecodePLC(inst->codec_ptr_inst.codec_state,
                    pw16_NetEqAlgorithm_buffer, 1);
            }
            else
            {
                len = inst->timestampsPerCall;
                /* ZeroStuffing... */
                WebRtcSpl_MemSetW16(pw16_NetEqAlgorithm_buffer, 0, len);
            }
            inst->ExpandInst.w16_consecExp = 0;
            inst->endTimestamp += len;
            break;
        case DSP_INSTR_DO_AUDIO_REPETITION:
            len = inst->timestampsPerCall;
            /* copy->paste... */
            WEBRTC_SPL_MEMCPY_W16(pw16_NetEqAlgorithm_buffer,
                                  &inst->speechBuffer[inst->endPosition-len], len);
            inst->ExpandInst.w16_consecExp = 0;
            break;
        case DSP_INSTR_DO_AUDIO_REPETITION_INC_TS:
            len = inst->timestampsPerCall;
            /* copy->paste... */
            WEBRTC_SPL_MEMCPY_W16(pw16_NetEqAlgorithm_buffer,
                                  &inst->speechBuffer[inst->endPosition-len], len);
            inst->ExpandInst.w16_consecExp = 0;
            inst->endTimestamp += len;
            break;

        case DSP_INSTR_PREEMPTIVE_EXPAND:
            if (len < 3 * inst->timestampsPerCall)
            {
                /* borrow samples from sync buffer if necessary */
                borrowedSamples = 3 * inst->timestampsPerCall - len; /* borrow this many samples */
                /* calculate how many of these are already played out */
                oldBorrowedSamples = WEBRTC_SPL_MAX(0,
                    borrowedSamples - (inst->endPosition - inst->curPosition));
                WEBRTC_SPL_MEMMOVE_W16(&pw16_decoded_buffer[borrowedSamples],
                                       pw16_decoded_buffer, len);
                WEBRTC_SPL_MEMCPY_W16(pw16_decoded_buffer,
                                      &(inst->speechBuffer[inst->endPosition-borrowedSamples]),
                                      borrowedSamples);
            }
            else
            {
                borrowedSamples = 0;
                oldBorrowedSamples = 0;
            }

#ifdef NETEQ_DELAY_LOGGING
            w16_tmp1 = len;
#endif
            /* do the expand */
            return_value = WebRtcNetEQ_PreEmptiveExpand(inst,
#ifdef SCRATCH
                /* use same scratch memory as Accelerate */
                pw16_scratchPtr + SCRATCH_NETEQ_ACCELERATE,
#endif
                pw16_decoded_buffer, len + borrowedSamples, oldBorrowedSamples,
                pw16_NetEqAlgorithm_buffer, &len, BGNonly);

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

            if (borrowedSamples > 0)
            {
                /* return borrowed samples */

                /* Copy back to last part of speechBuffer from beginning of output buffer */
                WEBRTC_SPL_MEMCPY_W16( &(inst->speechBuffer[inst->endPosition-borrowedSamples]),
                    pw16_NetEqAlgorithm_buffer,
                    borrowedSamples);

                len -= borrowedSamples; /* remove the borrowed samples from new total length */

                /* Move to beginning of output buffer from end of output buffer */
                WEBRTC_SPL_MEMMOVE_W16( pw16_NetEqAlgorithm_buffer,
                    &pw16_NetEqAlgorithm_buffer[borrowedSamples],
                    len);
            }

#ifdef NETEQ_DELAY_LOGGING
            temp_var = NETEQ_DELAY_LOGGING_SIGNAL_PREEMPTIVE_INFO;
            fwrite(&temp_var, sizeof(int), 1, delay_fid2);
            temp_var = len - w16_tmp1; /* number of samples added */
            fwrite(&temp_var, sizeof(int), 1, delay_fid2);
#endif
            /* If last packet was decoded as inband CNG, set mode to CNG instead */
            if (speechType == TYPE_CNG) inst->w16_mode = MODE_CODEC_INTERNAL_CNG;
#ifdef NETEQ_ATEVENT_DECODE
            if (playDtmf == 0)
            {
                inst->DTMFInst.reinit = 1;
            }
#endif
            break;

        case DSP_INSTR_FADE_TO_BGN:
        {
            int tempReturnValue;
            /* do not overwrite return_value, since it likely contains an error code */

            /* calculate interpolation length */
            w16_tmp3 = WEBRTC_SPL_MIN(inst->endPosition - inst->curPosition,
                    inst->timestampsPerCall);
            /* check that it will fit in pw16_NetEqAlgorithm_buffer */
            if (w16_tmp3 + inst->w16_frameLen > NETEQ_MAX_OUTPUT_SIZE)
            {
                w16_tmp3 = NETEQ_MAX_OUTPUT_SIZE - inst->w16_frameLen;
            }

            /* call Expand */
            len = inst->timestampsPerCall + inst->ExpandInst.w16_overlap;
            pos = 0;

            tempReturnValue = WebRtcNetEQ_Expand(inst,
#ifdef SCRATCH
                pw16_scratchPtr + SCRATCH_NETEQ_EXPAND,
#endif
                pw16_NetEqAlgorithm_buffer, &len, 1);

            if (tempReturnValue < 0)
            {
                /* error */
                /* this error value will override return_value */
                return tempReturnValue;
            }

            pos += len; /* got len samples from expand */

            /* copy to fill the demand */
            while (pos + len <= inst->w16_frameLen + w16_tmp3)
            {
                WEBRTC_SPL_MEMCPY_W16(&pw16_NetEqAlgorithm_buffer[pos],
                    pw16_NetEqAlgorithm_buffer, len);
                pos += len;
            }

            /* fill with fraction of the expand vector if needed */
            if (pos < inst->w16_frameLen + w16_tmp3)
            {
                WEBRTC_SPL_MEMCPY_W16(&pw16_NetEqAlgorithm_buffer[pos], pw16_NetEqAlgorithm_buffer,
                    inst->w16_frameLen + w16_tmp3 - pos);
            }

            len = inst->w16_frameLen + w16_tmp3; /* truncate any surplus samples since we don't want these */

            /*
             * Mix with contents in sync buffer. Find largest power of two that is less than
             * interpolate length divide 16384 with this number; result is in w16_tmp2.
             */
            w16_tmp1 = 2;
            w16_tmp2 = 16384;
            while (w16_tmp1 <= w16_tmp3)
            {
                w16_tmp2 >>= 1; /* divide with 2 */
                w16_tmp1 <<= 1; /* increase with a factor of 2 */
            }

            w16_tmp1 = 0;
            pos = 0;
            while (w16_tmp1 < 16384)
            {
                inst->speechBuffer[inst->curPosition + pos]
                    =
                    (WebRtc_Word16) WEBRTC_SPL_RSHIFT_W32(
                        WEBRTC_SPL_MUL_16_16( inst->speechBuffer[inst->endPosition - w16_tmp3 + pos],
                            16384-w16_tmp1 ) +
                        WEBRTC_SPL_MUL_16_16( pw16_NetEqAlgorithm_buffer[pos], w16_tmp1 ),
                        14 );
                w16_tmp1 += w16_tmp2;
                pos++;
            }

            /* overwrite remainder of speech buffer */

            WEBRTC_SPL_MEMCPY_W16( &inst->speechBuffer[inst->endPosition - w16_tmp3 + pos],
                &pw16_NetEqAlgorithm_buffer[pos], w16_tmp3 - pos);

            len -= w16_tmp3;
            /* shift algorithm buffer */

            WEBRTC_SPL_MEMMOVE_W16( pw16_NetEqAlgorithm_buffer,
                &pw16_NetEqAlgorithm_buffer[w16_tmp3],
                len );

            /* Update variables for VQmon */
            inst->w16_concealedTS += len;

            inst->w16_mode = MODE_FADE_TO_BGN;
#ifdef NETEQ_DELAY_LOGGING
            temp_var = NETEQ_DELAY_LOGGING_SIGNAL_EXPAND_INFO;
            fwrite(&temp_var, sizeof(int), 1, delay_fid2);
            temp_var = len;
            fwrite(&temp_var, sizeof(int), 1, delay_fid2);
#endif

            break;
        }

        default:
            inst->w16_mode = MODE_ERROR;
            dspInfo->lastMode = MODE_ERROR;
            return FAULTY_INSTRUCTION;
    } /* end of grand switch */

    /* Copy data directly to output buffer */

    w16_tmp2 = 0;
    if ((inst->endPosition + len - inst->curPosition - inst->ExpandInst.w16_overlap)
        >= inst->timestampsPerCall)
    {
        w16_tmp2 = inst->endPosition - inst->curPosition;
        w16_tmp2 = WEBRTC_SPL_MAX(w16_tmp2, 0); /* Additional error protection, just in case */
        w16_tmp1 = WEBRTC_SPL_MIN(w16_tmp2, inst->timestampsPerCall);
        w16_tmp2 = inst->timestampsPerCall - w16_tmp1;
        WEBRTC_SPL_MEMCPY_W16(pw16_outData, &inst->speechBuffer[inst->curPosition], w16_tmp1);
        WEBRTC_SPL_MEMCPY_W16(&pw16_outData[w16_tmp1], pw16_NetEqAlgorithm_buffer, w16_tmp2);
        DataEnough = 1;
    }
    else
    {
        DataEnough = 0;
    }

    if (playDtmf != 0)
    {
#ifdef NETEQ_ATEVENT_DECODE
        WebRtc_Word16 outDataIndex = 0;
        WebRtc_Word16 overdubLen = -1; /* default len */
        WebRtc_Word16 dtmfLen;

        /*
         * Overdub the output with DTMF. Note that this is not executed if the
         * DSP_INSTR_DTMF_GENERATE operation is performed above.
         */
        if (inst->DTMFInst.lastDtmfSample - inst->curPosition > 0)
        {
            /* special operation for transition from "DTMF only" to "DTMF overdub" */
            outDataIndex
                = WEBRTC_SPL_MIN(inst->DTMFInst.lastDtmfSample - inst->curPosition,
                    inst->timestampsPerCall);
            overdubLen = inst->timestampsPerCall - outDataIndex;
        }

        dtmfLen = WebRtcNetEQ_DTMFGenerate(&inst->DTMFInst, dtmfValue, dtmfVolume,
            &pw16_outData[outDataIndex], inst->fs, overdubLen);
        if (dtmfLen < 0)
        {
            /* error occurred */
            return_value = dtmfLen;
        }
        inst->DTMFInst.reinit = 0;
#else
        inst->w16_mode = MODE_ERROR;
        dspInfo->lastMode = MODE_ERROR;
        return FAULTY_INSTRUCTION;
#endif
    }

    /*
     * Shuffle speech buffer to allow more data. Move data from pw16_NetEqAlgorithm_buffer
     * to speechBuffer.
     */
    if (instr != DSP_INSTR_EXPAND)
    {
        w16_tmp1 = WEBRTC_SPL_MIN(inst->endPosition, len);
        WEBRTC_SPL_MEMMOVE_W16(inst->speechBuffer, inst->speechBuffer + w16_tmp1,
                               (inst->endPosition-w16_tmp1));
        WEBRTC_SPL_MEMCPY_W16(&inst->speechBuffer[inst->endPosition-w16_tmp1],
                              &pw16_NetEqAlgorithm_buffer[len-w16_tmp1], w16_tmp1);
#ifdef NETEQ_ATEVENT_DECODE
        /* Update index to end of DTMF data in speech buffer */
        if (instr == DSP_INSTR_DTMF_GENERATE)
        {
            /* We have written DTMF data to the end of speech buffer */
            inst->DTMFInst.lastDtmfSample = inst->endPosition;
        }
        else if (inst->DTMFInst.lastDtmfSample > 0)
        {
            /* The end of DTMF data in speech buffer has been shuffled */
            inst->DTMFInst.lastDtmfSample -= w16_tmp1;
        }
#endif
        /*
         * Update the BGN history if last operation was not expand (nor Merge, Accelerate
         * or Pre-emptive expand, to save complexity).
         */
        if ((inst->w16_mode != MODE_EXPAND) && (inst->w16_mode != MODE_MERGE)
            && (inst->w16_mode != MODE_SUCCESS_ACCELERATE) && (inst->w16_mode
            != MODE_LOWEN_ACCELERATE) && (inst->w16_mode != MODE_SUCCESS_PREEMPTIVE)
            && (inst->w16_mode != MODE_LOWEN_PREEMPTIVE) && (inst->w16_mode
            != MODE_FADE_TO_BGN) && (inst->w16_mode != MODE_DTMF) && (!BGNonly))
        {
            WebRtcNetEQ_BGNUpdate(inst
#ifdef SCRATCH
                , pw16_scratchPtr + SCRATCH_NETEQ_BGN_UPDATE
#endif
            );
        }
    }
    else /* instr == DSP_INSTR_EXPAND */
    {
        /* Nothing should be done since data is already copied to output. */
    }

    inst->curPosition -= len;

    /*
     * Extra protection in case something should go totally wrong in terms of sizes...
     * If everything is ok this should NEVER happen.
     */
    if (inst->curPosition < -inst->timestampsPerCall)
    {
        inst->curPosition = -inst->timestampsPerCall;
    }

    if ((instr != DSP_INSTR_EXPAND) && (instr != DSP_INSTR_MERGE) && (instr
        != DSP_INSTR_FADE_TO_BGN))
    {
        /* Reset concealed TS parameter if it does not seem to have been flushed */
        if (inst->w16_concealedTS > inst->timestampsPerCall)
        {
            inst->w16_concealedTS = 0;
        }
    }

    /*
     * Double-check that we actually have 10 ms to play. If we haven't, there has been a
     * serious error.The decoder might have returned way too few samples
     */
    if (!DataEnough)
    {
        /* This should not happen. Set outdata to zeros, and return error. */
        WebRtcSpl_MemSetW16(pw16_outData, 0, inst->timestampsPerCall);
        *pw16_len = inst->timestampsPerCall;
        inst->w16_mode = MODE_ERROR;
        dspInfo->lastMode = MODE_ERROR;
        return RECOUT_ERROR_SAMPLEUNDERRUN;
    }

    /*
     * Update Videosync timestamp (this special timestamp is needed since the endTimestamp
     * stops during CNG and Expand periods.
     */
    if ((inst->w16_mode != MODE_EXPAND) && (inst->w16_mode != MODE_RFC3389CNG))
    {
        WebRtc_UWord32 uw32_tmpTS;
        uw32_tmpTS = inst->endTimestamp - (inst->endPosition - inst->curPosition);
        if ((WebRtc_Word32) (uw32_tmpTS - inst->videoSyncTimestamp) > 0)
        {
            inst->videoSyncTimestamp = uw32_tmpTS;
        }
    }
    else
    {
        inst->videoSyncTimestamp += inst->timestampsPerCall;
    }

    /* After this, regardless of what has happened, deliver 10 ms of future data */
    inst->curPosition += inst->timestampsPerCall;
    *pw16_len = inst->timestampsPerCall;

    /* Remember if BGNonly was used */
    if (BGNonly)
    {
        inst->w16_mode |= MODE_BGN_ONLY;
    }

    return return_value;
}

#undef    SCRATCH_ALGORITHM_BUFFER
#undef    SCRATCH_NETEQ_NORMAL
#undef    SCRATCH_NETEQ_MERGE
#undef    SCRATCH_NETEQ_BGN_UPDATE
#undef    SCRATCH_NETEQ_EXPAND
#undef    SCRATCH_DSP_INFO
#undef    SCRATCH_NETEQ_ACCELERATE
#undef    SIZE_SCRATCH_BUFFER
