www.pudn.com > dtmffft.zip > dtmffft.c


// DTMFFFT.C (c) 2004 Howard Long (G6LVB), Hanlincrest Ltd. All rights reserved. 
// 72 Princes Gate 
// London SW7 2PA 
// United Kingdom 
// howard@hanlincrest.com 
// Free for educational and non-profit use. For commercial use please contact the author. 
 
#include  
#include  
#include  
 
#include "resource.h" 
 
#include "fft.h" 
#include "about.h" 
#include "parameter.h" 
#include "registry.h" 
 
#define DF_NUMCHANNELS 1 // Number of audio channels for calc of buffer size - we're mono, so only 1 
#define DF_SAMPLESPERSEC 11025 
#define DF_BITSPERSAMPLE 16 
#define DF_BLOCKALIGN ((DF_NUMCHANNELS * DF_BITSPERSAMPLE)/8) 
#define DF_AVGBYTESPERSEC (DF_BLOCKALIGN * DF_SAMPLESPERSEC) 
 
#define DF_FFTLEN 256 
#define DF_TIMEBUFFER 1 // Number of seconds to buffer 
#define DF_NUMBUFFERS (DF_TIMEBUFFER * DF_SAMPLESPERSEC / DF_FFTLEN) // Choose enough buffers 
#define DF_BUFFERSIZE (DF_FFTLEN * DF_BLOCKALIGN) 
 
#define DF_WM_USER_REDRAW (WM_USER) 
#define DF_WM_USER_NEWCHAR (WM_USER+1) 
 
// The size of each FFT bin in Hz 
#define DF_BINSIZE ((double)DF_SAMPLESPERSEC / DF_FFTLEN) 
 
// DTMF tones in Hz 
#define DF_DTMFTONEX0 1209 
#define DF_DTMFTONEX1 1336 
#define DF_DTMFTONEX2 1477 
#define DF_DTMFTONEX3 1633 
#define DF_DTMFTONEY0 697 
#define DF_DTMFTONEY1 770 
#define DF_DTMFTONEY2 852 
#define DF_DTMFTONEY3 941 
 
// DTMF FFT Bin indices 
#define DF_DTMFBINX0 ((int)((DF_DTMFTONEX0 / DF_BINSIZE) + 0.5)) 
#define DF_DTMFBINX1 ((int)((DF_DTMFTONEX1 / DF_BINSIZE) + 0.5)) 
#define DF_DTMFBINX2 ((int)((DF_DTMFTONEX2 / DF_BINSIZE) + 0.5)) 
#define DF_DTMFBINX3 ((int)((DF_DTMFTONEX3 / DF_BINSIZE) + 0.5)) 
#define DF_DTMFBINY0 ((int)((DF_DTMFTONEY0 / DF_BINSIZE) + 0.5)) 
#define DF_DTMFBINY1 ((int)((DF_DTMFTONEY1 / DF_BINSIZE) + 0.5)) 
#define DF_DTMFBINY2 ((int)((DF_DTMFTONEY2 / DF_BINSIZE) + 0.5)) 
#define DF_DTMFBINY3 ((int)((DF_DTMFTONEY3 / DF_BINSIZE) + 0.5)) 
 
#define DF_DTMFBINMIN DF_DTMFBINY0 
#define DF_DTMFBINMAX DF_DTMFBINX3 
#define DF_DTMFBINMINX DF_DTMFBINX0 
#define DF_DTMFBINMAXX DF_DTMFBINX3 
#define DF_DTMFBINMINY DF_DTMFBINY0 
#define DF_DTMFBINMAXY DF_DTMFBINY3 
 
#define DF_DLGSTRINGLEN 30 
#define DF_DEFAULTNUMDLGPARAMETERS 3 // Default number of dialog box parameters used 
#define DF_MAXDLGPARAMETERS 5 // Maximum number of dialog box parameters possible 
 
typedef struct 
{ 
	WAVEHDR wh; 
	DWORD dwTickCount; // Tick time that queue item was set up 
	BOOL bBufferReady; 
	BOOL bWHPrepared; 
} DFQUEUEWHSTRUCT, *PDFQUEUEWHSTRUCT; 
 
typedef struct 
{ 
	CRITICAL_SECTION cs; 
	DFQUEUEWHSTRUCT adfqwhs[DF_NUMBUFFERS]; // Pool of wave headers 
	int anIdx[DF_NUMBUFFERS]; // Queue list of indexes of DFQUEUEWHSTRUCTS 
	int nHead; 
	int nTail; 
} DFQUEUESTRUCT, *PDFQUEUESTRUCT; 
 
typedef struct 
{ 
	int nDlgIDName; 
	int nDlgIDUnits; 
	int nDlgIDValue; 
	char szName[DF_DLGSTRINGLEN]; 
	char szUnits[DF_DLGSTRINGLEN]; 
	double dOffset; 
	double dMultiplier; 
} DFDLGPARAMETERSTRUCT; 
 
