#include "precomp.h" #include "dtmf.h" #include // 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; }