URI: 
       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|