|
|
#include "precomp.h"
#include "dtmf.h"
#include <math.h>
// has to be #defines, not const int's because our build environment
// doesn't like the embedded structure stuff.
#define DTMF_ROW1_FREQ 697
#define DTMF_ROW2_FREQ 770
#define DTMF_ROW3_FREQ 852
#define DTMF_ROW4_FREQ 941
#define DTMF_COL1_FREQ 1209
#define DTMF_COL2_FREQ 1336
#define DTMF_COL3_FREQ 1477
#define DTMF_COL4_FREQ 1633
// For "A", "B", "C", "D"
// the length of all the tones are the same except for "zero"
// Zero needs longer because it just barely survives G.723 compression
const int DTMF_TONE_RAMP_MS = 60; // ramp up/down time
const int DTMF_TONE_LENGTH_MS = 240; // includes ramp time!
const int DTMF_SILENCE_LENGTH_MS = 240; // silence gap between tones
const double DTMF_AMP_FREQ1 = 17000; const double DTMF_AMP_FREQ2 = 14000;
struct DTMF_TONE { int freq1; int freq2; int nLengthMS; // length in milliseconds
};
const int DTMF_NUM_TONES = 16; const int DTMF_SILENCE = -1;
DTMF_TONE DTMF_TONE_DEF_LIST[] = { {DTMF_ROW4_FREQ, DTMF_COL2_FREQ, DTMF_TONE_LENGTH_MS}, //0
{DTMF_ROW1_FREQ, DTMF_COL1_FREQ, DTMF_TONE_LENGTH_MS}, //1
{DTMF_ROW1_FREQ, DTMF_COL2_FREQ, DTMF_TONE_LENGTH_MS}, //2
{DTMF_ROW1_FREQ, DTMF_COL3_FREQ, DTMF_TONE_LENGTH_MS}, //3
{DTMF_ROW2_FREQ, DTMF_COL1_FREQ, DTMF_TONE_LENGTH_MS}, //4
{DTMF_ROW2_FREQ, DTMF_COL2_FREQ, DTMF_TONE_LENGTH_MS}, //5
{DTMF_ROW2_FREQ, DTMF_COL3_FREQ, DTMF_TONE_LENGTH_MS}, //6
{DTMF_ROW3_FREQ, DTMF_COL1_FREQ, DTMF_TONE_LENGTH_MS}, //7
{DTMF_ROW3_FREQ, DTMF_COL2_FREQ, DTMF_TONE_LENGTH_MS}, //8
{DTMF_ROW3_FREQ, DTMF_COL3_FREQ, DTMF_TONE_LENGTH_MS}, //9
{DTMF_ROW4_FREQ, DTMF_COL1_FREQ, DTMF_TONE_LENGTH_MS}, //STAR
{DTMF_ROW4_FREQ, DTMF_COL3_FREQ, DTMF_TONE_LENGTH_MS}, //POUND
{DTMF_ROW1_FREQ, DTMF_COL4_FREQ, DTMF_TONE_LENGTH_MS}, //A
{DTMF_ROW2_FREQ, DTMF_COL4_FREQ, DTMF_TONE_LENGTH_MS}, //B
{DTMF_ROW3_FREQ, DTMF_COL4_FREQ, DTMF_TONE_LENGTH_MS}, //C
{DTMF_ROW4_FREQ, DTMF_COL4_FREQ, DTMF_TONE_LENGTH_MS}, //D
};
DTMFQueue::DTMFQueue() : m_aTones(NULL), m_bInitialized(false), m_nQueueHead(0), m_nQueueLength(0), m_hEvent(NULL) { InitializeCriticalSection(&m_cs); m_hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); }
DTMFQueue::~DTMFQueue() { DeleteCriticalSection(&m_cs); ReleaseToneBank(); CloseHandle(m_hEvent); }
HRESULT DTMFQueue::Initialize(WAVEFORMATEX *pWaveFormat) { if (m_bInitialized) { ReleaseToneBank(); } m_nQueueLength = 0; m_nQueueHead = 0;
return GenerateTones(pWaveFormat); };
HRESULT DTMFQueue::GenerateTones(WAVEFORMATEX *pWaveFormat) { int nIndex; int nToneLength; // tone length in bytes
int nToneLengthMS; // tone length in millisecs
ReleaseToneBank();
m_WaveFormat = *pWaveFormat;
DBG_SAVE_FILE_LINE m_aTones = new PBYTE[DTMF_NUM_TONES]; // array of 16 tones
if (m_aTones == NULL) { return E_OUTOFMEMORY; }
// allocate memory for each tone
for (nIndex = 0; nIndex < DTMF_NUM_TONES; nIndex++) { nToneLengthMS = DTMF_TONE_DEF_LIST[nIndex].nLengthMS;
nToneLength = (pWaveFormat->nSamplesPerSec) * (pWaveFormat->wBitsPerSample) / 8; nToneLength = (nToneLength * nToneLengthMS) / 1000;
DBG_SAVE_FILE_LINE m_aTones[nIndex] = new BYTE[nToneLength];
if (m_aTones[nIndex] == NULL) { return E_OUTOFMEMORY; }
CreateDTMFTone(m_aTones[nIndex], nToneLength, nIndex); }
m_bInitialized = true; return S_OK; }
void DTMFQueue::CreateDTMFTone(BYTE *pTone, int nToneLength, int toneID) { ZeroMemory(pTone, nToneLength);
AddSignal(pTone, DTMF_TONE_DEF_LIST[toneID].freq1, DTMF_AMP_FREQ1, nToneLength); AddSignal(pTone, DTMF_TONE_DEF_LIST[toneID].freq2, DTMF_AMP_FREQ2, nToneLength); }
void DTMFQueue::AddSignal(BYTE *pTone, int nFrequency, double dAmp, int nLength) { double d; int nIndex; SHORT *aSamples = (SHORT*)pTone; SHORT shSample; BYTE nSample8; double dRampAmpInc, dRampAmp; int nRampSamples; const double PI = 3.1415926535897932384626433832795;
nRampSamples = (m_WaveFormat.nSamplesPerSec * DTMF_TONE_RAMP_MS) / 1000; dRampAmpInc = 1.0 / nRampSamples; dRampAmp = 0.0;
if (m_WaveFormat.wBitsPerSample == 16) { nLength = nLength / 2; for (nIndex = 0; nIndex < nLength; nIndex++) { // y = sin((x * 2 * PI * f)/SRATE)
// d is a value between -1 and +1;
d = sin((PI * (2.0 * (nIndex * nFrequency))) / m_WaveFormat.nSamplesPerSec);
if (nIndex < nRampSamples) { dRampAmp = dRampAmpInc * nIndex; } else if ((nIndex+nRampSamples) >= nLength) { dRampAmp = dRampAmpInc * (nLength - nIndex - 1); } else { dRampAmp = 1.0; }
shSample = (SHORT)(dAmp * d * dRampAmp);
aSamples[nIndex] += shSample; }
return; }
// 8-bit samples have a center point of 128
// must invert high order bit to compensate
for (nIndex = 0; nIndex < nLength; nIndex++) { d = sin((PI * (2.0 * (nIndex * nFrequency))) / m_WaveFormat.nSamplesPerSec);
if (nIndex < nRampSamples) { dRampAmp = dRampAmpInc * nIndex; } else if ((nIndex+nRampSamples) >= nLength) { dRampAmp = dRampAmpInc * (nLength - nIndex - 1); } else { dRampAmp = 1.0; }
shSample = (SHORT)(dAmp * d * dRampAmp); shSample = (shSample >> 8) & 0x00ff; nSample8 = (BYTE)shSample; nSample8 = nSample8 ^ 0x80; pTone[nIndex] = nSample8; } return; };
void DTMFQueue::ReleaseToneBank() { int nIndex; if (m_aTones) { for (nIndex = 0; nIndex < DTMF_NUM_TONES; nIndex++) { delete [] m_aTones[nIndex]; } delete [] m_aTones; m_aTones = NULL; } m_bInitialized = false; }
HRESULT DTMFQueue::AddDigitToQueue(int nDigit) { int nQueueIndex; int nToneLength, nToneLengthMS; int nSilenceLength;
if (m_bInitialized == false) return E_FAIL;
if ((nDigit < 0) || (nDigit >= DTMF_NUM_TONES)) { return E_FAIL; }
EnterCriticalSection(&m_cs); if (m_nQueueLength >= (DTMF_QUEUE_SIZE-1)) { LeaveCriticalSection(&m_cs); return E_FAIL; }
nToneLengthMS = DTMF_TONE_DEF_LIST[nDigit].nLengthMS; nToneLength = (m_WaveFormat.nSamplesPerSec) * (m_WaveFormat.wBitsPerSample) / 8;
nSilenceLength = (nToneLength * DTMF_SILENCE_LENGTH_MS) / 1000; nToneLength = (nToneLength * nToneLengthMS) / 1000;
// add silence to pad between tones. Also helps to "reset" the codec
// to a good state
nQueueIndex = (m_nQueueHead + m_nQueueLength) % DTMF_QUEUE_SIZE; m_aTxQueue[nQueueIndex].nBytesToCopy = nSilenceLength; m_aTxQueue[nQueueIndex].nToneID = DTMF_SILENCE; m_aTxQueue[nQueueIndex].nOffsetStart = 0; m_nQueueLength++;
// add the tone to the read queue
nQueueIndex = (m_nQueueHead + m_nQueueLength) % DTMF_QUEUE_SIZE; m_aTxQueue[nQueueIndex].nBytesToCopy = nToneLength; m_aTxQueue[nQueueIndex].nToneID = nDigit; m_aTxQueue[nQueueIndex].nOffsetStart = 0; m_nQueueLength++;
LeaveCriticalSection(&m_cs); return S_OK; }
HRESULT DTMFQueue::ReadFromQueue(BYTE *pBuffer, UINT uSize) { DTMF_TX_ELEMENT *pQueueElement; int nSilenceOffset; BYTE fillByte;
if (m_bInitialized == false) return E_FAIL;
if (m_WaveFormat.wBitsPerSample == 8) { fillByte = 0x80; } else { ASSERT((uSize % 2) == 0); // uSize must be even for 16-bit fills
fillByte = 0; }
EnterCriticalSection(&m_cs);
if (m_nQueueLength <= 0) { LeaveCriticalSection(&m_cs); return E_FAIL; }
pQueueElement = &m_aTxQueue[m_nQueueHead];
if (pQueueElement->nBytesToCopy <= (int)uSize) { if (pQueueElement->nToneID == DTMF_SILENCE) { FillMemory(pBuffer, uSize, fillByte); } else { CopyMemory(pBuffer, pQueueElement->nOffsetStart + m_aTones[pQueueElement->nToneID], pQueueElement->nBytesToCopy); FillMemory(pBuffer+(pQueueElement->nBytesToCopy), uSize-(pQueueElement->nBytesToCopy), fillByte); } m_nQueueHead = (m_nQueueHead + 1) % DTMF_QUEUE_SIZE; m_nQueueLength--; }
else { if (pQueueElement->nToneID == DTMF_SILENCE) { FillMemory(pBuffer, uSize, fillByte); } else { CopyMemory(pBuffer, pQueueElement->nOffsetStart + m_aTones[pQueueElement->nToneID], uSize); } pQueueElement->nBytesToCopy -= uSize; pQueueElement->nOffsetStart += uSize; }
LeaveCriticalSection(&m_cs);
return S_OK; }
HRESULT DTMFQueue::ClearQueue() { if (m_bInitialized == false) return E_FAIL;
EnterCriticalSection(&m_cs); m_nQueueHead = 0; m_nQueueLength = 0; LeaveCriticalSection(&m_cs);
return S_OK; }
|