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

📄 dtmf.c

📁 Express DSP compliant C55x DTMF detector software is proposed in two versions: one with a 5 ms frame
💻 C
字号:
/*-------------------------------------------------------------------------*
 *                                                                         *
 *   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 "dtmfi.h"


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

/* it should be bigger than any State no */ 
#define DTMF_ERR    (DTMF_ST_MAX)

#define DTMF_MASK_ST_IDLE       0x3305
#define DTMF_MASK_ST_MAYBE      0x7fc3
#define DTMF_MASK_ST_TONE       0x7ff3
#define DTMF_MASK_ST_GOOD_TONE  0x7ff3
#define DTMF_MASK_ST_END        0x0008
#define DTMF_MASK_ST_ABORT      0x0000

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

const char DTMF_MIKET_aVersion[] = __DATE__;

#if defined (_dsp)                 

#pragma DATA_SECTION (dtmf_aLoBpI, ".dtmf_i")
#pragma DATA_SECTION (dtmf_aHiBpI, ".dtmf_i")
#pragma DATA_SECTION (dtmf_aDialI, ".dtmf_i")
#pragma DATA_SECTION (dtmf_aLoBpQ, ".dtmf_q")
#pragma DATA_SECTION (dtmf_aHiBpQ, ".dtmf_q")
#pragma DATA_SECTION (dtmf_aDialQ, ".dtmf_q")

#pragma DATA_SECTION (dtmf_aLo,    ".dtmf_d")
#pragma DATA_SECTION (dtmf_a697,   ".dtmf_d")
#pragma DATA_SECTION (dtmf_a770,   ".dtmf_d")
#pragma DATA_SECTION (dtmf_a852,   ".dtmf_d")
#pragma DATA_SECTION (dtmf_a941,   ".dtmf_d")
#pragma DATA_SECTION (dtmf_aHi,    ".dtmf_d")
#pragma DATA_SECTION (dtmf_a1209,  ".dtmf_d")
#pragma DATA_SECTION (dtmf_a1336,  ".dtmf_d")
#pragma DATA_SECTION (dtmf_a1477,  ".dtmf_d")
#pragma DATA_SECTION (dtmf_a1633,  ".dtmf_d")

#endif

#include "dtmf_t.cc"

/*--------------------- local vars ----------------------------------------*/
static const S16 __aDtmfTable[16] = {
    IDTMF_TONE_1,
    IDTMF_TONE_4,
    IDTMF_TONE_7,
    IDTMF_TONE_STAR,
    IDTMF_TONE_2,
    IDTMF_TONE_5,
    IDTMF_TONE_8,
    IDTMF_TONE_0,
    IDTMF_TONE_3,
    IDTMF_TONE_6,
    IDTMF_TONE_9,
    IDTMF_TONE_POUND,
    IDTMF_TONE_A,
    IDTMF_TONE_B,
    IDTMF_TONE_C,
    IDTMF_TONE_D
};