typedef struct 
{ 
	HWAVEIN hwi; 
	HWND hdlg; 
	HFONT hf; // Monospaced font 
	HANDLE ht; // Thread handle 
	DWORD dwThreadID; // Thread ID 
	DFQUEUESTRUCT dfqs; // Queue of wave headers 
	FILE *pf; // File holding output data 
	FFTSTRUCT fs; // FFT structure 
	CRITICAL_SECTION cs; 
	HANDLE hevent; // Used by application thread to signal FFT thread 
	BOOL bStarting; // Is thread in the process of starting up? 
	BOOL bStarted; // Has thread started? 
	BOOL bFinish; // Used to notify thread to end 
	BOOL bFinished; // Has thread ended? 
	S32 as32Buffer[DF_FFTLEN/2]; 
	int nThreshold; // Threshold set by user 
	S32 s32ThresholdAGC; // Threshold ajusted by AGC and user's threshold setting 
	int nWaveBuffers; // Number of WAV buffers queued up 
	int nLastToneBitmapX; // Bitmap of last tone X group 
	int nLastToneBitmapY; // Bitmap of last tone Y group 
	int nNumDlgParms; // Number of telemtry parameters (up to DF_MAXDLGPARAMETERS) 
	DFDLGPARAMETERSTRUCT ddps[DF_MAXDLGPARAMETERS]; 
} DFTHREADSTRUCT, *PDFTHREADSTRUCT; 
 
static HINSTANCE _hinst=NULL; 
 
static void DFQueueTerm(HWAVEIN hwi,PDFQUEUESTRUCT pdfqs) 
{ 
	int n; 
 
	for (n=0;nadfqwhs[n].bWHPrepared) 
		{ 
			waveInUnprepareHeader(hwi,&pdfqs->adfqwhs[n].wh,sizeof(pdfqs->adfqwhs[n].wh)); 
			pdfqs->adfqwhs[n].bWHPrepared=FALSE; 
		} 
 
		if (pdfqs->adfqwhs[n].wh.lpData!=NULL) 
		{ 
			free(pdfqs->adfqwhs[n].wh.lpData); 
			pdfqs->adfqwhs[n].wh.lpData=NULL; 
		} 
	} 
	DeleteCriticalSection(&pdfqs->cs); 
} 
 
static BOOL DFQueueInit(HWAVEIN hwi,PDFQUEUESTRUCT pdfqs) 
{ 
	int n; 
 
	InitializeCriticalSection(&pdfqs->cs); 
	pdfqs->nHead=0; 
	pdfqs->nTail=0; 
	for (n=0;nadfqwhs[n].wh.lpData=NULL; 
		pdfqs->adfqwhs[n].bWHPrepared=FALSE; 
		pdfqs->adfqwhs[n].bBufferReady=FALSE; 
		pdfqs->anIdx[n]=-1; 
	} 
	for (n=0;nadfqwhs[n].wh.lpData=malloc(DF_BUFFERSIZE))==NULL) 
		{ 
			DFQueueTerm(hwi,pdfqs); 
			return FALSE; 
		} 
		pdfqs->adfqwhs[n].wh.dwBufferLength=DF_BUFFERSIZE; 
		pdfqs->adfqwhs[n].wh.dwFlags=0; 
		pdfqs->adfqwhs[n].wh.dwUser=(DWORD)n; // So that when given a WAVEHDR, the callback can figure out which one it is. 
 
		if (waveInPrepareHeader(hwi,&pdfqs->adfqwhs[n].wh,sizeof(pdfqs->adfqwhs[n].wh))!=0) 
		{ 
			DFQueueTerm(hwi,pdfqs); 
			return FALSE; 
		} 
		pdfqs->adfqwhs[n].bWHPrepared=TRUE; 
		waveInAddBuffer(hwi,&pdfqs->adfqwhs[n].wh,sizeof(pdfqs->adfqwhs[n].wh)); 
	} 
	return TRUE; 
} 
 
static BOOL DFQueuePut(PDFQUEUESTRUCT pdfqs,LPWAVEHDR lpwh) 
{ 
	// Insert a filled buffer into the queue. 
	EnterCriticalSection(&pdfqs->cs); 
	{ 
		int nNext=((pdfqs->nHead)+1) % DF_NUMBUFFERS; 
		int nIdx=(int)lpwh->dwUser; // User data is index of WAVEHDR item 
 
		if (nNext==pdfqs->nTail) 
		{ 
			LeaveCriticalSection(&pdfqs->cs); 
			return FALSE; // Overrun! 
		} 
		pdfqs->anIdx[pdfqs->nHead]=nIdx; 
		pdfqs->adfqwhs[nIdx].bBufferReady=TRUE; // Flag as data ready - may be used in future, handy for debugging 
		pdfqs->adfqwhs[nIdx].dwTickCount=GetTickCount(); // Set a timestamp of when this happened 
		pdfqs->nHead=nNext; 
	} 
	LeaveCriticalSection(&pdfqs->cs); 
	return TRUE; 
} 
 
