📄 dtmf.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 + -