diff -Naur asterisk-1.8.0/channels/chan_sip.c asterisk-1.8.0/channels/chan_sip.c --- asterisk-1.8.0/channels/chan_sip.c 2010-10-15 16:12:04.000000000 -0400 +++ asterisk-1.8.0/channels/chan_sip.c 2010-12-12 18:42:56.000000000 -0500 @@ -206,6 +206,12 @@ */ +/* + This version has been kluged by Alex Fink to allow a new dtmfmode "both" + that recognises both rfc2833 and inband, and sends inband ('cause we're + sending for human consumption). +*/ + #include "asterisk.h" ASTERISK_FILE_VERSION(__FILE__, "$Revision: 291942 $") @@ -3960,12 +3966,17 @@ } if ((ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_INBAND) || - (ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_AUTO)) { + (ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_AUTO)) { if (!p->rtp || ast_rtp_instance_dtmf_mode_set(p->rtp, AST_RTP_DTMF_MODE_INBAND)) { features |= DSP_FEATURE_DIGIT_DETECT; - } + } + } else if ((ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_BOTH)) { + if (!p->rtp || ast_rtp_instance_dtmf_mode_set(p->rtp, AST_RTP_DTMF_MODE_BOTH)) { + features |= DSP_FEATURE_DIGIT_DETECT; + } } + if (ast_test_flag(&p->flags[1], SIP_PAGE2_FAX_DETECT_CNG)) { features |= DSP_FEATURE_FAX_DETECT; } @@ -4010,7 +4021,8 @@ break; case AST_OPTION_DIGIT_DETECT: if ((ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_INBAND) || - (ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_AUTO)) { + (ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_AUTO) || + (ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_BOTH)) { char *cp = (char *) data; ast_debug(1, "%sabling digit detection on %s\n", *cp ? "En" : "Dis", chan->name); @@ -4799,7 +4811,7 @@ ast_rtp_instance_set_hold_timeout(dialog->rtp, global_rtpholdtimeout); ast_rtp_instance_set_prop(dialog->rtp, AST_RTP_PROPERTY_RTCP, 1); - ast_rtp_instance_set_prop(dialog->rtp, AST_RTP_PROPERTY_DTMF, ast_test_flag(&dialog->flags[0], SIP_DTMF) == SIP_DTMF_RFC2833); + ast_rtp_instance_set_prop(dialog->rtp, AST_RTP_PROPERTY_DTMF, (ast_test_flag(&dialog->flags[0], SIP_DTMF) == SIP_DTMF_RFC2833) || (ast_test_flag(&dialog->flags[0], SIP_DTMF) == SIP_DTMF_BOTH)); ast_rtp_instance_set_prop(dialog->rtp, AST_RTP_PROPERTY_DTMF_COMPENSATE, ast_test_flag(&dialog->flags[1], SIP_PAGE2_RFC2833_COMPENSATE)); ast_rtp_instance_set_qos(dialog->rtp, global_tos_audio, global_cos_audio, "SIP RTP"); @@ -4860,7 +4872,7 @@ } if (dialog->rtp) { /* Audio */ - ast_rtp_instance_set_prop(dialog->rtp, AST_RTP_PROPERTY_DTMF, ast_test_flag(&dialog->flags[0], SIP_DTMF) == SIP_DTMF_RFC2833); + ast_rtp_instance_set_prop(dialog->rtp, AST_RTP_PROPERTY_DTMF, (ast_test_flag(&dialog->flags[0], SIP_DTMF) == SIP_DTMF_RFC2833 || ast_test_flag(&dialog->flags[0], SIP_DTMF) == SIP_DTMF_BOTH)); ast_rtp_instance_set_prop(dialog->rtp, AST_RTP_PROPERTY_DTMF_COMPENSATE, ast_test_flag(&dialog->flags[1], SIP_PAGE2_RFC2833_COMPENSATE)); ast_rtp_instance_set_timeout(dialog->rtp, peer->rtptimeout); ast_rtp_instance_set_hold_timeout(dialog->rtp, peer->rtpholdtimeout); @@ -4945,7 +4957,8 @@ dialog->timer_b = 64 * dialog->timer_t1; if ((ast_test_flag(&dialog->flags[0], SIP_DTMF) == SIP_DTMF_RFC2833) || - (ast_test_flag(&dialog->flags[0], SIP_DTMF) == SIP_DTMF_AUTO)) + (ast_test_flag(&dialog->flags[0], SIP_DTMF) == SIP_DTMF_AUTO) || + (ast_test_flag(&dialog->flags[0], SIP_DTMF) == SIP_DTMF_BOTH)) dialog->noncodeccapability |= AST_RTP_DTMF; else dialog->noncodeccapability &= ~AST_RTP_DTMF; @@ -6177,6 +6190,13 @@ res = -1; /* Tell Asterisk to generate inband indications */ } break; + case SIP_DTMF_BOTH: // both sends inband, so people hear it + if (p->rtp && ast_rtp_instance_dtmf_mode_get(p->rtp) == AST_RTP_DTMF_MODE_BOTH) { + ast_rtp_instance_dtmf_begin(p->rtp, digit); + } else { + res = -1; /* Tell Asterisk to generate inband indications */ + } + break; case SIP_DTMF_RFC2833: if (p->rtp) ast_rtp_instance_dtmf_begin(p->rtp, digit); @@ -6213,6 +6233,13 @@ res = -1; /* Tell Asterisk to stop inband indications */ } break; + case SIP_DTMF_BOTH: // both sends inband, so people hear it + if (p->rtp && ast_rtp_instance_dtmf_mode_get(p->rtp) == AST_RTP_DTMF_MODE_BOTH) { + ast_rtp_instance_dtmf_end(p->rtp, digit); + } else { + res = -1; /* Tell Asterisk to stop inband indications */ + } + break; } sip_pvt_unlock(p); @@ -6580,6 +6607,10 @@ if (i->rtp) { ast_rtp_instance_dtmf_mode_set(i->rtp, AST_RTP_DTMF_MODE_RFC2833); } + } else if (ast_test_flag(&i->flags[0], SIP_DTMF) == SIP_DTMF_BOTH) { + if (!i->rtp || ast_rtp_instance_dtmf_mode_set(i->rtp, AST_RTP_DTMF_MODE_BOTH)) { + enable_dsp_detect(i); + } } /* Set file descriptors for audio, video, realtime text and UDPTL as needed */ @@ -6888,7 +6919,8 @@ } /* Don't forward RFC2833 if we're not supposed to */ if (f && (f->frametype == AST_FRAME_DTMF_BEGIN || f->frametype == AST_FRAME_DTMF_END) && - (ast_test_flag(&p->flags[0], SIP_DTMF) != SIP_DTMF_RFC2833)) { + (ast_test_flag(&p->flags[0], SIP_DTMF) != SIP_DTMF_RFC2833) && + (ast_test_flag(&p->flags[0], SIP_DTMF) != SIP_DTMF_BOTH)) { ast_debug(1, "Ignoring DTMF (%c) RTP frame because dtmfmode is not RFC2833\n", f->subclass.integer); return &ast_null_frame; } @@ -6917,7 +6949,8 @@ ast_debug(1, "Fax CNG detected on %s\n", ast->name); *faxdetect = 1; /* If we only needed this DSP for fax detection purposes we can just drop it now */ - if (ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_INBAND) { + if ((ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_INBAND) || + (ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_BOTH)) { ast_dsp_set_features(p->dsp, DSP_FEATURE_DIGIT_DETECT); } else { ast_dsp_free(p->dsp); @@ -7176,7 +7209,8 @@ p->capability = sip_cfg.capability; p->allowtransfer = sip_cfg.allowtransfer; if ((ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_RFC2833) || - (ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_AUTO)) + (ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_AUTO) || + (ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_BOTH)) p->noncodeccapability |= AST_RTP_DTMF; if (p->udptl) { p->t38_maxdatagram = global_t38_maxdatagram; @@ -14998,7 +15032,8 @@ p->jointcapability &= p->peercapability; p->maxcallbitrate = peer->maxcallbitrate; if ((ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_RFC2833) || - (ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_AUTO)) + (ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_AUTO) || + (ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_BOTH)) p->noncodeccapability |= AST_RTP_DTMF; else p->noncodeccapability &= ~AST_RTP_DTMF; @@ -15796,6 +15831,7 @@ { SIP_DTMF_SHORTINFO, "shortinfo" }, { SIP_DTMF_INBAND, "inband" }, { SIP_DTMF_AUTO, "auto" }, + { SIP_DTMF_BOTH, "both" }, { -1, NULL }, /* terminator */ }; @@ -21264,7 +21300,7 @@ build_contact(p); /* Build our contact header */ if (p->rtp) { - ast_rtp_instance_set_prop(p->rtp, AST_RTP_PROPERTY_DTMF, ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_RFC2833); + ast_rtp_instance_set_prop(p->rtp, AST_RTP_PROPERTY_DTMF, (ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_RFC2833) || (ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_BOTH)); ast_rtp_instance_set_prop(p->rtp, AST_RTP_PROPERTY_DTMF_COMPENSATE, ast_test_flag(&p->flags[1], SIP_PAGE2_RFC2833_COMPENSATE)); } @@ -25227,8 +25263,10 @@ ast_set_flag(&flags[0], SIP_DTMF_SHORTINFO); else if (!strcasecmp(v->value, "auto")) ast_set_flag(&flags[0], SIP_DTMF_AUTO); + else if (!strcasecmp(v->value, "both")) + ast_set_flag(&flags[0], SIP_DTMF_BOTH); else { - ast_log(LOG_WARNING, "Unknown dtmf mode '%s' on line %d, using rfc2833\n", v->value, v->lineno); + ast_log(LOG_WARNING, "Unknown dtmf mode '%s' on line %d, using NOW THERE\'S DIFFERENT TEXT HERE rfc2833\n", v->value, v->lineno); ast_set_flag(&flags[0], SIP_DTMF_RFC2833); } } else if (!strcasecmp(v->name, "nat")) { @@ -27645,13 +27683,18 @@ ast_clear_flag(&p->flags[0], SIP_DTMF); ast_set_flag(&p->flags[0], SIP_DTMF_INBAND); p->jointnoncodeccapability &= ~AST_RTP_DTMF; + } else if (!strcasecmp(mode, "both")) { + ast_clear_flag(&p->flags[0], SIP_DTMF); + ast_set_flag(&p->flags[0], SIP_DTMF_BOTH); + p->jointnoncodeccapability |= AST_RTP_DTMF; } else { ast_log(LOG_WARNING, "I don't know about this dtmf mode: %s\n", mode); } if (p->rtp) - ast_rtp_instance_set_prop(p->rtp, AST_RTP_PROPERTY_DTMF, ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_RFC2833); + ast_rtp_instance_set_prop(p->rtp, AST_RTP_PROPERTY_DTMF, (ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_RFC2833) || (ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_BOTH)); if ((ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_INBAND) || - (ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_AUTO)) { + (ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_AUTO) || + (ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_BOTH)) { enable_dsp_detect(p); } else { disable_dsp_detect(p); diff -Naur asterisk-1.8.0/channels/sip/include/sip.h asterisk-1.8.0/channels/sip/include/sip.h --- asterisk-1.8.0/channels/sip/include/sip.h 2010-09-15 15:22:15.000000000 -0400 +++ asterisk-1.8.0/channels/sip/include/sip.h 2010-12-11 17:56:18.000000000 -0500 @@ -19,6 +19,11 @@ * \brief chan_sip header file */ +/* + This version has been kluged by Alex Fink to allow a new dtmfmode "both" + that recognises both rfc2833 and inband. +*/ + #ifndef _SIP_H #define _SIP_H @@ -254,6 +259,7 @@ #define SIP_DTMF_INFO (2 << 15) /*!< DP: DTMF Support: SIP Info messages - "info" */ #define SIP_DTMF_AUTO (3 << 15) /*!< DP: DTMF Support: AUTO switch between rfc2833 and in-band DTMF */ #define SIP_DTMF_SHORTINFO (4 << 15) /*!< DP: DTMF Support: SIP Info messages - "info" - short variant */ +#define SIP_DTMF_BOTH (5 << 15) /*!< DP: DTMF Support: like "rfc2833" and "inband" together */ /* NAT settings */ #define SIP_NAT_FORCE_RPORT (1 << 18) /*!< DP: Force rport even if not present in the request */ diff -Naur asterisk-1.8.0/include/asterisk/rtp_engine.h asterisk-1.8.0/include/asterisk/rtp_engine.h --- asterisk-1.8.0/include/asterisk/rtp_engine.h 2010-10-01 22:43:45.000000000 -0400 +++ asterisk-1.8.0/include/asterisk/rtp_engine.h 2010-12-11 18:12:51.000000000 -0500 @@ -62,6 +62,10 @@ * available for the RTP engine. */ +/* + This version kluged by Alex Fink to allow DTMF both ways at once. +*/ + #ifndef _ASTERISK_RTP_ENGINE_H #define _ASTERISK_RTP_ENGINE_H @@ -119,6 +123,8 @@ AST_RTP_DTMF_MODE_RFC2833, /*! DTMF is being carried inband over the RTP stream */ AST_RTP_DTMF_MODE_INBAND, + /*! both of the above, for incoming DTMF */ + AST_RTP_DTMF_MODE_BOTH, }; /*! Result codes when RTP glue is queried for information */ diff -Naur asterisk-1.8.0/main/dsp.c asterisk-1.8.0/main/dsp.c --- asterisk-1.8.0/main/dsp.c 2010-06-07 13:34:45.000000000 -0400 +++ asterisk-1.8.0/main/dsp.c 2010-12-11 17:03:45.000000000 -0500 @@ -19,6 +19,15 @@ * at the top of the source tree. */ +/* + * This version is klugishly modified by Alex Fink, 2010, to recognise + * the US 1700 & 2200 Hz coin tone as if it were a DTMF tone. + * + * Various things below suggest that a 33ms tone with 33ms separations + * might be too short to be recognised, but it seems to work okay. + * Does DTMF_GSIZE matter? + */ + /*! \file * * \brief Convenience Signal Processing routines @@ -156,6 +165,7 @@ #define DTMF_THRESHOLD 8.0e7 #define FAX_THRESHOLD 8.0e7 +#define COIN_THRESHOLD 8.0e7 #define FAX_2ND_HARMONIC 2.0 /* 4dB */ #define DTMF_NORMAL_TWIST 6.3 /* 8dB */ #ifdef RADIO_RELAX @@ -168,6 +178,7 @@ #define DTMF_2ND_HARMONIC_ROW (relax ? 1.7 : 2.5) /* 4dB normal */ #define DTMF_2ND_HARMONIC_COL 63.1 /* 18dB */ #define DTMF_TO_TOTAL_ENERGY 42.0 +#define COIN_TO_TOTAL_ENERGY 42.0 #define BELL_MF_THRESHOLD 1.6e9 #define BELL_MF_TWIST 4.0 /* 6dB */ @@ -256,6 +267,8 @@ { goertzel_state_t row_out[4]; goertzel_state_t col_out[4]; + goertzel_state_t coin_low_out; + goertzel_state_t coin_high_out; int hits_to_begin; /* How many successive hits needed to consider begin of a digit */ int misses_to_end; /* How many successive misses needed to consider end of a digit */ int hits; /* How many successive hits we have seen already */ @@ -296,10 +309,13 @@ static const float dtmf_col[] = { 1209.0, 1336.0, 1477.0, 1633.0 }; +static const float coin_low = 1700.0; +static const float coin_high = 2200.0; static const float mf_tones[] = { 700.0, 900.0, 1100.0, 1300.0, 1500.0, 1700.0 }; static const char dtmf_positions[] = "123A" "456B" "789C" "*0#D"; +static const char coin_char = '$'; static const char bell_mf_positions[] = "1247C-358A--69*---0B----#"; static int thresholds[THRESHOLD_MAX]; @@ -488,6 +504,8 @@ goertzel_init(&s->col_out[i], dtmf_col[i], DTMF_GSIZE); s->energy = 0.0; } + goertzel_init(&s->coin_low_out, coin_low, DTMF_GSIZE); + goertzel_init(&s->coin_high_out, coin_high, DTMF_GSIZE); s->current_sample = 0; s->hits = 0; s->misses = 0; @@ -642,6 +660,8 @@ { float row_energy[4]; float col_energy[4]; + float coin_low_energy; + float coin_high_energy; float famp; int i; int j; @@ -680,6 +700,8 @@ 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]); + goertzel_sample(&s->td.dtmf.coin_low_out, amp[j]); + goertzel_sample(&s->td.dtmf.coin_high_out, amp[j]); } s->td.dtmf.current_sample += (limit - sample); if (s->td.dtmf.current_sample < DTMF_GSIZE) { @@ -701,6 +723,22 @@ } } hit = 0; + /* Check for the coin: test if it's past threshold and + it beats all the DTMF frequency peaks. (No twist test.) */ + coin_low_energy = goertzel_result (&s->td.dtmf.coin_low_out); + coin_high_energy = goertzel_result (&s->td.dtmf.coin_high_out); + if (coin_low_energy >= COIN_THRESHOLD && + coin_high_energy >= COIN_THRESHOLD && + coin_low_energy > row_energy[best_row] && + coin_high_energy > row_energy[best_row] && + coin_low_energy > col_energy[best_col] && + coin_high_energy > col_energy[best_col]) { + /* fraction of total energy test */ + if ((coin_low_energy + coin_high_energy) > COIN_TO_TOTAL_ENERGY * s->td.dtmf.energy) { + /* Got a hit */ + hit = coin_char; + } + } /* Basic signal level test and the twist test */ if (row_energy[best_row] >= DTMF_THRESHOLD && col_energy[best_col] >= DTMF_THRESHOLD && @@ -774,6 +812,8 @@ goertzel_reset(&s->td.dtmf.row_out[i]); goertzel_reset(&s->td.dtmf.col_out[i]); } + goertzel_reset(&s->td.dtmf.coin_low_out); + goertzel_reset(&s->td.dtmf.coin_high_out); s->td.dtmf.energy = 0.0; s->td.dtmf.current_sample = 0; }