static BOOL DFQueueGet(HWAVEIN hwi,PDFQUEUESTRUCT pdfqs,PWORD pwData,PDWORD pdwTickCount) 
{ 
	int nIdx; 
 
	// Pull a buffer from the queue 
	EnterCriticalSection(&pdfqs->cs); 
 
	if (pdfqs->nHead==pdfqs->nTail) 
	{ 
		LeaveCriticalSection(&pdfqs->cs); 
		return FALSE; // Nothing in queue to get 
	} 
	LeaveCriticalSection(&pdfqs->cs); 
	 
	memcpy(pwData,pdfqs->adfqwhs[pdfqs->nTail].wh.lpData,DF_BUFFERSIZE); 
	if (pdwTickCount!=NULL) 
	{ 
		*pdwTickCount=pdfqs->adfqwhs[pdfqs->nTail].dwTickCount; 
	} 
 
	EnterCriticalSection(&pdfqs->cs); 
 
	nIdx=pdfqs->nTail; 
	pdfqs->adfqwhs[nIdx].bBufferReady=FALSE; 
	pdfqs->nTail=((pdfqs->nTail)+1) % DF_NUMBUFFERS; 
 
	LeaveCriticalSection(&pdfqs->cs); 
 
	waveInAddBuffer(hwi,&pdfqs->adfqwhs[nIdx].wh,sizeof(pdfqs->adfqwhs[nIdx].wh)); 
	return TRUE; 
} 
 
static void DFTSSetThreshold(PDFTHREADSTRUCT pdfts,int n) 
{ 
	EnterCriticalSection(&pdfts->cs); 
	pdfts->nThreshold=n; 
	LeaveCriticalSection(&pdfts->cs); 
} 
 
static int DFTSGetThreshold(PDFTHREADSTRUCT pdfts) 
{ 
	int n; 
 
	EnterCriticalSection(&pdfts->cs); 
	n=pdfts->nThreshold; 
	LeaveCriticalSection(&pdfts->cs); 
	return n; 
} 
 
static void DFTSSetThresholdAGC(PDFTHREADSTRUCT pdfts,S32 s32) 
{ 
	EnterCriticalSection(&pdfts->cs); 
	pdfts->s32ThresholdAGC=s32; 
	LeaveCriticalSection(&pdfts->cs); 
} 
 
static S32 DFTSGetThresholdAGC(PDFTHREADSTRUCT pdfts) 
{ 
	S32 s32; 
 
	EnterCriticalSection(&pdfts->cs); 
	s32=pdfts->s32ThresholdAGC; 
	LeaveCriticalSection(&pdfts->cs); 
	return s32; 
} 
 
static void DFTSSetStarting(PDFTHREADSTRUCT pdfts,BOOL bStarting) 
{ 
	EnterCriticalSection(&pdfts->cs); 
	pdfts->bStarting=bStarting; 
	LeaveCriticalSection(&pdfts->cs); 
} 
 
static BOOL DFTSGetStarting(PDFTHREADSTRUCT pdfts) 
{ 
	BOOL b; 
 
	EnterCriticalSection(&pdfts->cs); 
	b=pdfts->bStarting; 
	LeaveCriticalSection(&pdfts->cs); 
	return b; 
} 
 
static void DFTSSetStarted(PDFTHREADSTRUCT pdfts,BOOL bStarted) 
{ 
	EnterCriticalSection(&pdfts->cs); 
	pdfts->bStarted=bStarted; 
	LeaveCriticalSection(&pdfts->cs); 
} 
 
static BOOL DFTSGetStarted(PDFTHREADSTRUCT pdfts) 
{ 
	BOOL b; 
 
	EnterCriticalSection(&pdfts->cs); 
	b=pdfts->bStarted; 
	LeaveCriticalSection(&pdfts->cs); 
	return b; 
} 
 
static void DFTSSetFinish(PDFTHREADSTRUCT pdfts,BOOL bFinish) 
{ 
	EnterCriticalSection(&pdfts->cs); 
	pdfts->bFinish=bFinish; 
	LeaveCriticalSection(&pdfts->cs); 
} 
 
static BOOL DFTSGetFinish(PDFTHREADSTRUCT pdfts) 
{ 
	BOOL b; 
 
	EnterCriticalSection(&pdfts->cs); 
	b=pdfts->bFinish; 
	LeaveCriticalSection(&pdfts->cs); 
	return b; 
} 
 
static void DFTSSetFinished(PDFTHREADSTRUCT pdfts,BOOL bFinished) 
{ 
	EnterCriticalSection(&pdfts->cs); 
	pdfts->bFinished=bFinished; 
	LeaveCriticalSection(&pdfts->cs); 
} 
 
static BOOL DFTSGetFinished(PDFTHREADSTRUCT pdfts) 
{ 
	BOOL b; 
 
	EnterCriticalSection(&pdfts->cs); 
	b=pdfts->bFinished; 
	LeaveCriticalSection(&pdfts->cs); 
	return b; 
} 
 
static int DFTSIncWaveBuffers(PDFTHREADSTRUCT pdfts) 
{ 
	int n; 
 
	EnterCriticalSection(&pdfts->cs); 
	pdfts->nWaveBuffers++; 
	n=pdfts->nWaveBuffers; 
	LeaveCriticalSection(&pdfts->cs); 
	return n; 
} 
 
static int DFTSDecWaveBuffers(PDFTHREADSTRUCT pdfts) 
{ 
	int n; 
 
	EnterCriticalSection(&pdfts->cs); 
	pdfts->nWaveBuffers--; 
	n=pdfts->nWaveBuffers; 
	LeaveCriticalSection(&pdfts->cs); 
	return n; 
} 
 
static int DFTSGetWaveBuffers(PDFTHREADSTRUCT pdfts) 
{ 
	int n; 
 
	EnterCriticalSection(&pdfts->cs); 
	n=pdfts->nWaveBuffers; 
	LeaveCriticalSection(&pdfts->cs); 
	return n; 
} 
 
