Added dtmf2num tone decoding - warvox - VoIP based wardialing tool, forked from rapid7/warvox.
DIR Log
DIR Files
DIR Refs
DIR README
---
DIR commit 6ff719b811c876e58c6477c8f70411c5ce5a0291
DIR parent 953ea21a9aeace23a66b41311f6cb5e21c01137d
HTML Author: HD Moore <hd_moore@rapid7.com>
Date: Tue, 26 May 2009 03:20:48 +0000
Added dtmf2num tone decoding
Diffstat:
M Makefile | 7 ++++++-
M bin/verify_install.rb | 6 ++++++
M docs/ChangeLog | 1 +
M etc/warvox.conf | 1 +
M lib/warvox/jobs/analysis.rb | 27 ++++++++++++++++++++++++---
A src/dtmf2num/Makefile | 16 ++++++++++++++++
A src/dtmf2num/README | 5 +++++
A src/dtmf2num/dsp.c | 502 +++++++++++++++++++++++++++++++
A src/dtmf2num/dtmf2num.c | 372 +++++++++++++++++++++++++++++++
A src/dtmf2num/mywav.h | 244 +++++++++++++++++++++++++++++++
A src/dtmf2num/resample2.c | 342 +++++++++++++++++++++++++++++++
M web/app/controllers/analyze_contro… | 4 ++--
M web/app/views/analyze/view.html.erb | 9 +++++++--
M web/db/schema.rb | 4 +++-
14 files changed, 1531 insertions(+), 9 deletions(-)
---
DIR diff --git a/Makefile b/Makefile
@@ -3,8 +3,9 @@ all: test
test: install
bin/verify_install.rb
-install: iaxrecord ruby-kissfft db
+install: iaxrecord dtmf2num ruby-kissfft db
cp -a src/iaxrecord/iaxrecord bin/
+ cp -a src/dtmf2num/dtmf2num bin/
ruby-kissfft-install: ruby-kissfft
cp -a src/ruby-kissfft/kissfft.so lib/
@@ -12,6 +13,9 @@ ruby-kissfft-install: ruby-kissfft
iaxrecord:
make -C src/iaxrecord/
+dtmf2num:
+ make -C src/dtmf2num/
+
ruby-kissfft:
( cd src/ruby-kissfft/; ruby extconf.rb )
make -C src/ruby-kissfft/
@@ -29,4 +33,5 @@ clean:
( cd src/ruby-kissfft/; ruby extconf.rb )
make -C src/ruby-kissfft/ clean
make -C src/iaxrecord/ clean
+ make -C src/dtmf2num/ clean
rm -f bin/iaxrecord lib/kissfft.so
DIR diff --git a/bin/verify_install.rb b/bin/verify_install.rb
@@ -63,6 +63,12 @@ if(not WarVOX::Config.tool_path('iaxrecord'))
end
puts "[*] The IAXRECORD binary appears to be available"
+if(not WarVOX::Config.tool_path('dtmf2num'))
+ puts "[*] ERROR: The 'dtmf2num' binary could not be installed"
+ exit
+end
+puts "[*] The DTMF2NUM binary appears to be available"
+
puts " "
puts "[*] Congratulations! You are now ready to run WarVOX"
DIR diff --git a/docs/ChangeLog b/docs/ChangeLog
@@ -4,6 +4,7 @@
* overhaul of the signature system (etc/sigs/*.rb)
* filter analysis results by line type
* overhaul of results view + show signatures
+ * added dtmf2num support to decode dtmf tones
2009-05-10 H D Moore <hdm[at]metasploit.com>
DIR diff --git a/etc/warvox.conf b/etc/warvox.conf
@@ -25,6 +25,7 @@ tools:
sox: sox
lame: lame
iaxrecord: %BASE%/bin/iaxrecord
+ dtmf2num: %BASE%/bin/dtmf2num
#
# Concurrent processing jobs, change this to
DIR diff --git a/lib/warvox/jobs/analysis.rb b/lib/warvox/jobs/analysis.rb
@@ -284,7 +284,7 @@ class Analysis < Base
plotter.puts("plot \"#{frefile.path}\" using 1:2 notitle with lines")
plotter.flush
- system("gnuplot #{plotter.path}")
+ system("#{WarVOX::Config.tool_path('gnuplot')} #{plotter.path}")
File.unlink(plotter.path)
File.unlink(datfile.path)
File.unlink(frefile.path)
@@ -292,11 +292,32 @@ class Analysis < Base
datfile.close
frefile.path
+
+ # Detect DTMF and MF tones
+ dtmf = ''
+ mf = ''
+ pfd = IO.popen("#{WarVOX::Config.tool_path('dtmf2num')} -r 8000 1 16 #{rawfile.path} 2>/dev/null")
+ pfd.each_line do |line|
+ line = line.strip
+ if(line.strip =~ /^- MF numbers:\s+(.*)/)
+ next if $1 == 'none'
+ mf = $1
+ end
+ if(line.strip =~ /^- DTMF numbers:\s+(.*)/)
+ next if $1 == 'none'
+ dtmf = $1
+ end
+ end
+ pfd.close
+ res[:dtmf] = dtmf
+ res[:mf] = mf
+
+
# Generate a MP3 audio file
- system("sox -s -2 -r 8000 -t raw -c 1 #{rawfile.path} #{bname}.wav")
+ system("#{WarVOX::Config.tool_path('sox')} -s -2 -r 8000 -t raw -c 1 #{rawfile.path} #{bname}.wav")
# Default samples at 8k, bump it to 32k to get better quality
- system("lame -b 32 #{bname}.wav #{bname}.mp3 >/dev/null 2>&1")
+ system("#{WarVOX::Config.tool_path('lame')} -b 32 #{bname}.wav #{bname}.mp3 >/dev/null 2>&1")
File.unlink("#{bname}.wav")
File.unlink(rawfile.path)
DIR diff --git a/src/dtmf2num/Makefile b/src/dtmf2num/Makefile
@@ -0,0 +1,16 @@
+EXE = dtmf2num
+CFLAGS += -O2 -s
+BINDIR = $(PREFIX)/bin
+LIBS = -lm
+
+all:
+ $(CC) $(CFLAGS) -c dtmf2num.c
+ $(CC) $(CFLAGS) -c dsp.c
+ $(CC) $(CFLAGS) -c resample2.c
+ $(CC) $(CFLAGS) -o $(EXE) $(LIBS) dtmf2num.o dsp.o resample2.o
+
+clean:
+ @rm -f *.o $(EXE)
+
+.PHONY:
+ install
DIR diff --git a/src/dtmf2num/README b/src/dtmf2num/README
@@ -0,0 +1,5 @@
+This is a barely tweaked copy of DTMF2NUM, developed by Luigi Auriemma
+ - http://aluigi.org/mytoolz.htm#dtmf2num
+
+This tool is provided under the GPL:
+ - http://www.gnu.org/licenses/gpl.txt
DIR diff --git a/src/dtmf2num/dsp.c b/src/dtmf2num/dsp.c
@@ -0,0 +1,502 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2005, Digium, Inc.
+ *
+ * Mark Spencer <markster@digium.com>
+ *
+ * Goertzel routines are borrowed from Steve Underwood's tremendous work on the
+ * DTMF detector.
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ *
+ * \brief Convenience Signal Processing routines
+ *
+ * \author Mark Spencer <markster@digium.com>
+ * \author Steve Underwood <steveu@coppice.org>
+ */
+
+/* Some routines from tone_detect.c by Steven Underwood as published under the zapata library */
+/*
+ tone_detect.c - General telephony tone detection, and specific
+ detection of DTMF.
+
+ Copyright (C) 2001 Steve Underwood <steveu@coppice.org>
+
+ Despite my general liking of the GPL, I place this code in the
+ public domain for the benefit of all mankind - even the slimy
+ ones who might try to proprietize my work and use it to my
+ detriment.
+*/
+
+/*
+ modifications for decoding also damaged audio by Luigi auriemma "// aluigi work-around"
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+#include <math.h>
+#include <malloc.h>
+
+#define DSP_DIGITMODE_DTMF 0 /*!< Detect DTMF digits */
+#define DSP_DIGITMODE_MF 1 /*!< Detect MF digits */
+
+#define DSP_DIGITMODE_NOQUELCH (1 << 8) /*!< Do not quelch DTMF from in-band */
+#define DSP_DIGITMODE_RELAXDTMF (1 << 11) /*!< "Radio" mode (relaxed DTMF) */
+
+#define MAX_DTMF_DIGITS 1024
+
+/* Basic DTMF specs:
+ *
+ * Minimum tone on = 40ms
+ * Minimum tone off = 50ms
+ * Maximum digit rate = 10 per second
+ * Normal twist <= 8dB accepted
+ * Reverse twist <= 4dB accepted
+ * S/N >= 15dB will detect OK
+ * Attenuation <= 26dB will detect OK
+ * Frequency tolerance +- 1.5% will detect, +-3.5% will reject
+ */
+
+//#define DTMF_THRESHOLD 8.0e7
+#define DTMF_THRESHOLD 800000000.0 // aluigi work-around
+#define DTMF_NORMAL_TWIST 6.3 /* 8dB */
+#ifdef RADIO_RELAX
+#define DTMF_REVERSE_TWIST ((digitmode & DSP_DIGITMODE_RELAXDTMF) ? 6.5 : 2.5) /* 4dB normal */
+#else
+#define DTMF_REVERSE_TWIST ((digitmode & DSP_DIGITMODE_RELAXDTMF) ? 4.0 : 2.5) /* 4dB normal */
+#endif
+#define DTMF_RELATIVE_PEAK_ROW 6.3 /* 8dB */
+#define DTMF_RELATIVE_PEAK_COL 6.3 /* 8dB */
+#define DTMF_2ND_HARMONIC_ROW ((digitmode & DSP_DIGITMODE_RELAXDTMF) ? 1.7 : 2.5) /* 4dB normal */
+#define DTMF_2ND_HARMONIC_COL 63.1 /* 18dB */
+#define DTMF_TO_TOTAL_ENERGY 42.0
+
+//#define BELL_MF_THRESHOLD 1.6e9
+#define BELL_MF_THRESHOLD DTMF_THRESHOLD // aluigi work-around
+#define BELL_MF_TWIST 4.0 /* 6dB */
+#define BELL_MF_RELATIVE_PEAK 12.6 /* 11dB */
+
+static int SAMPLE_RATE = 8000;
+
+typedef struct {
+ int v2;
+ int v3;
+ int chunky;
+ int fac;
+ int samples;
+} goertzel_state_t;
+
+typedef struct {
+ int value;
+ int power;
+} goertzel_result_t;
+
+typedef struct
+{
+ goertzel_state_t row_out[4];
+ goertzel_state_t col_out[4];
+ int lasthit;
+ int current_hit;
+ float energy;
+ int current_sample;
+} dtmf_detect_state_t;
+
+typedef struct
+{
+ goertzel_state_t tone_out[6];
+ int current_hit;
+ int hits[5];
+ int current_sample;
+} mf_detect_state_t;
+
+typedef struct
+{
+ char digits[MAX_DTMF_DIGITS + 1];
+ int current_digits;
+ int detected_digits;
+ int lost_digits;
+
+ union {
+ dtmf_detect_state_t dtmf;
+ mf_detect_state_t mf;
+ } td;
+} digit_detect_state_t;
+
+static float dtmf_row[] =
+{
+ 697.0, 770.0, 852.0, 941.0
+};
+static float dtmf_col[] =
+{
+ 1209.0, 1336.0, 1477.0, 1633.0
+};
+
+static float mf_tones[] =
+{
+ 700.0, 900.0, 1100.0, 1300.0, 1500.0, 1700.0
+};
+
+static char dtmf_positions[] = "123A" "456B" "789C" "*0#D";
+
+static char bell_mf_positions[] = "1247C-358A--69*---0B----#";
+
+static inline void goertzel_sample(goertzel_state_t *s, short sample)
+{
+ int v1;
+
+ v1 = s->v2;
+ s->v2 = s->v3;
+
+ s->v3 = (s->fac * s->v2) >> 15;
+ s->v3 = s->v3 - v1 + (sample >> s->chunky);
+ if (abs(s->v3) > 32768) {
+ s->chunky++;
+ s->v3 = s->v3 >> 1;
+ s->v2 = s->v2 >> 1;
+ v1 = v1 >> 1;
+ }
+}
+
+static inline void goertzel_update(goertzel_state_t *s, short *samps, int count)
+{
+ int i;
+
+ for (i=0;i<count;i++)
+ goertzel_sample(s, samps[i]);
+}
+
+static inline float goertzel_result(goertzel_state_t *s)
+{
+ goertzel_result_t r;
+ r.value = (s->v3 * s->v3) + (s->v2 * s->v2);
+ r.value -= ((s->v2 * s->v3) >> 15) * s->fac;
+ r.power = s->chunky * 2;
+ return (float)r.value * (float)(1 << r.power);
+}
+
+static inline void goertzel_init(goertzel_state_t *s, float freq, int samples)
+{
+ s->v2 = s->v3 = s->chunky = 0.0;
+ s->fac = (int)(32768.0 * 2.0 * cos(2.0 * M_PI * freq / SAMPLE_RATE));
+ s->samples = samples;
+}
+
+static inline void goertzel_reset(goertzel_state_t *s)
+{
+ s->v2 = s->v3 = s->chunky = 0.0;
+}
+
+static void ast_dtmf_detect_init (dtmf_detect_state_t *s)
+{
+ int i;
+
+ s->lasthit = 0;
+ s->current_hit = 0;
+ for (i = 0; i < 4; i++) {
+ goertzel_init (&s->row_out[i], dtmf_row[i], 102);
+ goertzel_init (&s->col_out[i], dtmf_col[i], 102);
+ s->energy = 0.0;
+ }
+ s->current_sample = 0;
+}
+
+static void ast_mf_detect_init (mf_detect_state_t *s)
+{
+ int i;
+ s->hits[0] = s->hits[1] = s->hits[2] = s->hits[3] = s->hits[4] = 0;
+ for (i = 0; i < 6; i++) {
+ goertzel_init (&s->tone_out[i], mf_tones[i], 160);
+ }
+ s->current_sample = 0;
+ s->current_hit = 0;
+}
+
+static void ast_digit_detect_init(digit_detect_state_t *s, int mf)
+{
+ s->current_digits = 0;
+ s->detected_digits = 0;
+ s->lost_digits = 0;
+ s->digits[0] = '\0';
+
+ if (mf)
+ ast_mf_detect_init(&s->td.mf);
+ else
+ ast_dtmf_detect_init(&s->td.dtmf);
+}
+
+static void store_digit(digit_detect_state_t *s, char digit)
+{
+ s->detected_digits++;
+ if (s->current_digits < MAX_DTMF_DIGITS) {
+ s->digits[s->current_digits++] = digit;
+ s->digits[s->current_digits] = '\0';
+ } else {
+ //ast_log(LOG_WARNING, "Digit lost due to full buffer\n");
+ s->lost_digits++;
+ }
+}
+
+static int dtmf_detect(digit_detect_state_t *s, int16_t amp[], int samples,
+ int digitmode, int *writeback)
+{
+ float row_energy[4];
+ float col_energy[4];
+ float famp;
+ int i;
+ int j;
+ int sample;
+ int best_row;
+ int best_col;
+ int hit;
+ int limit;
+
+ hit = 0;
+ for (sample = 0; sample < samples; sample = limit) {
+ /* 102 is optimised to meet the DTMF specs. */
+ if ((samples - sample) >= (102 - s->td.dtmf.current_sample))
+ limit = sample + (102 - s->td.dtmf.current_sample);
+ else
+ limit = samples;
+ /* The following unrolled loop takes only 35% (rough estimate) of the
+ time of a rolled loop on the machine on which it was developed */
+ for (j = sample; j < limit; j++) {
+ famp = amp[j];
+ s->td.dtmf.energy += famp*famp;
+ /* With GCC 2.95, the following unrolled code seems to take about 35%
+ (rough estimate) as long as a neat little 0-3 loop */
+ goertzel_sample(s->td.dtmf.row_out, amp[j]);
+ goertzel_sample(s->td.dtmf.col_out, amp[j]);
+ goertzel_sample(s->td.dtmf.row_out + 1, amp[j]);
+ goertzel_sample(s->td.dtmf.col_out + 1, amp[j]);
+ goertzel_sample(s->td.dtmf.row_out + 2, amp[j]);
+ goertzel_sample(s->td.dtmf.col_out + 2, amp[j]);
+ goertzel_sample(s->td.dtmf.row_out + 3, amp[j]);
+ goertzel_sample(s->td.dtmf.col_out + 3, amp[j]);
+ }
+ s->td.dtmf.current_sample += (limit - sample);
+ if (s->td.dtmf.current_sample < 102) {
+ if (hit && !((digitmode & DSP_DIGITMODE_NOQUELCH))) {
+ /* If we had a hit last time, go ahead and clear this out since likely it
+ will be another hit */
+ for (i=sample;i<limit;i++)
+ amp[i] = 0;
+ *writeback = 1;
+ }
+ continue;
+ }
+ /* We are at the end of a DTMF detection block */
+ /* Find the peak row and the peak column */
+ row_energy[0] = goertzel_result (&s->td.dtmf.row_out[0]);
+ col_energy[0] = goertzel_result (&s->td.dtmf.col_out[0]);
+
+ for (best_row = best_col = 0, i = 1; i < 4; i++) {
+ row_energy[i] = goertzel_result (&s->td.dtmf.row_out[i]);
+ if (row_energy[i] > row_energy[best_row])
+ best_row = i;
+ col_energy[i] = goertzel_result (&s->td.dtmf.col_out[i]);
+ if (col_energy[i] > col_energy[best_col])
+ best_col = i;
+ }
+ hit = 0;
+
+ /* Basic signal level test and the twist test */
+ if (row_energy[best_row] >= DTMF_THRESHOLD &&
+ col_energy[best_col] >= DTMF_THRESHOLD &&
+// col_energy[best_col] < row_energy[best_row] *DTMF_REVERSE_TWIST && // aluigi work-around
+ col_energy[best_col]*DTMF_NORMAL_TWIST > row_energy[best_row]) {
+ /* Relative peak test */
+ for (i = 0; i < 4; i++) {
+ if ((i != best_col &&
+ col_energy[i]*DTMF_RELATIVE_PEAK_COL > col_energy[best_col]) ||
+ (i != best_row
+ && row_energy[i]*DTMF_RELATIVE_PEAK_ROW > row_energy[best_row])) {
+ break;
+ }
+ }
+ /* ... and fraction of total energy test */
+ if (i >= 4 /*&&
+ (row_energy[best_row] + col_energy[best_col]) > DTMF_TO_TOTAL_ENERGY*s->td.dtmf.energy*/) { // aluigi work-around
+ /* Got a hit */
+ hit = dtmf_positions[(best_row << 2) + best_col];
+ if (!(digitmode & DSP_DIGITMODE_NOQUELCH)) {
+ /* Zero out frame data if this is part DTMF */
+ for (i=sample;i<limit;i++)
+ amp[i] = 0;
+ *writeback = 1;
+ }
+ }
+ }
+
+ /* The logic in the next test is:
+ For digits we need two successive identical clean detects, with
+ something different preceeding it. This can work with
+ back to back differing digits. More importantly, it
+ can work with nasty phones that give a very wobbly start
+ to a digit */
+ if (hit != s->td.dtmf.current_hit) {
+ if (hit && s->td.dtmf.lasthit == hit) {
+ s->td.dtmf.current_hit = hit;
+ store_digit(s, hit);
+ } else if (s->td.dtmf.lasthit != s->td.dtmf.current_hit) {
+ s->td.dtmf.current_hit = 0;
+ }
+ }
+ s->td.dtmf.lasthit = hit;
+
+ /* Reinitialise the detector for the next block */
+ for (i = 0; i < 4; i++) {
+ goertzel_reset(&s->td.dtmf.row_out[i]);
+ goertzel_reset(&s->td.dtmf.col_out[i]);
+ }
+ s->td.dtmf.energy = 0.0;
+ s->td.dtmf.current_sample = 0;
+ }
+ return (s->td.dtmf.current_hit); /* return the debounced hit */
+}
+
+/* MF goertzel size */
+#define MF_GSIZE 120
+
+static int mf_detect(digit_detect_state_t *s, int16_t amp[],
+ int samples, int digitmode, int *writeback)
+{
+ float energy[6];
+ int best;
+ int second_best;
+ float famp;
+ int i;
+ int j;
+ int sample;
+ int hit;
+ int limit;
+
+ hit = 0;
+ for (sample = 0; sample < samples; sample = limit) {
+ /* 80 is optimised to meet the MF specs. */
+ if ((samples - sample) >= (MF_GSIZE - s->td.mf.current_sample))
+ limit = sample + (MF_GSIZE - s->td.mf.current_sample);
+ else
+ limit = samples;
+ /* The following unrolled loop takes only 35% (rough estimate) of the
+ time of a rolled loop on the machine on which it was developed */
+ for (j = sample; j < limit; j++) {
+ famp = amp[j];
+ /* With GCC 2.95, the following unrolled code seems to take about 35%
+ (rough estimate) as long as a neat little 0-3 loop */
+ goertzel_sample(s->td.mf.tone_out, amp[j]);
+ goertzel_sample(s->td.mf.tone_out + 1, amp[j]);
+ goertzel_sample(s->td.mf.tone_out + 2, amp[j]);
+ goertzel_sample(s->td.mf.tone_out + 3, amp[j]);
+ goertzel_sample(s->td.mf.tone_out + 4, amp[j]);
+ goertzel_sample(s->td.mf.tone_out + 5, amp[j]);
+ }
+ s->td.mf.current_sample += (limit - sample);
+ if (s->td.mf.current_sample < MF_GSIZE) {
+ if (hit && !((digitmode & DSP_DIGITMODE_NOQUELCH))) {
+ /* If we had a hit last time, go ahead and clear this out since likely it
+ will be another hit */
+ for (i=sample;i<limit;i++)
+ amp[i] = 0;
+ *writeback = 1;
+ }
+ continue;
+ }
+ /* We're at the end of an MF detection block. */
+ /* Find the two highest energies. The spec says to look for
+ two tones and two tones only. Taking this literally -ie
+ only two tones pass the minimum threshold - doesn't work
+ well. The sinc function mess, due to rectangular windowing
+ ensure that! Find the two highest energies and ensure they
+ are considerably stronger than any of the others. */
+ energy[0] = goertzel_result(&s->td.mf.tone_out[0]);
+ energy[1] = goertzel_result(&s->td.mf.tone_out[1]);
+ if (energy[0] > energy[1]) {
+ best = 0;
+ second_best = 1;
+ } else {
+ best = 1;
+ second_best = 0;
+ }
+ /*endif*/
+ for (i=2;i<6;i++) {
+ energy[i] = goertzel_result(&s->td.mf.tone_out[i]);
+ if (energy[i] >= energy[best]) {
+ second_best = best;
+ best = i;
+ } else if (energy[i] >= energy[second_best]) {
+ second_best = i;
+ }
+ }
+ /* Basic signal level and twist tests */
+ hit = 0;
+ if (energy[best] >= BELL_MF_THRESHOLD && energy[second_best] >= BELL_MF_THRESHOLD
+// && energy[best] < energy[second_best]*BELL_MF_TWIST // aluigi work-around
+ && energy[best]*BELL_MF_TWIST > energy[second_best]) {
+ /* Relative peak test */
+ hit = -1;
+ for (i=0;i<6;i++) {
+ if (i != best && i != second_best) {
+ if (energy[i]*BELL_MF_RELATIVE_PEAK >= energy[second_best]) {
+ /* The best two are not clearly the best */
+ hit = 0;
+ break;
+ }
+ }
+ }
+ }
+ if (hit) {
+ /* Get the values into ascending order */
+ if (second_best < best) {
+ i = best;
+ best = second_best;
+ second_best = i;
+ }
+ best = best*5 + second_best - 1;
+ hit = bell_mf_positions[best];
+ /* Look for two successive similar results */
+ /* The logic in the next test is:
+ For KP we need 4 successive identical clean detects, with
+ two blocks of something different preceeding it. For anything
+ else we need two successive identical clean detects, with
+ two blocks of something different preceeding it. */
+ if (hit == s->td.mf.hits[4] && hit == s->td.mf.hits[3] &&
+ ((hit != '*' && hit != s->td.mf.hits[2] && hit != s->td.mf.hits[1])||
+ (hit == '*' && hit == s->td.mf.hits[2] && hit != s->td.mf.hits[1] &&
+ hit != s->td.mf.hits[0]))) {
+ store_digit(s, hit);
+ }
+ }
+
+
+ if (hit != s->td.mf.hits[4] && hit != s->td.mf.hits[3]) {
+ /* Two successive block without a hit terminate current digit */
+ s->td.mf.current_hit = 0;
+ }
+
+ s->td.mf.hits[0] = s->td.mf.hits[1];
+ s->td.mf.hits[1] = s->td.mf.hits[2];
+ s->td.mf.hits[2] = s->td.mf.hits[3];
+ s->td.mf.hits[3] = s->td.mf.hits[4];
+ s->td.mf.hits[4] = hit;
+ /* Reinitialise the detector for the next block */
+ for (i = 0; i < 6; i++)
+ goertzel_reset(&s->td.mf.tone_out[i]);
+ s->td.mf.current_sample = 0;
+ }
+
+ return (s->td.mf.current_hit); /* return the debounced hit */
+}
DIR diff --git a/src/dtmf2num/dtmf2num.c b/src/dtmf2num/dtmf2num.c
@@ -0,0 +1,372 @@
+/*
+ Copyright 2008 Luigi Auriemma
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+ http://www.gnu.org/licenses/gpl.txt
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+#include <sys/stat.h>
+#include "mywav.h"
+#include "dsp.c"
+//#include "resample2.c"
+
+
+
+typedef int8_t i8;
+typedef uint8_t u8;
+typedef int16_t i16;
+typedef uint16_t u16;
+typedef int32_t i32;
+typedef uint32_t u32;
+
+
+
+#define VER "0.1c"
+
+
+
+int mywav_fri24(FILE *fd, uint32_t *num);
+i16 *do_samples(FILE *fd, int wavsize, int *ret_samples, int bits);
+int do_mono(i16 *smp, int samples, int ch);
+void do_dcbias(i16 *smp, int samples);
+void do_normalize(i16 *smp, int samples);
+int do_8000(i16 *smp, int samples, int *freq);
+void my_err(u8 *err);
+void std_err(void);
+
+
+
+int main(int argc, char *argv[]) {
+ digit_detect_state_t dtmf;
+ mywav_fmtchunk fmt;
+ struct stat xstat;
+ FILE *fd;
+ int i,
+ wavsize,
+ samples,
+ writeback,
+ raw = 0,
+ optimize = 1;
+ i16 *smp;
+ u8 *fname,
+ *outfile = NULL;
+
+ setbuf(stdin, NULL);
+ setbuf(stdout, NULL);
+
+ fputs("\n"
+ "DTMF2NUM "VER"\n"
+ "by Luigi Auriemma\n"
+ "e-mail: aluigi@autistici.org\n"
+ "web: aluigi.org\n"
+ "\n", stderr);
+
+ if(argc < 2) {
+ printf("\n"
+ "Usage: %s [options] <file.WAV>\n"
+ "\n"
+ "Options:\n"
+ "-r F C B consider the file as raw headerless PCM data, you must specify the\n"
+ " Frequency, Channels and Bits like -r 44100 2 16\n"
+ "-o disable the automatic optimizations: DC bias adjust and normalize.\n"
+ " use this option only if your file is already clean and normalized\n"
+ "-w FILE debug option for dumping the handled samples from the memory to FILE\n"
+ "\n", argv[0]);
+ exit(1);
+ }
+
+ argc--;
+ for(i = 1; i < argc; i++) {
+ if(((argv[i][0] != '-') && (argv[i][0] != '/')) || (strlen(argv[i]) != 2)) {
+ printf("\nError: wrong argument (%s)\n", argv[i]);
+ exit(1);
+ }
+ switch(argv[i][1]) {
+ case 'r': {
+ memset(&fmt, 0, sizeof(fmt));
+ if(!argv[++i]) exit(1);
+ fmt.dwSamplesPerSec = atoi(argv[i]);
+ if(!argv[++i]) exit(1);
+ fmt.wChannels = atoi(argv[i]);
+ if(!argv[++i]) exit(1);
+ fmt.wBitsPerSample = atoi(argv[i]);
+ fmt.wFormatTag = 1;
+ raw = 1;
+ } break;
+ case 'o': {
+ optimize = 0;
+ } break;
+ case 'w': {
+ if(!argv[++i]) exit(1);
+ outfile = argv[i];
+ } break;
+ default: {
+ printf("\nError: wrong option (%s)\n", argv[i]);
+ exit(1);
+ }
+ }
+ }
+
+ fname = argv[argc];
+
+ if(!strcmp(fname, "-")) {
+ printf("- open stdin\n");
+ fd = stdin;
+ } else {
+ printf("- open %s\n", fname);
+ fd = fopen(fname, "rb");
+ if(!fd) std_err();
+ }
+
+ if(raw) {
+ fstat(fileno(fd), &xstat);
+ wavsize = xstat.st_size;
+ } else {
+ wavsize = mywav_data(fd, &fmt);
+ }
+ fprintf(stderr,
+ " wave size %u\n"
+ " format tag %hu\n"
+ " channels: %hu\n"
+ " samples/sec: %u\n"
+ " avg/bytes/sec: %u\n"
+ " block align: %hu\n"
+ " bits: %hu\n",
+ wavsize,
+ fmt.wFormatTag,
+ fmt.wChannels,
+ fmt.dwSamplesPerSec,
+ fmt.dwAvgBytesPerSec,
+ fmt.wBlockAlign,
+ fmt.wBitsPerSample);
+
+ if(wavsize <= 0) my_err("corrupted WAVE file");
+ if(fmt.wFormatTag != 1) my_err("only the classical PCM WAVE files are supported");
+
+ smp = do_samples(fd, wavsize, &samples, fmt.wBitsPerSample);
+ fprintf(stderr, " samples: %d\n", samples);
+ if(fd != stdin) fclose(fd);
+
+ samples = do_mono(smp, samples, fmt.wChannels);
+ if(optimize) {
+ do_dcbias(smp, samples);
+ do_normalize(smp, samples);
+ }
+ samples = do_8000(smp, samples, &fmt.dwSamplesPerSec);
+
+ fmt.wFormatTag = 0x0001;
+ fmt.wChannels = 1;
+ fmt.wBitsPerSample = 16;
+ fmt.wBlockAlign = (fmt.wBitsPerSample >> 3) * fmt.wChannels;
+ fmt.dwAvgBytesPerSec = fmt.dwSamplesPerSec * fmt.wBlockAlign;
+ wavsize = samples * sizeof(* smp);
+
+ if(outfile) {
+ fprintf(stderr, "- dump %s\n", outfile);
+ fd = fopen(outfile, "wb");
+ if(!fd) std_err();
+ mywav_writehead(fd, &fmt, wavsize, NULL, 0);
+ fwrite(smp, 1, wavsize, fd);
+ fclose(fd);
+ }
+
+ SAMPLE_RATE = fmt.dwSamplesPerSec;
+
+ ast_digit_detect_init(&dtmf, DSP_DIGITMODE_MF);
+ mf_detect(&dtmf, smp, samples, DSP_DIGITMODE_NOQUELCH, &writeback);
+ printf("\n- MF numbers: %s\n", dtmf.digits[0] ? dtmf.digits : "none");
+
+ ast_digit_detect_init(&dtmf, DSP_DIGITMODE_DTMF);
+ dtmf_detect(&dtmf, smp, samples, DSP_DIGITMODE_NOQUELCH, &writeback);
+ printf("\n- DTMF numbers: %s\n", dtmf.digits[0] ? dtmf.digits : "none");
+
+ return(0);
+}
+
+
+
+int mywav_fri24(FILE *fd, uint32_t *num) {
+ uint32_t ret;
+ uint8_t tmp;
+
+ if(fread(&tmp, 1, 1, fd) != 1) return(-1); ret = tmp;
+ if(fread(&tmp, 1, 1, fd) != 1) return(-1); ret |= (tmp << 8);
+ if(fread(&tmp, 1, 1, fd) != 1) return(-1); ret |= (tmp << 16);
+ *num = ret;
+ return(0);
+}
+
+
+
+i16 *do_samples(FILE *fd, int wavsize, int *ret_samples, int bits) {
+ i32 tmp32;
+ int i = 0,
+ samples;
+ i16 *smp;
+ i8 tmp8;
+
+ samples = wavsize / (bits >> 3);
+ smp = malloc(sizeof(* smp) * samples);
+ if(!smp) std_err();
+
+ if(bits == 8) {
+ for(i = 0; i < samples; i++) {
+ if(mywav_fri08(fd, &tmp8) < 0) break;
+ smp[i] = (tmp8 << 8) - 32768;
+ }
+
+ } else if(bits == 16) {
+ for(i = 0; i < samples; i++) {
+ if(mywav_fri16(fd, &smp[i]) < 0) break;
+ }
+
+ } else if(bits == 24) {
+ for(i = 0; i < samples; i++) {
+ if(mywav_fri24(fd, &tmp32) < 0) break;
+ smp[i] = tmp32 >> 8;
+ }
+
+ } else if(bits == 32) {
+ for(i = 0; i < samples; i++) {
+ if(mywav_fri32(fd, &tmp32) < 0) break;
+ smp[i] = tmp32 >> 16;
+ }
+
+ } else {
+ my_err("number of bits used in the WAVE file not supported");
+ }
+ *ret_samples = i;
+ return(smp);
+}
+
+
+
+int do_mono(i16 *smp, int samples, int ch) {
+ i32 tmp; // max 65535 channels
+ int i,
+ j;
+
+ if(!ch) my_err("the WAVE file doesn't have channels");
+ if(ch == 1) return(samples);
+
+ for(i = 0; samples > 0; i++) {
+ tmp = 0;
+ for(j = 0; j < ch; j++) {
+ tmp += smp[(i * ch) + j];
+ }
+ smp[i] = tmp / ch;
+ samples -= ch;
+ }
+ return(i);
+}
+
+
+
+void do_dcbias(i16 *smp, int samples) {
+ int i;
+ i16 bias,
+ maxneg,
+ maxpos;
+
+ maxneg = 32767;
+ maxpos = -32768;
+ for(i = 0; i < samples; i++) {
+ if(smp[i] < maxneg) {
+ maxneg = smp[i];
+ } else if(smp[i] > maxpos) {
+ maxpos = smp[i];
+ }
+ }
+
+ bias = (maxneg + maxpos) / 2;
+ fprintf(stderr, " bias adjust: %d\n", bias);
+
+ for(i = 0; i < samples; i++) {
+ smp[i] -= bias;
+ }
+}
+
+
+
+void do_normalize(i16 *smp, int samples) {
+ int i;
+ i16 bias,
+ maxneg,
+ maxpos;
+
+ maxneg = 0;
+ maxpos = 0;
+ for(i = 0; i < samples; i++) {
+ if(smp[i] < maxneg) {
+ maxneg = smp[i];
+ } else if(smp[i] > maxpos) {
+ maxpos = smp[i];
+ }
+ }
+
+ fprintf(stderr, " volume peaks: %d %d\n", maxneg, maxpos);
+
+ if(maxneg < 0) maxneg = (-maxneg) - 1;
+ if(maxneg > maxpos) {
+ bias = maxneg;
+ } else {
+ bias = maxpos;
+ }
+ if(bias == 32767) return;
+
+ fprintf(stderr, " normalize: %d\n", 32767 - bias);
+
+ for(i = 0; i < samples; i++) {
+ smp[i] = (smp[i] * 32767) / bias;
+ }
+}
+
+
+
+int do_8000(i16 *smp, int samples, int *freq) {
+ void *res;
+ int consumed;
+
+ if(*freq <= 8000) return(samples);
+
+ fprintf(stderr, " resampling to: 8000hz\n");
+ res = av_resample_init(8000, *freq, 16, 10, 0, 0.8);
+ samples = av_resample(res, smp, smp, &consumed, samples, samples, 1);
+ av_resample_close(res);
+
+ *freq = 8000;
+ return(samples);
+}
+
+
+
+void my_err(u8 *err) {
+ fprintf(stderr, "\nError: %s\n", err);
+ exit(1);
+}
+
+
+
+void std_err(void) {
+ perror("\nError");
+ exit(1);
+}
+
+
DIR diff --git a/src/dtmf2num/mywav.h b/src/dtmf2num/mywav.h
@@ -0,0 +1,244 @@
+/*
+ MyWAV 0.1.1
+ by Luigi Auriemma
+ e-mail: aluigi@autistici.org
+ web: aluigi.org
+
+ Copyright 2005,2006 Luigi Auriemma
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+ http://www.gnu.org/licenses/gpl.txt
+*/
+
+#include <string.h>
+#include <stdint.h>
+
+
+
+/*
+the functions return ever 0 if success, other values (-1) if error
+note that these functions have been written with compatibility in mind
+so don't worry if you see useless instructions
+*/
+
+
+
+typedef struct {
+ uint8_t id[4];
+ uint32_t size;
+} mywav_chunk;
+
+typedef struct {
+ int16_t wFormatTag;
+ uint16_t wChannels;
+ uint32_t dwSamplesPerSec;
+ uint32_t dwAvgBytesPerSec;
+ uint16_t wBlockAlign;
+ uint16_t wBitsPerSample;
+} mywav_fmtchunk;
+
+
+
+ /* FILE WRITING */
+
+ // 8 bit
+int mywav_fwi08(FILE *fd, int num) {
+ if(fputc((num ) & 0xff, fd) < 0) return(-1);
+ return(0);
+}
+
+
+
+ // 16 bit
+int mywav_fwi16(FILE *fd, int num) {
+ if(fputc((num ) & 0xff, fd) < 0) return(-1);
+ if(fputc((num >> 8) & 0xff, fd) < 0) return(-1);
+ return(0);
+}
+
+
+
+ // 32 bit
+int mywav_fwi32(FILE *fd, int num) {
+ if(fputc((num ) & 0xff, fd) < 0) return(-1);
+ if(fputc((num >> 8) & 0xff, fd) < 0) return(-1);
+ if(fputc((num >> 16) & 0xff, fd) < 0) return(-1);
+ if(fputc((num >> 24) & 0xff, fd) < 0) return(-1);
+ return(0);
+}
+
+
+
+ // data
+int mywav_fwmem(FILE *fd, uint8_t *mem, int size) {
+ if(size) {
+ if(fwrite(mem, size, 1, fd) != 1) return(-1);
+ }
+ return(0);
+}
+
+
+
+ // chunk
+int mywav_fwchunk(FILE *fd, mywav_chunk *chunk) {
+ if(mywav_fwmem(fd, chunk->id, 4)) return(-1);
+ if(mywav_fwi32(fd, chunk->size)) return(-1);
+ return(0);
+}
+
+
+
+ // fmtchunk
+int mywav_fwfmtchunk(FILE *fd, mywav_fmtchunk *fmtchunk) {
+ if(mywav_fwi16(fd, fmtchunk->wFormatTag)) return(-1);
+ if(mywav_fwi16(fd, fmtchunk->wChannels)) return(-1);
+ if(mywav_fwi32(fd, fmtchunk->dwSamplesPerSec)) return(-1);
+ if(mywav_fwi32(fd, fmtchunk->dwAvgBytesPerSec)) return(-1);
+ if(mywav_fwi16(fd, fmtchunk->wBlockAlign)) return(-1);
+ if(mywav_fwi16(fd, fmtchunk->wBitsPerSample)) return(-1);
+ return(0);
+}
+
+
+
+ /* FILE READING */
+
+ // 8 bit
+int mywav_fri08(FILE *fd, uint8_t *num) {
+ if(fread(num, 1, 1, fd) != 1) return(-1);
+ return(0);
+}
+
+
+
+ // 16 bit
+int mywav_fri16(FILE *fd, uint16_t *num) {
+ uint16_t ret;
+ uint8_t tmp;
+
+ if(fread(&tmp, 1, 1, fd) != 1) return(-1); ret = tmp;
+ if(fread(&tmp, 1, 1, fd) != 1) return(-1); ret |= (tmp << 8);
+ *num = ret;
+ return(0);
+}
+
+
+
+ // 32 bit
+int mywav_fri32(FILE *fd, uint32_t *num) {
+ uint32_t ret;
+ uint8_t tmp;
+
+ if(fread(&tmp, 1, 1, fd) != 1) return(-1); ret = tmp;
+ if(fread(&tmp, 1, 1, fd) != 1) return(-1); ret |= (tmp << 8);
+ if(fread(&tmp, 1, 1, fd) != 1) return(-1); ret |= (tmp << 16);
+ if(fread(&tmp, 1, 1, fd) != 1) return(-1); ret |= (tmp << 24);
+ *num = ret;
+ return(0);
+}
+
+
+
+ // data
+int mywav_frmem(FILE *fd, uint8_t *mem, int size) {
+ if(size) {
+ if(fread(mem, size, 1, fd) != 1) return(-1);
+ }
+ return(0);
+}
+
+
+
+ // chunk
+int mywav_frchunk(FILE *fd, mywav_chunk *chunk) {
+ if(mywav_frmem(fd, (void *)&chunk->id, 4)) return(-1);
+ if(mywav_fri32(fd, (void *)&chunk->size)) return(-1);
+ return(0);
+}
+
+
+
+ // fmtchunk
+int mywav_frfmtchunk(FILE *fd, mywav_fmtchunk *fmtchunk) {
+ if(mywav_fri16(fd, (void *)&fmtchunk->wFormatTag)) return(-1);
+ if(mywav_fri16(fd, (void *)&fmtchunk->wChannels)) return(-1);
+ if(mywav_fri32(fd, (void *)&fmtchunk->dwSamplesPerSec)) return(-1);
+ if(mywav_fri32(fd, (void *)&fmtchunk->dwAvgBytesPerSec)) return(-1);
+ if(mywav_fri16(fd, (void *)&fmtchunk->wBlockAlign)) return(-1);
+ if(mywav_fri16(fd, (void *)&fmtchunk->wBitsPerSample)) return(-1);
+ return(0);
+}
+
+
+ /* MYWAV MAIN FUNCTIONS */
+
+int mywav_seekchunk(FILE *fd, uint8_t *find) {
+ mywav_chunk chunk;
+
+ if(fseek(fd, sizeof(mywav_chunk) + 4, SEEK_SET) < 0) return(-1);
+
+ while(!mywav_frchunk(fd, &chunk)) {
+ if(!memcmp(chunk.id, find, 4)) return(chunk.size);
+ if(fseek(fd, chunk.size, SEEK_CUR) < 0) break;
+ }
+ return(-1);
+}
+
+
+
+int mywav_data(FILE *fd, mywav_fmtchunk *fmt) {
+ mywav_chunk chunk;
+ uint8_t type[4];
+
+ if(mywav_frchunk(fd, &chunk) < 0) return(-1);
+ if(mywav_frmem(fd, type, 4) < 0) return(-1);
+ if(memcmp(type, "WAVE", 4)) return(-1);
+
+ if(mywav_seekchunk(fd, "fmt ") < 0) return(-1);
+ if(mywav_frfmtchunk(fd, fmt) < 0) return(-1);
+
+ return(mywav_seekchunk(fd, "data"));
+}
+
+
+
+int mywav_writehead(FILE *fd, mywav_fmtchunk *fmt, uint32_t data_size, uint8_t *more, int morelen) {
+ mywav_chunk chunk;
+
+ memcpy(chunk.id, "RIFF", 4);
+ chunk.size =
+ 4 +
+ sizeof(mywav_chunk) +
+ sizeof(mywav_fmtchunk) +
+ morelen +
+ sizeof(mywav_chunk) +
+ data_size;
+
+ if(mywav_fwchunk(fd, &chunk) < 0) return(-1);
+ if(mywav_fwmem(fd, "WAVE", 4) < 0) return(-1);
+
+ memcpy(chunk.id, "fmt ", 4);
+ chunk.size = sizeof(mywav_fmtchunk) + morelen;
+ if(mywav_fwchunk(fd, &chunk) < 0) return(-1);
+ if(mywav_fwfmtchunk(fd, fmt) < 0) return(-1);
+ if(mywav_fwmem(fd, more, morelen) < 0) return(-1);
+
+ memcpy(chunk.id, "data", 4);
+ chunk.size = data_size;
+ if(mywav_fwchunk(fd, &chunk) < 0) return(-1);
+ return(0);
+}
+
DIR diff --git a/src/dtmf2num/resample2.c b/src/dtmf2num/resample2.c
@@ -0,0 +1,342 @@
+/*
+ * audio resampling
+ * Copyright (c) 2004 Michael Niedermayer <michaelni@gmx.at>
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/**
+ * @file resample2.c
+ * audio resampling
+ * @author Michael Niedermayer <michaelni@gmx.at>
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+#include <math.h>
+#include <malloc.h>
+
+#define FFABS(a) ((a) >= 0 ? (a) : (-(a)))
+#define FFSIGN(a) ((a) > 0 ? 1 : -1)
+#define FFMAX(a,b) ((a) > (b) ? (a) : (b))
+#define FFMIN(a,b) ((a) > (b) ? (b) : (a))
+#define av_mallocz malloc
+#define av_freep free
+
+#ifndef CONFIG_RESAMPLE_HP
+#define FILTER_SHIFT 15
+
+#define FELEM int16_t
+#define FELEM2 int32_t
+#define FELEML int64_t
+#define FELEM_MAX INT16_MAX
+#define FELEM_MIN INT16_MIN
+#define WINDOW_TYPE 9
+#elif !defined(CONFIG_RESAMPLE_AUDIOPHILE_KIDDY_MODE)
+#define FILTER_SHIFT 30
+
+#define FELEM int32_t
+#define FELEM2 int64_t
+#define FELEML int64_t
+#define FELEM_MAX INT32_MAX
+#define FELEM_MIN INT32_MIN
+#define WINDOW_TYPE 12
+#else
+#define FILTER_SHIFT 0
+
+#define FELEM double
+#define FELEM2 double
+#define FELEML double
+#define WINDOW_TYPE 24
+#endif
+
+
+typedef struct AVResampleContext{
+ FELEM *filter_bank;
+ int filter_length;
+ int ideal_dst_incr;
+ int dst_incr;
+ int index;
+ int frac;
+ int src_incr;
+ int compensation_distance;
+ int phase_shift;
+ int phase_mask;
+ int linear;
+}AVResampleContext;
+
+/**
+ * 0th order modified bessel function of the first kind.
+ */
+static double bessel(double x){
+ double v=1;
+ double t=1;
+ int i;
+
+ x= x*x/4;
+ for(i=1; i<50; i++){
+ t *= x/(i*i);
+ v += t;
+ }
+ return v;
+}
+
+static inline int av_clip(int a, int amin, int amax)
+{
+ if (a < amin) return amin;
+ else if (a > amax) return amax;
+ else return a;
+}
+
+/**
+ * builds a polyphase filterbank.
+ * @param factor resampling factor
+ * @param scale wanted sum of coefficients for each filter
+ * @param type 0->cubic, 1->blackman nuttall windowed sinc, 2..16->kaiser windowed sinc beta=2..16
+ */
+void av_build_filter(FELEM *filter, double factor, int tap_count, int phase_count, int scale, int type){
+ int ph, i;
+ double x, y, w, tab[tap_count];
+ const int center= (tap_count-1)/2;
+
+ /* if upsampling, only need to interpolate, no filter */
+ if (factor > 1.0)
+ factor = 1.0;
+
+ for(ph=0;ph<phase_count;ph++) {
+ double norm = 0;
+ for(i=0;i<tap_count;i++) {
+ x = M_PI * ((double)(i - center) - (double)ph / phase_count) * factor;
+ if (x == 0) y = 1.0;
+ else y = sin(x) / x;
+ switch(type){
+ case 0:{
+ const float d= -0.5; //first order derivative = -0.5
+ x = fabs(((double)(i - center) - (double)ph / phase_count) * factor);
+ if(x<1.0) y= 1 - 3*x*x + 2*x*x*x + d*( -x*x + x*x*x);
+ else y= d*(-4 + 8*x - 5*x*x + x*x*x);
+ break;}
+ case 1:
+ w = 2.0*x / (factor*tap_count) + M_PI;
+ y *= 0.3635819 - 0.4891775 * cos(w) + 0.1365995 * cos(2*w) - 0.0106411 * cos(3*w);
+ break;
+ default:
+ w = 2.0*x / (factor*tap_count*M_PI);
+ y *= bessel(type*sqrt(FFMAX(1-w*w, 0)));
+ break;
+ }
+
+ tab[i] = y;
+ norm += y;
+ }
+
+ /* normalize so that an uniform color remains the same */
+ for(i=0;i<tap_count;i++) {
+#ifdef CONFIG_RESAMPLE_AUDIOPHILE_KIDDY_MODE
+ filter[ph * tap_count + i] = tab[i] / norm;
+#else
+ filter[ph * tap_count + i] = av_clip(lrintf(tab[i] * scale / norm), FELEM_MIN, FELEM_MAX);
+#endif
+ }
+ }
+#if 0
+ {
+#define LEN 1024
+ int j,k;
+ double sine[LEN + tap_count];
+ double filtered[LEN];
+ double maxff=-2, minff=2, maxsf=-2, minsf=2;
+ for(i=0; i<LEN; i++){
+ double ss=0, sf=0, ff=0;
+ for(j=0; j<LEN+tap_count; j++)
+ sine[j]= cos(i*j*M_PI/LEN);
+ for(j=0; j<LEN; j++){
+ double sum=0;
+ ph=0;
+ for(k=0; k<tap_count; k++)
+ sum += filter[ph * tap_count + k] * sine[k+j];
+ filtered[j]= sum / (1<<FILTER_SHIFT);
+ ss+= sine[j + center] * sine[j + center];
+ ff+= filtered[j] * filtered[j];
+ sf+= sine[j + center] * filtered[j];
+ }
+ ss= sqrt(2*ss/LEN);
+ ff= sqrt(2*ff/LEN);
+ sf= 2*sf/LEN;
+ maxff= FFMAX(maxff, ff);
+ minff= FFMIN(minff, ff);
+ maxsf= FFMAX(maxsf, sf);
+ minsf= FFMIN(minsf, sf);
+ if(i%11==0){
+ //av_log(NULL, AV_LOG_ERROR, "i:%4d ss:%f ff:%13.6e-%13.6e sf:%13.6e-%13.6e\n", i, ss, maxff, minff, maxsf, minsf);
+ minff=minsf= 2;
+ maxff=maxsf= -2;
+ }
+ }
+ }
+#endif
+}
+
+/**
+ * Initializes an audio resampler.
+ * Note, if either rate is not an integer then simply scale both rates up so they are.
+ */
+AVResampleContext *av_resample_init(int out_rate, int in_rate, int filter_size, int phase_shift, int linear, double cutoff){
+ AVResampleContext *c= av_mallocz(sizeof(AVResampleContext));
+ double factor= FFMIN(out_rate * cutoff / in_rate, 1.0);
+ int phase_count= 1<<phase_shift;
+
+ c->phase_shift= phase_shift;
+ c->phase_mask= phase_count-1;
+ c->linear= linear;
+
+ c->filter_length= FFMAX((int)ceil(filter_size/factor), 1);
+ c->filter_bank= av_mallocz(c->filter_length*(phase_count+1)*sizeof(FELEM));
+ av_build_filter(c->filter_bank, factor, c->filter_length, phase_count, 1<<FILTER_SHIFT, WINDOW_TYPE);
+ memcpy(&c->filter_bank[c->filter_length*phase_count+1], c->filter_bank, (c->filter_length-1)*sizeof(FELEM));
+ c->filter_bank[c->filter_length*phase_count]= c->filter_bank[c->filter_length - 1];
+
+ c->src_incr= out_rate;
+ c->ideal_dst_incr= c->dst_incr= in_rate * phase_count;
+ c->index= -phase_count*((c->filter_length-1)/2);
+
+ return c;
+}
+
+void av_resample_close(AVResampleContext *c){
+ av_freep(&c->filter_bank);
+ av_freep(&c);
+}
+
+/**
+ * Compensates samplerate/timestamp drift. The compensation is done by changing
+ * the resampler parameters, so no audible clicks or similar distortions ocur
+ * @param compensation_distance distance in output samples over which the compensation should be performed
+ * @param sample_delta number of output samples which should be output less
+ *
+ * example: av_resample_compensate(c, 10, 500)
+ * here instead of 510 samples only 500 samples would be output
+ *
+ * note, due to rounding the actual compensation might be slightly different,
+ * especially if the compensation_distance is large and the in_rate used during init is small
+ */
+void av_resample_compensate(AVResampleContext *c, int sample_delta, int compensation_distance){
+// sample_delta += (c->ideal_dst_incr - c->dst_incr)*(int64_t)c->compensation_distance / c->ideal_dst_incr;
+ c->compensation_distance= compensation_distance;
+ c->dst_incr = c->ideal_dst_incr - c->ideal_dst_incr * (int64_t)sample_delta / compensation_distance;
+}
+
+/**
+ * resamples.
+ * @param src an array of unconsumed samples
+ * @param consumed the number of samples of src which have been consumed are returned here
+ * @param src_size the number of unconsumed samples available
+ * @param dst_size the amount of space in samples available in dst
+ * @param update_ctx if this is 0 then the context wont be modified, that way several channels can be resampled with the same context
+ * @return the number of samples written in dst or -1 if an error occured
+ */
+int av_resample(AVResampleContext *c, short *dst, short *src, int *consumed, int src_size, int dst_size, int update_ctx){
+ int dst_index, i;
+ int index= c->index;
+ int frac= c->frac;
+ int dst_incr_frac= c->dst_incr % c->src_incr;
+ int dst_incr= c->dst_incr / c->src_incr;
+ int compensation_distance= c->compensation_distance;
+
+ if(compensation_distance == 0 && c->filter_length == 1 && c->phase_shift==0){
+ int64_t index2= ((int64_t)index)<<32;
+ int64_t incr= (1LL<<32) * c->dst_incr / c->src_incr;
+ dst_size= FFMIN(dst_size, (src_size-1-index) * (int64_t)c->src_incr / c->dst_incr);
+
+ for(dst_index=0; dst_index < dst_size; dst_index++){
+ dst[dst_index] = src[index2>>32];
+ index2 += incr;
+ }
+ frac += dst_index * dst_incr_frac;
+ index += dst_index * dst_incr;
+ index += frac / c->src_incr;
+ frac %= c->src_incr;
+ }else{
+ for(dst_index=0; dst_index < dst_size; dst_index++){
+ FELEM *filter= c->filter_bank + c->filter_length*(index & c->phase_mask);
+ int sample_index= index >> c->phase_shift;
+ FELEM2 val=0;
+
+ if(sample_index < 0){
+ for(i=0; i<c->filter_length; i++)
+ val += src[FFABS(sample_index + i) % src_size] * filter[i];
+ }else if(sample_index + c->filter_length > src_size){
+ break;
+ }else if(c->linear){
+ FELEM2 v2=0;
+ for(i=0; i<c->filter_length; i++){
+ val += src[sample_index + i] * (FELEM2)filter[i];
+ v2 += src[sample_index + i] * (FELEM2)filter[i + c->filter_length];
+ }
+ val+=(v2-val)*(FELEML)frac / c->src_incr;
+ }else{
+ for(i=0; i<c->filter_length; i++){
+ val += src[sample_index + i] * (FELEM2)filter[i];
+ }
+ }
+
+#ifdef CONFIG_RESAMPLE_AUDIOPHILE_KIDDY_MODE
+ dst[dst_index] = av_clip_int16(lrintf(val));
+#else
+ val = (val + (1<<(FILTER_SHIFT-1)))>>FILTER_SHIFT;
+ dst[dst_index] = (unsigned)(val + 32768) > 65535 ? (val>>31) ^ 32767 : val;
+#endif
+
+ frac += dst_incr_frac;
+ index += dst_incr;
+ if(frac >= c->src_incr){
+ frac -= c->src_incr;
+ index++;
+ }
+
+ if(dst_index + 1 == compensation_distance){
+ compensation_distance= 0;
+ dst_incr_frac= c->ideal_dst_incr % c->src_incr;
+ dst_incr= c->ideal_dst_incr / c->src_incr;
+ }
+ }
+ }
+ *consumed= FFMAX(index, 0) >> c->phase_shift;
+ if(index>=0) index &= c->phase_mask;
+
+ if(compensation_distance){
+ compensation_distance -= dst_index;
+ if(!(compensation_distance > 0)) return(-1);
+ }
+ if(update_ctx){
+ c->frac= frac;
+ c->index= index;
+ c->dst_incr= dst_incr_frac + c->src_incr*dst_incr;
+ c->compensation_distance= compensation_distance;
+ }
+#if 0
+ if(update_ctx && !c->compensation_distance){
+#undef rand
+ av_resample_compensate(c, rand() % (8000*2) - 8000, 8000*2);
+//av_log(NULL, AV_LOG_DEBUG, "%d %d %d\n", c->dst_incr, c->ideal_dst_incr, c->compensation_distance);
+ }
+#endif
+
+ return dst_index;
+}
DIR diff --git a/web/app/controllers/analyze_controller.rb b/web/app/controllers/analyze_controller.rb
@@ -81,7 +81,7 @@ class AnalyzeController < ApplicationController
@job_id,
:page => params[:page],
:order => 'number ASC',
- :per_page => 10,
+ :per_page => 20,
:conditions => [ 'completed = ? and processed = ? and busy = ? and line_type = ?', true, true, false, @shown ]
)
else
@@ -89,7 +89,7 @@ class AnalyzeController < ApplicationController
@job_id,
:page => params[:page],
:order => 'number ASC',
- :per_page => 10,
+ :per_page => 20,
:conditions => [ 'completed = ? and processed = ? and busy = ?', true, true, false ]
)
end
DIR diff --git a/web/app/views/analyze/view.html.erb b/web/app/views/analyze/view.html.erb
@@ -35,8 +35,13 @@
CallerID: <%= dial_result.cid%><br/>
Provider: <%=h dial_result.provider.name %><br/>
Audio: <%=h dial_result.seconds %> Seconds<br/>
- Ringer: <%=h dial_result.ringtime %> Seconds
-
+ Ringer: <%=h dial_result.ringtime %> Seconds<br/>
+ <% if(dial_result.dtmf and dial_result.dtmf.length > 0) %>
+ DTMF: <%=h dial_result.dtmf %><br/>
+ <% end %>
+ <% if(dial_result.mf and dial_result.mf.length > 0) %>
+ MF: <%=h dial_result.mf %><br/>
+ <% end %>
</td>
<td align='center'>
<b><%=h dial_result.line_type.upcase %></b><br/>
DIR diff --git a/web/db/schema.rb b/web/db/schema.rb
@@ -9,7 +9,7 @@
#
# It's strongly recommended to check this file into your version control system.
-ActiveRecord::Schema.define(:version => 20090522202032) do
+ActiveRecord::Schema.define(:version => 20090526031826) do
create_table "dial_jobs", :force => true do |t|
t.string "range"
@@ -45,6 +45,8 @@ ActiveRecord::Schema.define(:version => 20090522202032) do
t.string "line_type"
t.string "notes"
t.string "signatures"
+ t.string "dtmf"
+ t.string "mf"
end
create_table "providers", :force => true do |t|