view libgsmefr/dtx_enc.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 cf39a90f5196
children
line wrap: on
line source

/*
 * This file is a product of splitting ETSI EFR dtx.c into parts;
 * the present module is the encoder-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 "enc_state.h"

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

void reset_tx_dtx (struct EFR_encoder_state *st)
{
    Word16 i;

    /* suppose infinitely long speech period before start */

    st->txdtx_hangover = DTX_HANGOVER;
    st->txdtx_N_elapsed = 0x7fff;
    st->txdtx_ctrl = TX_SP_FLAG | TX_VAD_FLAG;

    for (i = 0; i < 6; i++)
    {
        st->old_CN_mem_tx[i] = 0;
    }

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

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

    st->L_pn_seed_tx = PN_INITIAL_SEED;

    st->buf_p_tx = 0;
    return;
}

/*************************************************************************
 *
 *   FUNCTION NAME: tx_dtx
 *
 *   PURPOSE: DTX handler of the speech encoder. Determines when to add
 *            the hangover period to the end of the speech burst, and
 *            also determines when to use old SID parameters, and when
 *            to update the SID parameters. This function also initializes
 *            the pseudo noise generator shift register.
 *
 *            Operation of the TX DTX handler is based on the VAD flag
 *            given as input from the speech encoder.
 *
 *   INPUTS:      VAD_flag      Voice activity decision
 *                *txdtx_ctrl   Old encoder DTX control word
 *
 *   OUTPUTS:     *txdtx_ctrl   Updated encoder DTX control word
 *                L_pn_seed_tx  Initialized pseudo noise generator shift
 *                              register (global variable)
 *
 *   RETURN VALUE: none
 *
 *************************************************************************/

void tx_dtx (
    struct EFR_encoder_state *st,
    Word16 VAD_flag
)
{
    /* N_elapsed (frames since last SID update) is incremented. If SID
       is updated N_elapsed is cleared later in this function */

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

    /* If voice activity was detected, reset hangover counter */

    if (VAD_flag == 1)
    {
        st->txdtx_hangover = DTX_HANGOVER;
        st->txdtx_ctrl = TX_SP_FLAG | TX_VAD_FLAG;
    }
    else
    {
        if (st->txdtx_hangover == 0)
        {
            /* Hangover period is over, SID should be updated */

            st->txdtx_N_elapsed = 0;

            /* Check if this is the first frame after hangover period */
            if ((st->txdtx_ctrl & TX_HANGOVER_ACTIVE) != 0)
            {
                st->txdtx_ctrl = TX_PREV_HANGOVER_ACTIVE
                    | TX_SID_UPDATE;
                st->L_pn_seed_tx = PN_INITIAL_SEED;
            }
            else
            {
                st->txdtx_ctrl = TX_SID_UPDATE;
            }
        }
        else
        {
            /* Hangover period is not over, update hangover counter */
            st->txdtx_hangover--;

            /* Check if elapsed time from last SID update is greater than
               threshold. If not, set SP=0 (although hangover period is not
               over) and use old SID parameters for new SID frame.
               N_elapsed counter must be summed with hangover counter in order
               to avoid erroneus SP=1 decision in case when N_elapsed is grown
               bigger than threshold and hangover period is still active */

            if (add (st->txdtx_N_elapsed, st->txdtx_hangover) <
                DTX_ELAPSED_THRESHOLD)
            {
                /* old SID frame should be used */
                st->txdtx_ctrl = TX_USE_OLD_SID;
            }
            else
            {
                if ((st->txdtx_ctrl & TX_HANGOVER_ACTIVE) != 0)
                {
                    st->txdtx_ctrl = TX_PREV_HANGOVER_ACTIVE
                        | TX_HANGOVER_ACTIVE
                        | TX_SP_FLAG;
                }
                else
                {
                    st->txdtx_ctrl = TX_HANGOVER_ACTIVE
                        | TX_SP_FLAG;
                }
            }
        }
    }

    return;
}

