interface
uses SysUtils, dspFFT, dspWindow;
const
cMinDTFMSampleRate = 11025;
cDefaultDTFMSampleRate = 11025;
cSupportedSampleRates:
Array[0..2]
of Integer = (11025, 22050, 44100);
cDTMFInvalidSampleRateMsg = '
Unsupported sample rate'#13#10'
Please use: 11025, 22050 or 44100';
type
EdspDTMFInvalidSampleRate =
class(
Exception);
TdspOnTone =
procedure(Sender: TObject; Tone: Char)
of object;
TdspDTMFArray =
Array[0..3]
of Single;
PdspDTMFArray = ^TdspDTMFArray;
TdspDTMF =
class
private
FFT: TdspFFT;
FOnTone: TdspOnTone;
FSampleRate: Integer;
FThreshold: Single;
FSkipCnt, FCounter: Integer;
FPos: Integer;
FRow, FCol: Integer;
FCols, FRows: TdspDTMFArray;
procedure FSetSampleRate(V: Integer);
procedure FindTones;
public
constructor Create;
destructor Destroy;
override;
procedure Initialize;
procedure Finilize;
procedure Put(V: Single);
property Threshold: Single
read FThreshold
write FThreshold;
property SampleRate: Integer
read FSampleRate
write FSetSampleRate
default cDefaultDTFMSampleRate;
property OnTone: TdspOnTone
read FOnTone
write FOnTone;
end;
implementation
constructor TdspDTMF.Create;
begin
FFT:= TdspFFT.Create(
nil);
FFT.BufferSize:= 256;
FFT.Window:= fwBlackman;
SampleRate:= cDefaultDTFMSampleRate;
end;
destructor TdspDTMF.Destroy;
begin
FFT.Free;
inherited;
end;
procedure TdspDTMF.FSetSampleRate(V: Integer);
var
Iz: Integer;
Supported: Boolean;
begin
if FSampleRate <> V
then
begin
Supported:= false;
for Iz:= Low(cSupportedSampleRates)
to High(cSupportedSampleRates)
do
Supported:= Supported
or (cSupportedSampleRates[Iz] = V);
if not Supported
then
raise EdspDTMFInvalidSampleRate.Create(cDTMFInvalidSampleRateMsg)
else begin
FSampleRate:= V;
FSkipCnt:= FSampleRate
div cMinDTFMSampleRate;
end;
end;
end;
procedure TdspDTMF.Initialize;
begin
FPos:= 0;
FRow:= -1;
FCol:= -1;
FFT.Clear;
end;
procedure TdspDTMF.Finilize;
begin
if FPos > 0
then FindTones;
end;
procedure TdspDTMF.Put(V: Single);
begin
if FCounter
mod FSkipCnt = 0
then
begin
FFT.RealIn[FPos]:= V;
Inc(FPos);
if FPos >= FFT.BufferSize
then FindTones;
end;
Inc(FCounter);
end;
procedure TdspDTMF.FindTones;
procedure LocateTones;
function GetFreqAmp(Freq: Single): Single;
const Step = 11025 / 256;
var Pos: Integer;
begin
Pos:= Round(Freq / Step);
Result:= FFT.RealOut[Pos];
end;
function FindPeak(Data: PdspDTMFArray;
var Position: Integer): Boolean;
var Iz: Integer;
begin
Position:= 0;
for Iz:= 1
to 3
do
if Data[Position] < Data[Iz]
then Position:= Iz;
Result:= true;
for Iz:= 0
to 3
do
if Iz <> Position
then Result:= Result
and (Data[Iz] * 3 < Data[Position]);
Result:= Result
and (Data[Position] > FThreshold);
end;
const Tones:
Array[0..3, 0..3]
of Char = (
('
1', '
4', '
7', '
*'),
('
2', '
5', '
8', '
0'),
('
3', '
6', '
9', '
#'),
('
A', '
B', '
C', '
D'));
var Col, Row: Integer;
begin
FCols[0]:= GetFreqAmp(1209);
FCols[1]:= GetFreqAmp(1336);
FCols[2]:= GetFreqAmp(1477);
FCols[3]:= GetFreqAmp(1633);
FRows[0]:= GetFreqAmp(697);
FRows[1]:= GetFreqAmp(770);
FRows[2]:= GetFreqAmp(852);
FRows[3]:= GetFreqAmp(941);
if FindPeak(@FCols, Col)
and FindPeak(@FRows, Row)
and
(Col <> -1)
and (Row <> -1)
then
begin
if (Row <> FRow)
or (Col <> FCol)
then
begin
FCol:= Col; FRow:= Row;
if Assigned(FOnTone)
then FOnTone(Self, Tones[FCol, FRow]);
end;
end else
begin
FRow:= -1;
FCol:= -1;
end;
end;
begin
FFT.FFT;
FFT.CalculateMagnitudes;
LocateTones;
FFT.Clear;
FPos:= -1;
end;
end.