package com.talixa.specan.demod.dtmf;
import com.talixa.audio.wav.WaveFile;
import com.talixa.audio.wav.WaveReader;
import com.talixa.specan.dsp.SharedDSPFunctions;
import com.talixa.specan.fft.Complex;
import com.talixa.specan.fft.FFT;
import com.talixa.specan.fft.FrequencyTools;
public class DTMFDecoder {
/*
* TONE TABLE
* 1209 Hz 1336 Hz 1477 Hz 1633 Hz
* 697 Hz 1 2 3 A
* 770 Hz 4 5 6 B
* 852 Hz 7 8 9 C
* 941 Hz * 0 # D
*
* Event Low frequency High frequency
* Busy signal (US) 480 Hz 620 Hz
* Ringback tone (US) 440 Hz 480 Hz
* Dial tone (US) 350 Hz 440 Hz
*/
/*
* 1) Run FFT on input data
* 2) Check for spikes at frequencies
*/
private static final int FFT_LEN = 256*2;
private static final int SHIFT_VALUE = FFT_LEN;
private static final int F0697 = SharedDSPFunctions.getSampleNumberByFrequency(FFT_LEN, 697);
private static final int F0770 = SharedDSPFunctions.getSampleNumberByFrequency(FFT_LEN, 770);
private static final int F0852 = SharedDSPFunctions.getSampleNumberByFrequency(FFT_LEN, 852);
private static final int F0941 = SharedDSPFunctions.getSampleNumberByFrequency(FFT_LEN, 941);
private static final int F1209 = SharedDSPFunctions.getSampleNumberByFrequency(FFT_LEN, 1209);
private static final int F1336 = SharedDSPFunctions.getSampleNumberByFrequency(FFT_LEN, 1336);
private static final int F1477 = SharedDSPFunctions.getSampleNumberByFrequency(FFT_LEN, 1477);
private static final int F1633 = SharedDSPFunctions.getSampleNumberByFrequency(FFT_LEN, 1633);
public static String detect(short[] data) {
int numberSamples = data.length;
Complex[] amplitudeData = new Complex[FFT_LEN];
Complex[] frequencyData = new Complex[FFT_LEN];
char lastChar = ' ';
StringBuilder dtmfChars = new StringBuilder();
for(int baseAddress = 0; (baseAddress)+FFT_LEN < numberSamples; baseAddress+=SHIFT_VALUE) {
// fill the data array
for (int offset = 0; offset < FFT_LEN; offset++) {
int desiredSampleIndex = (baseAddress)+offset;
short signedSample = data[desiredSampleIndex];
double sample = ((double) signedSample) / (Short.MAX_VALUE);
amplitudeData[offset] = new Complex(sample,0);
}
// run fft
frequencyData = FFT.fft(amplitudeData);
// Use FFT_LEN/2 since the data is mirrored within the array.
double[] scaledAmpFreq = new double[FFT_LEN/2];
for(int i=1;i < FFT_LEN/2-1;i++) {
double re = frequencyData[i].re();
double im = frequencyData[i].im();
//get amplitude and scale to range 0 - RESOLUTION
scaledAmpFreq[i]=FrequencyTools.amplitudeScaled(re,im,FFT_LEN,512)*1000;
}
int detectVol = 75;
char thisChar= ' ';
if (scaledAmpFreq[F0697] > detectVol && scaledAmpFreq[F1209] > detectVol) {
thisChar = '1';
} else if (scaledAmpFreq[F0697] > detectVol && scaledAmpFreq[F1336] > detectVol) {
thisChar = '2';
} else if (scaledAmpFreq[F0697] > detectVol && scaledAmpFreq[F1477] > detectVol) {
thisChar = '3';
} else if (scaledAmpFreq[F0770] > detectVol && scaledAmpFreq[F1209] > detectVol) {
thisChar = '4';
} else if (scaledAmpFreq[F0770] > detectVol && scaledAmpFreq[F1336] > detectVol) {
thisChar = '5';
} else if (scaledAmpFreq[F0770] > detectVol && scaledAmpFreq[F1477] > detectVol) {
thisChar = '6';
} else if (scaledAmpFreq[F0852] > detectVol && scaledAmpFreq[F1209] > detectVol) {
thisChar = '7';
} else if (scaledAmpFreq[F0852] > detectVol && scaledAmpFreq[F1336] > detectVol) {
thisChar = '8';
} else if (scaledAmpFreq[F0852] > detectVol && scaledAmpFreq[F1477] > detectVol) {
thisChar = '9';
} else if (scaledAmpFreq[F0941] > detectVol && scaledAmpFreq[F1209] > detectVol) {
thisChar = '*';
} else if (scaledAmpFreq[F0941] > detectVol && scaledAmpFreq[F1336] > detectVol) {
thisChar = '0';
} else if (scaledAmpFreq[F0941] > detectVol && scaledAmpFreq[F1477] > detectVol) {
thisChar = '#';
} else if (scaledAmpFreq[F0697] > detectVol && scaledAmpFreq[F1633] > detectVol) {
thisChar = 'A';
} else if (scaledAmpFreq[F0770] > detectVol && scaledAmpFreq[F1633] > detectVol) {
thisChar = 'B';
} else if (scaledAmpFreq[F0852] > detectVol && scaledAmpFreq[F1633] > detectVol) {
thisChar = 'C';
} else if (scaledAmpFreq[F0941] > detectVol && scaledAmpFreq[F1633] > detectVol) {
thisChar = 'D';
}
if (lastChar == ' ' && thisChar != ' ') {
dtmfChars.append(thisChar);
}
lastChar = thisChar;
}
return dtmfChars.toString();
}
public static void main(String[] args) {
testDemod();
}
private static final String PATH_WIN = "C:\\Users\\tgerlach\\git\\specan\\SpecAn\\res\\";
private static final String PATH_LINUX = "/home/thomas/git/specan/SpecAn/res/";
private static final String[] TEST_FILES = new String[] {"dtmf.wav"};
public static void testDemod() {
boolean usingWindows = System.getProperty("os.name").toLowerCase().contains("windows");
String path;
if (usingWindows) {
path = PATH_WIN;
} else {
path = PATH_LINUX;
}
for(int i = 0; i < TEST_FILES.length; ++i) {
System.out.print("***********************");
System.out.print(TEST_FILES[i]);
System.out.println("***********************");
String inputFile = path + TEST_FILES[i];
try {
WaveFile waveFile = WaveReader.readFromFile(inputFile);
short[] data = SharedDSPFunctions.extractWaveFileData(waveFile);
String tones = detect(data);
System.out.println(tones);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}