www.pudn.com > aec.rar > ec.cpp


// file: ec.cc
//

#include 
#include 
#include 

#include "ec.h"
#include "cb.h"
#include "ec_constants.h"

//-----------------------------------------------------------------------------
//
// method: process_cc
// description: processes one sample of data through the echo canceller
//
// arguments:
//  double ref: reference signal (outgoing speech)
//  double sig: incoming signal (signal plus echo)
//
// return:
//  a double value containing the echo canceller output
//
//-----------------------------------------------------------------------------
double 
Echo_canceller::process_cc(double ref, double sig) 
{
    int     k;
    double  Py;
    double  two_beta;
    
    // push the reference data onto the circular buffer
    y_d = ref;
    s_d = sig;
 
// 1.  FIR filtering
    // eq. (2): compute r
    double r = 0;
    
    for ( k = 0; k < N_d; k++ )
        r += a_d[ k ] * (double)y_d( -k );

    // eq. (3): compute the output value (see figure 3) and the error
    // note: the error is the same as the output signal when near-end
    // speech is not present
    double u_suppr = (double)s_d - r;

    // if the echo is being amplified then the echo canceller has gotten into
    // a bad state. This can often happen due to large impulsive noise
    // sources on the echo channel. The best thing to do here is to
    // ignore the echo canceller filter suggestion and just output the
    // input.
    if ( fabs( sig ) < fabs( u_suppr ) )
        u_suppr = sig;

    e_d = u_d = u_suppr;
 
// 2. power estimate of the return signal power & the reference
    // eq. (15): update power estimate of the return signal power
    // eq. (10): update power estimate of the reference
    Lu_d = ( 1.0 - sigma_Lu_d ) * Lu_d + sigma_Lu_d * fabs( (double)u_d );
    Ly_d = ( 1.0 - sigma_Ly_d ) * Ly_d + sigma_Ly_d * fabs( (double)y_d );
    if ( Ly_d < CUTOFF_d )
        Ly_d = CUTOFF_d;
 
// 3. near-end speech detector
    // eq. (12) and (13): update near-end speech detector
    s_tilde_d = ( 1.0 - alpha_st_d ) * s_tilde_d + alpha_st_d * fabs( (double)s_d );
    y_tilde_d = ( 1.0 - alpha_yt_d ) * y_tilde_d + alpha_st_d * fabs( (double)y_d );

    // eq. (14): is there near-end speech?
    double max_y_tilde = 0;
    for ( k = 0; k < N_d; k++ )
        if ( max_y_tilde < y_tilde_d( -k ) )
            max_y_tilde = y_tilde_d( -k );

    if ( s_tilde_d >= ( 0.5 * max_y_tilde ) )
        HCNTR_d = HANGT_d;
    else if ( HCNTR_d > (int)0 )
        HCNTR_d--;
 
// 4. update coefficients if no near-end speech
  #if 1 
    if ( ( HCNTR_d == 0 ) && ( i_d % M_d == (int)0 ) )
    {
        Py = Ly_d * Ly_d;
        two_beta = beta1_d / Py;

        for ( k = 0; k < N_d; k++ )
        {
            // eq. (7): 
            //  - compute an expectation over M_d samples
            //  - update the coefficient
            double grad = 0.0;

            for ( int m = 0; m < M_d; m++ )
                grad += (double)e_d( -m ) * (double)y_d( -m - k );
        
            a_d[ k ] += two_beta * grad;
        }
    }
  #endif 
 
  #if 0 
    if ( HCNTR_d == 0 ) 
    { 
        Py = Ly_d * Ly_d; 
        two_beta = beta1_d / Py; 
 
        // eq. (7):  
        //  - compute an expectation over M_d samples 
        //  - update the coefficient 
        double grad = 0.0; 
 
        for ( int m = 0; m < M_d; m++ ) 
            grad += (double)e_d( -m ) * (double)y_d( -m - k_i ); 
 
        a_d[ k_i ] += two_beta * grad; 
        k_i = ( k_i + 1 ) % N_d; 
    } 
  #endif 
 
// 5. residual error suppression
    // paragraph below eq. (15): if no near-end speech,
    // check for residual error suppression
    
    //  - "residual error suppression"是有必要的, 看看"下行数据为拨号音,
    //     上行数据只为回声"这种情况, 效果非常明显.
    //  - "residual error suppression"中的"缓慢减少e(i)"很有必要,
    //    否则会产生"choppyness". 想想摩擦音.
    if ( ( HCNTR_d == 0 ) && ( Lu_d * DEFAULT_SUPPR < Ly_d ) )
        u_suppr = 0;

    i_d++;
    return u_suppr;
}

