Index: include/asterisk/dsp.h =================================================================== --- include/asterisk/dsp.h (revision 100974) +++ include/asterisk/dsp.h (working copy) @@ -42,6 +42,10 @@ #define DSP_PROGRESS_CONGESTION (1 << 19) /*!< Enable congestion tone detection */ #define DSP_FEATURE_CALL_PROGRESS (DSP_PROGRESS_TALK | DSP_PROGRESS_RINGING | DSP_PROGRESS_BUSY | DSP_PROGRESS_CONGESTION) +#define DSP_FAXMODE_DETECT_CNG (1 << 0) +#define DSP_FAXMODE_DETECT_CED (1 << 1) +#define DSP_FAXMODE_DETECT_ALL (DSP_FAXMODE_DETECT_CNG | DSP_FAXMODE_DETECT_CED) + #define DSP_TONE_STATE_SILENCE 0 #define DSP_TONE_STATE_RINGING 1 #define DSP_TONE_STATE_DIALTONE 2 @@ -102,6 +106,9 @@ /*! \brief Set digit mode */ int ast_dsp_digitmode(struct ast_dsp *dsp, int digitmode); +/*! \brief Set fax mode */ +int ast_dsp_set_faxmode(struct ast_dsp *dsp, int faxmode); + /*! \brief Get tstate (Tone State) */ int ast_dsp_get_tstate(struct ast_dsp *dsp); Index: main/dsp.c =================================================================== --- main/dsp.c (revision 100974) +++ main/dsp.c (working copy) @@ -167,6 +167,19 @@ #error You cant use BUSYDETECT_TONEONLY together with BUSYDETECT_COMPARE_TONE_AND_SILENCE #endif +/* The CNG signal consists of the transmission of 1100 Hz for 1/2 second, followed by a 3 second silent (2100 Hz OFF) period. */ +#define FAX_TONE_CNG_FREQ 1100 +#define FAX_TONE_CNG_DURATION 500 +#define FAX_TONE_CNG_DB 16 + +/* This signal may be sent by the Terminating FAX machine anywhere between 1.8 to 2.5 seconds AFTER answering the call. The CED signal consists of a 2100 Hz tone that is from 2.6 to 4 seconds in duration. */ +#define FAX_TONE_CED_FREQ 2100 +#define FAX_TONE_CED_DURATION 2600 +#define FAX_TONE_CED_DB 16 + +#define SAMPLE_RATE 8000 + + typedef struct { int v2; int v3; @@ -182,37 +195,52 @@ typedef struct { + int freq; + int block_size; + int squelch; /* Remove (squelch) tone */ + goertzel_state_t tone; + float energy; /* Accumulated energy of the current block */ + int samples_pending; /* Samples remain to complete the current block */ + + int hits_required; /* How many successive blocks with tone we are looking for */ + float threshold; /* Energy of the tone relative to energy from all other signals to consider a hit */ + + int hit_count; /* How many successive blocks we consider tone present */ + int last_hit; /* Indicates if the last processed block was a hit */ + +} tone_detect_state_t; + +typedef struct +{ goertzel_state_t row_out[4]; goertzel_state_t col_out[4]; - goertzel_state_t fax_tone; int lasthit; - int mhit; + int current_hit; float energy; int current_sample; - - char digits[MAX_DTMF_DIGITS + 1]; - - int current_digits; - int detected_digits; - int lost_digits; - int digit_hits[16]; - int fax_hits; } dtmf_detect_state_t; typedef struct { goertzel_state_t tone_out[6]; - int mhit; + 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; -} mf_detect_state_t; + 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 @@ -227,8 +255,6 @@ 700.0, 900.0, 1100.0, 1300.0, 1500.0, 1700.0 }; -static float fax_freq = 1100.0; - static char dtmf_positions[] = "123A" "456B" "789C" "*0#D"; static char bell_mf_positions[] = "1247C-358A--69*---0B----#"; @@ -271,7 +297,7 @@ 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 / 8000.0))); + s->fac = (int)(32768.0 * 2.0 * cos(2.0 * M_PI * freq / SAMPLE_RATE)); s->samples = samples; } @@ -301,33 +327,90 @@ int tstate; int tcount; int digitmode; + int faxmode; int thinkdigit; float genergy; - union { - dtmf_detect_state_t dtmf; - mf_detect_state_t mf; - } td; + digit_detect_state_t digit_state; + tone_detect_state_t cng_tone_state; + tone_detect_state_t ced_tone_state; }; +static void ast_tone_detect_init(tone_detect_state_t *s, int freq, int duration, int amp) +{ + int duration_samples; + int min_block; + float x; + + s->freq = freq; + + /* We want to allow 10% deviation of tone duration. To achieve this we + attempt to have Goertzel block equal to 10% of tone duration and look for + 9 successful blocks in a row. That 1 block can be lost anyway when tone starts + in the middle of one block and ends in the middle of another... */ + + /* Desired tone duration in samples */ + duration_samples = duration * SAMPLE_RATE / 1000; + /* Minimum Goertzel block - at least 5 full periods at target frequency */ + min_block = 5 * SAMPLE_RATE / freq; + /* Desired Goertzel block size - 10% of required signal duration */ + s->block_size = duration_samples / 10; + /* Adjust block size to contain integer number of periods */ + s->block_size = (s->block_size * freq + SAMPLE_RATE) / freq; + /* And make sure block isn't too short */ + if (s->block_size < min_block) + s->block_size = min_block; + + /* TODO: We are not suppressing (squelching) fax tones in the signal. + When this tone detection routines will be used for anything requiring tone suppression, + block_size needs to be adjusted to not exceed the minimum voice frame size. + Otherwise tone will be detected too late to squelch it from previous frames. */ + s->squelch = 0; + + /* Account for the first and the last block to be incomplete + and thus no tone will be detected in them */ + s->hits_required = (duration_samples - (s->block_size - 1)) / s->block_size; + + goertzel_init(&s->tone, freq, s->block_size); + + s->samples_pending = s->block_size; + s->hit_count = 0; + s->last_hit = 0; + s->energy = 0.0; + + /* We want tone energy to be amp decibels above the rest of the signal (the noise). + According to Parseval's theorem the energy computed in time domain equals to energy + computed in frequency domain. So subtracting energy in the frequency domain (Goertzel result) + from the energy in the time domain we will get energy of the remaining signal (without the tone + we are detecting). We will be checking that + 10*log(Ew / (Et - Ew)) > amp + Calculate threshold so that we will be actually checking + Ew > Et * threshold + */ + + x = pow(10.0, amp / 10.0); + s->threshold = x / (x + 1); + + ast_debug(1, "Setup tone %d Hz, %d ms, block_size=%d, hits_required=%d\n", freq, duration, s->block_size, s->hits_required); +} + +static void ast_fax_detect_init(struct ast_dsp *s) +{ + ast_tone_detect_init(&s->cng_tone_state, FAX_TONE_CNG_FREQ, FAX_TONE_CNG_DURATION, FAX_TONE_CNG_DB); + ast_tone_detect_init(&s->ced_tone_state, FAX_TONE_CED_FREQ, FAX_TONE_CED_DURATION, FAX_TONE_CED_DB); +} + 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; } - /* Same for the fax dector */ - goertzel_init (&s->fax_tone, fax_freq, 102); - s->current_sample = 0; - s->detected_digits = 0; - s->current_digits = 0; - memset(&s->digits, 0, sizeof(s->digits)); - s->lost_digits = 0; - s->digits[0] = '\0'; } static void ast_mf_detect_init (mf_detect_state_t *s) @@ -337,21 +420,131 @@ 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; - memset(&s->digits, 0, sizeof(s->digits)); - s->current_sample = 0; s->detected_digits = 0; s->lost_digits = 0; s->digits[0] = '\0'; - s->mhit = 0; + + if (mf) + ast_mf_detect_init(&s->td.mf); + else + ast_dtmf_detect_init(&s->td.dtmf); } -static int dtmf_detect (dtmf_detect_state_t *s, int16_t amp[], int samples, - int digitmode, int *writeback, int faxdetect) +static int tone_detect(tone_detect_state_t *s, int16_t *amp, int samples, + int *writeback) { + float tone_energy; + int i; + int hit = 0; + int limit; + int res = 0; + int16_t *ptr; + + while (1) { + /* Process in blocks. */ + limit = (samples < s->samples_pending) ? samples : s->samples_pending; + + for (i = limit, ptr = amp ; i > 0; i--, ptr++) { + /* signed 32 bit int should be enough to suqare any possible signed 16 bit value */ + s->energy += (int32_t) *ptr * (int32_t) *ptr; + + goertzel_sample(&s->tone, *ptr); + } + + s->samples_pending -= limit; + + if (s->samples_pending) { + /* Finished incomplete (last) block */ + if (s->last_hit && s->squelch) { + /* If we had a hit last time, go ahead and clear this out since likely it + will be another hit */ + memset(amp, 0, sizeof(*amp) * limit); + if (writeback) + *writeback = 1; + } + break; + } + + + tone_energy = goertzel_result(&s->tone); + + /* Scale to make comparable */ + tone_energy *= 2.0; + s->energy *= s->block_size; + + ast_debug(10, "tone %d, Ew=%f, Et=%f, s/n=%10.2f\n", s->freq, tone_energy, s->energy, tone_energy / (s->energy - tone_energy)); + hit = 0; + if (tone_energy > s->energy * s->threshold) { + + ast_debug(10, "Hit! count=%d\n", s->hit_count); + hit = 1; + + if (s->squelch) { + /* Zero out frame data */ + memset(amp, 0, sizeof(*amp) * limit); + if (writeback) + *writeback = 1; + } + } + + if (s->hit_count) + s->hit_count++; + + if (hit == s->last_hit) { + if (!hit) { + /* Two successive misses. Tone ended */ + s->hit_count = 0; + } else if (!s->hit_count) { + s->hit_count++; + } + + } + + if (s->hit_count == s->hits_required) { + ast_debug(1, "%d Hz done detected\n", s->freq); + res = 1; + } + + s->last_hit = hit; + + /* Reset for the next block */ + goertzel_reset(&s->tone); + + /* Advance to the next block */ + s->energy = 0.0; + s->samples_pending = s->block_size; + + amp += limit; + samples -= limit; + } + + return res; +} + +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 fax_energy = 0.0; float famp; int i; int j; @@ -364,32 +557,28 @@ hit = 0; for (sample = 0; sample < samples; sample = limit) { /* 102 is optimised to meet the DTMF specs. */ - if ((samples - sample) >= (102 - s->current_sample)) - limit = sample + (102 - s->current_sample); + 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->energy += famp*famp; + 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->row_out, amp[j]); - goertzel_sample(s->col_out, amp[j]); - goertzel_sample(s->row_out + 1, amp[j]); - goertzel_sample(s->col_out + 1, amp[j]); - goertzel_sample(s->row_out + 2, amp[j]); - goertzel_sample(s->col_out + 2, amp[j]); - goertzel_sample(s->row_out + 3, amp[j]); - goertzel_sample(s->col_out + 3, amp[j]); - - /* Update fax tone */ - if (faxdetect) - goertzel_sample(&s->fax_tone, amp[j]); + 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->current_sample += (limit - sample); - if (s->current_sample < 102) { + 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 */ @@ -399,19 +588,16 @@ } continue; } - /* Detect the fax energy, too */ - if (faxdetect) - fax_energy = goertzel_result(&s->fax_tone); /* We are at the end of a DTMF detection block */ /* Find the peak row and the peak column */ - row_energy[0] = goertzel_result (&s->row_out[0]); - col_energy[0] = goertzel_result (&s->col_out[0]); + 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->row_out[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->col_out[i]); + col_energy[i] = goertzel_result (&s->td.dtmf.col_out[i]); if (col_energy[i] > col_energy[best_col]) best_col = i; } @@ -432,7 +618,7 @@ } /* ... and fraction of total energy test */ if (i >= 4 && - (row_energy[best_row] + col_energy[best_col]) > DTMF_TO_TOTAL_ENERGY*s->energy) { + (row_energy[best_row] + col_energy[best_col]) > DTMF_TO_TOTAL_ENERGY*s->td.dtmf.energy) { /* Got a hit */ hit = dtmf_positions[(best_row << 2) + best_col]; if (!(digitmode & DSP_DIGITMODE_NOQUELCH)) { @@ -444,64 +630,37 @@ } } - /* Look for two successive similar results */ /* The logic in the next test is: - We need two successive identical clean detects, with + 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->lasthit && hit != s->mhit) { - if (hit) { - s->digit_hits[(best_row << 2) + best_col]++; - s->detected_digits++; - if (s->current_digits < MAX_DTMF_DIGITS) { - s->digits[s->current_digits++] = hit; - s->digits[s->current_digits] = '\0'; - } else { - s->lost_digits++; - } + 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->mhit = hit; } + s->td.dtmf.lasthit = hit; - if (!hit && faxdetect && (fax_energy >= FAX_THRESHOLD) && - (fax_energy >= DTMF_TO_TOTAL_ENERGY*s->energy)) { - /* XXX Probably need better checking than just this the energy XXX */ - hit = 'f'; - s->fax_hits++; - } else { - if (s->fax_hits > 5) { - hit = 'f'; - s->mhit = 'f'; - s->detected_digits++; - if (s->current_digits < MAX_DTMF_DIGITS) { - s->digits[s->current_digits++] = hit; - s->digits[s->current_digits] = '\0'; - } else { - s->lost_digits++; - } - } - s->fax_hits = 0; - } - s->lasthit = hit; /* Reinitialise the detector for the next block */ for (i = 0; i < 4; i++) { - goertzel_reset(&s->row_out[i]); - goertzel_reset(&s->col_out[i]); + goertzel_reset(&s->td.dtmf.row_out[i]); + goertzel_reset(&s->td.dtmf.col_out[i]); } - if (faxdetect) - goertzel_reset (&s->fax_tone); - s->energy = 0.0; - s->current_sample = 0; + s->td.dtmf.energy = 0.0; + s->td.dtmf.current_sample = 0; } - return (s->mhit); /* return the debounced hit */ + return (s->td.dtmf.current_hit); /* return the debounced hit */ } /* MF goertzel size */ #define MF_GSIZE 120 -static int mf_detect (mf_detect_state_t *s, int16_t amp[], +static int mf_detect(digit_detect_state_t *s, int16_t amp[], int samples, int digitmode, int *writeback) { float energy[6]; @@ -517,8 +676,8 @@ hit = 0; for (sample = 0; sample < samples; sample = limit) { /* 80 is optimised to meet the MF specs. */ - if ((samples - sample) >= (MF_GSIZE - s->current_sample)) - limit = sample + (MF_GSIZE - s->current_sample); + 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 @@ -527,15 +686,15 @@ 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->tone_out, amp[j]); - goertzel_sample(s->tone_out + 1, amp[j]); - goertzel_sample(s->tone_out + 2, amp[j]); - goertzel_sample(s->tone_out + 3, amp[j]); - goertzel_sample(s->tone_out + 4, amp[j]); - goertzel_sample(s->tone_out + 5, amp[j]); + 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->current_sample += (limit - sample); - if (s->current_sample < MF_GSIZE) { + 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 */ @@ -552,8 +711,8 @@ 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->tone_out[0]); - energy[1] = goertzel_result(&s->tone_out[1]); + 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; @@ -563,7 +722,7 @@ } /*endif*/ for (i=2;i<6;i++) { - energy[i] = goertzel_result(&s->tone_out[i]); + energy[i] = goertzel_result(&s->td.mf.tone_out[i]); if (energy[i] >= energy[best]) { second_best = best; best = i; @@ -603,46 +762,57 @@ 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->hits[4] && hit == s->hits[3] && - ((hit != '*' && hit != s->hits[2] && hit != s->hits[1])|| - (hit == '*' && hit == s->hits[2] && hit != s->hits[1] && - hit != s->hits[0]))) { - s->detected_digits++; - if (s->current_digits < MAX_DTMF_DIGITS) { - s->digits[s->current_digits++] = hit; - s->digits[s->current_digits] = '\0'; - } else { - s->lost_digits++; - } + 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); } - } else { - hit = 0; } - s->hits[0] = s->hits[1]; - s->hits[1] = s->hits[2]; - s->hits[2] = s->hits[3]; - s->hits[3] = s->hits[4]; - s->hits[4] = 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->tone_out[i]); - s->current_sample = 0; + goertzel_reset(&s->td.mf.tone_out[i]); + s->td.mf.current_sample = 0; } - if ((!s->mhit) || (s->mhit != hit)) { - s->mhit = 0; - return(0); - } - return (hit); + + return (s->td.mf.current_hit); /* return the debounced hit */ } static int __ast_dsp_digitdetect(struct ast_dsp *dsp, short *s, int len, int *writeback) { - int res; + int res = 0; - if (dsp->digitmode & DSP_DIGITMODE_MF) - res = mf_detect(&dsp->td.mf, s, len, dsp->digitmode & DSP_DIGITMODE_RELAXDTMF, writeback); - else - res = dtmf_detect(&dsp->td.dtmf, s, len, dsp->digitmode & DSP_DIGITMODE_RELAXDTMF, writeback, dsp->features & DSP_FEATURE_FAX_DETECT); + if ((dsp->features & DSP_FEATURE_DTMF_DETECT) && (dsp->digitmode & DSP_DIGITMODE_MF)) + res = mf_detect(&dsp->digit_state, s, len, dsp->digitmode & DSP_DIGITMODE_RELAXDTMF, writeback); + else if (dsp->features & DSP_FEATURE_DTMF_DETECT) + res = dtmf_detect(&dsp->digit_state, s, len, dsp->digitmode & DSP_DIGITMODE_RELAXDTMF, writeback); + + if ((dsp->features & DSP_FEATURE_FAX_DETECT) && (dsp->faxmode & DSP_FAXMODE_DETECT_CNG)) { + if (tone_detect(&dsp->cng_tone_state, s, len, NULL)) { + store_digit(&dsp->digit_state, 'f'); + res = 'f'; + } + } + + if ((dsp->features & DSP_FEATURE_FAX_DETECT) && (dsp->faxmode & DSP_FAXMODE_DETECT_CED)) { + if (tone_detect(&dsp->ced_tone_state, s, len, NULL)) { + store_digit(&dsp->digit_state, 'e'); + res = 'e'; + } + } + return res; } @@ -685,29 +855,17 @@ return 1; } -int ast_dsp_getdigits (struct ast_dsp *dsp, char *buf, int max) +int ast_dsp_getdigits(struct ast_dsp *dsp, char *buf, int max) { - if (dsp->digitmode & DSP_DIGITMODE_MF) { - if (max > dsp->td.mf.current_digits) - max = dsp->td.mf.current_digits; - if (max > 0) { - memcpy(buf, dsp->td.mf.digits, max); - memmove(dsp->td.mf.digits, dsp->td.mf.digits + max, dsp->td.mf.current_digits - max); - dsp->td.mf.current_digits -= max; - } - buf[max] = '\0'; - return max; - } else { - if (max > dsp->td.dtmf.current_digits) - max = dsp->td.dtmf.current_digits; - if (max > 0) { - memcpy (buf, dsp->td.dtmf.digits, max); - memmove (dsp->td.dtmf.digits, dsp->td.dtmf.digits + max, dsp->td.dtmf.current_digits - max); - dsp->td.dtmf.current_digits -= max; - } - buf[max] = '\0'; - return max; + if (max > dsp->digit_state.current_digits) + max = dsp->digit_state.current_digits; + if (max > 0) { + memcpy(buf, dsp->digit_state.digits, max); + memmove(dsp->digit_state.digits, dsp->digit_state.digits + max, dsp->digit_state.current_digits - max); + dsp->digit_state.current_digits -= max; } + buf[max] = '\0'; + return max; } static int __ast_dsp_call_progress(struct ast_dsp *dsp, short *s, int len) @@ -1079,12 +1237,12 @@ ast_debug(1, "Requesting Hangup because the busy tone was detected on channel %s\n", chan->name); return &dsp->f; } - if ((dsp->features & DSP_FEATURE_DTMF_DETECT)) { + if (((dsp->features & DSP_FEATURE_DTMF_DETECT) || (dsp->features & DSP_FEATURE_FAX_DETECT))) { digit = __ast_dsp_digitdetect(dsp, shortdata, len, &writeback); #if 0 if (digit) printf("Performing digit detection returned %d, digitmode is %d\n", digit, dsp->digitmode); -#endif +#endif if (dsp->digitmode & (DSP_DIGITMODE_MUTECONF | DSP_DIGITMODE_MUTEMAX)) { if (!dsp->thinkdigit) { if (digit) { @@ -1144,35 +1302,21 @@ return &dsp->f; } } - } else if (!digit) { - /* Only check when there is *not* a hit... */ - if (dsp->digitmode & DSP_DIGITMODE_MF) { - if (dsp->td.mf.current_digits) { - memset(&dsp->f, 0, sizeof(dsp->f)); - dsp->f.frametype = AST_FRAME_DTMF; - dsp->f.subclass = dsp->td.mf.digits[0]; - memmove(dsp->td.mf.digits, dsp->td.mf.digits + 1, dsp->td.mf.current_digits); - dsp->td.mf.current_digits--; - FIX_INF(af); - if (chan) - ast_queue_frame(chan, af); - ast_frfree(af); - return &dsp->f; - } - } else { - if (dsp->td.dtmf.current_digits) { - memset(&dsp->f, 0, sizeof(dsp->f)); - dsp->f.frametype = AST_FRAME_DTMF_END; - dsp->f.subclass = dsp->td.dtmf.digits[0]; - memmove(dsp->td.dtmf.digits, dsp->td.dtmf.digits + 1, dsp->td.dtmf.current_digits); - dsp->td.dtmf.current_digits--; - FIX_INF(af); - if (chan) - ast_queue_frame(chan, af); - ast_frfree(af); - return &dsp->f; - } - } + } else if (dsp->digit_state.current_digits > 1 || + (dsp->digit_state.current_digits == 1 && digit != dsp->digit_state.digits[0])) { + /* Since we basically generate DTMF_END frames we do it only when a digit + has finished. */ + + memset(&dsp->f, 0, sizeof(dsp->f)); + dsp->f.frametype = AST_FRAME_DTMF; + dsp->f.subclass = dsp->digit_state.digits[0]; + memmove(dsp->digit_state.digits, dsp->digit_state.digits + 1, dsp->digit_state.current_digits); + dsp->digit_state.current_digits--; + FIX_INF(af); + if (chan) + ast_queue_frame(chan, af); + ast_frfree(af); + return &dsp->f; } } if ((dsp->features & DSP_FEATURE_CALL_PROGRESS)) { @@ -1225,10 +1369,14 @@ dsp->threshold = DEFAULT_THRESHOLD; dsp->features = DSP_FEATURE_SILENCE_SUPPRESS; dsp->busycount = DSP_HISTORY; - /* Initialize DTMF detector */ - ast_dtmf_detect_init(&dsp->td.dtmf); + dsp->digitmode = DSP_DIGITMODE_DTMF; + dsp->faxmode = DSP_FAXMODE_DETECT_CNG; + /* Initialize digit detector */ + ast_digit_detect_init(&dsp->digit_state, dsp->digitmode & DSP_DIGITMODE_MF); /* Initialize initial DSP progress detect parameters */ ast_dsp_prog_reset(dsp); + /* Initialize fax detector */ + ast_fax_detect_init(dsp); } return dsp; } @@ -1270,27 +1418,27 @@ dsp->thinkdigit = 0; if (dsp->digitmode & DSP_DIGITMODE_MF) { - memset(dsp->td.mf.digits, 0, sizeof(dsp->td.mf.digits)); - dsp->td.mf.current_digits = 0; + mf_detect_state_t *s = &dsp->digit_state.td.mf; /* Reinitialise the detector for the next block */ for (i = 0; i < 6; i++) { - goertzel_reset(&dsp->td.mf.tone_out[i]); + goertzel_reset(&s->tone_out[i]); } - dsp->td.mf.hits[4] = dsp->td.mf.hits[3] = dsp->td.mf.hits[2] = dsp->td.mf.hits[1] = dsp->td.mf.hits[0] = dsp->td.mf.mhit = 0; - dsp->td.mf.current_sample = 0; + s->hits[4] = s->hits[3] = s->hits[2] = s->hits[1] = s->hits[0] = s->current_hit = 0; + s->current_sample = 0; } else { - memset(dsp->td.dtmf.digits, 0, sizeof(dsp->td.dtmf.digits)); - dsp->td.dtmf.current_digits = 0; + dtmf_detect_state_t *s = &dsp->digit_state.td.dtmf; /* Reinitialise the detector for the next block */ for (i = 0; i < 4; i++) { - goertzel_reset(&dsp->td.dtmf.row_out[i]); - goertzel_reset(&dsp->td.dtmf.col_out[i]); + goertzel_reset(&s->row_out[i]); + goertzel_reset(&s->col_out[i]); } - goertzel_reset (&dsp->td.dtmf.fax_tone); - dsp->td.dtmf.lasthit = dsp->td.dtmf.mhit = 0; - dsp->td.dtmf.energy = 0.0; - dsp->td.dtmf.current_sample = 0; + s->lasthit = s->current_hit = 0; + s->energy = 0.0; + s->current_sample = 0; } + + dsp->digit_state.digits[0] = '\0'; + dsp->digit_state.current_digits = 0; } void ast_dsp_reset(struct ast_dsp *dsp) @@ -1315,15 +1463,21 @@ new = digitmode & (DSP_DIGITMODE_DTMF | DSP_DIGITMODE_MF | DSP_DIGITMODE_MUTECONF | DSP_DIGITMODE_MUTEMAX); if (old != new) { /* Must initialize structures if switching from MF to DTMF or vice-versa */ - if (new & DSP_DIGITMODE_MF) - ast_mf_detect_init(&dsp->td.mf); - else - ast_dtmf_detect_init(&dsp->td.dtmf); + ast_digit_detect_init(&dsp->digit_state, new & DSP_DIGITMODE_MF); } dsp->digitmode = digitmode; return 0; } +int ast_dsp_set_faxmode(struct ast_dsp *dsp, int faxmode) +{ + if (dsp->faxmode != faxmode) { + ast_fax_detect_init(dsp); + } + dsp->faxmode = faxmode; + return 0; +} + int ast_dsp_set_call_progress_zone(struct ast_dsp *dsp, char *zone) { int x;