SPAN_DECLARE(int) r2_mf_rx(r2_mf_rx_state_t *s, const int16_t amp[], int samples) { #if defined(SPANDSP_USE_FIXED_POINT) int32_t energy[6]; int16_t xamp; #else float energy[6]; float xamp; #endif int i; int j; int sample; int best; int second_best; int hit; int hit_digit; int limit; hit = 0; hit_digit = 0; for (sample = 0; sample < samples; sample = limit) { if ((samples - sample) >= (R2_MF_SAMPLES_PER_BLOCK - s->current_sample)) limit = sample + (R2_MF_SAMPLES_PER_BLOCK - s->current_sample); else limit = samples; for (j = sample; j < limit; j++) { xamp = goertzel_preadjust_amp(amp[j]); goertzel_samplex(&s->out[0], xamp); goertzel_samplex(&s->out[1], xamp); goertzel_samplex(&s->out[2], xamp); goertzel_samplex(&s->out[3], xamp); goertzel_samplex(&s->out[4], xamp); goertzel_samplex(&s->out[5], xamp); } s->current_sample += (limit - sample); if (s->current_sample < R2_MF_SAMPLES_PER_BLOCK) continue; /* We are at the end of an MF detection block */ /* Find the two highest energies */ energy[0] = goertzel_result(&s->out[0]); energy[1] = goertzel_result(&s->out[1]); if (energy[0] > energy[1]) { best = 0; second_best = 1; } else { best = 1; second_best = 0; } for (i = 2; i < 6; i++) { energy[i] = goertzel_result(&s->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 = FALSE; if (energy[best] >= R2_MF_THRESHOLD && energy[second_best] >= R2_MF_THRESHOLD && energy[best] < energy[second_best]*R2_MF_TWIST && energy[best]*R2_MF_TWIST > energy[second_best]) { /* Relative peak test */ hit = TRUE; for (i = 0; i < 6; i++) { if (i != best && i != second_best) { if (energy[i]*R2_MF_RELATIVE_PEAK >= energy[second_best]) { /* The best two are not clearly the best */ hit = FALSE; 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_digit = r2_mf_positions[best]; } else { hit_digit = 0; } if (s->current_digit != hit_digit && s->callback) { i = (hit_digit) ? -10 : -99; s->callback(s->callback_data, hit_digit, i, 0); } s->current_digit = hit_digit; s->current_sample = 0; } return 0; }
SPAN_DECLARE(int) ademco_contactid_sender_rx(ademco_contactid_sender_state_t *s, const int16_t amp[], int samples) { #if defined(SPANDSP_USE_FIXED_POINT) int32_t energy_1400; int32_t energy_2300; int16_t xamp; #else float energy_1400; float energy_2300; float xamp; #endif int sample; int limit; int hit; int j; for (sample = 0; sample < samples; sample = limit) { if ((samples - sample) >= (GOERTZEL_SAMPLES_PER_BLOCK - s->current_sample)) limit = sample + (GOERTZEL_SAMPLES_PER_BLOCK - s->current_sample); else limit = samples; for (j = sample; j < limit; j++) { xamp = amp[j]; xamp = goertzel_preadjust_amp(xamp); #if defined(SPANDSP_USE_FIXED_POINT) s->energy += ((int32_t) xamp*xamp); #else s->energy += xamp*xamp; #endif goertzel_samplex(&s->tone_1400, xamp); goertzel_samplex(&s->tone_2300, xamp); } s->current_sample += (limit - sample); if (s->current_sample < GOERTZEL_SAMPLES_PER_BLOCK) continue; energy_1400 = goertzel_result(&s->tone_1400); energy_2300 = goertzel_result(&s->tone_2300); hit = 0; if (energy_1400 > DETECTION_THRESHOLD || energy_2300 > DETECTION_THRESHOLD) { if (energy_1400 > energy_2300) { if (energy_1400 > TONE_TO_TOTAL_ENERGY*s->energy) hit = 1; } else { if (energy_2300 > TONE_TO_TOTAL_ENERGY*s->energy) hit = 2; } } if (hit != s->in_tone && hit == s->last_hit) { /* We have two successive indications that something has changed to a specific new state. */ switch (s->tone_state) { case 0: if (hit == 1) { span_log(&s->logging, SPAN_LOG_FLOW, "Receiving initial 1400Hz\n"); s->in_tone = hit; s->tone_state = 1; s->duration = 0; } break; case 1: /* We are looking for a burst of 1400Hz which is 100ms +- 5% long */ if (hit == 0) { if (s->duration < ms_to_samples(70) || s->duration > ms_to_samples(130)) { span_log(&s->logging, SPAN_LOG_FLOW, "Bad initial 1400Hz tone duration\n"); s->tone_state = 0; } else { span_log(&s->logging, SPAN_LOG_FLOW, "Received 1400Hz tone\n"); s->tone_state = 2; } s->in_tone = hit; s->duration = 0; } break; case 2: /* We are looking for 100ms +-5% of silence after the 1400Hz tone */ if (s->duration < ms_to_samples(70) || s->duration > ms_to_samples(130)) { span_log(&s->logging, SPAN_LOG_FLOW, "Bad silence length\n"); s->tone_state = 0; s->in_tone = hit; } else if (hit == 2) { span_log(&s->logging, SPAN_LOG_FLOW, "Received silence\n"); s->tone_state = 3; s->in_tone = hit; } else { s->tone_state = 0; s->in_tone = 0; } s->duration = 0; break; case 3: /* We are looking for a burst of 2300Hz which is 100ms +- 5% long */ if (hit == 0) { if (s->duration < ms_to_samples(70) || s->duration > ms_to_samples(130)) { span_log(&s->logging, SPAN_LOG_FLOW, "Bad initial 2300Hz tone duration\n"); s->tone_state = 0; } else { span_log(&s->logging, SPAN_LOG_FLOW, "Received 2300Hz\n"); if (s->callback) s->callback(s->callback_user_data, -1, 0, 0); s->tone_state = 4; /* Release the transmit side, and it will time the 250ms post tone delay */ s->clear_to_send = true; s->tries = 0; if (s->tx_digits_len) s->timer = ms_to_samples(3000); } s->in_tone = hit; s->duration = 0; } break; case 4: if (hit == 1) { span_log(&s->logging, SPAN_LOG_FLOW, "Receiving kissoff\n"); s->tone_state = 5; s->in_tone = hit; s->duration = 0; } break; case 5: if (hit == 0) { s->busy = false; if (s->duration < ms_to_samples(400) || s->duration > ms_to_samples(1500)) { span_log(&s->logging, SPAN_LOG_FLOW, "Bad kissoff duration %d\n", s->duration); if (++s->tries < 4) { dtmf_tx_put(&s->dtmf, s->tx_digits, s->tx_digits_len); s->timer = ms_to_samples(3000); s->tone_state = 4; } else { s->timer = 0; if (s->callback) s->callback(s->callback_user_data, false, 0, 0); } } else { span_log(&s->logging, SPAN_LOG_FLOW, "Received good kissoff\n"); s->clear_to_send = true; s->tx_digits_len = 0; if (s->callback) s->callback(s->callback_user_data, true, 0, 0); s->tone_state = 4; s->clear_to_send = true; s->tries = 0; if (s->tx_digits_len) s->timer = ms_to_samples(3000); } s->in_tone = hit; s->duration = 0; } break; } } s->last_hit = hit; s->duration += GOERTZEL_SAMPLES_PER_BLOCK; if (s->timer > 0) { s->timer -= GOERTZEL_SAMPLES_PER_BLOCK; if (s->timer <= 0) { span_log(&s->logging, SPAN_LOG_FLOW, "Timer expired\n"); if (s->tone_state == 4 && s->tx_digits_len) { if (++s->tries < 4) { dtmf_tx_put(&s->dtmf, s->tx_digits, s->tx_digits_len); s->timer = ms_to_samples(3000); } else { s->timer = 0; if (s->callback) s->callback(s->callback_user_data, false, 0, 0); } } } } s->energy = 0; s->current_sample = 0; } return 0; }
static void super_tone_chunk(super_tone_rx_state_t *s) { int i; int j; int k1; int k2; #if defined(SPANDSP_USE_FIXED_POINT) int32_t res[SUPER_TONE_BINS/2]; #else float res[SUPER_TONE_BINS/2]; #endif for (i = 0; i < s->desc->monitored_frequencies; i++) res[i] = goertzel_result(&s->state[i]); /* Find our two best monitored frequencies, which also have adequate energy. */ if (s->energy < DETECTION_THRESHOLD) { k1 = -1; k2 = -1; } else { if (res[0] > res[1]) { k1 = 0; k2 = 1; } else { k1 = 1; k2 = 0; } for (j = 2; j < s->desc->monitored_frequencies; j++) { if (res[j] >= res[k1]) { k2 = k1; k1 = j; } else if (res[j] >= res[k2]) { k2 = j; } } if ((res[k1] + res[k2]) < TONE_TO_TOTAL_ENERGY*s->energy) { k1 = -1; k2 = -1; } else if (res[k1] > TONE_TWIST*res[k2]) { k2 = -1; } else if (k2 < k1) { j = k1; k1 = k2; k2 = j; } } /* See if this differs from last time. */ if (k1 != s->segments[10].f1 || k2 != s->segments[10].f2) { /* It is different, but this might just be a transitional quirk, or a one shot hiccup (eg due to noise). Only if this same thing is seen a second time should we change state. */ s->segments[10].f1 = k1; s->segments[10].f2 = k2; /* While things are hopping around, consider this a continuance of the previous state. */ s->segments[9].min_duration++; } else { if (k1 != s->segments[9].f1 || k2 != s->segments[9].f2) { if (s->detected_tone >= 0) { /* Test for the continuance of the existing tone pattern, based on our new knowledge of an entire segment length. */ if (!test_cadence(s->desc->tone_list[s->detected_tone], -s->desc->tone_segs[s->detected_tone], s->segments, s->rotation++)) { s->detected_tone = -1; s->tone_callback(s->callback_data, s->detected_tone, -10, 0); } } if (s->segment_callback) { s->segment_callback(s->callback_data, s->segments[9].f1, s->segments[9].f2, s->segments[9].min_duration*SUPER_TONE_BINS/8); } memcpy (&s->segments[0], &s->segments[1], 9*sizeof(s->segments[0])); s->segments[9].f1 = k1; s->segments[9].f2 = k2; s->segments[9].min_duration = 1; } else { /* This is a continuance of the previous state */ if (s->detected_tone >= 0) { /* Test for the continuance of the existing tone pattern. We must do this here, so we can sense the discontinuance of the tone on an excessively long segment. */ if (!test_cadence(s->desc->tone_list[s->detected_tone], s->desc->tone_segs[s->detected_tone], s->segments, s->rotation)) { s->detected_tone = -1; s->tone_callback(s->callback_data, s->detected_tone, -10, 0); } } s->segments[9].min_duration++; } } if (s->detected_tone < 0) { /* Test for the start of any of the monitored tone patterns */ for (j = 0; j < s->desc->tones; j++) { if (test_cadence(s->desc->tone_list[j], s->desc->tone_segs[j], s->segments, -1)) { s->detected_tone = j; s->rotation = 0; s->tone_callback(s->callback_data, s->detected_tone, -10, 0); break; } } } #if defined(SPANDSP_USE_FIXED_POINT) s->energy = 0; #else s->energy = 0.0f; #endif }
SPAN_DECLARE(int) bell_mf_rx(bell_mf_rx_state_t *s, const int16_t amp[], int samples) { #if defined(SPANDSP_USE_FIXED_POINT) int32_t energy[6]; int16_t xamp; #else float energy[6]; float xamp; #endif int i; int j; int sample; int best; int second_best; int limit; uint8_t hit; hit = 0; for (sample = 0; sample < samples; sample = limit) { if ((samples - sample) >= (BELL_MF_SAMPLES_PER_BLOCK - s->current_sample)) limit = sample + (BELL_MF_SAMPLES_PER_BLOCK - s->current_sample); else limit = samples; for (j = sample; j < limit; j++) { xamp = goertzel_preadjust_amp(amp[j]); goertzel_samplex(&s->out[0], xamp); goertzel_samplex(&s->out[1], xamp); goertzel_samplex(&s->out[2], xamp); goertzel_samplex(&s->out[3], xamp); goertzel_samplex(&s->out[4], xamp); goertzel_samplex(&s->out[5], xamp); } s->current_sample += (limit - sample); if (s->current_sample < BELL_MF_SAMPLES_PER_BLOCK) continue; /* We are 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->out[0]); energy[1] = goertzel_result(&s->out[1]); if (energy[0] > energy[1]) { best = 0; second_best = 1; } else { best = 1; second_best = 0; } for (i = 2; i < 6; i++) { energy[i] = goertzel_result(&s->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 && energy[best]*BELL_MF_TWIST > energy[second_best]) { /* Relative peak test */ hit = 'X'; 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->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]))) { if (s->current_digits < MAX_BELL_MF_DIGITS) { s->digits[s->current_digits++] = (char) hit; s->digits[s->current_digits] = '\0'; if (s->digits_callback) { s->digits_callback(s->digits_callback_data, s->digits, s->current_digits); s->current_digits = 0; } } else { s->lost_digits++; } } } 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; s->current_sample = 0; } if (s->current_digits && s->digits_callback) { s->digits_callback(s->digits_callback_data, s->digits, s->current_digits); s->digits[0] = '\0'; s->current_digits = 0; } return 0; }
int r2_mf_rx(r2_mf_rx_state_t *s, const int16_t amp[], int samples) { float energy[6]; float famp; float v1; int i; int j; int sample; int best; int second_best; int hit; int hit_char; int limit; hit = 0; hit_char = 0; for (sample = 0; sample < samples; sample = limit) { if ((samples - sample) >= (s->samples - s->current_sample)) limit = sample + (s->samples - s->current_sample); else limit = samples; 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-5 loop */ v1 = s->out[0].v2; s->out[0].v2 = s->out[0].v3; s->out[0].v3 = s->out[0].fac*s->out[0].v2 - v1 + famp; v1 = s->out[1].v2; s->out[1].v2 = s->out[1].v3; s->out[1].v3 = s->out[1].fac*s->out[1].v2 - v1 + famp; v1 = s->out[2].v2; s->out[2].v2 = s->out[2].v3; s->out[2].v3 = s->out[2].fac*s->out[2].v2 - v1 + famp; v1 = s->out[3].v2; s->out[3].v2 = s->out[3].v3; s->out[3].v3 = s->out[3].fac*s->out[3].v2 - v1 + famp; v1 = s->out[4].v2; s->out[4].v2 = s->out[4].v3; s->out[4].v3 = s->out[4].fac*s->out[4].v2 - v1 + famp; v1 = s->out[5].v2; s->out[5].v2 = s->out[5].v3; s->out[5].v3 = s->out[5].fac*s->out[5].v2 - v1 + famp; } s->current_sample += (limit - sample); if (s->current_sample < s->samples) continue; /* We are at the end of an MF detection block */ /* Find the two highest energies */ energy[0] = goertzel_result(&s->out[0]); energy[1] = goertzel_result(&s->out[1]); if (energy[0] > energy[1]) { best = 0; second_best = 1; } else { best = 1; second_best = 0; } for (i = 2; i < 6; i++) { energy[i] = goertzel_result(&s->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 = FALSE; if (energy[best] >= R2_MF_THRESHOLD && energy[second_best] >= R2_MF_THRESHOLD && energy[best] < energy[second_best]*R2_MF_TWIST && energy[best]*R2_MF_TWIST > energy[second_best]) { /* Relative peak test */ hit = TRUE; for (i = 0; i < 6; i++) { if (i != best && i != second_best) { if (energy[i]*R2_MF_RELATIVE_PEAK >= energy[second_best]) { /* The best two are not clearly the best */ hit = FALSE; 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_char = r2_mf_positions[best]; } else { hit_char = 0; } /* Reinitialise the detector for the next block */ if (s->fwd) { for (i = 0; i < 6; i++) goertzel_reset(&s->out[i]); } else { for (i = 0; i < 6; i++) goertzel_reset(&s->out[i]); } s->current_sample = 0; } return hit_char; }
int bell_mf_rx(bell_mf_rx_state_t *s, const int16_t amp[], int samples) { float energy[6]; float famp; float v1; int i; int j; int sample; int best; int second_best; int limit; uint8_t hit; hit = 0; for (sample = 0; sample < samples; sample = limit) { if ((samples - sample) >= (120 - s->current_sample)) limit = sample + (120 - s->current_sample); else limit = samples; 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-5 loop */ v1 = s->out[0].v2; s->out[0].v2 = s->out[0].v3; s->out[0].v3 = s->out[0].fac*s->out[0].v2 - v1 + famp; v1 = s->out[1].v2; s->out[1].v2 = s->out[1].v3; s->out[1].v3 = s->out[1].fac*s->out[1].v2 - v1 + famp; v1 = s->out[2].v2; s->out[2].v2 = s->out[2].v3; s->out[2].v3 = s->out[2].fac*s->out[2].v2 - v1 + famp; v1 = s->out[3].v2; s->out[3].v2 = s->out[3].v3; s->out[3].v3 = s->out[3].fac*s->out[3].v2 - v1 + famp; v1 = s->out[4].v2; s->out[4].v2 = s->out[4].v3; s->out[4].v3 = s->out[4].fac*s->out[4].v2 - v1 + famp; v1 = s->out[5].v2; s->out[5].v2 = s->out[5].v3; s->out[5].v3 = s->out[5].fac*s->out[5].v2 - v1 + famp; } s->current_sample += (limit - sample); if (s->current_sample < 120) continue; /* We are 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->out[0]); energy[1] = goertzel_result(&s->out[1]); if (energy[0] > energy[1]) { best = 0; second_best = 1; } else { best = 1; second_best = 0; } for (i = 2; i < 6; i++) { energy[i] = goertzel_result(&s->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 && energy[best]*BELL_MF_TWIST > energy[second_best]) { /* Relative peak test */ hit = 'X'; 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->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]))) { if (s->current_digits < MAX_BELL_MF_DIGITS) { s->digits[s->current_digits++] = (char) hit; s->digits[s->current_digits] = '\0'; if (s->callback) { s->callback(s->callback_data, s->digits, s->current_digits); s->current_digits = 0; } } else { s->lost_digits++; } } } 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; /* Reinitialise the detector for the next block */ for (i = 0; i < 6; i++) goertzel_reset(&s->out[i]); s->current_sample = 0; } if (s->current_digits && s->callback) { s->callback(s->callback_data, s->digits, s->current_digits); s->digits[0] = '\0'; s->current_digits = 0; } return 0; }
static int dtmf_detect (dtmf_detect_state_t *s, int16_t amp[], int samples, int digitmode, int *writeback, int faxdetect) { float row_energy[4]; float col_energy[4]; #ifdef FAX_DETECT float fax_energy; #ifdef OLD_DSP_ROUTINES float fax_energy_2nd; #endif #endif /* FAX_DETECT */ float famp; float v1; 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->current_sample)) limit = sample + (102 - s->current_sample); else limit = samples; #if defined(USE_3DNOW) _dtmf_goertzel_update (s->row_out, amp + sample, limit - sample); _dtmf_goertzel_update (s->col_out, amp + sample, limit - sample); #ifdef OLD_DSP_ROUTINES _dtmf_goertzel_update (s->row_out2nd, amp + sample, limit2 - sample); _dtmf_goertzel_update (s->col_out2nd, amp + sample, limit2 - sample); #endif /* XXX Need to fax detect for 3dnow too XXX */ #warning "Fax Support Broken" #else /* 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; /* With GCC 2.95, the following unrolled code seems to take about 35% (rough estimate) as long as a neat little 0-3 loop */ v1 = s->row_out[0].v2; s->row_out[0].v2 = s->row_out[0].v3; s->row_out[0].v3 = s->row_out[0].fac*s->row_out[0].v2 - v1 + famp; v1 = s->col_out[0].v2; s->col_out[0].v2 = s->col_out[0].v3; s->col_out[0].v3 = s->col_out[0].fac*s->col_out[0].v2 - v1 + famp; v1 = s->row_out[1].v2; s->row_out[1].v2 = s->row_out[1].v3; s->row_out[1].v3 = s->row_out[1].fac*s->row_out[1].v2 - v1 + famp; v1 = s->col_out[1].v2; s->col_out[1].v2 = s->col_out[1].v3; s->col_out[1].v3 = s->col_out[1].fac*s->col_out[1].v2 - v1 + famp; v1 = s->row_out[2].v2; s->row_out[2].v2 = s->row_out[2].v3; s->row_out[2].v3 = s->row_out[2].fac*s->row_out[2].v2 - v1 + famp; v1 = s->col_out[2].v2; s->col_out[2].v2 = s->col_out[2].v3; s->col_out[2].v3 = s->col_out[2].fac*s->col_out[2].v2 - v1 + famp; v1 = s->row_out[3].v2; s->row_out[3].v2 = s->row_out[3].v3; s->row_out[3].v3 = s->row_out[3].fac*s->row_out[3].v2 - v1 + famp; v1 = s->col_out[3].v2; s->col_out[3].v2 = s->col_out[3].v3; s->col_out[3].v3 = s->col_out[3].fac*s->col_out[3].v2 - v1 + famp; #ifdef FAX_DETECT /* Update fax tone */ v1 = s->fax_tone.v2; s->fax_tone.v2 = s->fax_tone.v3; s->fax_tone.v3 = s->fax_tone.fac*s->fax_tone.v2 - v1 + famp; #endif /* FAX_DETECT */ #ifdef OLD_DSP_ROUTINES v1 = s->col_out2nd[0].v2; s->col_out2nd[0].v2 = s->col_out2nd[0].v3; s->col_out2nd[0].v3 = s->col_out2nd[0].fac*s->col_out2nd[0].v2 - v1 + famp; v1 = s->row_out2nd[0].v2; s->row_out2nd[0].v2 = s->row_out2nd[0].v3; s->row_out2nd[0].v3 = s->row_out2nd[0].fac*s->row_out2nd[0].v2 - v1 + famp; v1 = s->col_out2nd[1].v2; s->col_out2nd[1].v2 = s->col_out2nd[1].v3; s->col_out2nd[1].v3 = s->col_out2nd[1].fac*s->col_out2nd[1].v2 - v1 + famp; v1 = s->row_out2nd[1].v2; s->row_out2nd[1].v2 = s->row_out2nd[1].v3; s->row_out2nd[1].v3 = s->row_out2nd[1].fac*s->row_out2nd[1].v2 - v1 + famp; v1 = s->col_out2nd[2].v2; s->col_out2nd[2].v2 = s->col_out2nd[2].v3; s->col_out2nd[2].v3 = s->col_out2nd[2].fac*s->col_out2nd[2].v2 - v1 + famp; v1 = s->row_out2nd[2].v2; s->row_out2nd[2].v2 = s->row_out2nd[2].v3; s->row_out2nd[2].v3 = s->row_out2nd[2].fac*s->row_out2nd[2].v2 - v1 + famp; v1 = s->col_out2nd[3].v2; s->col_out2nd[3].v2 = s->col_out2nd[3].v3; s->col_out2nd[3].v3 = s->col_out2nd[3].fac*s->col_out2nd[3].v2 - v1 + famp; v1 = s->row_out2nd[3].v2; s->row_out2nd[3].v2 = s->row_out2nd[3].v3; s->row_out2nd[3].v3 = s->row_out2nd[3].fac*s->row_out2nd[3].v2 - v1 + famp; #ifdef FAX_DETECT /* Update fax tone */ v1 = s->fax_tone.v2; s->fax_tone2nd.v2 = s->fax_tone2nd.v3; s->fax_tone2nd.v3 = s->fax_tone2nd.fac*s->fax_tone2nd.v2 - v1 + famp; #endif /* FAX_DETECT */ #endif } #endif s->current_sample += (limit - sample); if (s->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; } #ifdef FAX_DETECT /* Detect the fax energy, too */ fax_energy = goertzel_result(&s->fax_tone); #endif /* 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]); for (best_row = best_col = 0, i = 1; i < 4; i++) { row_energy[i] = goertzel_result (&s->row_out[i]); if (row_energy[i] > row_energy[best_row]) best_row = i; col_energy[i] = goertzel_result (&s->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 && 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; } } #ifdef OLD_DSP_ROUTINES /* ... and second harmonic test */ if (i >= 4 && (row_energy[best_row] + col_energy[best_col]) > 42.0*s->energy && goertzel_result(&s->col_out2nd[best_col])*DTMF_2ND_HARMONIC_COL < col_energy[best_col] && goertzel_result(&s->row_out2nd[best_row])*DTMF_2ND_HARMONIC_ROW < row_energy[best_row]) { #else /* ... and fraction of total energy test */ if (i >= 4 && (row_energy[best_row] + col_energy[best_col]) > DTMF_TO_TOTAL_ENERGY*s->energy) { #endif /* 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; } /* Look for two successive similar results */ /* The logic in the next test is: 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 */ #ifdef OLD_DSP_ROUTINES if (hit == s->hit3 && s->hit3 != s->hit2) { s->mhit = 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++; } } #else if (hit == s->hits[2] && hit != s->hits[1] && hit != s->hits[0]) { s->mhit = 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++; } } #endif } } #ifdef FAX_DETECT if (!hit && (fax_energy >= FAX_THRESHOLD) && (fax_energy >= DTMF_TO_TOTAL_ENERGY*s->energy) && (faxdetect)) { #if 0 printf("Fax energy/Second Harmonic: %f\n", fax_energy); #endif /* 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; } #endif /* FAX_DETECT */ #ifdef OLD_DSP_ROUTINES s->hit1 = s->hit2; s->hit2 = s->hit3; s->hit3 = hit; #else s->hits[0] = s->hits[1]; s->hits[1] = s->hits[2]; s->hits[2] = hit; #endif /* 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]); #ifdef OLD_DSP_ROUTINES goertzel_reset(&s->row_out2nd[i]); goertzel_reset(&s->col_out2nd[i]); #endif } #ifdef FAX_DETECT goertzel_reset (&s->fax_tone); #ifdef OLD_DSP_ROUTINES goertzel_reset (&s->fax_tone2nd); #endif #endif s->energy = 0.0; s->current_sample = 0; } if ((!s->mhit) || (s->mhit != hit)) { s->mhit = 0; return(0); } return (hit); } /* MF goertzel size */ #ifdef OLD_DSP_ROUTINES #define MF_GSIZE 160 #else #define MF_GSIZE 120 #endif static int mf_detect (mf_detect_state_t *s, int16_t amp[], int samples, int digitmode, int *writeback) { #ifdef OLD_DSP_ROUTINES float tone_energy[6]; int best1; int best2; float max; int sofarsogood; #else float energy[6]; int best; int second_best; #endif float famp; float v1; 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->current_sample)) limit = sample + (MF_GSIZE - s->current_sample); else limit = samples; #if defined(USE_3DNOW) _dtmf_goertzel_update (s->row_out, amp + sample, limit - sample); _dtmf_goertzel_update (s->col_out, amp + sample, limit - sample); #ifdef OLD_DSP_ROUTINES _dtmf_goertzel_update (s->row_out2nd, amp + sample, limit2 - sample); _dtmf_goertzel_update (s->col_out2nd, amp + sample, limit2 - sample); #endif /* XXX Need to fax detect for 3dnow too XXX */ #warning "Fax Support Broken" #else /* 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]; #ifdef OLD_DSP_ROUTINES s->energy += famp*famp; #endif /* With GCC 2.95, the following unrolled code seems to take about 35% (rough estimate) as long as a neat little 0-3 loop */ v1 = s->tone_out[0].v2; s->tone_out[0].v2 = s->tone_out[0].v3; s->tone_out[0].v3 = s->tone_out[0].fac*s->tone_out[0].v2 - v1 + famp; v1 = s->tone_out[1].v2; s->tone_out[1].v2 = s->tone_out[1].v3; s->tone_out[1].v3 = s->tone_out[1].fac*s->tone_out[1].v2 - v1 + famp; v1 = s->tone_out[2].v2; s->tone_out[2].v2 = s->tone_out[2].v3; s->tone_out[2].v3 = s->tone_out[2].fac*s->tone_out[2].v2 - v1 + famp; v1 = s->tone_out[3].v2; s->tone_out[3].v2 = s->tone_out[3].v3; s->tone_out[3].v3 = s->tone_out[3].fac*s->tone_out[3].v2 - v1 + famp; v1 = s->tone_out[4].v2; s->tone_out[4].v2 = s->tone_out[4].v3; s->tone_out[4].v3 = s->tone_out[4].fac*s->tone_out[4].v2 - v1 + famp; v1 = s->tone_out[5].v2; s->tone_out[5].v2 = s->tone_out[5].v3; s->tone_out[5].v3 = s->tone_out[5].fac*s->tone_out[5].v2 - v1 + famp; #ifdef OLD_DSP_ROUTINES v1 = s->tone_out2nd[0].v2; s->tone_out2nd[0].v2 = s->tone_out2nd[0].v3; s->tone_out2nd[0].v3 = s->tone_out2nd[0].fac*s->tone_out2nd[0].v2 - v1 + famp; v1 = s->tone_out2nd[1].v2; s->tone_out2nd[1].v2 = s->tone_out2nd[1].v3; s->tone_out2nd[1].v3 = s->tone_out2nd[1].fac*s->tone_out2nd[1].v2 - v1 + famp; v1 = s->tone_out2nd[2].v2; s->tone_out2nd[2].v2 = s->tone_out2nd[2].v3; s->tone_out2nd[2].v3 = s->tone_out2nd[2].fac*s->tone_out2nd[2].v2 - v1 + famp; v1 = s->tone_out2nd[3].v2; s->tone_out2nd[3].v2 = s->tone_out2nd[3].v3; s->tone_out2nd[3].v3 = s->tone_out2nd[3].fac*s->tone_out2nd[3].v2 - v1 + famp; v1 = s->tone_out2nd[4].v2; s->tone_out2nd[4].v2 = s->tone_out2nd[4].v3; s->tone_out2nd[4].v3 = s->tone_out2nd[4].fac*s->tone_out2nd[2].v2 - v1 + famp; v1 = s->tone_out2nd[3].v2; s->tone_out2nd[5].v2 = s->tone_out2nd[6].v3; s->tone_out2nd[5].v3 = s->tone_out2nd[6].fac*s->tone_out2nd[3].v2 - v1 + famp; #endif } #endif s->current_sample += (limit - sample); if (s->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; } #ifdef OLD_DSP_ROUTINES /* We're at the end of an MF detection block. Go ahead and calculate all the energies. */ for (i=0;i<6;i++) { tone_energy[i] = goertzel_result(&s->tone_out[i]); } /* Find highest */ best1 = 0; max = tone_energy[0]; for (i=1;i<6;i++) { if (tone_energy[i] > max) { max = tone_energy[i]; best1 = i; } } /* Find 2nd highest */ if (best1) { max = tone_energy[0]; best2 = 0; } else { max = tone_energy[1]; best2 = 1; } for (i=0;i<6;i++) { if (i == best1) continue; if (tone_energy[i] > max) { max = tone_energy[i]; best2 = i; } } hit = 0; if (best1 != best2) sofarsogood=1; else sofarsogood=0; /* Check for relative energies */ for (i=0;i<6;i++) { if (i == best1) continue; if (i == best2) continue; if (tone_energy[best1] < tone_energy[i] * MF_RELATIVE_PEAK) { sofarsogood = 0; break; } if (tone_energy[best2] < tone_energy[i] * MF_RELATIVE_PEAK) { sofarsogood = 0; break; } } if (sofarsogood) { /* Check for 2nd harmonic */ if (goertzel_result(&s->tone_out2nd[best1]) * MF_2ND_HARMONIC > tone_energy[best1]) sofarsogood = 0; else if (goertzel_result(&s->tone_out2nd[best2]) * MF_2ND_HARMONIC > tone_energy[best2]) sofarsogood = 0; } if (sofarsogood) { hit = mf_hit[best1][best2]; 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; } /* Look for two consecutive clean hits */ if ((hit == s->hit3) && (s->hit3 != s->hit2)) { s->mhit = hit; s->detected_digits++; if (s->current_digits < MAX_DTMF_DIGITS - 2) { s->digits[s->current_digits++] = hit; s->digits[s->current_digits] = '\0'; } else { s->lost_digits++; } } } s->hit1 = s->hit2; s->hit2 = s->hit3; s->hit3 = hit; /* Reinitialise the detector for the next block */ for (i = 0; i < 6; i++) { goertzel_reset(&s->tone_out[i]); goertzel_reset(&s->tone_out2nd[i]); } s->energy = 0.0; s->current_sample = 0; } #else /* 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->tone_out[0]); energy[1] = goertzel_result(&s->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->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 && 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->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++; } } } 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; /* Reinitialise the detector for the next block */ for (i = 0; i < 6; i++) goertzel_reset(&s->tone_out[i]); s->current_sample = 0; }
SPAN_DECLARE(int) dtmf_rx(dtmf_rx_state_t *s, const int16_t amp[], int samples) { #if defined(SPANDSP_USE_FIXED_POINT) int32_t row_energy[4]; int32_t col_energy[4]; int16_t xamp; float famp; #else float row_energy[4]; float col_energy[4]; float xamp; float famp; #endif float v1; int i; int j; int sample; int best_row; int best_col; int limit; uint8_t hit; hit = 0; for (sample = 0; sample < samples; sample = limit) { /* The block length is optimised to meet the DTMF specs. */ if ((samples - sample) >= (DTMF_SAMPLES_PER_BLOCK - s->current_sample)) limit = sample + (DTMF_SAMPLES_PER_BLOCK - s->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++) { xamp = amp[j]; if (s->filter_dialtone) { famp = xamp; /* Sharp notches applied at 350Hz and 440Hz - the two common dialtone frequencies. These are rather high Q, to achieve the required narrowness, without using lots of sections. */ v1 = 0.98356f*famp + 1.8954426f*s->z350[0] - 0.9691396f*s->z350[1]; famp = v1 - 1.9251480f*s->z350[0] + s->z350[1]; s->z350[1] = s->z350[0]; s->z350[0] = v1; v1 = 0.98456f*famp + 1.8529543f*s->z440[0] - 0.9691396f*s->z440[1]; famp = v1 - 1.8819938f*s->z440[0] + s->z440[1]; s->z440[1] = s->z440[0]; s->z440[0] = v1; xamp = famp; } xamp = goertzel_preadjust_amp(xamp); #if defined(SPANDSP_USE_FIXED_POINT) s->energy += ((int32_t) xamp*xamp); #else s->energy += xamp*xamp; #endif goertzel_samplex(&s->row_out[0], xamp); goertzel_samplex(&s->col_out[0], xamp); goertzel_samplex(&s->row_out[1], xamp); goertzel_samplex(&s->col_out[1], xamp); goertzel_samplex(&s->row_out[2], xamp); goertzel_samplex(&s->col_out[2], xamp); goertzel_samplex(&s->row_out[3], xamp); goertzel_samplex(&s->col_out[3], xamp); } if (s->duration < INT_MAX - (limit - sample)) s->duration += (limit - sample); s->current_sample += (limit - sample); if (s->current_sample < DTMF_SAMPLES_PER_BLOCK) 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->row_out[0]); best_row = 0; col_energy[0] = goertzel_result(&s->col_out[0]); best_col = 0; for (i = 1; i < 4; i++) { row_energy[i] = goertzel_result(&s->row_out[i]); if (row_energy[i] > row_energy[best_row]) best_row = i; col_energy[i] = goertzel_result(&s->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] >= s->threshold && col_energy[best_col] >= s->threshold) { if (col_energy[best_col] < row_energy[best_row]*s->reverse_twist && col_energy[best_col]*s->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->energy) { /* Got a hit */ hit = dtmf_positions[(best_row << 2) + best_col]; } } if (span_log_test(&s->logging, SPAN_LOG_FLOW)) { /* Log information about the quality of the signal, to aid analysis of detection problems */ /* Logging at this point filters the total no-hoper frames out of the log, and leaves anything which might feasibly be a DTMF digit. The log will then contain a list of the total, row and coloumn power levels for detailed analysis of detection problems. */ span_log(&s->logging, SPAN_LOG_FLOW, "Potentially '%c' - total %.2fdB, row %.2fdB, col %.2fdB, duration %d - %s\n", dtmf_positions[(best_row << 2) + best_col], log10f(s->energy)*10.0f - DTMF_POWER_OFFSET + DBM0_MAX_POWER, log10f(row_energy[best_row]/DTMF_TO_TOTAL_ENERGY)*10.0f - DTMF_POWER_OFFSET + DBM0_MAX_POWER, log10f(col_energy[best_col]/DTMF_TO_TOTAL_ENERGY)*10.0f - DTMF_POWER_OFFSET + DBM0_MAX_POWER, s->duration, (hit) ? "hit" : "miss"); } } /* The logic in the next test should ensure the following for different successive hit patterns: -----ABB = start of digit B. ----B-BB = start of digit B ----A-BB = start of digit B BBBBBABB = still in digit B. BBBBBB-- = end of digit B BBBBBBC- = end of digit B BBBBACBB = B ends, then B starts again. BBBBBBCC = B ends, then C starts. BBBBBCDD = B ends, then D starts. This can work with: - Back to back differing digits. Back-to-back digits should not happen. The spec. says there should be a gap between digits. However, many real phones do not impose a gap, and rolling across the keypad can produce little or no gap. - It tolerates nasty phones that give a very wobbly start to a digit. - VoIP can give sample slips. The phase jumps that produces will cause the block it is in to give no detection. This logic will ride over a single missed block, and not falsely declare a second digit. If the hiccup happens in the wrong place on a minimum length digit, however we would still fail to detect that digit. Could anything be done to deal with that? Packet loss is clearly a no-go zone. Note this is only relevant to VoIP using A-law, u-law or similar. Low bit rate codecs scramble DTMF too much for it to be recognised, and often slip in units larger than a sample. */ if (hit != s->in_digit && s->last_hit != s->in_digit) { /* We have two successive indications that something has changed. */ /* To declare digit on, the hits must agree. Otherwise we declare tone off. */ hit = (hit && hit == s->last_hit) ? hit : 0; if (s->realtime_callback) { /* Avoid reporting multiple no digit conditions on flaky hits */ if (s->in_digit || hit) { i = (s->in_digit && !hit) ? -99 : lfastrintf(log10f(s->energy)*10.0f - DTMF_POWER_OFFSET + DBM0_MAX_POWER); s->realtime_callback(s->realtime_callback_data, hit, i, s->duration); s->duration = 0; } } else { if (hit) { if (s->current_digits < MAX_DTMF_DIGITS) { s->digits[s->current_digits++] = (char) hit; s->digits[s->current_digits] = '\0'; if (s->digits_callback) { s->digits_callback(s->digits_callback_data, s->digits, s->current_digits); s->current_digits = 0; } } else { s->lost_digits++; } } } s->in_digit = hit; } s->last_hit = hit; #if defined(SPANDSP_USE_FIXED_POINT) s->energy = 0; #else s->energy = 0.0f; #endif s->current_sample = 0; } if (s->current_digits && s->digits_callback) { s->digits_callback(s->digits_callback_data, s->digits, s->current_digits); s->digits[0] = '\0'; s->current_digits = 0; } return 0; }
int CGoertzelDTMFFax::fax_detect ( double amp[], int samples ) { double fax_energy; double fax_energy_2nd; double famp; float v1; int i; int j; int sample; 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 - state->current_sample)) limit = sample + (102 - state->current_sample); else limit = samples; for (j=sample;j<limit;j++) { famp = amp[j]; state->energy += famp*famp; //goertzel_sample(&(state->fax_tone), famp); v1 = state->fax_tone.v2; state->fax_tone.v2 = state->fax_tone.v3; state->fax_tone.v3 = state->fax_tone.fac*state->fax_tone.v2 - v1 + famp; //goertzel_sample(&(state->fax_tone2nd), famp); v1 = state->fax_tone.v2; state->fax_tone2nd.v2 = state->fax_tone2nd.v3; state->fax_tone2nd.v3 = state->fax_tone2nd.fac*state->fax_tone2nd.v2 - v1 + famp; } state->current_sample += (limit - sample); /* Detect the fax energy */ fax_energy = goertzel_result(&state->fax_tone); hit = 0; if ((fax_energy >= FAX_THRESHOLD) && (fax_energy >= DTMF_TO_TOTAL_ENERGY*state->energy)) { //Bug here... //Test the 2nd harmonic too /*fax_energy_2nd = goertzel_result(&state->fax_tone2nd); if (fax_energy_2nd * FAX_2ND_HARMONIC < fax_energy) { Probably need better checking than just this the energy }*/ hit = 'f'; state->fax_hits++; //Enough of it... if (state->fax_hits > 5) { break; } } goertzel_reset (&state->fax_tone); goertzel_reset (&state->fax_tone2nd); state->energy = 0.0; state->current_sample = 0; } return (hit); }