⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 lec.cpp

📁 The line echo canceller (LEC) is designed to provide the maximum attainable transparent voice qualit
💻 CPP
字号:
/*-------------------------------------------------------------------------*
 *                                                                         *
 *   THIS IS AN UNPUBLISHED WORK CONTAINING CONFIDENTIAL AND PROPRIETARY   *
 *   INFORMATION.  IF PUBLICATION OCCURS, THE FOLLOWING NOTICE APPLIES:    *
 *      "COPYRIGHT 2001 MICHAEL TSIROULNIKOV, ALL RIGHTS RESERVED"         *
 *                                                                         *
 *-------------------------------------------------------------------------*/


#if !defined _dsp
#include <memory.h>
#include <math.h>
#include <stdlib.h>
#else
#include <string.h> /* memcpy() */
#endif

#include "stddefs.h"
#include "leci.h"

/*--------------------- local defs ----------------------------------------*/


/* the min erl we expect to be at least is 3 dB  */
#define _ERL_OK_CNT             (3)

/* only reused constants */
#define _ZC_NOISE_START 		LEC_DB(15)


/*--------------------- public vars ---------------------------------------*/

/*--------------------- local vars ----------------------------------------*/

#if defined (_dsp)
#endif

/*--------------------- local functions -----------------------------------*/
/*-------------------------------------------------------------------------*/
static void                     _vad_init
/*-------------------------------------------------------------------------*/
(
LEC_tVad *p,
S16 sEnNoise
)
{
    p->sZcNoise = _ZC_NOISE_START;
	p->sEnNoise = sEnNoise;
    p->sEnNoiseMax = sEnNoise;
}
/*-------------------------------------------------------------------------*/
static S16                      _vad_process
/*-------------------------------------------------------------------------*/
(
LEC_tVad *pVad,
S16 *psData
)
{
    S16 sState = pVad->sState;

    pVad->slEnergy = lec_pkt_energy(psData);
    pVad->sEn = lec_en2log(pVad->slEnergy);
	pVad->sZc = lec_pkt_zc(psData);
    
    lec_vad_logic(pVad);

    return (pVad->sState - sState);
}

/*-------------------------------------------------------------------------*/
static void                     _test_erl
/*-------------------------------------------------------------------------*/
(
LEC_tDb *pDb,
LEC_tSc *pSc
)
{
    S32 slTmp = lec_pkt_energy(pSc->psRx);
    pSc->sRxEn = lec_en2log(slTmp);
    slTmp = pDb->asTxEn[2];
    slTmp += pDb->asTxEn[3];
    slTmp >>= 1;
    pSc->sErl = (S16)slTmp - pSc->sRxEn;

    /*  we do not want to adapt on short spikes. there are very big chances
        that it will bring no good at all, so we delay the start of
        adaptation till we know for sure that it is not a transient process.

        we end adaptation as soon as we see active RX. actually, it is
        usually preceeded by a (possibly, very short) DT period 
        as a transition between TX and RX dominances.
     */
    if (pSc->sErl > pDb->Cfg.sErlMin)
    {
        pDb->sErlOkCnt++;
        if (pDb->sErlOkCnt > _ERL_OK_CNT)
        {
            pDb->sErlOkCnt = _ERL_OK_CNT;
            pSc->uFlags |= LEC_FLAG_ERL_OK;
        }
    }
    else 
    {
        pDb->sErlOkCnt--;
        if (pDb->sErlOkCnt < 0)
            pDb->sErlOkCnt = 0;
    }
}

/*-------------------------------------------------------------------------*/
static void                     _update_history
/*-------------------------------------------------------------------------*/
(
LEC_tDb *pDb,
LEC_tSc *pSc
)
{
	/* shift old data in delay line one section down */
	lec_pkts_cpy(
        pDb->psHst, 
        pDb->psHst + ILEC_FR_SZ, 
        LEC_HST_FRAMES - 1);

	/* add the new section to the end of the delay line */
	lec_pkts_cpy(
        pDb->psHst + (LEC_HST_SZ - ILEC_FR_SZ), 
        pSc->psTx, 
        1);
}


