windows-nt/Source/XPSP1/NT/enduser/netmeeting/av/nac/dtmf.cpp

384 lines
8.1 KiB
C++
Raw Normal View History

2020-09-26 03:20:57 -05:00
#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;
}