FreeCalypso > hg > gsm-codec-lib
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; }