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

📄 lecdtd.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 MIKET DSP SOLUTIONS, ALL RIGHTS RESERVED"          *
 *                                                                         *
 *-------------------------------------------------------------------------*/


#if ! defined(_dsp)
#include <stdio.h>
#include <conio.h>
#include <math.h>
#endif

#include "leci.h"

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

#define _DT_CRITERIA_MAX        (30)

/*--------------------- public vars ---------------------------------------*/
/*--------------------- local vars ----------------------------------------*/
/*--------------------- local functions -----------------------------------*/
/*-------------------------------------------------------------------------*/
static void                     _dt_start
/*-------------------------------------------------------------------------*/
(
LEC_tDb *pDb,
LEC_tSc *pSc
)
{
//    return;
    /* we start from assuming that this may be an echo path change.
       data base reset to:
            S32 slAcc;  - not modified. we need to keep it continuous.
            S16 sERL;   - as is;
            S16 sMSE;   - as is.
                MSE will be fast averaging during this period, so we keep
                current value.
            S16 sShift; - stays the same.
            S16 sStabCnt = 0;
               we also reset StabCnt, to re-init fast convergence tracking.
            S16 sErlAveraged; - as is
            S16 sErleAveraged; take a current Erle, and drop it by .... 
                it affects EstimatedErrorEn settings.
            U16 uConverged = 0;
                we set uConverged back to 0, to ensure fast adaptation.

     */
    pDb->Adf2.sErleAveraged = pSc->sErle;
    pDb->Adf2.uConverged = 0;
    // +: initialize erle test for echo path change
    pDb->sErrOverrideCnt = 0;

    if (pDb->Adf2.sShift != 0)
    {
        lec_rshift_adf(pDb->asAdf2, pDb->Adf2.sShift);
        pDb->Adf2.sShift = 0;
        pDb->Adf2.sNegShift = 0;
    }

}