static void DFTSSetWaveBuffers(PDFTHREADSTRUCT pdfts,int n) 
{ 
	EnterCriticalSection(&pdfts->cs); 
	pdfts->nWaveBuffers=n; 
	LeaveCriticalSection(&pdfts->cs); 
} 
 
void CALLBACK DFWaveInCallback(HWAVEIN hwi,UINT uMsg,PDFTHREADSTRUCT pdfts,DWORD dwParam1,DWORD dwParam2) 
{ 
	if(uMsg==MM_WIM_DATA) 
	{ 
		DFQueuePut(&pdfts->dfqs,(LPWAVEHDR)dwParam1); 
		DFTSIncWaveBuffers(pdfts); // Tell thread we have more data 
 
		SetEvent(pdfts->hevent); 
	} 
} 
 
static void DFWaveInTerm(PDFTHREADSTRUCT pdfts) 
{ 
	DFQueueTerm(pdfts->hwi,&pdfts->dfqs); 
	if (pdfts->hwi!=NULL) 
	{ 
		waveInClose(pdfts->hwi); 
		pdfts->hwi=NULL; 
	} 
	FFTTerm(&pdfts->fs); 
} 
 
static BOOL DFWaveInInit(PDFTHREADSTRUCT pdfts,UINT uDeviceID) 
{ 
	// Open the wave device... 
	WAVEFORMATEX wfe= 
	{ 
		WAVE_FORMAT_PCM, // wFormatTag 
		DF_NUMCHANNELS, // nChannels 
		DF_SAMPLESPERSEC, // nSamplesPerSec 
		DF_AVGBYTESPERSEC, // nAvgBytesPerSec=nBlockAlign*nSamplesPerSec 
		DF_BLOCKALIGN, //nBlockAlign=(nChannels*wBitsPerSample)/8 
		DF_BITSPERSAMPLE, // wBitsPerSample 
		0 //cbSize 
	}; 
 
	pdfts->hwi=NULL; 
 
	if (!FFTInit(&pdfts->fs,DF_FFTLEN)) 
	{ 
		return FALSE; 
	} 
 
	if (waveInOpen(&pdfts->hwi,uDeviceID,&wfe,(DWORD)DFWaveInCallback,(DWORD)pdfts,CALLBACK_FUNCTION)!=0) 
	{ 
		DFWaveInTerm(pdfts); 
		return FALSE; 
	} 
 
	if (!DFQueueInit(pdfts->hwi,&pdfts->dfqs)) 
	{ 
		DFWaveInTerm(pdfts); 
		return FALSE; 
	} 
 
	waveInStart(pdfts->hwi); 
 
	return TRUE; 
} 
 