//-----------------------------------------------------------------------------
// method: constructor
// description: creates an echo canceller object and initializes all buffers
//
//-----------------------------------------------------------------------------
Echo_canceller::Echo_canceller() 
{
  a_d = (double*)NULL;
}

Echo_canceller::~Echo_canceller() 
{
  delete [] a_d;
}

void Echo_canceller::init_cc(double gamma, int N, int M, double beta1,
			     double sigma_ly, double sigma_lu,
			     double alpha_st, double alpha_yt, double cutoff,
			     int hangt, double suppr, double tau) 
{
    gamma_d = gamma;
    N_d = N;
    M_d = M;
    beta1_d = beta1;
    beta2_d = beta1;
    tau_d = tau;
    start_speech_d = 0;
    flag_d = 1;
    sigma_Ly_d = sigma_ly;
    sigma_Lu_d = sigma_lu;
    alpha_st_d = alpha_st;
    alpha_yt_d = alpha_yt;
    CUTOFF_d = cutoff;
    HANGT_d = hangt;
    SUPPR_d = suppr;
 
    k_i = 0; 
        
    // reset the high-pass filter
    //
    s_i_d = 0.0;
    sdc_d = 0.0;
    sdc_im1_d = 0.0;
    
    // reset the circular buffers
    //
    y_d.allocate_cc(N_d + M_d);
    s_d.allocate_cc(N_d + M_d);
    u_d.allocate_cc(M_d);
    e_d.allocate_cc(M_d);
    
    // allocate a buffer for the reference signal power computation
    //
    y_tilde_d.allocate_cc(N_d);
    
    // allocate coefficient memory
    //
    if (a_d != (double*)NULL)
        delete [] a_d;

    a_d = new double[N_d];
    memset(a_d, (int)0, sizeof(double)*N_d);
    
    // reset absolute time
    //
    i_d = (int)0;
    
    // reset the power computations (for y and u)
    //
    Ly_d = CUTOFF_d;
    Lu_d = 0.0;
    
    // reset the near-end speech detector
    //
    s_tilde_d = 0.0;
    HCNTR_d = (int)0;    
}

//-----------------------------------------------------------------------------
//
// method: max_cc
// description: returns the maximum of two numbers (I hate macros!)
//
//-----------------------------------------------------------------------------
int Echo_canceller::max_cc(int x, int y) {
  if (x > y)
    {return x;}
  else
    {return y;}
}

double Echo_canceller::max_cc(double x, double y) {
  if (x > y)
    {return x;}
  else
    {return y;}
}


//-----------------------------------------------------------------------------
//
// method: clip_cc
// description: clips a sample value to +/-MAX_AMPL
//
//----------------------------------------------------------------------------- 
 
short int Echo_canceller::clip_cc(double value) {

  // restore the range of value
  //
  value *= AMPL_SCALE_2;

  // clip it to +/-AMPL_SCALE_2
  //
  if (value > AMPL_SCALE_2)
    {return (short int)AMPL_SCALE_2;}
  else if (value < -AMPL_SCALE_2)
    {return (short int)-AMPL_SCALE_2;}
  else
    //{return (short int)rint(value);} 
    {return (short int)floor(value);}
}