DTMF code sample

Discuss the development of new homebrew software, tools and libraries.

Moderators: cheriff, TyRaNiD

Post Reply
Art
Posts: 642
Joined: Wed Nov 09, 2005 8:01 am

DTMF code sample

Post by Art »

Here's a DTMF code sample I based on Shine's Polyphonic wave sample.
It makes the correct frequencies, but still doesn't dial numbers on a phone.
The sound is a little different to listen to.. tinny like a car horn, yet in tune.

If you play high quality recorded DTMF sounds the phone DOES dial,
anyone can do that with chunky code that stores the samples.
There must be still some problem with this code.
If anyone sorts it out I'd appreciate the solution.

Code: Select all


#include <pspkernel.h>
#include <pspdebug.h>
#include <pspaudiolib.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
#include <pspctrl.h>

PSP_MODULE_INFO&#40;"DTMF", 0, 1, 1&#41;;
PSP_MAIN_THREAD_ATTR&#40;THREAD_ATTR_USER | THREAD_ATTR_VFPU&#41;;

#define printf	pspDebugScreenPrintf

int ix;			// counter
int done;		// exit flag

void dump_threadstatus&#40;void&#41;;

int exit_callback&#40;int arg1, int arg2, void *common&#41; &#123;
	done = 1;
	sceKernelExitGame&#40;&#41;;
	return 0;
&#125;

int CallbackThread&#40;SceSize args, void *argp&#41; &#123;
	int cbid;
	cbid = sceKernelCreateCallback&#40;"Exit Callback", &#40;void *&#41; exit_callback, NULL&#41;;
	sceKernelRegisterExitCallback&#40;cbid&#41;;
	sceKernelSleepThreadCB&#40;&#41;;
	return 0;
&#125;


int SetupCallbacks&#40;void&#41; &#123;
	int thid = 0;
	thid = sceKernelCreateThread&#40;"update_thread", CallbackThread, 0x11, 0xFA0, 0, 0&#41;;
	if&#40;thid >= 0&#41;&#123;sceKernelStartThread&#40;thid, 0, 0&#41;;&#125;
	return thid;
&#125;

int  pspAudioInit&#40;&#41;;
void pspAudioEndPre&#40;&#41;;
void pspAudioEnd&#40;&#41;;

#define SAMPLE_COUNT 0x10000
float sample&#91;SAMPLE_COUNT&#93;;

#define SAMPLE_RATE 44100
#define OCTAVE_COUNT 6
#define NOTE_END -2
#define NOTE_PAUSE -1

float octaves&#91;OCTAVE_COUNT&#93;&#91;12&#93;;

typedef struct &#123;
	int note;
	int octave;
	int duration;
&#125; Note_t;

typedef struct &#123;
	Note_t currentNote;
	int noteIndex;
	int currentTime;
	float currentsampleIndex;
	float currentsampleIncrement;
&#125; ChannelState_t;

ChannelState_t channelStates&#91;3&#93;;


#define EIGHT_NOTE&#40;note, octave, duration&#41; &#123; note, octave, SAMPLE_RATE * duration / 8&#125;

Note_t channel0&#91;&#93; = &#123;
	EIGHT_NOTE&#40;3, 0, 4&#41;,
	EIGHT_NOTE&#40;0, 0, 4&#41;,
	EIGHT_NOTE&#40;0, 0, 4&#41;,
	EIGHT_NOTE&#40;0, 0, 4&#41;,
	EIGHT_NOTE&#40;1, 0, 4&#41;,
	EIGHT_NOTE&#40;1, 0, 4&#41;,
	EIGHT_NOTE&#40;1, 0, 4&#41;,
	EIGHT_NOTE&#40;2, 0, 4&#41;,
	EIGHT_NOTE&#40;2, 0, 4&#41;,
	EIGHT_NOTE&#40;2, 0, 4&#41;,
	EIGHT_NOTE&#40;3, 0, 4&#41;,
	EIGHT_NOTE&#40;3, 0, 4&#41;,
	EIGHT_NOTE&#40;-1, 0, 4&#41;,
	&#123; NOTE_END, 0, 0 &#125;
