view libtwamr/dtx_enc.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 5a1d18542f8a
children
line wrap: on
line source

/*
********************************************************************************
*
*      GSM AMR-NB speech codec   R98   Version 7.6.0   December 12, 2001
*                                R99   Version 3.3.0                
*                                REL-4 Version 4.1.0                
*
********************************************************************************
*
*      File             : dtx_enc.c
*      Purpose          : DTX mode computation of SID parameters
*
********************************************************************************
*/

/*
********************************************************************************
*                         MODULE INCLUDE FILE AND VERSION ID
********************************************************************************
*/
#include "namespace.h"
#include "dtx_enc.h"
 
/*
********************************************************************************
*                         INCLUDE FILES
********************************************************************************
*/
#include "tw_amr.h"
#include "typedef.h"
#include "basic_op.h"
#include "oper_32b.h"
#include "q_plsf.h"
#include "memops.h"
#include "log2.h"
#include "lsp_lsf.h"
#include "reorder.h"
#include "no_count.h"
#include "lsp_tab.h"

/*
********************************************************************************
*                         PUBLIC PROGRAM CODE
********************************************************************************
*/
 
/*
**************************************************************************
*
*  Function    : dtx_enc_reset
*
**************************************************************************
*/
void dtx_enc_reset (dtx_encState *st)
{
  Word16 i;

  st->hist_ptr = 0;
  st->log_en_index = 0;
  st->init_lsf_vq_index = 0;
  st->lsp_index[0] = 0;
  st->lsp_index[1] = 0;
  st->lsp_index[2] = 0;
 
  /* Init lsp_hist[] */
  for(i = 0; i < DTX_HIST_SIZE; i++)
  {
    Copy(lsp_init_data, &st->lsp_hist[i * M], M);
  }

  /* Reset energy history */
  Set_zero(st->log_en_hist, M);

  st->dtxHangoverCount = DTX_HANG_CONST;
  st->decAnaElapsedCount = 32767; 
}
 
/*
**************************************************************************
*
*  Function    : dtx_enc
*                
**************************************************************************
*/
int dtx_enc(dtx_encState *st,        /* i/o : State struct                    */
            Word16 computeSidFlag,   /* i   : compute SID                     */
            Q_plsfState *qSt,        /* i/o : Qunatizer state struct          */
            gc_predState* predState, /* i/o : State struct                    */
	    Word16 **anap            /* o   : analysis parameters             */
	    )
{
   Word16 i,j;
   Word16 log_en;
   Word16 lsf[M];
   Word16 lsp[M];
   Word16 lsp_q[M];
   Word32 L_lsp[M];

   /* VOX mode computation of SID parameters */
   test (); test ();
   if ((computeSidFlag != 0))
   {
      /* compute new SID frame if safe i.e don't
       * compute immediately after a talk spurt  */
      log_en = 0;                                           move16 ();
      for (i = 0; i < M; i++)
      {
         L_lsp[i] = 0;                                      move16 ();
      }
      
      /* average energy and lsp */
      for (i = 0; i < DTX_HIST_SIZE; i++)
      {
         log_en = add(log_en,
                      shr(st->log_en_hist[i],2));

         for (j = 0; j < M; j++)
         {
            L_lsp[j] = L_add(L_lsp[j],
                             L_deposit_l(st->lsp_hist[i * M + j]));
         }
      }

      log_en = shr(log_en, 1);
      for (j = 0; j < M; j++)
      {
         lsp[j] = extract_l(L_shr(L_lsp[j], 3));   /* divide by 8 */
      }

      /*  quantize logarithmic energy to 6 bits */
      st->log_en_index = add(log_en, 2560);          /* +2.5 in Q10      */
      st->log_en_index = add(st->log_en_index, 128); /* add 0.5/4 in Q10 */
      st->log_en_index = shr(st->log_en_index, 8);

      test ();
      if (sub(st->log_en_index, 63) > 0)
      {
         st->log_en_index = 63;                             move16 ();
      }
      test ();
      if (st->log_en_index < 0)
      {
         st->log_en_index = 0;                              move16 ();
      }
         
      /* update gain predictor memory */
      log_en = shl(st->log_en_index, -2+10); /* Q11 and divide by 4 */
      log_en = sub(log_en, 2560);            /* add 2.5 in Q11      */
      
      log_en = sub(log_en, 9000);
      test ();
      if (log_en > 0)
      {
         log_en = 0;                                        move16 ();
      }
      test ();
      if (sub(log_en, -14436) < 0)
      {
         log_en = -14436;                                   move16 ();
      }
      
      /* past_qua_en for other modes than MR122 */      
      predState->past_qua_en[0] = log_en;                   move16 ();
      predState->past_qua_en[1] = log_en;                   move16 ();
      predState->past_qua_en[2] = log_en;                   move16 ();
      predState->past_qua_en[3] = log_en;                   move16 ();

      /* scale down by factor 20*log10(2) in Q15 */
      log_en = mult(5443, log_en);
      
      /* past_qua_en for mode MR122 */      
      predState->past_qua_en_MR122[0] = log_en;             move16 ();
      predState->past_qua_en_MR122[1] = log_en;             move16 ();
      predState->past_qua_en_MR122[2] = log_en;             move16 ();
      predState->past_qua_en_MR122[3] = log_en;             move16 ();
 
      /* make sure that LSP's are ordered */
      Lsp_lsf(lsp, lsf, M);
      Reorder_lsf(lsf, LSF_GAP, M);
      Lsf_lsp(lsf, lsp, M);
      
      /* Quantize lsp and put on parameter list */
      Q_plsf_3(qSt, MRDTX, lsp, lsp_q, st->lsp_index, 
               &st->init_lsf_vq_index);
   }
   
   *(*anap)++ = st->init_lsf_vq_index; /* 3 bits */         move16 ();
   
   *(*anap)++ = st->lsp_index[0];      /* 8 bits */         move16 ();
   *(*anap)++ = st->lsp_index[1];      /* 9 bits */         move16 ();
   *(*anap)++ = st->lsp_index[2];      /* 9 bits */         move16 ();
   
   
   *(*anap)++ = st->log_en_index;      /* 6 bits    */      move16 ();
                                       /* = 35 bits */
   
   return 0;
}

/*
**************************************************************************
*
*  Function    : dtx_buffer
*  Purpose     : handles the DTX buffer
*                
**************************************************************************
*/
int dtx_buffer(dtx_encState *st,   /* i/o : State struct                    */
	       Word16 lsp_new[],   /* i   : LSP vector                      */
	       Word16 speech[]     /* i   : speech samples                  */
	       )
{
   Word16 i;
   Word32 L_frame_en;
   Word16 log_en_e;
   Word16 log_en_m;
   Word16 log_en;
   
   /* update pointer to circular buffer      */
   st->hist_ptr = add(st->hist_ptr, 1);
   test ();
   if (sub(st->hist_ptr, DTX_HIST_SIZE) == 0)
   {
      st->hist_ptr = 0;                                     move16 ();
   }
   
   /* copy lsp vector into buffer */
   Copy(lsp_new, &st->lsp_hist[st->hist_ptr * M], M);
   
   /* compute log energy based on frame energy */
   L_frame_en = 0;     /* Q0 */                             move32 ();
   for (i=0; i < L_FRAME; i++)
   {
      L_frame_en = L_mac(L_frame_en, speech[i], speech[i]); 
   }
   Log2(L_frame_en, &log_en_e, &log_en_m);
   
   /* convert exponent and mantissa to Word16 Q10 */
   log_en = shl(log_en_e, 10);  /* Q10 */
   log_en = add(log_en, shr(log_en_m, 15-10));
   
   /* divide with L_FRAME i.e subtract with log2(L_FRAME) = 7.32193 */
   log_en = sub(log_en, 8521);
   
   /* insert into log energy buffer with division by 2 */
   log_en = shr(log_en, 1);
   st->log_en_hist[st->hist_ptr] = log_en; /* Q10 */        move16 ();

   return 0;
}

/*
**************************************************************************
*
*  Function    : tx_dtx_handler
*  Purpose     : adds extra speech hangover to analyze speech on the decoding side. 
*                
**************************************************************************
*/
Word16 tx_dtx_handler(dtx_encState *st,      /* i/o : State struct           */
                      Word16 vad_flag,       /* i   : vad decision           */
                      enum Mode *usedMode    /* i/o : mode changed or not    */
                      )
{
   Word16 compute_new_sid_possible;
   
   /* this state machine is in synch with the GSMEFR txDtx machine      */ 
   st->decAnaElapsedCount = add(st->decAnaElapsedCount, 1);  
   
   compute_new_sid_possible = 0;                       move16(); 

   test();
   if (vad_flag != 0)
   {
      st->dtxHangoverCount = DTX_HANG_CONST;           move16();
   }
   else 
   {  /* non-speech */
      test();
      if (st->dtxHangoverCount == 0)
      {  /* out of decoder analysis hangover  */
         st->decAnaElapsedCount = 0;                   move16();        
         *usedMode = MRDTX;                            move16(); 
         compute_new_sid_possible = 1;                 move16(); 
      }
      else
      { /* in possible analysis hangover */
         st->dtxHangoverCount = sub(st->dtxHangoverCount, 1);
         
         /* decAnaElapsedCount + dtxHangoverCount < DTX_ELAPSED_FRAMES_THRESH */
         test ();
         if (sub(add(st->decAnaElapsedCount, st->dtxHangoverCount),
                 DTX_ELAPSED_FRAMES_THRESH) < 0)
         {
            *usedMode = MRDTX;                         move16(); 
            /* if short time since decoder update, do not add extra HO */            
         }
         /*
          else 
            override VAD and stay in 
            speech mode *usedMode 
            and add extra hangover
         */
      }
   }
   
   return compute_new_sid_possible;
}