/*************************************************************************
 *
 *   FUNCTION NAME: CN_encoding
 *
 *   PURPOSE:  Encoding of the comfort noise parameters into a SID frame.
 *             Use old SID parameters if necessary. Set the parameter
 *             indices not used by comfort noise parameters to zero.
 *
 *   INPUTS:      params[0..56]  Comfort noise parameter frame from the
 *                               speech encoder
 *                txdtx_ctrl     TX DTX handler control word
 *
 *   OUTPUTS:     params[0..56]  Comfort noise encoded parameter frame
 *
 *   RETURN VALUE: none
 *
 *************************************************************************/

void CN_encoding (
    struct EFR_encoder_state *st,
    Word16 params[],
    Word16 txdtx_ctrl
)
{
    Word16 i;

    if ((txdtx_ctrl & TX_SID_UPDATE) != 0)
    {
        /* Store new CN parameters in memory to be used later as old
           CN parameters */

        /* LPC parameter indices */
        for (i = 0; i < 5; i++)
        {
            st->old_CN_mem_tx[i] = params[i];
        }
        /* Codebook index computed in last subframe */
        st->old_CN_mem_tx[5] = params[56];
    }
    if ((txdtx_ctrl & TX_USE_OLD_SID) != 0)
    {
        /* Use old CN parameters previously stored in memory */
        for (i = 0; i < 5; i++)
        {
            params[i] = st->old_CN_mem_tx[i];
        }
        params[17] = st->old_CN_mem_tx[5];
        params[30] = st->old_CN_mem_tx[5];
        params[43] = st->old_CN_mem_tx[5];
        params[56] = st->old_CN_mem_tx[5];
    }
    /* Set all the rest of the parameters to zero (SID codeword will
       be written later) */
    for (i = 0; i < 12; i++)
    {
        params[i + 5] = 0;
        params[i + 18] = 0;
        params[i + 31] = 0;
        params[i + 44] = 0;
    }

    return;
}

/*************************************************************************
 *
 *   FUNCTION NAME: aver_lsf_history
 *
 *   PURPOSE: Compute the averaged LSF parameter vector. Computation is
 *            performed by averaging the LSF parameter vectors which exist
 *            in the LSF parameter history, together with the LSF
 *            parameter vectors of the current frame.
 *
 *   INPUTS:      lsf_old[0..DTX_HANGOVER-1][0..M-1]
 *                                   LSF parameter history
 *                lsf1[0..M-1]       LSF vector of the 1st half of the frame
 *                lsf2[0..M-1]       LSF vector of the 2nd half of the frame
 *
 *   OUTPUTS:     lsf_aver[0..M-1]   Averaged LSF parameter vector
 *
 *   RETURN VALUE: none
 *
 *************************************************************************/

void aver_lsf_history (
    Word16 lsf_old[DTX_HANGOVER][M],
    Word16 lsf1[M],
    Word16 lsf2[M],
    Word16 lsf_aver[M]
)
{
    Word16 i, j;
    Word32 L_temp;

    for (j = 0; j < M; j++)
    {
        L_temp = L_mult (0x3fff, lsf1[j]);
        L_temp = L_mac (L_temp, 0x3fff, lsf2[j]);
        L_temp = L_mult (INV_DTX_HANGOVER_P1, extract_h (L_temp));

        for (i = 0; i < DTX_HANGOVER; i++)
        {
            L_temp = L_mac (L_temp, INV_DTX_HANGOVER_P1, lsf_old[i][j]);
        }

        lsf_aver[j] = extract_h (L_temp);              move16 (); 
    }

    return;
}

/*************************************************************************
 *
 *   FUNCTION NAME: update_gain_code_history_tx
 *
 *   PURPOSE: Update the fixed codebook gain parameter history of the
 *            encoder. The fixed codebook gain parameters kept in the buffer
 *            are used later for computing the reference fixed codebook
 *            gain parameter value and the averaged 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 encoder
 *
 *   OUTPUTS:     gain_code_old_tx[0..4*DTX_HANGOVER-1]
 *                                Updated fixed codebook gain history of encoder
 *
 *   RETURN VALUE: none
 *
 *************************************************************************/