static DWORD WINAPI DFThreadProc(PDFTHREADSTRUCT pdfts) 
{ 
	static WORD awBuffer[DF_BUFFERSIZE]; 
	BOOL bFinish=FALSE; 
 
	DFTSSetStarted(pdfts,TRUE); 
	DFTSSetStarting(pdfts,FALSE); 
 
	while (!bFinish) 
	{ 
		DWORD dwTickCount; 
		BOOL bRedraw=FALSE; // True if we need to post a message to redraw the spectrum. 
 
		while (DFQueueGet(pdfts->hwi,&pdfts->dfqs,awBuffer,&dwTickCount)) // Should always be true if we're here 
		{ 
			// Search the buffer DF_SAMPLESPERSEC / DF_FFTLEN times per sec for a DTMF tone 
			S32 as32Buffer[DF_FFTLEN/2]; 
 
			bRedraw=TRUE; 
 
			FFTRun(&pdfts->fs,awBuffer,as32Buffer); 
			EnterCriticalSection(&pdfts->cs); 
			memcpy(pdfts->as32Buffer,as32Buffer,sizeof(as32Buffer)); 
			LeaveCriticalSection(&pdfts->cs); 
 
			// Now figure out if we have any DTMF tones worth talking about... 
			{ 
				// Array of DTMF bin indices into the FFT table 
				static const int anDTMFBinX[4]= 
				{ 
					DF_DTMFBINX0, 
					DF_DTMFBINX1, 
					DF_DTMFBINX2, 
					DF_DTMFBINX3 
				}; 
 
				static const int anDTMFBinY[4]= 
				{ 
					DF_DTMFBINY0, 
					DF_DTMFBINY1, 
					DF_DTMFBINY2, 
					DF_DTMFBINY3 
				}; 
				int nToneBitmapX=0; 
				int nToneBitmapY=0; 
				int nToneX=-1; 
				int nToneY=-1; 
				int nCountX=0; 
				int nCountY=0; 
				int nBitMap; 
				int n; 
				S32 s32AvgX=0; 
				S32 s32AvgY=0; 
				S32 s32Avg=0; 
				S32 s32ThresholdAGC; 
				int nThreshold=DFTSGetThreshold(pdfts); 
				static char cLast='\0'; // Last decoded 
				static char cLastSent='\0'; // Last decoded sent 
 
				// Get the average without the first few bins... 
				for (n=DF_DTMFBINMIN-1;n<=DF_DTMFBINMAX+1;n++) 
				{ 
					s32Avg+=as32Buffer[n]; 
				} 
 
				// Remove the bins for our freqs of interest and for grins 
				// get the average of the X and Y bins 
				for (n=0;n<4;n++) 
				{ 
					s32Avg-=as32Buffer[anDTMFBinX[n]]; 
					s32Avg-=as32Buffer[anDTMFBinY[n]]; 
					s32AvgX+=as32Buffer[anDTMFBinX[n]]; 
					s32AvgY+=as32Buffer[anDTMFBinY[n]]; 
				} 
				s32Avg=(s32Avg)/(DF_DTMFBINMAX-DF_DTMFBINMIN+3-8); 
				s32ThresholdAGC=s32Avg*nThreshold/2; 
				DFTSSetThresholdAGC(pdfts,s32ThresholdAGC); 
				s32AvgX=s32AvgX*3/4; // We'd like the tones to be significantly over the average 
				s32AvgY=s32AvgY*3/4; 
 
				for (n=0,nBitMap=1;n<4;n++,nBitMap<<=1) 
				{ 
					if (as32Buffer[anDTMFBinX[n]]>s32ThresholdAGC) 
					{ 
						nToneBitmapX |= nBitMap; 
						nToneX=n; 
						nCountX++; 
					} 
					if (as32Buffer[anDTMFBinY[n]]>s32ThresholdAGC) 
					{ 
						nToneBitmapY |= nBitMap; 
						nToneY=n; 
						nCountY++; 
					} 
				} 
 
// Pessimistic algorithm - need two successive correct tone decodes the same 
				if (nCountX==1 && nCountY==1) // Only one tone in each group please 
				{ 
					static const char aacToneDecode[4][4]= 
					{ 
						{'1','2','3','A'}, 
						{'4','5','6','B'}, 
						{'7','8','9','C'}, 
						{'*','0','#','D'} 
					}; 
					char c=aacToneDecode[nToneY][nToneX]; 
 
					if (pdfts->nLastToneBitmapX==nToneBitmapX && pdfts->nLastToneBitmapY==nToneBitmapY) 
					{ 
						if (c==cLast) 
						{ 
							if (cLastSent!=c) 
							{ 
								PostMessage(pdfts->hdlg,DF_WM_USER_NEWCHAR,(WPARAM)c,(LPARAM)dwTickCount); 
								cLastSent=c; 
							} 
						} 
					} 
					cLast=c; 
				} 
				else 
				{ 
					cLast='\0'; 
					cLastSent='\0'; 
				} 
 
/* Optimistic algorithm 
				if (nCountX==1 && nCountY==1) // Only one tone in each group please 
				{ 
					if (pdfts->nLastToneBitmapX!=nToneBitmapX || pdfts->nLastToneBitmapY!=nToneBitmapY) // If changed from last time 
					{ 
						static const char aacToneDecode[4][4]= 
						{ 
							{'1','2','3','A'}, 
							{'4','5','6','B'}, 
							{'7','8','9','C'}, 
							{'*','0','#','D'} 
						}; 
						char c=aacToneDecode[nToneY][nToneX]; 
 
						PostMessage(pdfts->hdlg,DF_WM_USER_NEWCHAR,(WPARAM)c,0); 
					} 
				} 
*/ 
				pdfts->nLastToneBitmapX=nToneBitmapX; 
				pdfts->nLastToneBitmapY=nToneBitmapY; 
 
			} 
			DFTSDecWaveBuffers(pdfts); 
		} 
 
		if (bRedraw) 
		{ 
			PostMessage(pdfts->hdlg,DF_WM_USER_REDRAW,0,0); 
		} 
 
		switch(WaitForSingleObject(pdfts->hevent,100)) 
		{ 
			case WAIT_OBJECT_0: 
				break; 
			case WAIT_TIMEOUT: 
				break; 
		} 
 
		bFinish=DFTSGetFinish(pdfts); 
	} 
 
	DFTSSetStarted(pdfts,FALSE); 
	DFTSSetFinished(pdfts,TRUE); 
	pdfts->dwThreadID=0; 
	pdfts->ht=NULL; 
	ExitThread(0); 
	return 0; 
} 
 
static void DFThreadTerm(PDFTHREADSTRUCT pdfts) 
{ 
	if (DFTSGetStarting(pdfts) || DFTSGetStarted(pdfts)) 
	{ 
		DFTSSetFinished(pdfts,FALSE); 
		DFTSSetFinish(pdfts,TRUE); 
		SetEvent(pdfts->hevent); 
		while (!DFTSGetFinished(pdfts)) 
		{ 
			Sleep(100); 
		} 
	} 
} 
 
