view libgsmefr/dtx_dec.c @ 242:f081a6850fb5

libgsmfrp: new refined implementation The previous implementation exhibited the following defects, which are now fixed: 1) The last received valid SID was cached forever for the purpose of handling future invalid SIDs - we could have received some valid SID ages ago, then lots of speech or NO_DATA, and if we then get an invalid SID, we would resurrect the last valid SID from ancient history - a bad design. In our new design, we handle invalid SID based on the current state, much like BFI. 2) GSM 06.11 spec says clearly that after the second lost SID (received BFI=1 && TAF=1 in CN state) we need to gradually decrease the output level, rather than jump directly to emitting silence frames - we previously failed to implement such logic. 3) Per GSM 06.12 section 5.2, Xmaxc should be the same in all 4 subframes in a SID frame. What should we do if we receive an otherwise valid SID frame with different Xmaxc? Our previous approach would replicate this Xmaxc oddity in every subsequent generated CN frame, which is rather bad. In our new design, the very first CN frame (which can be seen as a transformation of the SID frame itself) retains the original 4 distinct Xmaxc, but all subsequent CN frames are based on the Xmaxc from the last subframe of the most recent SID.
author Mychaela Falconia <falcon@freecalypso.org>
date Tue, 09 May 2023 05:16:31 +0000
parents 17f690749099
children
line wrap: on
line source

/*
 * This file is a product of splitting ETSI EFR dtx.c into parts;
 * the present module is the decoder-specific part.
 */

#include "gsm_efr.h"
#include "typedef.h"
#include "namespace.h"
#include "basic_op.h"
#include "cnst.h"
#include "sig_proc.h"
#include "memops.h"
#include "no_count.h"
#include "dtx.h"
#include "dtx_defs.h"
#include "dec_state.h"

/*************************************************************************
 *
 *   FUNCTION NAME: reset_rx_dtx
 *
 *   PURPOSE:  Resets the static variables of the RX DTX handler to their
 *             initial values
 *
 *************************************************************************/

void reset_rx_dtx (struct EFR_decoder_state *st)
{
    Word16 i;

    /* suppose infinitely long speech period before start */

    st->rxdtx_aver_period = DTX_HANGOVER;
    st->rxdtx_N_elapsed = 0x7fff;
    st->rxdtx_ctrl = RX_SP_FLAG;

    for (i = 0; i < DTX_HANGOVER; i++)
    {
        st->lsf_old_rx[i][0] = 1384;
        st->lsf_old_rx[i][1] = 2077;
        st->lsf_old_rx[i][2] = 3420;
        st->lsf_old_rx[i][3] = 5108;
        st->lsf_old_rx[i][4] = 6742;
        st->lsf_old_rx[i][5] = 8122;
        st->lsf_old_rx[i][6] = 9863;
        st->lsf_old_rx[i][7] = 11092;
        st->lsf_old_rx[i][8] = 12714;
        st->lsf_old_rx[i][9] = 13701;
    }

    for (i = 0; i < 4 * DTX_HANGOVER; i++)
    {
        st->gain_code_old_rx[i] = 0;
    }

    st->L_pn_seed_rx = PN_INITIAL_SEED;
    st->rx_dtx_state = CN_INT_PERIOD - 1;

    st->prev_SID_frames_lost = 0;
    st->buf_p_rx = 0;

    return;
}

/*************************************************************************
 *
 *   FUNCTION NAME: rx_dtx
 *
 *   PURPOSE: DTX handler of the speech decoder. Determines when to update
 *            the reference comfort noise parameters (LSF and gain) at the
 *            end of the speech burst. Also classifies the incoming frames
 *            according to SID flag and BFI flag
 *            and determines when the transmission is active during comfort
 *            noise insertion. This function also initializes the pseudo
 *            noise generator shift register.
 *
 *            Operation of the RX DTX handler is based on measuring the
 *            lengths of speech bursts and the lengths of the pauses between
 *            speech bursts to determine when there exists a hangover period
 *            at the end of a speech burst. The idea is to keep in sync with
 *            the TX DTX handler to be able to update the reference comfort
 *            noise parameters at the same time instances.
 *
 *   INPUTS:      *rxdtx_ctrl   Old decoder DTX control word
 *                TAF           Time alignment flag
 *                bfi           Bad frame indicator flag
 *                SID_flag      Silence descriptor flag
 *
 *   OUTPUTS:     *rxdtx_ctrl   Updated decoder DTX control word
 *                rx_dtx_state  Updated state of comfort noise interpolation
 *                              period (global variable)
 *                L_pn_seed_rx  Initialized pseudo noise generator shift
 *                              register (global variable)
 *
 *   RETURN VALUE: none
 *
 *************************************************************************/

