static void spandsp_dtmf_rx_realtime_callback(void *user_data, int code, int level, int duration) { switch_inband_dtmf_t *pvt = (switch_inband_dtmf_t *)user_data; char digit = (char)code; pvt->samples += duration; if (digit) { /* prevent duplicate DTMF */ if (digit != pvt->last_digit || (pvt->samples - pvt->last_digit_end) > pvt->min_dup_digit_spacing) { switch_dtmf_t dtmf = {0}; switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(pvt->session), SWITCH_LOG_DEBUG, "DTMF BEGIN DETECTED: [%c]\n", digit); pvt->last_digit = digit; dtmf.digit = digit; dtmf.duration = switch_core_default_dtmf_duration(0); dtmf.source = SWITCH_DTMF_INBAND_AUDIO; switch_channel_queue_dtmf(switch_core_session_get_channel(pvt->session), &dtmf); pvt->digit_begin = pvt->samples; } else { switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(pvt->session), SWITCH_LOG_DEBUG, "DUP DTMF DETECTED: [%c]\n", digit); pvt->last_digit_end = pvt->samples; } } else { switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(pvt->session), SWITCH_LOG_DEBUG, "DTMF END DETECTED: [%c], duration = %u ms\n", pvt->last_digit, (pvt->samples - pvt->digit_begin) / 8); pvt->last_digit_end = pvt->samples; } }
SWITCH_DECLARE(switch_status_t) switch_core_session_send_dtmf(switch_core_session_t *session, const switch_dtmf_t *dtmf) { switch_io_event_hook_send_dtmf_t *ptr; switch_status_t status = SWITCH_STATUS_FALSE; switch_dtmf_t new_dtmf; if (switch_channel_down(session->channel)) { return SWITCH_STATUS_FALSE; } switch_assert(dtmf); new_dtmf = *dtmf; if (new_dtmf.duration > switch_core_max_dtmf_duration(0)) { switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "%s EXCESSIVE DTMF DIGIT [%c] LEN [%d]\n", switch_channel_get_name(session->channel), new_dtmf.digit, new_dtmf.duration); new_dtmf.duration = switch_core_max_dtmf_duration(0); } else if (new_dtmf.duration < switch_core_min_dtmf_duration(0)) { switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "%s SHORT DTMF DIGIT [%c] LEN [%d]\n", switch_channel_get_name(session->channel), new_dtmf.digit, new_dtmf.duration); new_dtmf.duration = switch_core_min_dtmf_duration(0); } else if (!new_dtmf.duration) { new_dtmf.duration = switch_core_default_dtmf_duration(0); } for (ptr = session->event_hooks.send_dtmf; ptr; ptr = ptr->next) { if ((status = ptr->send_dtmf(session, dtmf, SWITCH_DTMF_SEND)) != SWITCH_STATUS_SUCCESS) { return SWITCH_STATUS_SUCCESS; } } if (session->endpoint_interface->io_routines->send_dtmf) { if (dtmf->digit == 'w') { switch_yield(500000); } else if (dtmf->digit == 'W') { switch_yield(1000000); } else { status = session->endpoint_interface->io_routines->send_dtmf(session, &new_dtmf); } } return status; }
SWITCH_DECLARE(switch_status_t) switch_core_session_recv_dtmf(switch_core_session_t *session, const switch_dtmf_t *dtmf) { switch_io_event_hook_recv_dtmf_t *ptr; switch_status_t status; switch_dtmf_t new_dtmf; int fed = 0; if (switch_channel_down(session->channel)) { return SWITCH_STATUS_FALSE; } switch_assert(dtmf); new_dtmf = *dtmf; if (new_dtmf.duration > switch_core_max_dtmf_duration(0)) { switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG1, "%s EXCESSIVE DTMF DIGIT [%c] LEN [%d]\n", switch_channel_get_name(session->channel), new_dtmf.digit, new_dtmf.duration); new_dtmf.duration = switch_core_max_dtmf_duration(0); } else if (new_dtmf.duration < switch_core_min_dtmf_duration(0)) { switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG1, "%s SHORT DTMF DIGIT [%c] LEN [%d]\n", switch_channel_get_name(session->channel), new_dtmf.digit, new_dtmf.duration); new_dtmf.duration = switch_core_min_dtmf_duration(0); } else if (!new_dtmf.duration) { new_dtmf.duration = switch_core_default_dtmf_duration(0); } if (!switch_test_flag(dtmf, DTMF_FLAG_SKIP_PROCESS)) { if (session->dmachine && !switch_channel_test_flag(session->channel, CF_BROADCAST)) { char str[2] = { dtmf->digit, '\0' }; switch_ivr_dmachine_feed(session->dmachine, str, NULL); fed = 1; } for (ptr = session->event_hooks.recv_dtmf; ptr; ptr = ptr->next) { if ((status = ptr->recv_dtmf(session, &new_dtmf, SWITCH_DTMF_RECV)) != SWITCH_STATUS_SUCCESS) { return status; } } } return fed ? SWITCH_STATUS_FALSE : SWITCH_STATUS_SUCCESS; }
SWITCH_DECLARE(switch_status_t) switch_core_session_send_dtmf_string(switch_core_session_t *session, const char *dtmf_string) { char *p; switch_dtmf_t dtmf = { 0, switch_core_default_dtmf_duration(0) }; int sent = 0, dur; char *string; int i, argc; char *argv[256]; int dur_total = 0; switch_assert(session != NULL); if (switch_channel_down(session->channel)) { return SWITCH_STATUS_FALSE; } if (zstr(dtmf_string)) { return SWITCH_STATUS_FALSE; } if (strlen(dtmf_string) > 99) { switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Attempt to send very large dtmf string ignored!\n"); return SWITCH_STATUS_FALSE; } string = switch_core_session_strdup(session, dtmf_string); argc = switch_separate_string(string, '+', argv, (sizeof(argv) / sizeof(argv[0]))); if (argc) { switch_channel_pre_answer(session->channel); } for (i = 0; i < argc; i++) { dtmf.duration = switch_core_default_dtmf_duration(0); dur = switch_core_default_dtmf_duration(0) / 8; if ((p = strchr(argv[i], '@'))) { *p++ = '\0'; if ((dur = atoi(p)) > 50) { dtmf.duration = dur * 8; } } if (dtmf.duration > switch_core_max_dtmf_duration(0)) { switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "%s EXCESSIVE DTMF DIGIT [%c] LEN [%d]\n", switch_channel_get_name(session->channel), dtmf.digit, dtmf.duration); dtmf.duration = switch_core_max_dtmf_duration(0); } else if (dtmf.duration < switch_core_min_dtmf_duration(0)) { switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "%s SHORT DTMF DIGIT [%c] LEN [%d]\n", switch_channel_get_name(session->channel), dtmf.digit, dtmf.duration); dtmf.duration = switch_core_min_dtmf_duration(0); } else if (!dtmf.duration) { dtmf.duration = switch_core_default_dtmf_duration(0); } for (p = argv[i]; p && *p; p++) { if (is_dtmf(*p)) { dtmf.digit = *p; if (switch_core_session_send_dtmf(session, &dtmf) == SWITCH_STATUS_SUCCESS) { switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "%s send dtmf\ndigit=%c ms=%u samples=%u\n", switch_channel_get_name(session->channel), dtmf.digit, dur, dtmf.duration); sent++; dur_total += dtmf.duration + 2000; /* account for 250ms pause */ } } } if (dur_total) { char tmp[32] = ""; switch_snprintf(tmp, sizeof(tmp), "%d", dur_total / 8); switch_channel_set_variable(session->channel, "last_dtmf_duration", tmp); } } return sent ? SWITCH_STATUS_SUCCESS : SWITCH_STATUS_FALSE; }
static switch_status_t channel_read_frame(switch_core_session_t *session, switch_frame_t **frame, switch_io_flag_t flags, int stream_id) { ftdm_wait_flag_t wflags = FTDM_READ; ftdm_status_t status; ctdm_private_t *tech_pvt; const char *name; switch_channel_t *channel; int chunk; uint32_t span_id, chan_id; ftdm_size_t len; char dtmf[128] = ""; channel = switch_core_session_get_channel(session); assert(channel != NULL); tech_pvt = switch_core_session_get_private(session); assert(tech_pvt != NULL); name = switch_channel_get_name(channel); top: wflags = FTDM_READ; chunk = ftdm_channel_get_io_interval(tech_pvt->ftdm_channel) * 2; status = ftdm_channel_wait(tech_pvt->ftdm_channel, &wflags, chunk); span_id = ftdm_channel_get_span_id(tech_pvt->ftdm_channel); chan_id = ftdm_channel_get_id(tech_pvt->ftdm_channel); if (status == FTDM_FAIL) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to read from channel %s device %d:%d!\n", name, span_id, chan_id); goto fail; } if (status == FTDM_TIMEOUT) { goto top; } if (!(wflags & FTDM_READ)) { goto top; } len = tech_pvt->read_frame.buflen; if (ftdm_channel_read(tech_pvt->ftdm_channel, tech_pvt->read_frame.data, &len) != FTDM_SUCCESS) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Failed to read from channel %s device %d:%d!\n", name, span_id, chan_id); } *frame = &tech_pvt->read_frame; tech_pvt->read_frame.datalen = (uint32_t)len; tech_pvt->read_frame.samples = tech_pvt->read_frame.datalen; tech_pvt->read_frame.codec = &tech_pvt->read_codec; if (ftdm_channel_get_codec(tech_pvt->ftdm_channel) == FTDM_CODEC_SLIN) { tech_pvt->read_frame.samples /= 2; } while (ftdm_channel_dequeue_dtmf(tech_pvt->ftdm_channel, dtmf, sizeof(dtmf))) { switch_dtmf_t _dtmf = { 0, switch_core_default_dtmf_duration(0) }; char *p; for (p = dtmf; p && *p; p++) { if (is_dtmf(*p)) { _dtmf.digit = *p; ftdm_log(FTDM_LOG_DEBUG, "Queuing DTMF [%c] in channel %s device %d:%d\n", *p, name, span_id, chan_id); switch_channel_queue_dtmf(channel, &_dtmf); } } } return SWITCH_STATUS_SUCCESS; fail: return SWITCH_STATUS_GENERR; }