view libtwamr/agc.c @ 282:9ee8ad3d4d30

frtest: rm gsmfr-hand-test and gsmfr-max-out utils These hack programs were never properly documented and were written only as part of a debug chase, in pursuit of a bug that ultimately turned out to be in our then-hacky patch to osmo-bts-sysmo, before beginning of proper patches in Osmocom. These hack programs need to be dropped from the present sw package because they depend on old libgsm, and we are eliminating that dependency.
author Mychaela Falconia <falcon@freecalypso.org>
date Sun, 14 Apr 2024 05:44:47 +0000
parents 07f936338de1
children 6b33f3ba4289
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             : agc.c
*
*****************************************************************************
*/

/*
*****************************************************************************
*                         MODULE INCLUDE FILE AND VERSION ID
*****************************************************************************
*/
#include "namespace.h"
#include "agc.h"

/*
*****************************************************************************
*                         INCLUDE FILES
*****************************************************************************
*/
#include <stdlib.h>
#include <stdio.h>
#include "typedef.h"
#include "basic_op.h"
#include "no_count.h"
#include "cnst.h"
#include "inv_sqrt.h"
 
/*
*****************************************************************************
*                         LOCAL VARIABLES AND TABLES
*****************************************************************************
*/

/*
*****************************************************************************
*                         LOCAL PROGRAM CODE
*****************************************************************************
*/

static Word32 energy_old( /* o : return energy of signal     */
    Word16 in[],          /* i : input signal (length l_trm) */
    Word16 l_trm          /* i : signal length               */
)
{
    Word32 s;
    Word16 i, temp;

    temp = shr (in[0], 2);
    s = L_mult (temp, temp);
    
    for (i = 1; i < l_trm; i++)
    {
        temp = shr (in[i], 2);
        s = L_mac (s, temp, temp);
    }

    return s;
}

static Word32 energy_new( /* o : return energy of signal     */
    Word16 in[],          /* i : input signal (length l_trm) */
    Word16 l_trm          /* i : signal length               */
)
{
    Word32 s;
    Word16 i;
    Flag ov_save;

    ov_save = Overflow; move16 (); /* save overflow flag in case energy_old */
                                   /* must be called                        */
    s = L_mult(in[0], in[0]);
    for (i = 1; i < l_trm; i++)
    {
        s = L_mac(s, in[i], in[i]);
    }
    
    /* check for overflow */
    test (); 
    if (L_sub (s, MAX_32) == 0L)
    {
        Overflow = ov_save; move16 (); /* restore overflow flag */
        s = energy_old (in, l_trm); move32 (); /* function result */
    }
    else
    {
       s = L_shr(s, 4);
    }

    return s;
}
/*
*****************************************************************************
*                         PUBLIC PROGRAM CODE
*****************************************************************************
*/
/*
**************************************************************************
*
*  Function    : agc_init
*  Purpose     : Allocates memory for agc state and initializes
*                state memory
*
**************************************************************************
*/
int agc_init (agcState **state)
{
  agcState* s;
 
  if (state == (agcState **) NULL){
      fprintf(stderr, "agc_init: invalid parameter\n");
      return -1;
  }
  *state = NULL;
 
  /* allocate memory */
  if ((s= (agcState *) malloc(sizeof(agcState))) == NULL){
      fprintf(stderr, "agc_init: can not malloc state structure\n");
      return -1;
  }
  
  agc_reset(s);
  *state = s;
  
  return 0;
}
 
/*
**************************************************************************
*
*  Function    : agc_reset
*  Purpose     : Reset of agc (i.e. set state memory to 1.0)
*
**************************************************************************
*/
int agc_reset (agcState *state)
{
  if (state == (agcState *) NULL){
      fprintf(stderr, "agc_reset: invalid parameter\n");
      return -1;
  }
  
  state->past_gain = 4096;   /* initial value of past_gain = 1.0  */
  
  return 0;
}
 
/*
**************************************************************************
*
*  Function    : agc_exit
*  Purpose     : The memory used for state memory is freed
*
**************************************************************************
*/
void agc_exit (agcState **state)
{
  if (state == NULL || *state == NULL)
      return;
 
  /* deallocate memory */
  free(*state);
  *state = NULL;
  
  return;
}
 