static const S16 __asMask[] = 
{
    DTMF_MASK_ST_IDLE,
    DTMF_MASK_ST_MAYBE,
    DTMF_MASK_ST_TONE,
    DTMF_MASK_ST_GOOD_TONE,
    DTMF_MASK_ST_END,
    DTMF_MASK_ST_ABORT
};
/*--------------------- local functions -----------------------------------*/
/*-------------------------------------------------------------------------*/
static void                 dtmf_reset_stats
/*-------------------------------------------------------------------------*/
(
DTMF_tDb *pDb,
DTMF_tSc *pSc
)
{
    pDb->v.Stat.sEn        = 0;
    pDb->v.Stat.sLoEn      = 0;
    pDb->v.Stat.sHiEn      = 0;
    pDb->v.Stat.sLoFreqDev = 0;
    pDb->v.Stat.sHiFreqDev = 0;
}
/*-------------------------------------------------------------------------*/
static void                 dtmf_init_stats
/*-------------------------------------------------------------------------*/
(
DTMF_tDb *pDb,
DTMF_tSc *pSc
)
{
    pDb->v.Stat.sEn        = pSc->sSumEn;
    pDb->v.Stat.sLoEn      = pSc->sLoMaxFreqEn;
    pDb->v.Stat.sHiEn      = pSc->sHiMaxFreqEn;
    pDb->v.Stat.sLoFreqDev = pSc->sLoFreqDev;
    pDb->v.Stat.sHiFreqDev = pSc->sHiFreqDev;
}
/*-------------------------------------------------------------------------*/
static void                 dtmf_update_stats
/*-------------------------------------------------------------------------*/
(
DTMF_tDb *pDb,
DTMF_tSc *pSc
)
{
    S32 ac0;
    S32 ac1;

    ac0 = pDb->v.Stat.sEn;
    ac1 = pSc->sSumEn;
    ac0 += (ac1 - ac0)>>2;
    pDb->v.Stat.sEn = (S16)ac0;

    ac0 = pDb->v.Stat.sLoEn;
    ac1 = pSc->sLoMaxFreqEn;
    ac0 += (ac1 - ac0)>>2;
    pDb->v.Stat.sLoEn = (S16)ac0;

    ac0 = pDb->v.Stat.sHiEn;
    ac1 = pSc->sHiMaxFreqEn;
    ac0 += (ac1 - ac0)>>2;
    pDb->v.Stat.sHiEn = (S16)ac0;

    ac0 = pDb->v.Stat.sLoFreqDev;
    ac1 = pSc->sLoFreqDev;
    ac0 += (ac1 - ac0)>>2;
    pDb->v.Stat.sLoFreqDev = (S16)ac0;

    ac0 = pDb->v.Stat.sHiFreqDev;
    ac1 = pSc->sHiFreqDev;
    ac0 += (ac1 - ac0)>>2;
    pDb->v.Stat.sHiFreqDev = (S16)ac0;
}
/*-------------------------------------------------------------------------*/
static S16                  dtmf_state_machine
/*-------------------------------------------------------------------------*/
(
DTMF_tDb *pDb,
DTMF_tSc *pSc
)
{
    const IDTMF_tCfg *pCfg = pDb->pCfg;
    S16 sNewDigit = pSc->sDigit;
    S16 sReport = 0;
    S16 sErr;

    sErr = (pSc->sTestFailed & __asMask[pDb->v.sState]) ? DTMF_ERR : 0;
    
    switch(pDb->v.sState + sErr)
    {
    /* IDLE: we are looking for an energy jump, 
        and some degree of spectrum cleanness */
    case DTMF_ST_IDLE + DTMF_ERR:
        dtmf_reset_stats(pDb, pSc);
        break;
    case DTMF_ST_IDLE + 0:   
        pDb->v.sState = DTMF_ST_MAYBE;
        pDb->v.sLastDigit = sNewDigit;
        break;

    /* MAYBE: one frame state. to confirm that energy is ok, 
        and freq content is fine: 2 consequence packets have the same freq,
        and spectrum is clean enough */
    case DTMF_ST_MAYBE + DTMF_ERR:  
        pDb->v.sState = DTMF_ST_IDLE;
        break;
    case DTMF_ST_MAYBE + 0:  
        dtmf_init_stats(pDb, pSc);
        pDb->v.sState = DTMF_ST_TONE;
        sReport = __aDtmfTable[sNewDigit] | IDTMF_EV_EARLY_ON;
        break;

    /* TONE: here we expect several good frame after a seemingly good beginning,
       max 1 bad frame in a row is allowed, 
        due to a short hole (s) / interference */
    case DTMF_ST_TONE + DTMF_ERR:   
        pDb->v.sBad += 2;
        if (pDb->v.sBad > 3)
        {
            pDb->v.sState = DTMF_ST_ABORT;
            sReport = __aDtmfTable[pDb->v.sLastDigit] | IDTMF_EV_EARLY_OFF;
        }
        else ; /* only the first time -- do nothing */
        break;
    case DTMF_ST_TONE + 0:   
        dtmf_update_stats(pDb, pSc);
        if (pDb->v.sBad > 0) pDb->v.sBad -= 1;

        pDb->v.sGood++;
        if (pDb->v.sGood > pCfg->sMinToneDuration)
        {
            pDb->v.sState = DTMF_ST_GOOD_TONE;
            sReport = __aDtmfTable[pDb->v.sLastDigit] | IDTMF_EV_START;
        }
        break;

    /* GOOD_TONE: the tone has lasted for a min duration. 
        let's wait till it ends */
    case DTMF_ST_GOOD_TONE + DTMF_ERR:
        pDb->v.sBad += 2;
        if (pDb->v.sBad > 3) /* 2 bad packets in a row.. or 4 apart */
        {
            if ((pSc->sTestFailed & DTMF_MASK_FL_EDGE) == 0)
            {
                pDb->v.sState = DTMF_ST_END;
                pDb->v.sGood = 3;
                pDb->v.sBad = 0;
            }
            else /* where is the post-silence ?? abort it ! */
            {
                pDb->v.sState = DTMF_ST_ABORT;
                sReport = __aDtmfTable[pDb->v.sLastDigit] | IDTMF_EV_ABORT;
            }
        }
        break;
    case DTMF_ST_GOOD_TONE + 0:
        dtmf_update_stats(pDb, pSc);
        if (pDb->v.sBad > 0) pDb->v.sBad -= 1;
        break;

    /* END: there should be a period of relative silence after the tone,
       and we also shall ignore possible DTMF echo shortly ather the tone.
       here we test only that energy is low.
       if we have 2 bad packets, we go to idle without reporting of END */
    case DTMF_ST_END + DTMF_ERR:
        pDb->v.sGood = 0;
        pDb->v.sBad++;
        if (pDb->v.sBad > 1)
        {
            /* go to idle without reporting */
            pDb->v.sState = DTMF_ST_IDLE; 
            pDb->v.sGood = 0;
            pDb->v.sBad = 0;
        }
        else ; /* still wait for 2nd bad packet */
        break;
    case DTMF_ST_END + 0:
        pDb->v.sGood++;
        if (pDb->v.sGood > pCfg->sMinPostSilenceDuration)
        {
            pDb->v.sState = DTMF_ST_IDLE;
            sReport = __aDtmfTable[pDb->v.sLastDigit] | IDTMF_EV_END;
            pDb->v.sGood = 0;
            pDb->v.sBad = 0;
        }
        else ; /* do nothing */
        break;

    /* ABORT: just wait for some time and back to work, ignoring error mask */
    case DTMF_ST_ABORT:
        pDb->v.sBad++;
        if (pDb->v.sBad > pCfg->sAbortTimeout)
        {
            pDb->v.sState = DTMF_ST_IDLE;
            pDb->v.sGood = 0;
            pDb->v.sBad = 0;
        }
        break;
    }
    return sReport;
}