void update_gain_code_history_tx (
    struct EFR_encoder_state *st,
    Word16 new_gain_code
)
{
    /* Circular buffer */
    st->gain_code_old_tx[st->buf_p_tx] = new_gain_code;

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

    return;
}

/*************************************************************************
 *
 *   FUNCTION NAME: compute_CN_excitation_gain
 *
 *   PURPOSE: Compute the unquantized fixed codebook gain. Computation is
 *            based on the energy of the Linear Prediction residual signal.
 *
 *   INPUTS:      res2[0..39]   Linear Prediction residual signal
 *
 *   OUTPUTS:     none
 *
 *   RETURN VALUE: Unquantized fixed codebook gain
 *
 *************************************************************************/

Word16 compute_CN_excitation_gain (
    Word16 res2[L_SUBFR]
)
{
    Word16 i, norm, norm1, temp, overfl;
    Word32 L_temp;

    /* Compute the energy of the LP residual signal */

    norm = 0;                                          move16 (); 
    do
    {
        overfl = 0;                                    move16 ();

        L_temp = 0L;                                   move32 (); 
        for (i = 0; i < L_SUBFR; i++)
        {
            temp = shr (res2[i], norm);
            L_temp = L_mac (L_temp, temp, temp);
        }

        test (); 
        if (L_temp == MAX_32)
        {
            norm = add (norm, 1);
            overfl = 1;                 move16 (); /* Set the overflow flag */
        }
        test (); 
    }
    while (overfl != 0);

    L_temp = L_add (L_temp, 1L);             /* Avoid the case of all zeros */

    /* Take the square root of the obtained energy value (sqroot is a 2nd
       order Taylor series approximation) */

    norm1 = norm_l (L_temp);
    temp = extract_h (L_shl (L_temp, norm1));
    L_temp = L_mult (temp, temp);
    L_temp = L_sub (805306368L, L_shr (L_temp, 3));
    L_temp = L_add (L_temp, L_mult (24576, temp));

    temp = extract_h (L_temp);
    test (); logic16 (); 
    if ((norm1 & 0x0001) != 0)
    {
        temp = mult_r (temp, 23170);
        norm1 = sub (norm1, 1);
    }
    /* Divide the result of sqroot operation by sqroot(10) */

    temp = mult_r (temp, 10362);

    /* Re-scale to get the final value */

    norm1 = shr (norm1, 1);
    norm1 = sub (norm1, norm);

    test (); 
    if (norm1 >= 0)
    {
        temp = shr (temp, norm1);
    }
    else
    {
        temp = shl (temp, abs_s (norm1));
    }

    return temp;
}

/*************************************************************************
 *
 *   FUNCTION NAME: aver_gain_code_history
 *
 *   PURPOSE: Compute the averaged fixed codebook gain parameter value.
 *            Computation is performed by averaging the fixed codebook
 *            gain parameter values which exist in the fixed codebook
 *            gain parameter history, together with the fixed codebook
 *            gain parameter value of the current subframe.
 *
 *   INPUTS:      CN_excitation_gain
 *                              Unquantized fixed codebook gain value
 *                              of the current subframe
 *                gain_code_old[0..4*DTX_HANGOVER-1]
 *                              fixed codebook gain parameter history
 *
 *   OUTPUTS:     none
 *
 *   RETURN VALUE: Averaged fixed codebook gain value
 *
 *************************************************************************/

Word16 aver_gain_code_history (
    Word16 CN_excitation_gain,
    Word16 gain_code_old[4 * DTX_HANGOVER]
)
{
    Word16 i;
    Word32 L_ret;

    L_ret = L_mult (0x470, CN_excitation_gain);

    for (i = 0; i < (4 * DTX_HANGOVER); i++)
    {
        L_ret = L_mac (L_ret, 0x470, gain_code_old[i]);
    }
    return extract_h (L_ret);
}