&#125;;

Note_t channel1&#91;&#93; = &#123;
	EIGHT_NOTE&#40;5, 0, 4&#41;,
	EIGHT_NOTE&#40;4, 0, 4&#41;,
	EIGHT_NOTE&#40;5, 0, 4&#41;,
	EIGHT_NOTE&#40;6, 0, 4&#41;,
	EIGHT_NOTE&#40;4, 0, 4&#41;,
	EIGHT_NOTE&#40;5, 0, 4&#41;,
	EIGHT_NOTE&#40;6, 0, 4&#41;,
	EIGHT_NOTE&#40;4, 0, 4&#41;,
	EIGHT_NOTE&#40;5, 0, 4&#41;,
	EIGHT_NOTE&#40;6, 0, 4&#41;,
	EIGHT_NOTE&#40;4, 0, 4&#41;,
	EIGHT_NOTE&#40;6, 0, 4&#41;,
	EIGHT_NOTE&#40;-1, 0, 4&#41;,
	&#123; NOTE_END, 0, 0 &#125;
&#125;;

Note_t* channels&#91;&#93; = &#123; channel0, channel1 &#125;;

void nextNote&#40;int channel&#41;
&#123;
	ChannelState_t* state = &channelStates&#91;channel&#93;;
	state->currentNote = channels&#91;channel&#93;&#91;state->noteIndex&#93;;
	state->currentTime = 0;
	state->currentsampleIndex = 0;
	int note = state->currentNote.note;
	if &#40;note == NOTE_PAUSE&#41; &#123;
		state->currentsampleIncrement = 0;
	&#125; else &#123;
		state->currentsampleIncrement = octaves&#91;state->currentNote.octave&#93;&#91;note&#93; * &#40;&#40;float&#41; SAMPLE_COUNT&#41; / &#40;&#40;float&#41; SAMPLE_RATE&#41;;
	&#125;

	state->noteIndex++;
	if &#40;channels&#91;channel&#93;&#91;state->noteIndex&#93;.note == NOTE_END&#41; state->noteIndex = 0;
&#125;

// calculate current value of attack/delay/sustain/release envelope
float adsr&#40;float time, float duration&#41; &#123;
	if &#40;time < 0.0&#41; return 0.0;

	const float attack = 0.0;
	const float decay = 0.0;
	const float sustain = 0.5;
	const float release = 0.0;

	if &#40;time < attack&#41; return time / attack;
	if &#40;time < decay&#41; return &#40;decay - time&#41; / decay * &#40;1.0 - sustain&#41; + sustain;
	if &#40;time < duration&#41; return sustain;
	time -= duration;
	if &#40;time < release&#41; return &#40;release - time&#41; / release * sustain;
	return 0.0;
&#125;

void audioOutCallback&#40;int channel, unsigned short* buf, unsigned int reqn&#41;
&#123;
	ChannelState_t* state = &channelStates&#91;channel&#93;;
	unsigned int i;
	for &#40;i = 0; i < reqn; i++&#41; &#123;
		float time = &#40;&#40;float&#41; state->currentTime&#41; / &#40;&#40;float&#41; SAMPLE_RATE&#41;;
		if &#40;state->currentTime++ == state->currentNote.duration&#41;;

		float value;
		if &#40;state->currentsampleIncrement == 0.0&#41; &#123;
			value = 0.0;
		&#125; else &#123;
			value = sample&#91;&#40;int&#41;state->currentsampleIndex&#93; * adsr&#40;time, &#40;&#40;float&#41; state->currentNote.duration&#41; / &#40;&#40;float&#41; SAMPLE_RATE&#41;&#41;;
			value *= &#40;float&#41; 0x7000;
			state->currentsampleIndex += state->currentsampleIncrement;
			if &#40;state->currentsampleIndex >= SAMPLE_COUNT&#41; state->currentsampleIndex -= &#40;float&#41; SAMPLE_COUNT;
		&#125;
		buf&#91;0&#93; = value;
		buf&#91;1&#93; = value;
		buf += 2;
	&#125;