/*---------------------------------------------------------------------*/
void                        dtmf_reset_db
/*---------------------------------------------------------------------*/
(
DTMF_tDb *pDb
)
{
        memset(pDb, 0, sizeof(DTMF_tDb));
        pDb->v.sLastDigit = -1; 
#if (IDTMF_FR_SZ == 40)
        pDb->v.slDialEn = 0x80000L;
#endif
}
        
/*--------------------- public  functions ---------------------------------*/

/*---------------------------------------------------------------------*/
void                        DTMF_MIKET_init_db
/*---------------------------------------------------------------------*/
(
void *p2db,
IDTMF_tCfg *pCfg
)
{
    DTMF_tDb *pDb = (DTMF_tDb *)p2db;

    dtmf_reset_db(pDb); 
    pDb->pCfg = pCfg;
}

/*---------------------------------------------------------------------*/
void                         DTMF_MIKET_control
/*---------------------------------------------------------------------*/
(
void *p2db,
Int sCmd,
IDTMF_Status *pStatus
)
{
    DTMF_tDb *pDb = (DTMF_tDb *)p2db;

    if (sCmd & IDTMF_CMD_RESET) 
    {
        const IDTMF_tCfg *pCfgSav = pDb->pCfg;
        dtmf_reset_db(pDb);
        pDb->pCfg = pCfgSav;
    }

    pDb->sIsOff = (sCmd & IDTMF_CMD_OFF) ? TRUE : FALSE;
    
    if (sCmd & IDTMF_CMD_CFG)
    {
    	pDb->pCfg = pStatus->pCfg;
    }
    
    if (sCmd & IDTMF_CMD_GETSTAT)
    {
    	pStatus->sLoEn = pDb->v.Stat.sLoEn;
    	pStatus->sHiEn = pDb->v.Stat.sHiEn;
    	pStatus->sLoFreqDev = pDb->v.Stat.sLoFreqDev;
    	pStatus->sHiFreqDev = pDb->v.Stat.sHiFreqDev;
    }
    pStatus->size = sizeof(IDTMF_Status);

}
/*-------------------------------------------------------------------------*/
S16                         DTMF_MIKET_process
/*-------------------------------------------------------------------------*/
(
void *p2db,
void *p2sc,
S16 *pIn
)
{
    DTMF_tDb *pDb = (DTMF_tDb *)p2db;
    DTMF_tSc *pSc = (DTMF_tSc *)p2sc;
    S16 sReport = 0;

    if (!pDb->sIsOff)
    {
/*    	memset(pSc, 0, sizeof(DTMF_tSc)); */
        dtmf_filter_test (pDb, pSc, pIn);
        sReport = dtmf_state_machine (pDb, pSc);
    }
    return sReport;
}



⌨️ 快捷键说明

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