diff --git a/core/AmDtmfDetector.cpp b/core/AmDtmfDetector.cpp index 5f9772d1..dd6e159b 100644 --- a/core/AmDtmfDetector.cpp +++ b/core/AmDtmfDetector.cpp @@ -374,54 +374,31 @@ static int IVR_dtmf_matrix[4][4] = }; -#define DTMF_TRESH 100000 /* above this is dtmf */ -#define SILENCE_TRESH 100 /* below this is silence */ -#define H2_TRESH 35000 /* 2nd harmonic */ -#define DTMF_DUR 25 /* nr of samples to detect a DTMF tone */ -#define AMP_BITS 9 /* bits per sample, reduced to avoid overflow */ -#define NCOEFF 16 /* number of frequencies to be analyzed */ #define LOGRP 0 #define HIGRP 1 -#define REL_NCOEFF 8 /* number of frequencies to be analyzed */ #define REL_DTMF_TRESH 4000 /* above this is dtmf */ #define REL_SILENCE_TRESH 200 /* below this is silence */ #define REL_AMP_BITS 9 /* bits per sample, reduced to avoid overflow */ #define PI 3.1415926 +#define NELEMSOF(x) (sizeof(x)/sizeof(*x)) typedef struct { int freq; /* frequency */ int grp; /* low/high group */ - int k; /* k */ - int k2; /* k for 1st harmonic */ } dtmf_t; - -static int cos2pik[NCOEFF] = { /* 2 * cos(2 * PI * k / N), precalculated */ - 55812, 29528, 53603, 24032, - 51193, 14443, 48590, 6517 -}; // high group missing... - -/* For DTMF recognition: - * 2 * cos(2 * PI * k / N) precalculated for all k - */ -static int rel_cos2pik[REL_NCOEFF] = - { - 55813, 53604, 51193, 48591, // low group - 38114, 33057, 25889, 18332 // high group - }; - static dtmf_t dtmf_tones[8] = { - { 697, LOGRP, 0, 1 }, - { 770, LOGRP, 2, 3 }, - { 852, LOGRP, 4, 5 }, - { 941, LOGRP, 6, 7 }, - {1209, HIGRP, 8, 9 }, - {1336, HIGRP, 10, 11 }, - {1477, HIGRP, 12, 13 }, - {1633, HIGRP, 14, 15 } + { 697, LOGRP}, + { 770, LOGRP}, + { 852, LOGRP}, + { 941, LOGRP}, + {1209, HIGRP}, + {1336, HIGRP}, + {1477, HIGRP}, + {1633, HIGRP} }; static char dtmf_matrix[4][4] = @@ -436,21 +413,15 @@ AmInbandDtmfDetector::AmInbandDtmfDetector(AmDtmfDetector *owner) : m_owner(owner), m_last(' '), m_idx(0), - m_count(0) + m_count(0), + SAMPLERATE(8000) { /* precalculate 2 * cos (2 PI k / N) */ - int i, kp, k; - kp = 0; - for(i = 0; i < 8; i++) - { - k = (int)rint((double)dtmf_tones[i].freq * DTMF_NPOINTS / SAMPLERATE); - cos2pik[kp] = (int)(2 * 32768 * cos(2 * PI * k / DTMF_NPOINTS)); - dtmf_tones[i].k = kp++; - - k = (int)rint((double)dtmf_tones[i].freq * 2 * DTMF_NPOINTS / SAMPLERATE); - cos2pik[kp] = (int)(2 * 32768 * cos(2 * PI * k / DTMF_NPOINTS)); - dtmf_tones[i].k2 = kp++; - } + for(unsigned i = 0; i < NELEMSOF(rel_cos2pik); i++) { + // FIXME: fixed samplerate. won't work for wideband + int k = (int)((double)dtmf_tones[i].freq * REL_DTMF_NPOINTS / SAMPLERATE + 0.5); + rel_cos2pik[i] = (int)(2 * 32768 * cos(2 * PI * k / REL_DTMF_NPOINTS)); + } } /* @@ -463,32 +434,32 @@ void AmInbandDtmfDetector::isdn_audio_goertzel_relative() { int sk, sk1, sk2; - for (int k = 0; k < REL_NCOEFF; k++) - { - sk = sk1 = sk2 = 0; - for (int n = 0; n < REL_DTMF_NPOINTS; n++) - { - sk = m_buf[n] + ((rel_cos2pik[k] * sk1) >> 15) - sk2; - sk2 = sk1; - sk1 = sk; - } - /* Avoid overflows */ - sk >>= 1; - sk2 >>= 1; - /* compute |X(k)|**2 */ - /* report overflows. This should not happen. */ - /* Comment this out if desired */ - /*if (sk < -32768 || sk > 32767) - DBG("isdn_audio: dtmf goertzel overflow, sk=%d\n", sk); - if (sk2 < -32768 || sk2 > 32767) - DBG("isdn_audio: dtmf goertzel overflow, sk2=%d\n", sk2); - */ - - m_result[k] = - ((sk * sk) >> REL_AMP_BITS) - - ((((rel_cos2pik[k] * sk) >> 15) * sk2) >> REL_AMP_BITS) + - ((sk2 * sk2) >> REL_AMP_BITS); + for (int k = 0; k < REL_NCOEFF; k++) { + // like m_buf, sk..sk2 are in (32-REL_AMP_BITS).REL_AMP_BITS fixed-point format + sk = sk1 = sk2 = 0; + for (int n = 0; n < REL_DTMF_NPOINTS; n++) { + sk = m_buf[n] + ((rel_cos2pik[k] * sk1) >> 15) - sk2; + sk2 = sk1; + sk1 = sk; } + /* Avoid overflows */ + sk >>= 1; + sk2 >>= 1; + /* compute |X(k)|**2 */ + /* report overflows. This should not happen. */ + /* Comment this out if desired */ + /*if (sk < -32768 || sk > 32767) + DBG("isdn_audio: dtmf goertzel overflow, sk=%d\n", sk); + if (sk2 < -32768 || sk2 > 32767) + DBG("isdn_audio: dtmf goertzel overflow, sk2=%d\n", sk2); + */ + + // note that the result still is in (32-REL_AMP_BITS).REL_AMP_BITS format + m_result[k] = + ((sk * sk) >> REL_AMP_BITS) - + ((((rel_cos2pik[k] * sk) >> 15) * sk2) >> REL_AMP_BITS) + + ((sk2 * sk2) >> REL_AMP_BITS); + } } @@ -549,7 +520,8 @@ void AmInbandDtmfDetector::isdn_audio_eval_dtmf_relative() if (what != m_last) { - gettimeofday(&m_startTime, NULL); + m_startTime.tv_sec = m_last_ts / SAMPLERATE; + m_startTime.tv_usec = (m_last_ts * 1000 / SAMPLERATE) % 1000; } } else what = '.'; @@ -559,7 +531,7 @@ void AmInbandDtmfDetector::isdn_audio_eval_dtmf_relative() } if (what != ' ' && what != '.') { - if (m_count++ >= DTMF_INTERVAL) + if (++m_count >= DTMF_INTERVAL) { m_owner->registerKeyPressed(m_lastCode, Dtmf::SOURCE_INBAND); } @@ -569,7 +541,8 @@ void AmInbandDtmfDetector::isdn_audio_eval_dtmf_relative() if (m_last != ' ' && m_last != '.' && m_count >= DTMF_INTERVAL) { struct timeval stop; - gettimeofday(&stop, NULL); + stop.tv_sec = m_last_ts / SAMPLERATE; + stop.tv_usec = (m_last_ts * 1000 / SAMPLERATE) % 1000; m_owner->registerKeyReleased(m_lastCode, Dtmf::SOURCE_INBAND, m_startTime, stop); } m_count = 0; @@ -577,32 +550,36 @@ void AmInbandDtmfDetector::isdn_audio_eval_dtmf_relative() m_last = what; } -void AmInbandDtmfDetector::isdn_audio_calc_dtmf(const signed short* buf, int len) +void AmInbandDtmfDetector::isdn_audio_calc_dtmf(const signed short* buf, int len, unsigned int ts) { int c; - while (len) - { - c = (len < (DTMF_NPOINTS - m_idx)) ? len : (DTMF_NPOINTS - m_idx); + if(m_idx == 0) m_last_ts = ts; - if (c <= 0) - break; + while (len) { + c = (len < ((int)NELEMSOF(m_buf) - m_idx)) ? len : (NELEMSOF(m_buf) - m_idx); - for (int i = 0; i < c; i++) { - m_buf[m_idx++] = (*buf++) >> (15 - AMP_BITS); - } - if (m_idx == DTMF_NPOINTS) - { - isdn_audio_goertzel_relative(); - isdn_audio_eval_dtmf_relative(); - m_idx = 0; - } - len -= c; + if (c <= 0) break; + + for (int i = 0; i < c; i++) { + // m_buf is in (32-REL_AMP_BITS).REL_AMP_BITS fixed-point format, the samples + // itself are in the last REL_AMP_BITS bits, i.e. they go from -1.0 to +1.0 + // (or more exactly from -1.0 to ~+0.996) + m_buf[m_idx++] = (*buf++) >> (15 - REL_AMP_BITS); } + if (m_idx == NELEMSOF(m_buf)) { + isdn_audio_goertzel_relative(); + isdn_audio_eval_dtmf_relative(); + m_idx = 0; + m_last_ts = ts + c; + } + len -= c; + ts += c; + } } -int AmInbandDtmfDetector::streamPut(const unsigned char* samples, unsigned int size, unsigned int) +int AmInbandDtmfDetector::streamPut(const unsigned char* samples, unsigned int size, unsigned int user_ts) { - isdn_audio_calc_dtmf((const signed short *)samples, size / 2); + isdn_audio_calc_dtmf((const signed short *)samples, size / 2, user_ts); return size; } diff --git a/core/AmDtmfDetector.h b/core/AmDtmfDetector.h index 8d1f9c4e..a867dc27 100644 --- a/core/AmDtmfDetector.h +++ b/core/AmDtmfDetector.h @@ -174,26 +174,33 @@ class AmInbandDtmfDetector */ struct timeval m_startTime; - static const int DTMF_NPOINTS = 205; /* Number of samples for DTMF recognition */ static const int REL_DTMF_NPOINTS = 205; /* Number of samples for DTMF recognition */ - static const int SAMPLERATE = 8000; + static const int REL_NCOEFF = 8; /* number of frequencies to be analyzed */ + + const int SAMPLERATE; /** * DTMF recognition successfull only if no less than DTMF_INTERVAL * audio packets were processed and all gave the same result */ static const int DTMF_INTERVAL = 3; - int m_buf[DTMF_NPOINTS]; + /* For DTMF recognition: + * 2 * cos(2 * PI * k / N) precalculated for all k + */ + int rel_cos2pik[REL_NCOEFF]; + + int m_buf[REL_DTMF_NPOINTS]; char m_last; int m_idx; int m_result[16]; int m_lastCode; + int m_last_ts; // timestamp representative for the currently analysed filter block int m_count; void isdn_audio_goertzel_relative(); void isdn_audio_eval_dtmf_relative(); - void isdn_audio_calc_dtmf(const signed short* buf, int len); + void isdn_audio_calc_dtmf(const signed short* buf, int len, unsigned int ts); public: AmInbandDtmfDetector(AmDtmfDetector *owner);