#!/usr/bin/perl -w # # simple DTMF decoder # # Given a sound file that contains a _single_ DTMF tone, prints the # associated code. This is probably tolerant of white noise and # arbitrary amounts of silence before, after, or interspersed with the # signal. All that's needed is for the two primary frequencies to be # louder at some point than the other candidates. Using mean instead # of max would make it robust to different sorts of data pollution. # # This program can NOT cope with multiple DTMF tones in a single input # file. If you have a sequence of tones to decode, you'll need to # segment them first. # # Seth Golub http://www.aigeek.com/ # 6 Jul 2004 # # This program is hereby released into the public domain. use strict; use English; my $file = $ARGV[0] or die "Usage: $0 filename\n"; die "$file not readable.\n" unless -r $file; my %map = ( "697 1209" => "1", "697 1336" => "2", "697 1477" => "3", "697 1633" => "A", "770 1209" => "4", "770 1336" => "5", "770 1477" => "6", "770 1633" => "B", "852 1209" => "7", "852 1336" => "8", "852 1477" => "9", "852 1633" => "C", "941 1209" => "*", "941 1336" => "0", "941 1477" => "#", "941 1633" => "D", "350 440" => "DIALTONE", "440 480" => "RING", "480 620" => "BUSY", ); my @centers = (350, 440, 480, 620, 697, 770, 852, 941, 1209, 1336, 1477, 1633); my @energies = (); foreach my $freq ( @centers ) { my $width = int(0.02 * $freq); # somewhat arbitrary my @output = `sox $file -e band $freq $width stat 2>&1`; die "@output" if $CHILD_ERROR; @output = grep {/Max.* ampl/} @output; my ( $energy ) = $output[0] =~ /Maximum amplitude:.*?([0-9.]+)/; push(@energies, [$freq, $energy]); # print "$freq $energy\n"; # if anyone wants the raw data } @energies = sort { $b->[1] <=> $a->[1] } @energies; # highest energy first # The top two energies ought to be much higher than the third highest # We could assert that, but then we lose our robustness to noise and padding my @top2 = map { $ARG->[0] } @energies[0..1]; # discard the energy values @top2 = sort { $a <=> $b } @top2; # sort them into a predictable order print $map{"@top2"}. "\n";