static BOOL DFThreadInit(PDFTHREADSTRUCT pdfts,LONG lDeviceID) 
{ 
	BOOL brc=FALSE; // pessimistic return code 
 
	if (!DFTSGetStarting(pdfts) && !DFTSGetStarted(pdfts)) 
	{ 
		pdfts->ht=NULL; 
		pdfts->dwThreadID=0; 
		pdfts->nLastToneBitmapX=0; 
		pdfts->nLastToneBitmapY=0; 
 
		// Open the wave device... 
		DFTSSetWaveBuffers(pdfts,0); 
		if (!DFWaveInInit(pdfts,(UINT)lDeviceID)) 
		{ 
			DFThreadTerm(pdfts); 
			MessageBox(pdfts->hdlg,"Could not open wave device","DTMF FFT",MB_OK | MB_ICONEXCLAMATION); 
			return FALSE; 
		} 
 
		// Start the thread... 
		DFTSSetStarting(pdfts,TRUE); 
		DFTSSetStarted(pdfts,FALSE); 
		DFTSSetFinish(pdfts,FALSE); 
		DFTSSetFinished(pdfts,TRUE); 
 
		if ((pdfts->ht=CreateThread(NULL,0,DFThreadProc,(LPVOID)pdfts,0,&pdfts->dwThreadID))==NULL) 
		{ 
			DFThreadTerm(pdfts); 
			MessageBox(pdfts->hdlg,"Could not start thread","DTMF FFT",MB_OK | MB_ICONEXCLAMATION); 
			return FALSE; 
		} 
		brc=TRUE; 
	} 
	return brc; 
} 
 
static void DFTerm(PDFTHREADSTRUCT pdfts) 
{ 
	// Close thread down... 
	DFThreadTerm(pdfts); 
 
	// Close down WAV handle... 
	DFWaveInTerm(pdfts); 
 
	// Close down event... 
	CloseHandle(pdfts->hevent); 
	pdfts->hevent=NULL; 
 
	// Close down critical section... 
	DeleteCriticalSection(&pdfts->cs); 
 
	// Delete any font I've created 
	if (pdfts->hf!=NULL) 
	{ 
		DeleteObject(pdfts->hf); 
		pdfts->hf=NULL; 
	} 
 
	// Close the log file 
	if (pdfts->pf!=NULL) 
	{ 
		SYSTEMTIME st; 
 
		GetSystemTime(&st); 
		fprintf(pdfts->pf,"DTMFFFT,%04d%02d%02d %02d:%02d:%02d.%03d,UTC,Close  \n",st.wYear,st.wMonth,st.wDay,st.wHour,st.wMinute,st.wSecond,st.wMilliseconds); 
		fclose(pdfts->pf); 
		pdfts->pf=NULL; 
	} 
} 
 
static void DFInitSetParameterFields(PDFTHREADSTRUCT pdfts) 
 
{ // Try to get the registry values and fudge factors, and set dialog field texts 
	int n; 
	char szKey[30]="NumParameters"; 
	char szVal[30]; 
 
	// First try to get the number of parameters... 
	if (!RegGetString(szKey,szVal,sizeof(szVal))) 
	{ 
		sprintf(szVal,"%d",DF_DEFAULTNUMDLGPARAMETERS); 
		RegSetString(szKey,szVal); 
	} 
	pdfts->nNumDlgParms=atoi(szVal); 
 
	for (n=0;nddps[n].nDlgIDName=anDlgIDName[n]; 
		pdfts->ddps[n].nDlgIDValue=anDlgIDValue[n]; 
		pdfts->ddps[n].nDlgIDUnits=anDlgIDUnits[n]; 
 
		strcpy(pdfts->ddps[n].szName,aszNameDefault[n]); 
		sprintf(szKey,"ParameterName%d",n+1); 
		if (!RegGetString(szKey,pdfts->ddps[n].szName,DF_DLGSTRINGLEN)) 
		{ 
			RegSetString(szKey,pdfts->ddps[n].szName); 
		} 
		SetDlgItemText(pdfts->hdlg,pdfts->ddps[n].nDlgIDName,pdfts->ddps[n].szName); 
 
		sprintf(szKey,"ParameterOffset%d",n+1); 
		if (RegGetString(szKey,szVal,sizeof(szVal))) 
		{ 
			pdfts->ddps[n].dOffset=atof(szVal); 
		} 
		else 
		{ 
			pdfts->ddps[n].dOffset=adOffsetDefault[n]; 
			sprintf(szVal,"%G",pdfts->ddps[n].dOffset); 
			RegSetString(szKey,szVal); 
		} 
 
		sprintf(szKey,"ParameterMultiplier%d",n+1); 
		if (RegGetString(szKey,szVal,sizeof(szVal))) 
		{ 
			pdfts->ddps[n].dMultiplier=atof(szVal); 
		} 
		else 
		{ 
			pdfts->ddps[n].dMultiplier=adMultiplierDefault[n]; 
			sprintf(szVal,"%G",pdfts->ddps[n].dMultiplier); 
			RegSetString(szKey,szVal); 
		} 
 
		strcpy(pdfts->ddps[n].szUnits,aszUnitsDefault[n]); 
		sprintf(szKey,"ParameterUnits%d",n+1); 
		if (!RegGetString(szKey,pdfts->ddps[n].szUnits,DF_DLGSTRINGLEN)) 
		{ 
			RegSetString(szKey,pdfts->ddps[n].szUnits); 
		} 
		SetDlgItemText(pdfts->hdlg,pdfts->ddps[n].nDlgIDUnits,pdfts->ddps[n].szUnits); 
	} 
 
	// Show/hide fields... 
	{ 
		int n; 
 
		for (n=0;nhdlg,afs[n].nNameID),nnNumDlgParms ? SW_SHOW : SW_HIDE); 
			ShowWindow(GetDlgItem(pdfts->hdlg,afs[n].nEditID),nnNumDlgParms ? SW_SHOW : SW_HIDE); 
			ShowWindow(GetDlgItem(pdfts->hdlg,afs[n].nUnitsID),nnNumDlgParms ? SW_SHOW : SW_HIDE); 
		} 
	} 
 
} 
 
