SPAN_DECLARE(int) ademco_contactid_sender_put(ademco_contactid_sender_state_t *s, const ademco_contactid_report_t *report) { if (s->busy) return -1; if ((s->tx_digits_len = encode_msg(s->tx_digits, report)) < 0) return -1; s->busy = true; return dtmf_tx_put(&s->dtmf, s->tx_digits, s->tx_digits_len); }
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; }
int main(int argc, char *argv[]) { dtmf_tx_state_t *gen; int16_t amp[16384]; int len; SNDFILE *outhandle; int add_digits; if ((outhandle = sf_open_telephony_write(OUTPUT_FILE_NAME, 1)) == NULL) { fprintf(stderr, " Cannot open audio file '%s'\n", OUTPUT_FILE_NAME); exit(2); } gen = dtmf_tx_init(NULL, NULL, NULL); len = dtmf_tx(gen, amp, 16384); printf("Generated %d samples\n", len); sf_writef_short(outhandle, amp, len); if (dtmf_tx_put(gen, "123", -1)) { printf("Ooops\n"); exit(2); } len = dtmf_tx(gen, amp, 16384); printf("Generated %d samples\n", len); sf_writef_short(outhandle, amp, len); if (dtmf_tx_put(gen, "456", -1)) { printf("Ooops\n"); exit(2); } len = dtmf_tx(gen, amp, 160); printf("Generated %d samples\n", len); sf_writef_short(outhandle, amp, len); if (dtmf_tx_put(gen, "789", -1)) { printf("Ooops\n"); exit(2); } len = dtmf_tx(gen, amp, 160); printf("Generated %d samples\n", len); sf_writef_short(outhandle, amp, len); if (dtmf_tx_put(gen, "*#", -1)) { printf("Ooops\n"); exit(2); } len = dtmf_tx(gen, amp, 160); printf("Generated %d samples\n", len); sf_writef_short(outhandle, amp, len); add_digits = 1; do { len = dtmf_tx(gen, amp, 160); printf("Generated %d samples\n", len); if (len > 0) sf_writef_short(outhandle, amp, len); if (add_digits) { if (dtmf_tx_put(gen, "1234567890", -1)) { printf("Digit buffer full\n"); add_digits = 0; } } } while (len > 0); dtmf_tx_init(gen, NULL, NULL); len = dtmf_tx(gen, amp, 16384); printf("Generated %d samples\n", len); sf_writef_short(outhandle, amp, len); if (dtmf_tx_put(gen, "123", -1)) { printf("Ooops\n"); exit(2); } len = dtmf_tx(gen, amp, 16384); printf("Generated %d samples\n", len); sf_writef_short(outhandle, amp, len); if (dtmf_tx_put(gen, "456", -1)) { printf("Ooops\n"); exit(2); } len = dtmf_tx(gen, amp, 160); printf("Generated %d samples\n", len); sf_writef_short(outhandle, amp, len); if (dtmf_tx_put(gen, "789", -1)) { printf("Ooops\n"); exit(2); } len = dtmf_tx(gen, amp, 160); printf("Generated %d samples\n", len); sf_writef_short(outhandle, amp, len); if (dtmf_tx_put(gen, "0*#", -1)) { printf("Ooops\n"); exit(2); } len = dtmf_tx(gen, amp, 160); printf("Generated %d samples\n", len); sf_writef_short(outhandle, amp, len); if (dtmf_tx_put(gen, "ABCD", -1)) { printf("Ooops\n"); exit(2); } len = dtmf_tx(gen, amp, 160); printf("Generated %d samples\n", len); sf_writef_short(outhandle, amp, len); /* Try modifying the level and length of the digits */ printf("Try different levels and timing\n"); dtmf_tx_set_level(gen, -20, 5); dtmf_tx_set_timing(gen, 100, 200); if (dtmf_tx_put(gen, "123", -1)) { printf("Ooops\n"); exit(2); } do { len = dtmf_tx(gen, amp, 160); printf("Generated %d samples\n", len); if (len > 0) sf_writef_short(outhandle, amp, len); } while (len > 0); printf("Restore normal levels and timing\n"); dtmf_tx_set_level(gen, -10, 0); dtmf_tx_set_timing(gen, 50, 55); if (dtmf_tx_put(gen, "A", -1)) { printf("Ooops\n"); exit(2); } add_digits = TRUE; do { len = dtmf_tx(gen, amp, 160); printf("Generated %d samples\n", len); if (len > 0) sf_writef_short(outhandle, amp, len); if (add_digits) { if (dtmf_tx_put(gen, "1234567890", -1)) { printf("Digit buffer full\n"); add_digits = FALSE; } } } while (len > 0); if (sf_close_telephony(outhandle)) { fprintf(stderr, " Cannot close audio file '%s'\n", OUTPUT_FILE_NAME); exit(2); } return 0; }