FreeCalypso > hg > gsm-codec-lib
view libgsmefr/dtx_dec.c @ 581:e2d5cad04cbf
libgsmhr1 RxFE: store CN R0+LPC separately from speech
In the original GSM 06.06 code the ECU for speech mode is entirely
separate from the CN generator, maintaining separate state. (The
main intertie between them is the speech vs CN state variable,
distinguishing between speech and CN BFIs, in addition to the
CN-specific function of distinguishing between initial and update
SIDs.)
In the present RxFE implementation I initially thought that we could
use the same saved_frame buffer for both ECU and CN, overwriting
just the first 4 params (R0 and LPC) when a valid SID comes in.
However, I now realize it was a bad idea: the original code has a
corner case (long sequence of speech-mode BFIs to put the ECU in
state 6, then SID and CN-mode BFIs, then a good speech frame) that
would be broken by that buffer reuse approach. We could eliminate
this corner case by resetting the ECU state when passing through
a CN insertion period, but doing so would needlessly increase
the behavioral diffs between GSM 06.06 and our version.
Solution: use a separate CN-specific buffer for CN R0+LPC parameters,
and match the behavior of GSM 06.06 code in this regard.
author | Mychaela Falconia <falcon@freecalypso.org> |
---|---|
date | Thu, 13 Feb 2025 10:02:45 +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; }