&#125;

void audioOutCallback0&#40;void *buf, unsigned int reqn, void *userdata&#41; &#123; audioOutCallback&#40;0, buf, reqn&#41;; &#125;
void audioOutCallback1&#40;void *buf, unsigned int reqn, void *userdata&#41; &#123; audioOutCallback&#40;1, buf, reqn&#41;; &#125;

void createPitches&#40;float base, float* target&#41;
&#123;
		target&#91;0&#93; = 697;
		target&#91;1&#93; = 770;
		target&#91;2&#93; = 852;
		target&#91;3&#93; = 941;
		target&#91;4&#93; = 1209;
		target&#91;5&#93; = 1336;
		target&#91;6&#93; = 1477;
		target&#91;7&#93; = 1633;
		target&#91;8&#93; = 1;
		target&#91;9&#93; = 1;
		target&#91;10&#93; = 1;
		target&#91;11&#93; = 1;
		target&#91;12&#93; = 1;
&#125;

int main&#40;void&#41;
&#123;
	pspDebugScreenInit&#40;&#41;;
	SetupCallbacks&#40;&#41;;
	printf&#40;"DTMF Touch Tone Generator by Art 2008!\n"&#41;;
	printf&#40;"Based on Polyphonic sample by Shine\n"&#41;;

        int i;
	int maxAt = SAMPLE_COUNT / 16;
	for &#40;i = 0; i < SAMPLE_COUNT; i++&#41; &#123;
		float value;
		if &#40;i < maxAt&#41; &#123;
			value = &#40;&#40;float&#41; i&#41; / &#40;&#40;float&#41; maxAt&#41; * 2.0 - 1.0;
		&#125; else &#123;
			value = 1.0 - &#40;&#40;float&#41; &#40;i - maxAt&#41;&#41; / &#40;&#40;float&#41; &#40;SAMPLE_COUNT - maxAt&#41;&#41; * 2.0;
		&#125;
		sample&#91;i&#93; = value;
	&#125;
	float base = 0.0;
	for &#40;i = 0; i < OCTAVE_COUNT; i++&#41; &#123;
		createPitches&#40;base, octaves&#91;i&#93;&#41;;
		base *= 2;
	&#125;

	channelStates&#91;0&#93;.noteIndex = 0;// nextNote&#40;0&#41;;
	channelStates&#91;1&#93;.noteIndex = 0;// nextNote&#40;1&#41;;

	pspAudioInit&#40;&#41;;
	pspAudioSetVolume&#40;0, 0x5000, 0x5000&#41;;
	pspAudioSetVolume&#40;1, 0x5000, 0x5000&#41;;
	pspAudioSetChannelCallback&#40;0, audioOutCallback0, NULL&#41;;
	pspAudioSetChannelCallback&#40;1, audioOutCallback1, NULL&#41;;