/*-------------------------------------------------------------------------*/
static void                     _ec_process
/*-------------------------------------------------------------------------*/
(
LEC_tDb *pDb,
LEC_tSc *pSc
)
{
    lec_analyse_tx(pDb, pSc);
    _test_erl(pDb, pSc);

    lec_cancel_pkt(pDb, pSc, pSc->asErr1);

    pSc->sErr1En = lec_en2log(lec_pkt_energy(pSc->asErr1));
    pSc->sExpectedErrorEn = pSc->sRxEn - pDb->Adf2.sErleAveraged;
    pSc->sStepSize = 0;
   	pSc->uAdaptMode = 0;
    pSc->uFltCpyMode = 3;

    if ((!(pDb->Cfg.uControl & ILEC_CMD_ADAPT_OFF)) &&
        (pSc->uFlags & LEC_FLAG_ERL_OK) &&
        (pDb->uTone == 0) &&
        (!(pSc->uFlags & LEC_FLAG_HIRCV)))
    {
        pSc->uFlags |= LEC_FLAG_ADAPT;

        if (!pDb->Adf2.uConverged)
        {
            pSc->uAdaptMode = 1;
            lec_adapt_fast(pDb, pSc);
        }
        else
        {
            pSc->uAdaptMode = 2;
            lec_adapt_slow(pDb, pSc);
        }
    }

    lec_compare_adfs(pDb, pSc);

	_vad_process(&pDb->VadErr, pSc->asErr1);
    lec_convergence_control(pDb, pSc);

    /* RxNoiseLevel is slowly rising from -80dBm */
    if (pDb->VadErr.sState == LEC_VAD_ST_IDLE)
    {
        // noise level shall not be higher than backgrownd noise,
        // the constant is empiric
        lec_avrg(&pDb->sRxNoiseLevel, pDb->VadErr.sEnNoise, Q15(0.01));
    }

    lec_double_talk_detector(pDb, pSc);
    lec_non_linear_processor(pDb, pSc);

    lec_update_flt(pDb, pSc);
    _update_history(pDb, pSc);
}

/*-------------------------------------------------------------------------*/
static void                     _update_statistics
/*-------------------------------------------------------------------------*/
(
LEC_tDb *pDb,
LEC_tSc *pSc
)
{
    LEC_MIKET_tStts *pStts = &(pDb->Stts);

    pStts->uFlags = 0;
    if (pSc->uFlags & LEC_FLAG_ADAPT)
        pStts->uFlags |= (1<<0);
    if (pDb->VadErr.sState >= LEC_VAD_ST_ACTIVE)
        pStts->uFlags |= (1<<1);
    if (pSc->sTxEn >= LEC_DB(-35))
        pStts->uFlags |= (1<<2);
    if (pDb->uIsDT)
        pStts->uFlags |= (1<<3);

    if (pDb->Cfg.uControl & ILEC_CMD_ADAPT_OFF)
    	pStts->uFlags |= (1<<4);
    if (pSc->uFlags & LEC_FLAG_ERL_OK)
    	pStts->uFlags |= (1<<5);
    if (pDb->uTone)
    	pStts->uFlags |= (1<<6);
    if (pSc->uFlags & LEC_FLAG_HIRCV)
    	pStts->uFlags |= (1<<7);

    pStts->uFlags |= (pSc->uNlpMode << 8);
    pStts->uFlags |= (pSc->uAdaptMode << 12);

    pStts->sTxEn = (S16)((pSc->sTxEn * (65536L/17)) >> 16);
    pStts->sRxInEn = (S16)((pSc->sRxEn * (65536L/17)) >> 16);
    pStts->sErrEn = (S16)((pDb->VadErr.sEn * (65536L/17)) >> 16);
    pStts->sErl = (S16)((pDb->Adf1.sERL * (65536L/17)) >> 16);
    pStts->sErle = (S16)((pDb->Adf1.sErleAveraged * (65536L/17)) >> 16);
}