void rx_dtx (
    struct EFR_decoder_state *st,
    Word16 TAF,
    Word16 bfi,
    Word16 SID_flag
)
{
    Word16 frame_type;

    /* Frame classification according to bfi-flag and ternary-valued
       SID flag. The frames between SID updates (not actually trans-
       mitted) are also classified here; they will be discarded later
       and provided with "NO TRANSMISSION"-flag */

    if ((SID_flag == 2) && (bfi == 0))
    {
        frame_type = VALID_SID_FRAME;                   move16 (); 
    }
    else if ((SID_flag == 0) && (bfi == 0))
    {
        frame_type = GOOD_SPEECH_FRAME;                 move16 (); 
    }
    else if ((SID_flag == 0) && (bfi != 0))
    {
        frame_type = UNUSABLE_FRAME;                    move16 (); 
    }
    else
    {
        frame_type = INVALID_SID_FRAME;                 move16 (); 
    }

    /* Update of decoder state */
    /* Previous frame was classified as a speech frame */
    if ((st->rxdtx_ctrl & RX_SP_FLAG) != 0)
    {
        if (frame_type == VALID_SID_FRAME)
        {
            st->rxdtx_ctrl = RX_FIRST_SID_UPDATE;
        }
        else if (frame_type == INVALID_SID_FRAME)
        {
            st->rxdtx_ctrl = RX_FIRST_SID_UPDATE
                           | RX_INVALID_SID_FRAME;
        }
        else if (frame_type == UNUSABLE_FRAME)
        {
            st->rxdtx_ctrl = RX_SP_FLAG;
        }
        else if (frame_type == GOOD_SPEECH_FRAME)
        {
            st->rxdtx_ctrl = RX_SP_FLAG;
        }
    }
    else
    {
        if (frame_type == VALID_SID_FRAME)
        {
            st->rxdtx_ctrl = RX_CONT_SID_UPDATE;
        }
        else if (frame_type == INVALID_SID_FRAME)
        {
            st->rxdtx_ctrl = RX_CONT_SID_UPDATE
                           | RX_INVALID_SID_FRAME;
        }
        else if (frame_type == UNUSABLE_FRAME)
        {
            st->rxdtx_ctrl = RX_CNI_BFI;
        }
        else if (frame_type == GOOD_SPEECH_FRAME)
        {
            /* If the previous frame (during CNI period) was muted,
               raise the RX_PREV_DTX_MUTING flag */
            if ((st->rxdtx_ctrl & RX_DTX_MUTING) != 0)
            {
                st->rxdtx_ctrl = RX_SP_FLAG | RX_FIRST_SP_FLAG
                               | RX_PREV_DTX_MUTING;
            }
            else
            {
                st->rxdtx_ctrl = RX_SP_FLAG | RX_FIRST_SP_FLAG;
            }
        }
    }

    if ((st->rxdtx_ctrl & RX_SP_FLAG) != 0)
    {
        st->prev_SID_frames_lost = 0;
        st->rx_dtx_state = CN_INT_PERIOD - 1;
    }
    else
    {
        /* First SID frame */
        if ((st->rxdtx_ctrl & RX_FIRST_SID_UPDATE) != 0)
        {
            st->prev_SID_frames_lost = 0;
            st->rx_dtx_state = CN_INT_PERIOD - 1;
        }

        /* SID frame detected, but not the first SID */
        if ((st->rxdtx_ctrl & RX_CONT_SID_UPDATE) != 0)
        {
            st->prev_SID_frames_lost = 0;

            if (frame_type == VALID_SID_FRAME)
            {
                st->rx_dtx_state = 0;
            }
            else if (frame_type == INVALID_SID_FRAME)
            {
                if (st->rx_dtx_state < (CN_INT_PERIOD - 1))
                {
                    st->rx_dtx_state++;
                }
            }
        }

        /* Bad frame received in CNI mode */
        if ((st->rxdtx_ctrl & RX_CNI_BFI) != 0)
        {
            if (st->rx_dtx_state < (CN_INT_PERIOD - 1))
            {
                st->rx_dtx_state++;
            }

            /* If an unusable frame is received during CNI period
               when TAF == 1, the frame is classified as a lost
               SID frame */
            if (TAF)
            {
                st->rxdtx_ctrl = st->rxdtx_ctrl | RX_LOST_SID_FRAME;
                st->prev_SID_frames_lost = add (st->prev_SID_frames_lost, 1);
            }
            else /* No transmission occurred */
            {
                st->rxdtx_ctrl = st->rxdtx_ctrl | RX_NO_TRANSMISSION;
            }

            if (st->prev_SID_frames_lost > 1)
            {
                st->rxdtx_ctrl = st->rxdtx_ctrl | RX_DTX_MUTING;
            }
        }
    }

    /* N_elapsed (frames since last SID update) is incremented. If SID
       is updated N_elapsed is cleared later in this function */

    st->rxdtx_N_elapsed = add (st->rxdtx_N_elapsed, 1);

    if ((st->rxdtx_ctrl & RX_SP_FLAG) != 0)
    {
        st->rxdtx_aver_period = DTX_HANGOVER;
    }
    else
    {
        if (st->rxdtx_N_elapsed > DTX_ELAPSED_THRESHOLD)
        {
            st->rxdtx_ctrl |= RX_UPD_SID_QUANT_MEM;
            st->rxdtx_N_elapsed = 0;
            st->rxdtx_aver_period = 0;
            st->L_pn_seed_rx = PN_INITIAL_SEED;
        }
        else if (st->rxdtx_aver_period == 0)
        {
            st->rxdtx_N_elapsed = 0;
        }
        else
        {
            st->rxdtx_aver_period--;
        }
    }

    return;
}

/*************************************************************************
 *
 *   FUNCTION NAME: update_gain_code_history_rx
 *
 *   PURPOSE: Update the fixed codebook gain parameter history of the
 *            decoder. The fixed codebook gain parameters kept in the buffer
 *            are used later for computing the reference fixed codebook
 *            gain parameter value.
 *
 *   INPUTS:      new_gain_code   New fixed codebook gain value
 *
 *                gain_code_old_tx[0..4*DTX_HANGOVER-1]
 *                                Old fixed codebook gain history of decoder
 *
 *   OUTPUTS:     gain_code_old_tx[0..4*DTX_HANGOVER-1]
 *                                Updated fixed codebk gain history of decoder
 *
 *   RETURN VALUE: none
 *
 *************************************************************************/

void update_gain_code_history_rx (
    struct EFR_decoder_state *st,
    Word16 new_gain_code
)
{
    /* Circular buffer */
    st->gain_code_old_rx[st->buf_p_rx] = new_gain_code;

    if (st->buf_p_rx == (4 * DTX_HANGOVER - 1))
    {
        st->buf_p_rx = 0;
    }
    else
    {
        st->buf_p_rx++;
    }

    return;
}

/*************************************************************************
 *
 *   FUNCTION NAME: interpolate_CN_param
 *
 *   PURPOSE: Interpolate a comfort noise parameter value over the comfort
 *            noise update period.
 *
 *   INPUTS:      old_param     The older parameter of the interpolation
 *                              (the endpoint the interpolation is started
 *                              from)
 *                new_param     The newer parameter of the interpolation
 *                              (the endpoint the interpolation is ended to)
 *                rx_dtx_state  State of the comfort noise insertion period
 *
 *   OUTPUTS:     none
 *
 *   RETURN VALUE: Interpolated CN parameter value
 *
 *************************************************************************/

Word16 interpolate_CN_param (
    Word16 old_param,
    Word16 new_param,
    Word16 rx_dtx_state
)
{
    static const Word16 interp_factor[CN_INT_PERIOD] =
    {
        0x0555, 0x0aaa, 0x1000, 0x1555, 0x1aaa, 0x2000,
        0x2555, 0x2aaa, 0x3000, 0x3555, 0x3aaa, 0x4000,
        0x4555, 0x4aaa, 0x5000, 0x5555, 0x5aaa, 0x6000,
        0x6555, 0x6aaa, 0x7000, 0x7555, 0x7aaa, 0x7fff};
    Word16 temp;
    Word32 L_temp;

    L_temp = L_mult (interp_factor[rx_dtx_state], new_param);
    temp = sub (0x7fff, interp_factor[rx_dtx_state]);
    temp = add (temp, 1);
    L_temp = L_mac (L_temp, temp, old_param);
    temp = round (L_temp);

    return temp;
}

/*************************************************************************
 *
 *   FUNCTION NAME:  interpolate_CN_lsf
 *
 *   PURPOSE: Interpolate comfort noise LSF parameter vector over the comfort
 *            noise update period.
 *
 *   INPUTS:      lsf_old_CN[0..9]
 *                              The older LSF parameter vector of the
 *                              interpolation (the endpoint the interpolation
 *                              is started from)
 *                lsf_new_CN[0..9]
 *                              The newer LSF parameter vector of the
 *                              interpolation (the endpoint the interpolation
 *                              is ended to)
 *                rx_dtx_state  State of the comfort noise insertion period
 *
 *   OUTPUTS:     lsf_interp_CN[0..9]
 *                              Interpolated LSF parameter vector
 *
 *   RETURN VALUE: none
 *
 *************************************************************************/

void interpolate_CN_lsf (
    Word16 lsf_old_CN[M],
    Word16 lsf_new_CN[M],
    Word16 lsf_interp_CN[M],
    Word16 rx_dtx_state
)
{
    Word16 i;

    for (i = 0; i < M; i++)
    {
        lsf_interp_CN[i] = interpolate_CN_param (lsf_old_CN[i],
                                                 lsf_new_CN[i],
                                                 rx_dtx_state); move16 (); 
    }

    return;
}