while &#40;done == 0&#41; &#123;
			SceCtrlData xpad;
			sceCtrlSetSamplingMode&#40;1&#41;; 
			sceCtrlPeekBufferPositive&#40;&xpad, 1&#41;;

			if&#40;xpad.Buttons & PSP_CTRL_CROSS&#41; &#123;
			channelStates&#91;0&#93;.noteIndex = 1;nextNote&#40;0&#41;;
			channelStates&#91;1&#93;.noteIndex = 1;nextNote&#40;1&#41;;
			&#125;

			if&#40;xpad.Buttons & PSP_CTRL_CIRCLE&#41; &#123;
			channelStates&#91;0&#93;.noteIndex = 2;nextNote&#40;0&#41;;
			channelStates&#91;1&#93;.noteIndex = 2;nextNote&#40;1&#41;;
			&#125;

			if&#40;xpad.Buttons & PSP_CTRL_TRIANGLE&#41; &#123;
			channelStates&#91;0&#93;.noteIndex = 3;nextNote&#40;0&#41;;
			channelStates&#91;1&#93;.noteIndex = 3;nextNote&#40;1&#41;;
			&#125;

			if&#40;xpad.Buttons & PSP_CTRL_SQUARE&#41; &#123;
			channelStates&#91;0&#93;.noteIndex = 4;nextNote&#40;0&#41;;
			channelStates&#91;1&#93;.noteIndex = 4;nextNote&#40;1&#41;;
			&#125;

			if&#40;xpad.Buttons & PSP_CTRL_DOWN&#41; &#123;
			channelStates&#91;0&#93;.noteIndex = 5;nextNote&#40;0&#41;;
			channelStates&#91;1&#93;.noteIndex = 5;nextNote&#40;1&#41;;
			&#125;

			if&#40;xpad.Buttons & PSP_CTRL_RIGHT&#41; &#123;
			channelStates&#91;0&#93;.noteIndex = 6;nextNote&#40;0&#41;;
			channelStates&#91;1&#93;.noteIndex = 6;nextNote&#40;1&#41;;
			&#125;

			if&#40;xpad.Buttons & PSP_CTRL_UP&#41; &#123;
			channelStates&#91;0&#93;.noteIndex = 7;nextNote&#40;0&#41;;
			channelStates&#91;1&#93;.noteIndex = 7;nextNote&#40;1&#41;;
			&#125;

			if&#40;xpad.Buttons & PSP_CTRL_LEFT&#41; &#123;
			channelStates&#91;0&#93;.noteIndex = 8;nextNote&#40;0&#41;;
			channelStates&#91;1&#93;.noteIndex = 8;nextNote&#40;1&#41;;
			&#125;

			if&#40;xpad.Buttons & PSP_CTRL_LTRIGGER&#41; &#123;
			channelStates&#91;0&#93;.noteIndex = 9;nextNote&#40;0&#41;;
			channelStates&#91;1&#93;.noteIndex = 9;nextNote&#40;1&#41;;
			&#125;

			if&#40;xpad.Buttons & PSP_CTRL_RTRIGGER&#41; &#123;
			channelStates&#91;0&#93;.noteIndex = 0;nextNote&#40;0&#41;;
			channelStates&#91;1&#93;.noteIndex = 0;nextNote&#40;1&#41;;
			&#125;

&#125; // while
	done = 1;
	sceKernelExitGame&#40;&#41;;
	return 0;
&#125;


Cheers, Art.
If not actually, then potentially.
adrahil
Posts: 274
Joined: Thu Mar 16, 2006 1:55 am

Post by adrahil »

Nice sample :)
Art
Posts: 642
Joined: Wed Nov 09, 2005 8:01 am

Post by Art »

I'm not sure if I want to call a bastardised non-working version of Shine's code a sample yet :D
If not actually, then potentially.
J.F.
Posts: 2906
Joined: Sun Feb 22, 2004 11:41 am

Post by J.F. »

It seems to be using a triangle wave as the sample. Are you sure you shouldn't be using a sine wave instead? Seems to me that anything other than a sine wave would introduce harmonics that would interfere with the recognition of the tone.
Art
Posts: 642
Joined: Wed Nov 09, 2005 8:01 am

Post by Art »

That's what you get for fiddling with code you don't understand.
Of course pi isn't defined in it.

I found this:
http://www.ocf.berkeley.edu/~fricke/dtmf.html

Might be more suitable.
If not actually, then potentially.
J.F.
Posts: 2906
Joined: Sun Feb 22, 2004 11:41 am

Post by J.F. »

Looks like I was right - that uses a sine wave sample. :)
Post Reply