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;n adfqwhs[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;n adfqwhs[n].wh.lpData=NULL; pdfqs->adfqwhs[n].bWHPrepared=FALSE; pdfqs->adfqwhs[n].bBufferReady=FALSE; pdfqs->anIdx[n]=-1; } for (n=0;n adfqwhs[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;n ddps[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;n hdlg,afs[n].nNameID),n nNumDlgParms ? SW_SHOW : SW_HIDE); ShowWindow(GetDlgItem(pdfts->hdlg,afs[n].nEditID),n nNumDlgParms ? SW_SHOW : SW_HIDE); ShowWindow(GetDlgItem(pdfts->hdlg,afs[n].nUnitsID),n nNumDlgParms ? 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;u hdlg,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;n 5000)) { 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;n s32Max) { 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;n 20) { 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); }