dtmf2num-0.1.1/0000755000000000000000000000000013076131766011755 5ustar rootrootdtmf2num-0.1.1/mywav.h0000644000000000000000000001403410465321424013262 0ustar rootroot/* 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 #include /* 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); } dtmf2num-0.1.1/dsp.c0000644000000000000000000003545012311627472012711 0ustar rootroot// modified by Luigi Auriemma to work also with damaged audio and allow customized parameters /* * Asterisk -- An open source telephony toolkit. * * Copyright (C) 1999 - 2005, Digium, Inc. * * Mark Spencer * * 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 * \author Steve Underwood */ /* 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 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. */ #include #include #include #include #include //#include #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 */ /* PARAMETERS */ static double DTMF_OPTIMIZED_VALUE = 102; //#define DTMF_THRESHOLD 8.0e7 static double DTMF_THRESHOLD = 800000000.0; // aluigi work-around static double DTMF_NORMAL_TWIST = 6.3; /* 8dB */ #if 0 #ifdef RADIO_RELAX static double DTMF_REVERSE_TWIST1 = 6.5; static double DTMF_REVERSE_TWIST2 = 2.5; #else static double DTMF_REVERSE_TWIST1 = 4.0; static double DTMF_REVERSE_TWIST2 = 2.5; #endif #define DTMF_REVERSE_TWIST ((digitmode & DSP_DIGITMODE_RELAXDTMF) ? DTMF_REVERSE_TWIST1 : DTMF_REVERSE_TWIST2) /* 4dB normal */ static double DTMF_2ND_HARMONIC_ROW1 = 1.7; static double DTMF_2ND_HARMONIC_ROW2 = 2.5; #define DTMF_2ND_HARMONIC_ROW ((digitmode & DSP_DIGITMODE_RELAXDTMF) ? DTMF_2ND_HARMONIC_ROW1 : DTMF_2ND_HARMONIC_ROW2) /* 4dB normal */ static double DTMF_2ND_HARMONIC_COL = 63.1; /* 18dB */ static double DTMF_TO_TOTAL_ENERGY = 42.0; #endif static double DTMF_RELATIVE_PEAK_ROW = 6.3; /* 8dB */ static double DTMF_RELATIVE_PEAK_COL = 6.3; /* 8dB */ //#define BELL_MF_THRESHOLD 1.6e9 static double BELL_MF_THRESHOLD = 800000000.0; // aluigi work-around static double BELL_MF_TWIST = 4.0; /* 6dB */ static double 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; double 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 double dtmf_row[] = { 697.0, 770.0, 852.0, 941.0 }; static double dtmf_col[] = { 1209.0, 1336.0, 1477.0, 1633.0 }; static double 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;iv3 * s->v3) + (s->v2 * s->v2); r.value -= ((s->v2 * s->v3) >> 15) * s->fac; r.power = s->chunky * 2; return (double)r.value * (double)(1 << r.power); } static inline void goertzel_init(goertzel_state_t *s, double 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], DTMF_OPTIMIZED_VALUE); goertzel_init (&s->col_out[i], dtmf_col[i], DTMF_OPTIMIZED_VALUE); 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) { double row_energy[4]; double col_energy[4]; double 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) { /* DTMF_OPTIMIZED_VALUE is optimised to meet the DTMF specs. */ if ((samples - sample) >= (DTMF_OPTIMIZED_VALUE - s->td.dtmf.current_sample)) limit = sample + (DTMF_OPTIMIZED_VALUE - 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 < DTMF_OPTIMIZED_VALUE) { 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;itd.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;itd.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) { double energy[6]; int best; int second_best; //double 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;itd.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 */ } dtmf2num-0.1.1/dtmf2num.c0000644000000000000000000002643612311630432013651 0ustar rootroot/* Copyright 2008-2014 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 #include #include #include #include #include "mywav.h" #include "dsp.c" //#include "resample2.c" #ifdef WIN32 #else #define stricmp strcasecmp #endif 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.1.1" 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); typedef struct { char *par; double *val; } dsp_par_val_t; dsp_par_val_t dsp_par_val[] = { #define SET_dsp_par_val(X) { #X, &X }, SET_dsp_par_val(DTMF_OPTIMIZED_VALUE) SET_dsp_par_val(DTMF_THRESHOLD) SET_dsp_par_val(DTMF_NORMAL_TWIST) SET_dsp_par_val(DTMF_RELATIVE_PEAK_ROW) SET_dsp_par_val(DTMF_RELATIVE_PEAK_COL) SET_dsp_par_val(BELL_MF_THRESHOLD) SET_dsp_par_val(BELL_MF_TWIST) SET_dsp_par_val(BELL_MF_RELATIVE_PEAK) { NULL, NULL } }; int main(int argc, char *argv[]) { digit_detect_state_t dtmf; mywav_fmtchunk fmt; struct stat xstat; FILE *fd; int i, j, wavsize, samples, writeback, raw = 0, optimize = 1; i16 *smp = NULL; 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] \n" "\n", argv[0]); printf( "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" "-z P V set specific parameters of dsp.c:\n" "\n"); for(i = 0; dsp_par_val[i].par; i++) { printf(" %-25s %.2f\n", dsp_par_val[i].par, *dsp_par_val[i].val); } printf("\n"); 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; case 'z': if(!argv[++i]) exit(1); for(j = 0; dsp_par_val[j].par; j++) { if(!stricmp(argv[i], dsp_par_val[j].par)) { sscanf(argv[++i], "%lf", dsp_par_val[j].val); break; } } break; default: printf("\nError: wrong option (%s)\n", argv[i]); exit(1); break; } } 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; memset(&dtmf, 0, sizeof(dtmf)); // useless 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"); memset(&dtmf, 0, sizeof(dtmf)); // useless 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"); if(smp) free(smp); 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; double t; 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++) { t = smp[i]; t = (t * (double)32767) / (double)bias; if(t > (double)32767) t = 32767; if(t < (double)-32768) t = -32768; smp[i] = t; } } void *av_resample_init(int, int, int, int, int, double); int av_resample(void *, short *, short *, int *, int, int, int); void av_resample_close(void *); int do_8000(i16 *smp, int samples, int *freq) { void *res = NULL; 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); } dtmf2num-0.1.1/resample2.c0000644000000000000000000002657312020666224014016 0ustar rootroot/* * audio resampling * Copyright (c) 2004 Michael Niedermayer * * 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 */ #include #include #include #include #include //#include #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;phphase_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_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){ if(c) { if(c->filter_bank) 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; ifilter_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; ifilter_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; ifilter_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; } dtmf2num-0.1.1/Makefile0000644000000000000000000000045012311631066013401 0ustar rootrootEXE = dtmf2num CFLAGS += -O2 -s PREFIX = /usr/local BINDIR = $(PREFIX)/bin LIBS = -lm all: $(CC) $(CFLAGS) -c dtmf2num.c $(CC) $(CFLAGS) -c resample2.c $(CC) $(CFLAGS) -o $(EXE) $(LIBS) *.o -lm install: install -m 755 -d $(BINDIR) install -m 755 $(EXE) $(BINDIR)/$(EXE) .PHONY: install