/*-------------------------------------------------------------------------*/
static void                     _dt_end
/*-------------------------------------------------------------------------*/
(
LEC_tDb *pDb,
LEC_tSc *pSc
)
{
//    return;
    /* we think that it was a echo path change 
       if mse2 is good and erle gain is good.
       sometimes, mse2 is very good, 
       but erle gain did not have enough time to climb high.

       if DT was short, we do not have enough information to
       decide on echo path change, so we'd better
       to return to previous adf1.
    */

    S16 sErleDlt = pDb->sErrOverrideCnt;
    pSc->uFltCpyMode = 2;

    if (sErleDlt > LEC_DB(7.5))
    {
        S16 sMseDlt = pDb->Adf2.sMSE - pDb->Adf1.sMSE;
        sErleDlt = (S16)((sErleDlt * (S32)Q15(0.40))>>15);

        if ((sErleDlt - sMseDlt) > 0)
        {
            pSc->uFltCpyMode = 1;
        }
    }
}
/*-------------------------------------------------------------------------*/
static S16                      _dtd_expected_echo_en
/*-------------------------------------------------------------------------*/
(
LEC_tDb *pDb,
LEC_tSc *pSc
)
{
    // minus effect of possible misconvergence
    // adf2.slacc is always bigger than adf.slacc.
    S16 sConvLack = lec_en2log(LEC_SLACC_MAX) - // this is a const
                    lec_en2log(pDb->Adf1.slAcc);

    S16 sSuppression = pDb->Adf1.sErleAveraged;
    // subtraction of entire ConLack is too much...
    sSuppression -= sConvLack/2;
    if (sSuppression < 0)
        sSuppression = 0;

    return (pSc->sRxEn - sSuppression);
}
/*-------------------------------------------------------------------------*/
static S16                      _dtd_clip_level
/*-------------------------------------------------------------------------*/
(
LEC_tDb *pDb,
LEC_tSc *pSc,
S16 sExpectedEchoEn
)
{
    S16 sClipLevel;
    S16 sDlt;

    /* sometimes at the end of the word, vocal tract starts to vibrate
       at the basic frequency, and this is a wave form not observed
       before, and it is weak, and preceeded by a strong signal,
       so it is hard to adapt.
       that's why we slowly decay sExpectedEchoEn,
       but keep the rising edge fast.
     */
    if (pDb->sClipLevel < sExpectedEchoEn)
    {
        pDb->sClipLevel = sExpectedEchoEn;
    }
    else
    {
        lec_avrg(&pDb->sClipLevel, sExpectedEchoEn, Q15(0.2));
    }

    sClipLevel = pDb->sClipLevel;

    /* Noise floor estimations are never fully reliable,
       because it fluctates and we can not track it ideally.
       we need to provide some degree of robustness, otherwise 
       dt detector will give too many false alarms.
     */
    sDlt = sClipLevel - pDb->VadErr.sEnNoise;
    if (sDlt < LEC_DB(6))
    {
        sClipLevel -= sDlt/2 - LEC_DB(3);
    }
    if (sDlt < LEC_DB(-6))
    {
        sClipLevel -= sDlt/2 + LEC_DB(3);
    }

    pSc->sClipThreshold = lec_exp (sClipLevel + pDb->Cfg.sClipThr);

    return sClipLevel;
}
/*-------------------------------------------------------------------------*/
static S16                      _dtd_criteria
/*-------------------------------------------------------------------------*/
(
LEC_tDb *pDb,
LEC_tSc *pSc,
S16 sClipLevel
)
{
    /*  the criteria (current frame) is composed from erle loss
        and the number of samples exceeding clipping threshold.
        if any goes wrong, we have to suspect dt.
     */
    S16 sDtCriteria = pDb->VadErr.sEn - (sClipLevel + pDb->Cfg.sClipThr/4); 
    if (sDtCriteria < 0) 
        sDtCriteria = 0;
    sDtCriteria >>= 7;
    sDtCriteria += lec_pkt_excess(&pSc->asErr1[0], pSc->sClipThreshold);
    sDtCriteria -= 5;

    /* at least 3 frames for DT declaration */
    if (sDtCriteria > _DT_CRITERIA_MAX/10) 
        sDtCriteria = _DT_CRITERIA_MAX/10;
    return sDtCriteria;
}
/*-------------------------------------------------------------------------*/
static void                     _dtd_set_nlp_mode
/*-------------------------------------------------------------------------*/
(
LEC_tDb *pDb,
LEC_tSc *pSc,
S16 sDtCriteria
)
{
    S16 sErl = pDb->Adf1.sERL - LEC_DB(8);
    if (sErl < 0)
        sErl = 0;
    pSc->uNlpMode = LEC_NLP_PASS;

    /* if current frame is not a pure RX */
//    if (pDb->sErlOkCnt > 0)
    if (pSc->sErl > sErl)
    {
        if (pDb->uTone > 0)
        {
            if (pDb->Cfg.uControl & ILEC_CMD_TONE_ECHO)
            {
                // leave tone echo...
                pSc->uNlpMode = LEC_NLP_PASS;
            }
            else
            {
                // remove tone echo
                pSc->uNlpMode = LEC_NLP_CLEAR;
            }
        }
        else if (pSc->uFlags & LEC_FLAG_HIRCV)
        {
            if (pDb->Cfg.uControl & ILEC_CMD_CLR_HIRCV_DT)
            {
                pSc->uNlpMode = LEC_NLP_CLEAR;
            }
            else
            {
                if (! pDb->uIsDT)
                {
                    pSc->uNlpMode = LEC_NLP_CLEAR;
                }
                else
                {
                    pSc->uNlpMode = LEC_NLP_CLIP;
                }
            }
        }
    	else
        {
    	    pDb->sDtCriteria += sDtCriteria;

            if (pDb->sDtCriteria > ((2*_DT_CRITERIA_MAX)/3))
	        {
       	        pDb->uIsDT = TRUE;
                pSc->uNlpMode = LEC_NLP_PASS;

                /* limit from above */
	            /* hangover after DT is 120ms (20 frames) */
        	    if (pDb->sDtCriteria > _DT_CRITERIA_MAX)
	            {
    	    	    pDb->sDtCriteria = _DT_CRITERIA_MAX;
	            }
    	    }
	        else /* DtCrit < MAX/2 */
            {
                if (pDb->sDtCriteria <= 0)
                {
	    	        pDb->sDtCriteria = 0;
		            pDb->uIsDT = FALSE;
                }

                pSc->uNlpMode = LEC_NLP_CLIP;
                /* whether Dt or not, we calculate average values for erl and Erle
                   if dt, they will be discarged in the process of 1-2 flt copy.
                   if it is echo path change, that's a good start.
                 */
                if ((pSc->sRxEn - pDb->Adf2.sErleAveraged) > pDb->VadErr.sEnNoise)
                {
                    lec_avrg(&pDb->Adf2.sErleAveraged, pSc->sErle, Q15(0.03));
                }
	        }
        }
	}
	else // rx is not an echo
    {
        /* if this is a short rx, do not change dt status */
        pDb->sDtCriteria -= 1;
        if (pDb->sDtCriteria <= 0)
        {
            pDb->sDtCriteria = 0;
		    pDb->uIsDT = FALSE;
        }
        pSc->uNlpMode = LEC_NLP_PASS;
    }

    if (pDb->Cfg.uControl & ILEC_CMD_NLP_OFF)
    {
        // pass as is
        pSc->uNlpMode = LEC_NLP_PASS;
    }
}
/*--------------------- public  functions ---------------------------------*/

/*-------------------------------------------------------------------------*/
void                            lec_double_talk_detector
/*-------------------------------------------------------------------------*/
(
LEC_tDb *pDb,
LEC_tSc *pSc
)
{
    U16 uIsDT = pDb->uIsDT;
    S16 sExpectedEchoEn = _dtd_expected_echo_en(pDb, pSc);
    S16 sClipLevel      = _dtd_clip_level(pDb, pSc, sExpectedEchoEn);
    S16 sDtCriteria     = _dtd_criteria(pDb, pSc, sClipLevel);

    _dtd_set_nlp_mode(pDb, pSc, sDtCriteria);

    if (uIsDT != pDb->uIsDT)
    {
        if (pDb->uIsDT)
        {
            _dt_start(pDb, pSc);
        }
        else
        {
            _dt_end(pDb, pSc);
        }
    }
}

⌨️ 快捷键说明

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