/*
**************************************************************************
*
*  Function    : agc
*  Purpose     : Scales the postfilter output on a subframe basis
*
**************************************************************************
*/
int agc (
    agcState *st,      /* i/o : agc state                        */
    Word16 *sig_in,    /* i   : postfilter input signal  (l_trm) */
    Word16 *sig_out,   /* i/o : postfilter output signal (l_trm) */
    Word16 agc_fac,    /* i   : AGC factor                       */
    Word16 l_trm       /* i   : subframe size                    */
)
{
    Word16 i, exp;
    Word16 gain_in, gain_out, g0, gain;
    Word32 s;
            
    /* calculate gain_out with exponent */
    s = energy_new(sig_out, l_trm); move32 (); /* function result */
        
    test (); 
    if (s == 0)
    {
        st->past_gain = 0;          move16 (); 
        return 0;
    }
    exp = sub (norm_l (s), 1);
    gain_out = round (L_shl (s, exp));

    /* calculate gain_in with exponent */
    s = energy_new(sig_in, l_trm);   move32 (); /* function result */
    
    test (); 
    if (s == 0)
    {
        g0 = 0;                 move16 (); 
    }
    else
    {
        i = norm_l (s);
        gain_in = round (L_shl (s, i));
        exp = sub (exp, i);

        /*---------------------------------------------------*
         *  g0 = (1-agc_fac) * sqrt(gain_in/gain_out);       *
         *---------------------------------------------------*/

        s = L_deposit_l (div_s (gain_out, gain_in));
        s = L_shl (s, 7);       /* s = gain_out / gain_in */
        s = L_shr (s, exp);     /* add exponent */

        s = Inv_sqrt (s); move32 (); /* function result */
        i = round (L_shl (s, 9));

        /* g0 = i * (1-agc_fac) */
        g0 = mult (i, sub (32767, agc_fac));
    }

    /* compute gain[n] = agc_fac * gain[n-1]
                        + (1-agc_fac) * sqrt(gain_in/gain_out) */
    /* sig_out[n] = gain[n] * sig_out[n]                        */

    gain = st->past_gain;           move16 (); 

    for (i = 0; i < l_trm; i++)
    {
        gain = mult (gain, agc_fac);
        gain = add (gain, g0);
        sig_out[i] = extract_h (L_shl (L_mult (sig_out[i], gain), 3));
                                move16 (); 
    }

    st->past_gain = gain;           move16 (); 

    return 0;
}

/*
**************************************************************************
*
*  Function    : agc2
*  Purpose     : Scales the excitation on a subframe basis
*
**************************************************************************
*/
void agc2 (
 Word16 *sig_in,        /* i   : postfilter input signal  */
 Word16 *sig_out,       /* i/o : postfilter output signal */
 Word16 l_trm           /* i   : subframe size            */
)
{
    Word16 i, exp;
    Word16 gain_in, gain_out, g0;
    Word32 s;
    
    /* calculate gain_out with exponent */
    s = energy_new(sig_out, l_trm);   move32 (); /* function result */
        
    test (); 
    if (s == 0)
    {
        return;
    }
    exp = sub (norm_l (s), 1);
    gain_out = round (L_shl (s, exp));

    /* calculate gain_in with exponent */
    s = energy_new(sig_in, l_trm);   move32 (); /* function result */
    
    test (); 
    if (s == 0)
    {
        g0 = 0;                 move16 (); 
    }
    else
    {
        i = norm_l (s);
        gain_in = round (L_shl (s, i));
        exp = sub (exp, i);

        /*---------------------------------------------------*
         *  g0 = sqrt(gain_in/gain_out);                     *
         *---------------------------------------------------*/

        s = L_deposit_l (div_s (gain_out, gain_in));
        s = L_shl (s, 7);       /* s = gain_out / gain_in */
        s = L_shr (s, exp);     /* add exponent */

        s = Inv_sqrt (s); move32 (); /* function result */
        g0 = round (L_shl (s, 9));
    }

    /* sig_out(n) = gain(n) sig_out(n) */

    for (i = 0; i < l_trm; i++)
    {
        sig_out[i] = extract_h (L_shl (L_mult (sig_out[i], g0), 3));
                                move16 (); 
    }

    return;
}