/*-------------------------------------------------------------------------*/
static void                     _reset_db
/*-------------------------------------------------------------------------*/
(
LEC_tDb *pDb,
U16 uZeroFilter
)
{
    memset(pDb->psHst,  0, LEC_HST_SZ*sizeof(S16));
    memset(pDb->psTxF,  0, LEC_HST_SZ*sizeof(S16));

   	if (uZeroFilter)
	{
		memset(pDb->asAdf1, 0, LEC_FLT2_SZ*sizeof(S16));
		memset(pDb->asAdf2, 0, LEC_FLT2_SZ*sizeof(S16));
	}

    {
        S16 *psHst = pDb->psHst;
        ILEC_tCfg Cfg = pDb->Cfg;

        memset(pDb, 0, sizeof(LEC_tDb));

        pDb->Cfg = Cfg;
        pDb->psHst = psHst;
        pDb->psTxF = psHst + LEC_HST_SZ;
    }

	/* init db */

	_vad_init(
        &pDb->VadErr, // pDb
        LEC_DB(-40)   // en noise max
        );

    pDb->sRxNoiseLevel = LEC_DB(-70);
    pDb->sClipLevel = LEC_DB(-70);
    pDb->Adf2.sERL = pDb->Cfg.sErlMin;
}


       
/*--------------------- public  functions ---------------------------------*/


/*-------------------------------------------------------------------------*/
void                            LEC_MIKET_init_db
/*-------------------------------------------------------------------------*/
(
void *p2db,  /* data base */
const ILEC_tCfg *pCfg,
S16 *pHst  /* tx history */
)
{
    LEC_tDb *pDb = (LEC_tDb *)p2db;

	pDb->psHst  = pHst;
    pDb->psTxF  = pHst + LEC_HST_SZ;
    pDb->Cfg    = *pCfg;

    pDb->Cfg.sErlMin *= 17;
    pDb->Cfg.sClipThr *= 17;

	_reset_db(pDb, TRUE);

}

/*-------------------------------------------------------------------------*/
void                            LEC_MIKET_control
/*-------------------------------------------------------------------------*/
(
void *p2db,
S16 sCmd,
ILEC_Status *pStatus
)
{
    LEC_tDb *pDb = (LEC_tDb *)p2db;

	if (pDb != NULL)
	{
		if (sCmd & ILEC_CMD_RESET)
		{
			/* do NOT zero filter coeffs */
			U16 uZeroFilter = TRUE;
            if (sCmd & ILEC_CMD_PRESERVE_ADF)
            {
                uZeroFilter = FALSE;
            };

			_reset_db(pDb, uZeroFilter);

			sCmd &= ~(ILEC_CMD_RESET | ILEC_CMD_PRESERVE_ADF);
		}

		pDb->Cfg.uControl = sCmd;
	}

    pStatus->size = sizeof(ILEC_Status);
}

/*-------------------------------------------------------------------------*/
void                        LEC_MIKET_process
/*-------------------------------------------------------------------------*/
(
void *p2db,
void *p2sc,
S16 *psTx,
S16 *psRx,
S16  sIsTone
)
{
    LEC_tDb *pDb = (LEC_tDb *)p2db;
    LEC_tSc *pSc = (LEC_tSc *)p2sc;

    if (pDb->Cfg.uControl & ILEC_CMD_OFF)
    {
        return;
    }
    else
    {
        memset(pSc, 0, sizeof(LEC_tSc));
	    pSc->psTx = psTx;
	    pSc->psRx = psRx;
        if (sIsTone)
            pSc->uFlags |= LEC_FLAG_TONE;

	    if (pDb->Cfg.uControl & ILEC_CMD_LOOPBACK)
	    {
        	S16 k;
		    for (k = 0; k < ILEC_FR_SZ; k++)
		    {
			    pSc->psRx[k] = (pDb->psHst[k + 83])>>2;

		    }
	    }


        _ec_process(pDb, pSc);

        _update_statistics(pDb, pSc);

	}
}


⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -