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

📄 lecns.c

📁 The line echo canceller (LEC) is designed to provide the maximum attainable transparent voice qualit
💻 C
📖 第 1 页 / 共 2 页
字号:
/*-------------------------------------------------------------------------*
 *                                                                         *
 *   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 _LINEAR                 (0)
#define _DT_CRITERIA_MAX        (30)

#define _NLP_PASS               (1)
#define _NLP_CLIP               (2)
#define _NLP_CLEAR              (3)

#define _NSE_PLS                (LEC_DB(11))


/*--------------------- public vars ---------------------------------------*/
/*--------------------- local vars ----------------------------------------*/
#if defined (_dsp)
#pragma DATA_SECTION(_aasLpFilter, ".lecd")
#endif
const S16 _aasLpFilter[][6] = 
{
    {Q15(0.167), Q15(0.167), Q15(0.167), Q15(0.167), Q15(0.167), Q15(0.167)}, // 1.0
    {Q15(0.271), Q15(0.217), Q15(0.174), Q15(0.139), Q15(0.111), Q15(0.089)}, // 0.8
    {Q15(0.420), Q15(0.252), Q15(0.151), Q15(0.091), Q15(0.054), Q15(0.033)}, // 0.6
    {Q15(0.603), Q15(0.241), Q15(0.097), Q15(0.039), Q15(0.015), Q15(0.006)}, // 0.4
    {Q15(0.800), Q15(0.160), Q15(0.032), Q15(0.006), Q15(0.001), Q15(0.000)}, // 0.2
    {32767,      Q15(0.000), Q15(0.000), Q15(0.000), Q15(0.000), Q15(0.000)}, // 0.0
};

#if defined (_dsp)
#pragma DATA_SECTION(_asNoiseAdd, ".lece")
#endif
const S16 _asNoiseAdd[9] = {
    LEC_DB(-12.0),
    LEC_DB(-7.0),
    LEC_DB(-5.0),
    LEC_DB(-3.0),

    LEC_DB(-2.0),

    LEC_DB(-1.5),
    LEC_DB(-1.0),
    LEC_DB(-0.5),
    LEC_DB(-0.25),
};

/*--------------------- local functions -----------------------------------*/
/*-------------------------------------------------------------------------*/
void                     _lp_residual_error
/*-------------------------------------------------------------------------*/
(
LEC_tDb *pDb,
LEC_tSc *pSc
)
{
    S16 sIdx;
    
    /* if residual echo is not going to be masked by rx,
       then we'd better filter it out. 
       if Err signal is higher than noise level,
       and erle loss is high, then we should
       increase filtering as erle loss progresses.
     */
#if _LINEAR // transparent mode
    if (pDb->sClipLevel > pDb->VadErr.sEnNoise - LEC_DB(12))
#else
    if (pDb->sClipLevel > pDb->VadErr.sEnNoise + LEC_DB(6))
#endif
    {
        sIdx = pDb->VadErr.sEn - pDb->sClipLevel;
        sIdx >>= 10; // an idx per 6 db erle loss
        if (sIdx < 0) sIdx = 0;
        if (sIdx > 5) sIdx = 5;
    }
    else // no filter
    {
        sIdx = 5;
    }
    
    lec_lp_residual_error(pDb, pSc, &_aasLpFilter[sIdx][0]);
}

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

/*-------------------------------------------------------------------------*/
void                        lec_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.sStabCnt = 0;
    pDb->Adf2.uConverged = 0;

    if (pDb->Adf2.sShift != 0)
    {
        S16 k;
        S16 rnd = (1<<(pDb->Adf2.sShift-1));

        for (k = 0; k < LEC_FLT2_SZ; k++)
        {
            pDb->psFlt2[k] = (pDb->psFlt2[k] + rnd) >> pDb->Adf2.sShift;
        }
        pDb->Adf2.sShift = 0;
    }

    // +: initialize erle test for echo path change
    pDb->sErrOverrideCnt = 0;
}

/*-------------------------------------------------------------------------*/
void                        lec_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;
        }
    }
}

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

    S16 sSuppression = pDb->Adf1.sErleAveraged;

    // minus effect of possible misconvergence
    // adf2.slacc is always bigger than adf.slacc.
    S16 sConvLack = lec_en2log(LEC_SLACC_MAX,0) - // this is a const
                    lec_en2log(pDb->Adf1.slAcc,0);

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

    sExpectedEchoEn = pSc->sRxEn - sSuppression;

    /* 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.
     */
    if (sClipLevel < pDb->VadErr.sEnNoise - LEC_DB(6))
    {
        sClipLevel = pDb->VadErr.sEnNoise;
    }
    /* if between -6 and +6, then: interpolate */
    else if (sClipLevel < pDb->VadErr.sEnNoise + LEC_DB(6))
    {
        S16 sDlt = sClipLevel - (pDb->VadErr.sEnNoise - LEC_DB(6));
        sClipLevel = pDb->VadErr.sEnNoise + sDlt/2;
    }
    else; // unmodified

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

    /*  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.
     */
    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;

    pSc->uNlpMode = _NLP_PASS;

    /* if current frame is not a pure RX */
    if (pDb->sErlOkCnt > 0)
    {
        if (pDb->uIsTone)
        {
//            sDtCriteria = 0;
            if (pDb->Cfg.uControl & ILEC_CMD_TONE_ECHO_MASK)
            {
                // leave tone echo...
                pSc->uNlpMode = _NLP_PASS;
            }
            else
            {
                // remove tone echo
//                pDb->sDtCriteria = 0;
//                pDb->uIsDT = FALSE;

⌨️ 快捷键说明

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