April 19, 2024, 09:37:13 AM

News:

Own IWBasic 2.x ? -----> Get your free upgrade to 3.x now.........


DTMF dialer

Started by sapero, August 05, 2009, 05:16:41 PM

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

sapero

This small example implements a simple DTMF dialer keypad. Click a button to generate sound :)
// resource file in attachment
#include "windows.inc"
declare wave_format();
declare wave_header();
declare wave_samples();
declare angle_lookup();

HWAVEOUT g_hwout;
WNDPROC  g_wndprocButton;


sub main()
{
DialogBoxParam(_hinstance, 1000, 0, &DialogMain, 0);
}


sub DialogMain(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam),BOOL
{
switch (uMsg)
{
case WM_INITDIALOG:
OnInitDialog(hwnd);

case WM_CLOSE:
OnClose(hwnd);
}
return FALSE;
}


sub OnInitDialog(HWND hwnd)
{
if (!waveOutOpen(&g_hwout, WAVE_MAPPER, &wave_format, hwnd, 0, CALLBACK_WINDOW)
&& !waveOutPrepareHeader(g_hwout, &wave_header, sizeof(WAVEHDR)))
{
EnumChildWindows(hwnd, &EnumProc, 0); // subclass all buttons
}
}


sub EnumProc(HWND hwnd, LPARAM lParam),BOOL
{
if (!g_wndprocButton)
g_wndprocButton = GetWindowLong(hwnd, GWL_WNDPROC);

SetWindowLong(hwnd, GWL_WNDPROC, &MyButtonProc);
return TRUE;
}


sub Play(int controlId)
{
int id = controlId - 1000;

int tone1 = (id & 3) | 4;
int tone2 = id >> 2;

double *angles = &angle_lookup;
double angle1  = *angles[tone1];
double angle2  = *angles[tone2];
WAVEHDR *header = &wave_header;
word    *samples = &wave_samples;

for (int n=0; n<header->dwBufferLength/2; n++)
{
*samples[n] = (sin(n*angle1) + sin(n*angle2)) * 4096;
}
waveOutReset(g_hwout);
waveOutWrite(g_hwout, &wave_header, sizeof(WAVEHDR));
}

sub MyButtonProc(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam),LRESULT
{
switch (uMsg)
{
case WM_LBUTTONDOWN: // play
Play(GetWindowLong(hwnd, GWL_ID));

case WM_LBUTTONUP: // stop
WAVEHDR *header = &wave_header;
ZeroMemory(&wave_samples, header->dwBufferLength);
waveOutBreakLoop(g_hwout);

case WM_DESTROY:
SetWindowLong(hwnd, GWL_WNDPROC, g_wndprocButton);
}
return CallWindowProc(g_wndprocButton, hwnd, uMsg, wParam, lParam);;
}


sub OnClose(HWND hwnd)
{
if (g_hwout)
{
waveOutUnprepareHeader(g_hwout, &wave_header, sizeof(WAVEHDR));
waveOutClose(g_hwout);
g_hwout = 0;
}
EndDialog(hwnd, 0);
}


#asm
segment .data

angle_lookup: ; 2*PI*freq/44100
dq 0.0993056725420447114379419501133 ;  697 Hz
dq 0.1097064101253578591208253968254 ;  770
dq 0.1213894304244219428194068027210 ;  852
dq 0.1340697817246256434190866213151 ;  941
dq 0.1722533114825424047754258503401 ; 1209
dq 0.1903477453603611685524970521542 ; 1336
dq 0.2104368412404591661317650793650 ; 1477
dq 0.2326630749801420570705297052154 ; 1633

wave_format: ; WAVEFORMATEX struct
dw 1,1
dd 44100,88200
dw 2,16,0,0

%define WAVESIZE    (2*44100*1) ; one second
%define REPEATCOUNT 5

wave_header: ; WAVEHDR struct
dd wave_samples, WAVESIZE, 0,0,4|8,REPEATCOUNT,0,0

segment .bss

wave_samples: resw WAVESIZE
#endasm

sapero