static BOOL DFInit(PDFTHREADSTRUCT pdfts,HWND hdlg) 
{ 
	pdfts->hdlg=hdlg; 
	{ 
		char szFileName[30]; 
		SYSTEMTIME st; 
 
		pdfts->pf=NULL; 
		GetSystemTime(&st); 
		sprintf(szFileName,"DTMFFFT%04d%02d%02d.csv",st.wYear,st.wMonth,st.wDay); 
		if ((pdfts->pf=fopen(szFileName,"at"))==NULL) 
		{ 
			MessageBox(pdfts->hdlg,"Could not open log file","DTMF FFT",MB_OK | MB_ICONEXCLAMATION); 
		} 
 
		if (pdfts->pf!=NULL) 
		{ 
			fprintf(pdfts->pf,"DTMFFFT,%04d%02d%02d %02d:%02d:%02d.%03d,UTC,Open   \n",st.wYear,st.wMonth,st.wDay,st.wHour,st.wMinute,st.wSecond,st.wMilliseconds); 
		} 
	} 
 
	DFInitSetParameterFields(pdfts); 
 
	// Create a monospace font for the window edit control 
	pdfts->hf=CreateFont(0,0,0,0,FW_DONTCARE,FALSE,FALSE,FALSE, 
		DEFAULT_CHARSET,OUT_DEFAULT_PRECIS,CLIP_DEFAULT_PRECIS, 
		DEFAULT_QUALITY,FIXED_PITCH | FF_DONTCARE,NULL); 
 
	if (pdfts->hf!=NULL) 
	{ 
		SendDlgItemMessage(pdfts->hdlg,IDC_EDIT_WINDOW,WM_SETFONT,(WPARAM)pdfts->hf,MAKELPARAM(FALSE,0)); 
	} 
 
	// Let's initialize the inter-thread communications 
	{ 
		char sz[10]; 
		pdfts->nThreshold=8; 
		sprintf(sz,"%d",pdfts->nThreshold); 
		SetDlgItemText(pdfts->hdlg,IDC_EDIT_THRESHOLD,sz); 
	} 
 
	InitializeCriticalSection(&pdfts->cs); 
	pdfts->hevent=CreateEvent(NULL,FALSE,FALSE,NULL); 
	pdfts->hwi=NULL; 
	DFTSSetWaveBuffers(pdfts,0); 
	DFTSSetStarting(pdfts,FALSE); 
	DFTSSetStarted(pdfts,FALSE); 
	DFTSSetFinish(pdfts,FALSE); 
	DFTSSetFinished(pdfts,TRUE); 
 
	// Now let's populate the combo box... 
	{ 
		UINT uNum=waveInGetNumDevs(); 
		UINT u; 
		int nItemID=0; 
 
		for (u=0;uhdlg,IDC_COMBO_DEVICE,CB_ADDSTRING,0,(LPARAM)wic.szPname); 
				// Set the device ID to the 32 bit combo box item data... 
				SendDlgItemMessage(pdfts->hdlg,IDC_COMBO_DEVICE,CB_SETITEMDATA,(WPARAM)nItemID,(LPARAM)u); 
				nItemID++; 
			} 
		} 
		// Select the first item... 
		SendDlgItemMessage(pdfts->hdlg,IDC_COMBO_DEVICE,CB_SETCURSEL,0,0); 
	} 
	return TRUE; 
} 
 