August 06, 2009, 04:26:00 AM #1 Last Edit: August 07, 2009, 02:26:03 AM by sapero
And this example is able to decode the tones captured from your soundcard. You can even use your cell phone for testing :)

EDIT: updated invalid handle in CloseHandle, should be hEvent instead hWave.
// DTMF decoder console app
#include "windows.inc"
#include "conio.inc"

declare wave_format();
declare wave_header();
declare float_buf();

sub main()
{
HANDLE hEvent = CreateEvent(0,0,0,0);
HWAVEIN hWave;
if (!waveInOpen(&hWave, WAVE_MAPPER, &wave_format, hEvent, 0, CALLBACK_EVENT))
{
// eat WIM_OPEN
WaitForSingleObject(hEvent, 500);

if (!waveInPrepareHeader(hWave, &wave_header, sizeof(WAVEHDR)))
{
WAVEHDR *header = &wave_header;
print("press any key to quit\n");
waveInStart(hWave);

while (!kbhit() && !waveInAddBuffer(hWave, &wave_header, sizeof(WAVEHDR)))
{
// wait for data
if (!WaitForSingleObject(hEvent, 700))
{
OnAudioSamples(header->lpData, header->dwBytesRecorded/2);
}
}
waveInUnprepareHeader(hWave, &wave_header, sizeof(WAVEHDR));
}
waveInClose(hWave);
}
CloseHandle(hEvent);
}


sub OnAudioSamples(word *pwSamples, int count)
{
// convert words to floats
float *floats = &float_buf;

for (int n=0; n<count; n++)
{
*floats[n] = *pwSamples[n] * 0.000030517578125; // 1/32768
}
// base DTMF frequencies
float frequency[8];
frequency = 697,770,852,941,1209,1336,1477,1633;

// compute magnitudes (amplitudes)
float gain[8];
for (n=0; n<8; n++)
{
gain[n] = Goerzel(floats, count, frequency[n], 44100);
}

// find the first peak
int tone1index = 0;
for (n=1; n<8; n++)
{
if (gain[n] > gain[tone1index])
{
tone1index = n;
}
}

// find the second peak; pick any index other than tone1index
int tone2index = tone1index xor 1;
for (n=0; n<8; n++)
{
if ((n != tone1index) && (gain[n] > gain[tone2index]))
{
tone2index = n;
}
}

// check "direction"
if (tone1index > tone2index)
{
n = tone1index;
tone1index = tone2index;
tone2index = n;
}

if ((tone1index <= 3) && (tone2index >= 4))
{
if ((gain[tone1index] > -50) && (gain[tone2index] > -50))
{
unsigned byte *keys = "147*2580369#ABCD";
i = tone1index + ((tone2index-4)*4);
print(*keys[i]);
}
}
}

sub Goerzel(float *floats, int count, float frequency, int samplerate),float
{
// http://www.exstrom.com/journal/sigproc/
float u0, u1, u2;

double theta = 6.28318530717958647692 * frequency / samplerate;
double Qr;
double Qi;

double cosine = cos(theta);
double sine   = sin(theta);
double coeff  = 2.0*cosine;
double CN = cos((count-1)*theta);
double SN = sin((count-1)*theta);
u0 = 0.0;
u1 = 0.0;

for(int i = 0; i<count; i++)
{
u2 = *floats[i] + coeff*u1 - u0;
u0 = u1;
u1 = u2;
}

float yr = u2 - cosine*u0;
float yi = sine*u0;

Qr = yr*CN + yi*SN;
Qi = yi*CN - yr*SN;
float value = sqrt(Qr*Qr + Qi*Qi);
return 20 * log10(value / count);
}

#asm
segment .data

wave_format: ; WAVEFORMATEX struct
dw 1,1
dd 44100,88200
dw 2,16,0,0

%define WAVESIZE    (2*44100/10) ; 1/10s - 100ms record buffer

wave_header: ; WAVEHDR struct
dd wave_samples, WAVESIZE, 0,0,0,0,0,0

segment .bss

wave_samples: resw WAVESIZE ; word[] buffer for audio samples
float_buf     resd WAVESIZE ; float[] buffer for DSP stuff
#endasm