static LRESULT CALLBACK DFDlg(HWND hdlg,UINT message,WPARAM wParam,LPARAM lParam) 
{ 
	static DFTHREADSTRUCT dfts; 
 
	switch (message)  
	{ 
		case WM_INITDIALOG: 
			DFInit(&dfts,hdlg); 
			break; 
		case WM_DESTROY: 
			DFTerm(&dfts); 
			break; 
		case DF_WM_USER_NEWCHAR: // wParam has the character, lParam has the ticker time that the character was decoded 
			{ 
				char sz[2]; 
				char c=(char)wParam; 
				static char szDTMFDecode[]="D84#195A206B3*7C"; 
				static char szHex[]="0123456789ABCDEF"; 
				static char szWindow[DF_MAXDLGPARAMETERS*2+1]=""; // last DF_MAXDLGPARAMETERS*2 hex characters 
				static DWORD adwWindowTicks[DF_MAXDLGPARAMETERS*2+1]; // last DF_MAXDLGPARAMETERS*2+1 character's ticker counts 
				static BOOL bFirstTime=TRUE; 
				int n; 
 
				sz[0]=c; 
				sz[1]='\0'; 
 
				SendDlgItemMessage(hdlg,IDC_EDIT_DTMF,EM_REPLACESEL,(WPARAM)FALSE,(LPARAM)sz); 
 
				sz[0]=szHex[strchr(szDTMFDecode,c)-szDTMFDecode]; 
				SendDlgItemMessage(hdlg,IDC_EDIT_DTMF_DECODE,EM_REPLACESEL,(WPARAM)FALSE,(LPARAM)sz); 
 
				n=strlen(szWindow); 
 
				if (n>=dfts.nNumDlgParms*2-1) // We only want the last nNumDlgParms*2-1 characters 
				{ 
					strcpy(szWindow,szWindow+n-(dfts.nNumDlgParms*2-1)); 
				} 
				strcat(szWindow,sz); // append last decoded character to make nNumDlgParms*2 characters 
				SetDlgItemText(hdlg,IDC_EDIT_WINDOW,szWindow); 
 
				for (n=0;n5000))  
				{ 
					int anVal[DF_MAXDLGPARAMETERS]; 
					double adVal[DF_MAXDLGPARAMETERS]; 
					bFirstTime=FALSE; 
					SendDlgItemMessage(hdlg,IDC_LIST_DECODE,LB_ADDSTRING,0,(LPARAM)szWindow); 
 
					// Select the last item inserted... 
					n=(int)SendDlgItemMessage(hdlg,IDC_LIST_DECODE,LB_GETCOUNT,0,0); 
					SendDlgItemMessage(hdlg,IDC_LIST_DECODE,LB_SETCURSEL,(WPARAM)(n-1),0); 
 
					// Now disect and present the data in fields... 
					for (n=0;ns32Max) 
				{ 
					s32Max=s32ThresholdAGC; 
				} 
				 
				if (s32Max<10000) 
				{ 
					s32Max=10000; 
				} 
				GetWindowRect(hwnd,&rc); 
 
				rc.right-=rc.left; 
				rc.bottom-=rc.top; 
				rc.left=0; 
				rc.top=0; 
 
				FillRect(hdc,&rc,(HBRUSH)(COLOR_WINDOW+1)); // Clear the space out. 
 
				{ 
					int n; 
					int nMaxy=rc.bottom; 
					int nMaxx=rc.right; 
					double dScaley=(double)s32Max/nMaxy; 
					int nIncx=nMaxx/(DF_FFTLEN/2); 
 
					for (n=0;n20) 
				{ 
					nThreshold=20; 
				} 
				DFTSSetThreshold(&dfts,nThreshold); 
				sprintf(sz,"%d",nThreshold); 
				SetDlgItemText(hdlg,IDC_EDIT_THRESHOLD,sz); 
			} 
			break; 
		case WM_COMMAND: 
			switch (LOWORD(wParam)) 
			{ 
				case IDOK: 
				case IDCANCEL: 
				case IDM_EXIT: 
					EndDialog(hdlg, TRUE); 
					break; 
				case IDC_BUTTON_STARTSTOP: 
					{ 
						char sz[10]; 
						 
						GetDlgItemText(hdlg,IDC_BUTTON_STARTSTOP,sz,sizeof(sz)); 
						if (strcmp(sz,"&Start")==0) 
						{ 
							LONG l=SendDlgItemMessage(hdlg,IDC_COMBO_DEVICE,CB_GETCURSEL,0,0); 
							LONG lDeviceID=SendDlgItemMessage(hdlg,IDC_COMBO_DEVICE,CB_GETITEMDATA,(WPARAM)l,0); 
 
							if (DFThreadInit(&dfts,lDeviceID)) 
							{ 
								if (dfts.pf!=NULL) 
								{ 
									SYSTEMTIME st; 
 
									GetSystemTime(&st); 
									fprintf(dfts.pf,"DTMFFFT,%04d%02d%02d %02d:%02d:%02d.%03d,UTC,Start  \n",st.wYear,st.wMonth,st.wDay,st.wHour,st.wMinute,st.wSecond,st.wMilliseconds); 
								} 
								SetDlgItemText(hdlg,IDC_BUTTON_STARTSTOP,"&Stop"); 
								SendDlgItemMessage(hdlg,IDC_COMBO_DEVICE,WM_ENABLE,(WPARAM)FALSE,0); 
							} 
						} 
						else 
						{ 
							DFThreadTerm(&dfts); 
							DFWaveInTerm(&dfts); 
							ResetEvent(dfts.hevent); // Do this or next time the thread starts it'll think something's happened. 
							if (dfts.pf!=NULL) 
							{ 
								SYSTEMTIME st; 
 
								GetSystemTime(&st); 
								fprintf(dfts.pf,"DTMFFFT,%04d%02d%02d %02d:%02d:%02d.%03d,UTC,Stop  \n",st.wYear,st.wMonth,st.wDay,st.wHour,st.wMinute,st.wSecond,st.wMilliseconds); 
							} 
							SetDlgItemText(hdlg,IDC_BUTTON_STARTSTOP,"&Start"); 
							SendDlgItemMessage(hdlg,IDC_COMBO_DEVICE,WM_ENABLE,(WPARAM)TRUE,0); 
						} 
					} 
					break; 
				case IDM_ABOUT: 
					About(hdlg,_hinst); 
					break; 
				case IDM_PARAMETERS: 
					if (Parameter(hdlg,_hinst)) 
					{ 
						DFInitSetParameterFields(&dfts); 
					} 
					break; 
				default: 
					break; 
			} 
            return TRUE; 
		default: 
			break; 
	} 
    return FALSE; 
} 
 
int APIENTRY WinMain(HINSTANCE hinst,HINSTANCE hPrevInst,LPSTR lpCmdLine,int nCmdShow) 
{ 
	_hinst=hinst; 
	DialogBox(hinst,MAKEINTRESOURCE(IDD_DTMFFFT),NULL,DFDlg); 
}