diff -urpN asterisk-1.4.22.2/apps/app_dial.c asterisk-1.4.22.2.tcl/apps/app_dial.c --- asterisk-1.4.22.2/apps/app_dial.c 2008-08-05 20:13:20.000000000 -0300 +++ asterisk-1.4.22.2.tcl/apps/app_dial.c 2009-03-28 16:15:50.000000000 -0300 @@ -32,7 +32,7 @@ #include "asterisk.h" -ASTERISK_FILE_VERSION(__FILE__, "$Revision: 135799 $") +ASTERISK_FILE_VERSION(__FILE__, "$Revision: 166093 $") #include #include @@ -126,15 +126,19 @@ static char *descrip = " Optionally, an extension, or extension and context may be specified. \n" " Otherwise, the current extension is used. You cannot use any additional\n" " action post answer options in conjunction with this option.\n" -" h - Allow the called party to hang up by sending the '*' DTMF digit.\n" -" H - Allow the calling party to hang up by hitting the '*' DTMF digit.\n" +" h - Allow the called party to hang up by sending the '*' DTMF digit, or\n" +" whatever sequence was defined in the featuremap section for\n" +" 'disconnect' in features.conf\n" +" H - Allow the calling party to hang up by hitting the '*' DTMF digit, or\n" +" whatever sequence was defined in the featuremap section for\n" +" 'disconnect' in features.conf\n" " i - Asterisk will ignore any forwarding requests it may receive on this\n" " dial attempt.\n" " j - Jump to priority n+101 if all of the requested channels were busy.\n" " k - Allow the called party to enable parking of the call by sending\n" -" the DTMF sequence defined for call parking in features.conf.\n" +" the DTMF sequence defined for call parking in the featuremap section of features.conf.\n" " K - Allow the calling party to enable parking of the call by sending\n" -" the DTMF sequence defined for call parking in features.conf.\n" +" the DTMF sequence defined for call parking in the featuremap section of features.conf.\n" " L(x[:y][:z]) - Limit the call to 'x' ms. Play a warning when 'y' ms are\n" " left. Repeat the warning every 'z' ms. The following special\n" " variables can be used with this option:\n" @@ -195,13 +199,17 @@ static char *descrip = " S(x) - Hang up the call after 'x' seconds *after* the called party has\n" " answered the call.\n" " t - Allow the called party to transfer the calling party by sending the\n" -" DTMF sequence defined in features.conf.\n" +" DTMF sequence defined in the blindxfer setting in the featuremap section\n" +" of features.conf.\n" " T - Allow the calling party to transfer the called party by sending the\n" -" DTMF sequence defined in features.conf.\n" +" DTMF sequence defined in the blindxfer setting in the featuremap section\n" +" of features.conf.\n" " w - Allow the called party to enable recording of the call by sending\n" -" the DTMF sequence defined for one-touch recording in features.conf.\n" +" the DTMF sequence defined in the automon setting in the featuremap section\n" +" of features.conf.\n" " W - Allow the calling party to enable recording of the call by sending\n" -" the DTMF sequence defined for one-touch recording in features.conf.\n"; +" the DTMF sequence defined in the automon setting in the featuremap section\n" +" of features.conf.\n"; /* RetryDial App by Anthony Minessale II Jan/2005 */ static char *rapp = "RetryDial"; @@ -393,10 +401,13 @@ static void senddialevent(struct ast_cha "CallerID: %s\r\n" "CallerIDName: %s\r\n" "SrcUniqueID: %s\r\n" - "DestUniqueID: %s\r\n", + "DestUniqueID: %s\r\n" + "DialData: %s\r\n", src->name, dst->name, S_OR(src->cid.cid_num, ""), S_OR(src->cid.cid_name, ""), src->uniqueid, - dst->uniqueid); + dst->uniqueid, + src->data + ); } static struct ast_channel *wait_for_answer(struct ast_channel *in, struct dial_localuser *outgoing, int *to, struct ast_flags *peerflags, int *sentringing, char *status, size_t statussize, int busystart, int nochanstart, int congestionstart, int priority_jump, int *result) @@ -824,6 +835,35 @@ static void set_dial_features(struct ast ast_set_flag(&(features->features_caller), AST_FEATURE_PARKCALL); } +static void end_bridge_callback (void *data) +{ + char buf[80]; + time_t end; + struct ast_channel *chan = data; + + if (!chan->cdr) { + return; + } + + time(&end); + + ast_channel_lock(chan); + if (chan->cdr->answer.tv_sec) { + snprintf(buf, sizeof(buf), "%ld", end - chan->cdr->answer.tv_sec); + pbx_builtin_setvar_helper(chan, "ANSWEREDTIME", buf); + } + + if (chan->cdr->start.tv_sec) { + snprintf(buf, sizeof(buf), "%ld", end - chan->cdr->start.tv_sec); + pbx_builtin_setvar_helper(chan, "DIALEDTIME", buf); + } + ast_channel_unlock(chan); +} + +static void end_bridge_callback_data_fixup(struct ast_bridge_config *bconfig, struct ast_channel *originator, struct ast_channel *terminator) { + bconfig->end_bridge_callback_data = originator; +} + static int dial_exec_full(struct ast_channel *chan, void *data, struct ast_flags *peerflags, int *continue_exec) { int res = -1; @@ -839,7 +879,7 @@ static int dial_exec_full(struct ast_cha char numsubst[256]; char cidname[AST_MAX_EXTENSION] = ""; int privdb_val = 0; - unsigned int calldurationlimit = 0; + int calldurationlimit = -1; long timelimit = 0; long play_warning = 0; long warning_freq = 0; @@ -877,6 +917,13 @@ static int dial_exec_full(struct ast_cha return -1; } + /* Reset all DIAL variables back to blank, to prevent confusion (in case we don't reset all of them). */ + pbx_builtin_setvar_helper(chan, "DIALSTATUS", ""); + pbx_builtin_setvar_helper(chan, "DIALEDPEERNUMBER", ""); + pbx_builtin_setvar_helper(chan, "DIALEDPEERNAME", ""); + pbx_builtin_setvar_helper(chan, "ANSWEREDTIME", ""); + pbx_builtin_setvar_helper(chan, "DIALEDTIME", ""); + u = ast_module_user_add(chan); parse = ast_strdupa(data); @@ -973,7 +1020,7 @@ static int dial_exec_full(struct ast_cha start_sound = S_OR(var, NULL); /* XXX not much of a point in doing this! */ /* undo effect of S(x) in case they are both used */ - calldurationlimit = 0; + calldurationlimit = -1; /* more efficient to do it like S(x) does since no advanced opts */ if (!play_warning && !start_sound && !end_sound && timelimit) { calldurationlimit = timelimit / 1000; @@ -1339,6 +1386,9 @@ static int dial_exec_full(struct ast_cha ast_log(LOG_DEBUG, "ast call on peer returned %d\n", res); if (option_verbose > 2) ast_verbose(VERBOSE_PREFIX_3 "Couldn't call %s\n", numsubst); + if (tmp->chan->hangupcause) { + chan->hangupcause = tmp->chan->hangupcause; + } ast_hangup(tmp->chan); tmp->chan = NULL; free(tmp); @@ -1419,9 +1469,9 @@ static int dial_exec_full(struct ast_cha /* almost done, although the 'else' block is 400 lines */ } else { const char *number; - time_t end_time, answer_time = time(NULL); strcpy(status, "ANSWER"); + pbx_builtin_setvar_helper(chan, "DIALSTATUS", status); /* Ah ha! Someone answered within the desired timeframe. Of course after this we will always return with -1 so that it is hung up properly after the conversation. */ @@ -1704,6 +1754,9 @@ static int dial_exec_full(struct ast_cha if (!res) { if (calldurationlimit > 0) { peer->whentohangup = time(NULL) + calldurationlimit; + } else if (calldurationlimit != -1 && timelimit > 0) { + /* Not enough granularity to make it less, but we can't use the special value 0 */ + peer->whentohangup = time(NULL) + 1; } if (!ast_strlen_zero(dtmfcalled)) { if (option_verbose > 2) @@ -1716,7 +1769,7 @@ static int dial_exec_full(struct ast_cha res = ast_dtmf_stream(chan,peer,dtmfcalling,250); } } - + if (!res) { struct ast_bridge_config config; @@ -1741,6 +1794,8 @@ static int dial_exec_full(struct ast_cha ast_set_flag(&(config.features_callee), AST_FEATURE_PARKCALL); if (ast_test_flag(peerflags, OPT_CALLER_PARK)) ast_set_flag(&(config.features_caller), AST_FEATURE_PARKCALL); + if (ast_test_flag(peerflags, OPT_GO_ON)) + ast_set_flag(&(config.features_caller), AST_FEATURE_NO_H_EXTEN); config.timelimit = timelimit; config.play_warning = play_warning; @@ -1748,6 +1803,9 @@ static int dial_exec_full(struct ast_cha config.warning_sound = warning_sound; config.end_sound = end_sound; config.start_sound = start_sound; + config.end_bridge_callback = end_bridge_callback; + config.end_bridge_callback_data = chan; + config.end_bridge_callback_data_fixup = end_bridge_callback_data_fixup; if (moh) { moh = 0; ast_moh_stop(chan); @@ -1778,27 +1836,13 @@ static int dial_exec_full(struct ast_cha AST_OPTION_OPRMODE,&oprmode,sizeof(struct oprmode),0); } res = ast_bridge_call(chan,peer,&config); - time(&end_time); - { - char toast[80]; - snprintf(toast, sizeof(toast), "%ld", (long)(end_time - answer_time)); - pbx_builtin_setvar_helper(chan, "ANSWEREDTIME", toast); - } } else { - time(&end_time); res = -1; } - { - char toast[80]; - snprintf(toast, sizeof(toast), "%ld", (long)(end_time - start_time)); - pbx_builtin_setvar_helper(chan, "DIALEDTIME", toast); - } - - if (res != AST_PBX_NO_HANGUP_PEER) { - if (!chan->_softhangup) - chan->hangupcause = peer->hangupcause; - ast_hangup(peer); - } + + if (!chan->_softhangup) + chan->hangupcause = peer->hangupcause; + ast_hangup(peer); } out: if (moh) { @@ -1814,12 +1858,11 @@ out: if (option_debug) ast_log(LOG_DEBUG, "Exiting with DIALSTATUS=%s.\n", status); - if ((ast_test_flag(peerflags, OPT_GO_ON)) && (!chan->_softhangup) && (res != AST_PBX_KEEPALIVE)) { + if (ast_test_flag(peerflags, OPT_GO_ON) && !chan->_softhangup) { if (calldurationlimit) chan->whentohangup = 0; res = 0; } - done: ast_module_user_remove(u); return res; diff -urpN asterisk-1.4.22.2/channels/chan_sip.c asterisk-1.4.22.2.tcl/channels/chan_sip.c --- asterisk-1.4.22.2/channels/chan_sip.c 2008-09-08 18:10:10.000000000 -0300 +++ asterisk-1.4.22.2.tcl/channels/chan_sip.c 2009-03-29 19:56:21.000000000 -0300 @@ -150,6 +150,19 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revisi #include "asterisk/threadstorage.h" #include "asterisk/translate.h" +#include +#include + +// ASTLDFLAGS="-lglib-2.0" make install +static GHashTable *negRtPeerNameCache; // combined users + friends +static GHashTable *negRtPeerHostCache; +static long nc_last_exp_check; +static const long nc_max_age = 60; +static const long nc_exp_check_interval = 30; +AST_MUTEX_DEFINE_STATIC(nc_lock); +AST_MUTEX_DEFINE_STATIC(nc_lock_sin); + + #ifndef FALSE #define FALSE 0 #endif @@ -2505,6 +2518,136 @@ static void update_peer(struct sip_peer } +/* rjm - neg caching on realtime_peer lookups by name */ + +static void nc_print(gpointer key, gpointer val, gpointer data) +{ + int fd = (int)data; + ast_cli(fd, " %s\n", (gchar *)key); +} + +static int sip_show_nc(int fd, int argc, char *argv[]) { + + ast_cli(fd, "keys in negRtPeerNameCache:\n"); + ast_mutex_lock(&nc_lock); + g_hash_table_foreach(negRtPeerNameCache, nc_print, (gpointer)fd); + ast_mutex_unlock(&nc_lock); + + ast_cli(fd, "keys in negRtPeerHostCache:\n"); + ast_mutex_lock(&nc_lock_sin); + g_hash_table_foreach(negRtPeerHostCache, nc_print, (gpointer)fd); + ast_mutex_unlock(&nc_lock_sin); + + return RESULT_SUCCESS; +} + +static gboolean nc_remove_expired(gpointer key, gpointer val, gpointer data) { + + long ts = (long)val; + long now = (long)data; + + if ((now - ts) > nc_max_age) { + //printf(".. expired [%s] ..\n", (gchar *)key); + return TRUE; + } + return FALSE; +} + + +static int nc_disabled = 0; + +static void nc_add_sin(struct sockaddr_in *sin) +{ + if (nc_disabled) return; + + const char *iabuf = ast_inet_ntoa(sin->sin_addr); + int portnum = ntohs(sin->sin_port); + + char buf[32]; + sprintf(buf, "%s:%d", iabuf, portnum); + + long now = time(NULL); + ast_mutex_lock(&nc_lock_sin); + g_hash_table_insert(negRtPeerHostCache, g_strdup(buf), (gpointer)now); + ast_mutex_unlock(&nc_lock_sin); + +} + +static int nc_neg_cached_sin(struct sockaddr_in *sin) +{ + if (nc_disabled) return 0; + + const char *iabuf = ast_inet_ntoa(sin->sin_addr); + int portnum = ntohs(sin->sin_port); + + char buf[32]; + sprintf(buf, "%s:%d", iabuf, portnum); + + long now = time(NULL); + + gpointer e; + ast_mutex_lock(&nc_lock_sin); + e = g_hash_table_lookup(negRtPeerHostCache, buf); + ast_mutex_unlock(&nc_lock_sin); + + if (e != NULL) { + long ts = (long)e; + if ((now - ts) <= nc_max_age) return 1; + } + + if ((now - nc_last_exp_check) > nc_exp_check_interval) { // time to check for expired entries + ast_mutex_lock(&nc_lock_sin); + g_hash_table_foreach_remove(negRtPeerHostCache, nc_remove_expired, (gpointer)now); + ast_mutex_unlock(&nc_lock_sin); + } + + return 0; // peername is not in cache +} + + + + +static void nc_add(const char* peername) +{ + if (nc_disabled) return; + + long now = time(NULL); + + ast_mutex_lock(&nc_lock); + g_hash_table_insert(negRtPeerNameCache, g_strdup(peername), (gpointer)now); + ast_mutex_unlock(&nc_lock); +} + +static int nc_neg_cached(const char* peername) +{ + + if (nc_disabled) return 0; + + long now = time(NULL); + + gpointer e; + ast_mutex_lock(&nc_lock); + e = g_hash_table_lookup(negRtPeerNameCache, peername); + ast_mutex_unlock(&nc_lock); + + if (e != NULL) { + long ts = (long)e; + if ((now - ts) <= nc_max_age) return 1; + } + + + if ((now - nc_last_exp_check) > nc_exp_check_interval) { // time to check for expired entries + ast_mutex_lock(&nc_lock); + g_hash_table_foreach_remove(negRtPeerNameCache, nc_remove_expired, (gpointer)now); + ast_mutex_unlock(&nc_lock); + } + + return 0; // peername is not in cache +} + + + + /*! \brief realtime_peer: Get peer from realtime storage * Checks the "sippeers" realtime family from extconfig.conf * \todo Consider adding check of port address when matching here to follow the same @@ -2525,72 +2668,84 @@ static struct sip_peer *realtime_peer(co /* First check on peer name */ if (newpeername) { - var = ast_load_realtime("sippeers", "name", newpeername, "host", "dynamic", NULL); - if (!var && sin) - var = ast_load_realtime("sippeers", "name", newpeername, "host", ast_inet_ntoa(sin->sin_addr), NULL); - if (!var) { - var = ast_load_realtime("sippeers", "name", newpeername, NULL); - /*!\note - * If this one loaded something, then we need to ensure that the host - * field matched. The only reason why we can't have this as a criteria - * is because we only have the IP address and the host field might be - * set as a name (and the reverse PTR might not match). - */ - if (var && sin) { - for (tmp = var; tmp; tmp = tmp->next) { - if (!strcasecmp(tmp->name, "host")) { - struct hostent *hp; - struct ast_hostent ahp; - if (!(hp = ast_gethostbyname(tmp->value, &ahp)) || (memcmp(&hp->h_addr, &sin->sin_addr, sizeof(hp->h_addr)))) { - /* No match */ - ast_variables_destroy(var); - var = NULL; - } - break; - } - } - } - } + + if (! nc_neg_cached(newpeername)) { // checks negative cache on name + + var = ast_load_realtime("sippeers", "name", newpeername, "host", "dynamic", NULL); + if (!var && sin) + var = ast_load_realtime("sippeers", "name", newpeername, "host", ast_inet_ntoa(sin->sin_addr), NULL); + if (!var) { + var = ast_load_realtime("sippeers", "name", newpeername, NULL); + /*!\note + * If this one loaded something, then we need to ensure that the host + * field matched. The only reason why we can't have this as a criteria + * is because we only have the IP address and the host field might be + * set as a name (and the reverse PTR might not match). + */ + if (var && sin) { + for (tmp = var; tmp; tmp = tmp->next) { + if (!strcasecmp(tmp->name, "host")) { + struct hostent *hp; + struct ast_hostent ahp; + if (!(hp = ast_gethostbyname(tmp->value, &ahp)) || (memcmp(&hp->h_addr, &sin->sin_addr, sizeof(hp->h_addr)))) { + /* No match */ + ast_variables_destroy(var); + var = NULL; + } + break; + } + } + } + } + } + + if (! var) nc_add(newpeername); // next time skip query } if (!var && sin) { /* Then check on IP address */ - iabuf = ast_inet_ntoa(sin->sin_addr); - portnum = ntohs(sin->sin_port); - sprintf(portstring, "%d", portnum); - var = ast_load_realtime("sippeers", "host", iabuf, "port", portstring, NULL); /* First check for fixed IP hosts */ - if (!var) - var = ast_load_realtime("sippeers", "ipaddr", iabuf, "port", portstring, NULL); /* Then check for registered hosts */ - if (!var) { - peerlist = ast_load_realtime_multientry("sippeers", "host", iabuf, NULL); /*No exact match, see if port is insecure, try host match first*/ - if(peerlist){ - while((cat = ast_category_browse(peerlist, cat))) - { - insecure = ast_variable_retrieve(peerlist, cat, "insecure"); - set_insecure_flags(&flags, insecure, -1); - if(ast_test_flag(&flags, SIP_INSECURE_PORT)) { - var = ast_category_root(peerlist, cat); - break; - } - } - } - if(!var) { - ast_config_destroy(peerlist); - peerlist = NULL; /*for safety's sake*/ - cat = NULL; - peerlist = ast_load_realtime_multientry("sippeers", "ipaddr", iabuf, NULL); /*No exact match, see if port is insecure, now try ip address match*/ - if(peerlist) { - while((cat = ast_category_browse(peerlist, cat))) - { - insecure = ast_variable_retrieve(peerlist, cat, "insecure"); - set_insecure_flags(&flags, insecure, -1); - if(ast_test_flag(&flags, SIP_INSECURE_PORT)) { - var = ast_category_root(peerlist, cat); - break; - } - } - } - } - } + + if (! nc_neg_cached_sin(sin)) { // checks negative cache on sin + + iabuf = ast_inet_ntoa(sin->sin_addr); + portnum = ntohs(sin->sin_port); + sprintf(portstring, "%d", portnum); + var = ast_load_realtime("sippeers", "host", iabuf, "port", portstring, NULL); /* First check for fixed IP hosts */ + if (!var) + var = ast_load_realtime("sippeers", "ipaddr", iabuf, "port", portstring, NULL); /* Then check for registered hosts */ + if (!var) { + peerlist = ast_load_realtime_multientry("sippeers", "host", iabuf, NULL); /*No exact match, see if port is insecure, try host match first*/ + if(peerlist){ + while((cat = ast_category_browse(peerlist, cat))) + { + insecure = ast_variable_retrieve(peerlist, cat, "insecure"); + set_insecure_flags(&flags, insecure, -1); + if(ast_test_flag(&flags, SIP_INSECURE_PORT)) { + var = ast_category_root(peerlist, cat); + break; + } + } + } + if(!var) { + ast_config_destroy(peerlist); + peerlist = NULL; /*for safety's sake*/ + cat = NULL; + peerlist = ast_load_realtime_multientry("sippeers", "ipaddr", iabuf, NULL); /*No exact match, see if port is insecure, now try ip address match*/ + if(peerlist) { + while((cat = ast_category_browse(peerlist, cat))) + { + insecure = ast_variable_retrieve(peerlist, cat, "insecure"); + set_insecure_flags(&flags, insecure, -1); + if(ast_test_flag(&flags, SIP_INSECURE_PORT)) { + var = ast_category_root(peerlist, cat); + break; + } + } + } + } + } + } + + if (! var) nc_add_sin(sin); // next time skip query } if (!var) { @@ -2707,8 +2862,12 @@ static struct sip_user *realtime_user(co struct ast_variable *tmp; struct sip_user *user = NULL; + if (nc_neg_cached(username)) return NULL; // checks negative cache on name + var = ast_load_realtime("sipusers", "name", username, NULL); + if (! var) nc_add(username); // next time skip query + if (!var) return NULL; @@ -12173,6 +12332,26 @@ static int sip_reinvite_retry(const void } +/* any message that causes ast_queue_control() to be called I want to send an AMI event */ +static void sr_xami(struct sip_pvt *p, const char * control, int respCode) +{ + struct ast_channel *c = p->owner; + if (c) { + manager_event(EVENT_FLAG_CALL, "SipRespToInvite", + "Channel: %s\r\n" + "Uniqueid: %s\r\n" + "SipCallId: %s\r\n" + "RespCode: %d\r\n" + "NextState: %s\r\n", + c->name, + c->uniqueid, + p->callid, + respCode, + control + ); + } +} + /*! \brief Handle SIP response to INVITE dialogue */ static void handle_response_invite(struct sip_pvt *p, int resp, char *rest, struct sip_request *req, int seqno) { @@ -12181,7 +12360,7 @@ static void handle_response_invite(struc int xmitres = 0; int reinvite = (p->owner && p->owner->_state == AST_STATE_UP); struct ast_channel *bridgepeer = NULL; - + if (option_debug > 3) { if (reinvite) ast_log(LOG_DEBUG, "SIP response %d to RE-invite on %s call %s\n", resp, outgoing ? "outgoing" : "incoming", p->callid); @@ -12213,6 +12392,7 @@ static void handle_response_invite(struc if (resp >= 300 && (p->invitestate == INV_CALLING || p->invitestate == INV_PROCEEDING || p->invitestate == INV_EARLY_MEDIA )) p->invitestate = INV_COMPLETED; + if (resp >= 300) sr_xami(p, "AST_CONTROL_CONGESTION", resp); switch (resp) { case 100: /* Trying */ @@ -12228,6 +12408,7 @@ static void handle_response_invite(struc ast_log(LOG_WARNING, "Unable to cancel SIP destruction. Expect bad things.\n"); if (!ast_test_flag(req, SIP_PKT_IGNORE) && p->owner) { ast_queue_control(p->owner, AST_CONTROL_RINGING); + sr_xami(p, "AST_CONTROL_RINGING", resp); if (p->owner->_state != AST_STATE_UP) { ast_setstate(p->owner, AST_STATE_RINGING); } @@ -12239,6 +12420,7 @@ static void handle_response_invite(struc if (!ast_test_flag(req, SIP_PKT_IGNORE) && p->owner) { /* Queue a progress frame only if we have SDP in 180 or 182 */ ast_queue_control(p->owner, AST_CONTROL_PROGRESS); + sr_xami(p, "AST_CONTROL_PROGRESS", resp); } } check_pendings(p); @@ -12255,6 +12437,7 @@ static void handle_response_invite(struc if (!ast_test_flag(req, SIP_PKT_IGNORE) && p->owner) { /* Queue a progress frame */ ast_queue_control(p->owner, AST_CONTROL_PROGRESS); + sr_xami(p, "AST_CONTROL_PROGRESS", resp); } } check_pendings(p); @@ -12343,6 +12526,7 @@ static void handle_response_invite(struc if (!ast_test_flag(req, SIP_PKT_IGNORE) && p->owner) { if (!reinvite) { ast_queue_control(p->owner, AST_CONTROL_ANSWER); + sr_xami(p, "AST_CONTROL_ANSWER", resp); } else { /* RE-invite */ ast_queue_frame(p->owner, &ast_null_frame); } @@ -12498,6 +12682,7 @@ static void handle_response_invite(struc } if (xmitres == XMIT_ERROR) ast_log(LOG_WARNING, "Could not transmit message in dialog %s\n", p->callid); + } /* \brief Handle SIP response in REFER transaction @@ -13004,7 +13189,10 @@ static void handle_response(struct sip_p case 600: /* Busy everywhere */ case 603: /* Decline */ if (p->owner) + { ast_queue_control(p->owner, AST_CONTROL_BUSY); + sr_xami(p, "AST_CONTROL_BUSY", resp); + } break; case 482: /* \note SIP is incapable of performing a hairpin call, which @@ -13031,7 +13219,10 @@ static void handle_response(struct sip_p case 503: /* Service Unavailable */ case 504: /* Server Timeout */ if (owner) + { ast_queue_control(p->owner, AST_CONTROL_CONGESTION); + if (sipmethod == SIP_INVITE) sr_xami(p, "AST_CONTROL_CONGESTION", resp); + } break; default: /* Send hangup */ @@ -14407,6 +14598,26 @@ static int handle_request_invite(struct /* Pre-lock the call */ ast_channel_lock(c); } + + if (c) { + manager_event(EVENT_FLAG_CALL, "NewSipChannelInbound", + "Channel: %s\r\n" + "CallerIDNum: %s\r\n" + "CallerIDName: %s\r\n" + "Uniqueid: %s\r\n" + "PeerIP: %s %d\r\n" + "SipUserAgent: %s\r\n" + "SipCallId: %s\r\n" + "SipURI: %s\r\n", + c->name, + S_OR(p->cid_num, ""), S_OR(p->cid_name, ""), + c->uniqueid, + ast_inet_ntoa(p->recv.sin_addr), ntohs(p->recv.sin_port), + p->useragent, + p->callid, + p->uri + ); + } } } else { if (option_debug > 1 && sipdebug) { @@ -15242,6 +15453,18 @@ static int handle_request_bye(struct sip ast_clear_flag(&p->flags[1], SIP_PAGE2_DIALOG_ESTABLISHED); transmit_response(p, "200 OK", req); + c = p->owner; + if (c) { + manager_event(EVENT_FLAG_CALL, "SipChannelRecvdBye", + "Channel: %s\r\n" + "Uniqueid: %s\r\n" + "SipCallId: %s\r\n", + c->name, + c->uniqueid, + p->callid + ); + } + return 1; } @@ -16512,6 +16735,20 @@ static struct ast_channel *sip_request_c sip_destroy(p); ast_update_use_count(); restart_monitor(); + + manager_event(EVENT_FLAG_CALL, "NewSipChannelOutbound", + "Channel: %s\r\n" + "Uniqueid: %s\r\n" + "PeerIP: %s %d\r\n" + "SipCallId: %s\r\n" + "Dial: %s\r\n", + tmpc->name, + tmpc->uniqueid, + ast_inet_ntoa(p->sa.sin_addr), ntohs(p->sa.sin_port), + p->callid, + dest + ); + return tmpc; } @@ -18354,7 +18591,9 @@ static void sip_send_all_registers(void) if (!regobjs) return; regspacing = default_expiry * 1000/regobjs; - if (regspacing > 100) + if (regspacing > 600) + regspacing = 600; + if (regspacing < 100) regspacing = 100; ms = regspacing; ASTOBJ_CONTAINER_TRAVERSE(®l, 1, do { @@ -18520,6 +18759,10 @@ static struct ast_cli_entry cli_sip[] = { { "sip", "reload", NULL }, sip_reload, "Reload SIP configuration", sip_reload_usage }, + + { { "sip", "show", "nc", NULL }, + sip_show_nc, "show negRtPeer cache contents", + NULL }, }; /*! \brief PBX load module - initialization */ @@ -18578,6 +18821,9 @@ static int load_module(void) ast_manager_register2("SIPshowpeer", EVENT_FLAG_SYSTEM, manager_sip_show_peer, "Show SIP peer (text format)", mandescr_show_peer); + negRtPeerNameCache = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); + negRtPeerHostCache = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); + sip_poke_all_peers(); sip_send_all_registers(); @@ -18668,6 +18914,10 @@ restartdestroy: close(sipsock); sched_context_destroy(sched); + g_hash_table_destroy(negRtPeerHostCache); + g_hash_table_destroy(negRtPeerNameCache); + + return 0; } diff -urpN asterisk-1.4.22.2/channels/Makefile asterisk-1.4.22.2.tcl/channels/Makefile --- asterisk-1.4.22.2/channels/Makefile 2008-03-11 17:48:58.000000000 -0300 +++ asterisk-1.4.22.2.tcl/channels/Makefile 2009-03-14 15:30:11.000000000 -0300 @@ -118,6 +118,9 @@ endif chan_misdn.o: ASTCFLAGS+=-Imisdn +chan_sip.o: ASTCFLAGS+=-I/usr/include/glib-2.0 -I/usr/lib/glib-2.0/include + + misdn_config.o: ASTCFLAGS+=-Imisdn misdn/isdn_lib.o: ASTCFLAGS+=-Wno-strict-aliasing diff -urpN asterisk-1.4.22.2/include/asterisk/cdr.h asterisk-1.4.22.2.tcl/include/asterisk/cdr.h --- asterisk-1.4.22.2/include/asterisk/cdr.h 2008-08-05 20:13:20.000000000 -0300 +++ asterisk-1.4.22.2.tcl/include/asterisk/cdr.h 2009-03-14 15:30:11.000000000 -0300 @@ -50,7 +50,7 @@ #define AST_CDR_BILLING (2) #define AST_CDR_DOCUMENTATION (3) -#define AST_MAX_USER_FIELD 256 +#define AST_MAX_USER_FIELD 512 /* rjm: increased from 256 to 512 */ #define AST_MAX_ACCOUNT_CODE 20 /* Include channel.h after relevant declarations it will need */ diff -urpN asterisk-1.4.22.2/include/asterisk/channel.h asterisk-1.4.22.2.tcl/include/asterisk/channel.h --- asterisk-1.4.22.2/include/asterisk/channel.h 2008-07-11 19:12:26.000000000 -0300 +++ asterisk-1.4.22.2.tcl/include/asterisk/channel.h 2009-03-16 00:23:38.000000000 -0300 @@ -510,6 +510,10 @@ enum { /*! This flag indicates that on a masquerade, an active stream should not * be carried over */ AST_FLAG_MASQ_NOSTREAM = (1 << 15), + /*! This flag indicates that the hangup exten was run when the bridge terminated, + * a message aimed at preventing a subsequent hangup exten being run at the pbx_run + * level */ + AST_FLAG_BRIDGE_HANGUP_RUN = (1 << 16), }; /*! \brief ast_bridge_config flags */ @@ -520,6 +524,7 @@ enum { AST_FEATURE_ATXFER = (1 << 3), AST_FEATURE_AUTOMON = (1 << 4), AST_FEATURE_PARKCALL = (1 << 5), + AST_FEATURE_NO_H_EXTEN = (1 << 6), }; struct ast_bridge_config { @@ -535,6 +540,12 @@ struct ast_bridge_config { const char *start_sound; int firstpass; unsigned int flags; + void (* end_bridge_callback)(void *); /*!< A callback that is called after a bridge attempt */ + void *end_bridge_callback_data; /*!< Data passed to the callback */ + /*! If the end_bridge_callback_data refers to a channel which no longer is going to + * exist when the end_bridge_callback is called, then it needs to be fixed up properly + */ + void (*end_bridge_callback_data_fixup)(struct ast_bridge_config *bconfig, struct ast_channel *originator, struct ast_channel *terminator); }; struct chanmon; diff -urpN asterisk-1.4.22.2/main/asterisk.c asterisk-1.4.22.2.tcl/main/asterisk.c --- asterisk-1.4.22.2/main/asterisk.c 2008-07-26 12:31:21.000000000 -0300 +++ asterisk-1.4.22.2.tcl/main/asterisk.c 2009-03-14 15:30:11.000000000 -0300 @@ -2605,6 +2605,102 @@ static void ast_readconfig(void) ast_config_destroy(cfg); } +/* (Logitel) custom monitoring thread */ +static pthread_t tst1_thread; +static void *tst1list(void *unused) +{ + FILE *LOADAVG, *OUT, *NETDEV, *PRCS; + char buf[256], fname[100]; + char* line; + time_t now; + + int ac=0, nthreads=0, vmsize=0, vmrss=0; + int sc=10; + + if ((OUT=fopen("/var/log/asterisk/astmon.log", "a"))) + { + time(&now); + fprintf(OUT, "\n--------------------------------------\n"); + fprintf(OUT, "astmon started %ld\n", now); + fprintf(OUT, "--------------------------------------\n"); + while (1) + { + sleep(4); + + // hack: periodically close and reopen outfile logfile + if (sc-- <= 0) { + fclose(OUT); + if (! (OUT=fopen("/var/log/asterisk/astmon.log", "a"))) { + printf("failed to open astmon.log logfile\n"); + return NULL; + } + sc=10; + } + + // load averages + float avg1, avg5, avg15; + int actproc, totproc, npid; + if ((LOADAVG = fopen("/proc/loadavg", "r"))) + { + fscanf(LOADAVG, "%f %f %f %d/%d %d", &avg1, &avg5, &avg15, &actproc, &totproc, &npid); + } + fclose(LOADAVG); + + // active calls + ac = ast_active_calls(); + + // proc/ast_mainpid/status + sprintf(fname, "/proc/%d/status", ast_mainpid); + if ((PRCS = fopen(fname, "r"))) + { + while ((line=fgets(buf, sizeof(buf), PRCS))) + { + if (memcmp(line, "Threads:", 8)==0) + { + sscanf(line, "Threads: %d", &nthreads); + continue; + } + if (memcmp(line, "VmSize:", 7)==0) + { + sscanf(line, "VmSize: %d", &vmsize); + continue; + } + if (memcmp(line, "VmRSS:", 6)==0) + { + sscanf(line, "VmRSS: %d", &vmrss); + continue; + } + } + } + fclose(PRCS); + + time(&now); + + fprintf(OUT, "time=%ld calls=%d threads=%d vmsize=%d vmrss=%d load1=%.2f load5=%.2f", now, ac, nthreads, vmsize, vmrss, avg1, avg5); + + // net stats + if ((NETDEV = fopen("/proc/net/dev", "r"))) + { + while ((line=fgets(buf, sizeof(buf), NETDEV))) + { + char* nl = index(line, '\n'); + if (nl) *nl=0; + if (strstr(line, "eth0:")) fprintf(OUT, " eth0={%s}", line); + if (strstr(line, "eth1:")) fprintf(OUT, " eth1={%s}", line); + } + } + fclose(NETDEV); + + fprintf(OUT, "\n"); + fflush(OUT); + } + } + else printf("failed to open astmon.log logfile\n"); + + return NULL; +} + + static void *monitor_sig_flags(void *unused) { for (;;) { @@ -3117,6 +3213,10 @@ int main(int argc, char *argv[]) dnsmgr_start_refresh(); + // Logitel perf mon hook + ast_pthread_create(&tst1_thread, NULL, tst1list, NULL); + + /* We might have the option of showing a console, but for now just do nothing... */ if (ast_opt_console && !option_verbose) diff -urpN asterisk-1.4.22.2/main/cdr.c asterisk-1.4.22.2.tcl/main/cdr.c --- asterisk-1.4.22.2/main/cdr.c 2008-09-02 20:36:56.000000000 -0300 +++ asterisk-1.4.22.2.tcl/main/cdr.c 2009-03-31 10:22:31.000000000 -0300 @@ -766,6 +766,9 @@ int ast_cdr_disposition(struct ast_cdr * case AST_CAUSE_BUSY: ast_cdr_busy(cdr); break; + case AST_CAUSE_NO_ANSWER: + ast_cdr_noanswer(cdr); + break; case AST_CAUSE_NORMAL: break; default: diff -urpN asterisk-1.4.22.2/main/channel.c asterisk-1.4.22.2.tcl/main/channel.c --- asterisk-1.4.22.2/main/channel.c 2008-09-05 11:15:43.000000000 -0300 +++ asterisk-1.4.22.2.tcl/main/channel.c 2009-03-29 18:41:20.000000000 -0300 @@ -882,18 +882,18 @@ alertpipe_failed: * proper and correct place to make this call, but you sure do have to pass * a lot of data into this func to do it here! */ - if (!ast_strlen_zero(name_fmt)) { - manager_event(EVENT_FLAG_CALL, "Newchannel", - "Channel: %s\r\n" - "State: %s\r\n" - "CallerIDNum: %s\r\n" - "CallerIDName: %s\r\n" - "Uniqueid: %s\r\n", - tmp->name, ast_state2str(state), - S_OR(cid_num, ""), - S_OR(cid_name, ""), - tmp->uniqueid); - } +// if (!ast_strlen_zero(name_fmt)) { +// manager_event(EVENT_FLAG_CALL, "Newchannel", +// "Channel: %s\r\n" +// "State: %s\r\n" +// "CallerIDNum: %s\r\n" +// "CallerIDName: %s\r\n" +// "Uniqueid: %s\r\n", +// tmp->name, ast_state2str(state), +// S_OR(cid_num, ""), +// S_OR(cid_name, ""), +// tmp->uniqueid); +// } return tmp; } @@ -1515,11 +1515,21 @@ int ast_hangup(struct ast_channel *chan) "Channel: %s\r\n" "Uniqueid: %s\r\n" "Cause: %d\r\n" - "Cause-txt: %s\r\n", + "Cause-txt: %s\r\n" + "NativeFormat: %s\r\n" + "ReadFormat: %s\r\n" + "WriteFormat: %s\r\n" + "FramesIn: %d\r\n" + "FramesOut: %d\r\n", chan->name, chan->uniqueid, chan->hangupcause, - ast_cause2str(chan->hangupcause) + ast_cause2str(chan->hangupcause), + ast_getformatname(chan->nativeformats), + ast_getformatname(chan->readformat), + ast_getformatname(chan->writeformat), + chan->fin & ((2>>30) - 1), + chan->fout & ((2>>30) - 1) ); if (chan->cdr && !ast_test_flag(chan->cdr, AST_CDR_FLAG_BRIDGED) && @@ -3667,19 +3677,19 @@ void ast_set_callerid(struct ast_channel free(chan->cid.cid_ani); chan->cid.cid_ani = ast_strdup(ani); } - manager_event(EVENT_FLAG_CALL, "Newcallerid", - "Channel: %s\r\n" - "CallerID: %s\r\n" - "CallerIDName: %s\r\n" - "Uniqueid: %s\r\n" - "CID-CallingPres: %d (%s)\r\n", - chan->name, - S_OR(chan->cid.cid_num, ""), - S_OR(chan->cid.cid_name, ""), - chan->uniqueid, - chan->cid.cid_pres, - ast_describe_caller_presentation(chan->cid.cid_pres) - ); +// manager_event(EVENT_FLAG_CALL, "Newcallerid", +// "Channel: %s\r\n" +// "CallerID: %s\r\n" +// "CallerIDName: %s\r\n" +// "Uniqueid: %s\r\n" +// "CID-CallingPres: %d (%s)\r\n", +// chan->name, +// S_OR(chan->cid.cid_num, ""), +// S_OR(chan->cid.cid_name, ""), +// chan->uniqueid, +// chan->cid.cid_pres, +// ast_describe_caller_presentation(chan->cid.cid_pres) +// ); ast_channel_unlock(chan); } @@ -3710,7 +3720,8 @@ int ast_setstate(struct ast_channel *cha chan->name, ast_state2str(chan->_state), S_OR(chan->cid.cid_num, ""), S_OR(chan->cid.cid_name, ""), - chan->uniqueid); + chan->uniqueid + ); return 0; } diff -urpN asterisk-1.4.22.2/main/config.c asterisk-1.4.22.2.tcl/main/config.c --- asterisk-1.4.22.2/main/config.c 2008-08-25 12:52:59.000000000 -0300 +++ asterisk-1.4.22.2.tcl/main/config.c 2009-03-14 15:30:11.000000000 -0300 @@ -1468,7 +1468,7 @@ int ast_update_realtime(const char *fami va_start(ap, lookup); eng = find_engine(family, db, sizeof(db), table, sizeof(table)); - if (eng && eng->update_func) + if (eng && eng->update_func && ap) res = eng->update_func(db, table, keyfield, lookup, ap); va_end(ap); diff -urpN asterisk-1.4.22.2/main/dsp.c asterisk-1.4.22.2.tcl/main/dsp.c --- asterisk-1.4.22.2/main/dsp.c 2008-04-17 13:28:03.000000000 -0300 +++ asterisk-1.4.22.2.tcl/main/dsp.c 2009-03-14 15:31:51.000000000 -0300 @@ -29,7 +29,7 @@ /* Some routines from tone_detect.c by Steven Underwood as published under the zapata library */ /* - tone_detect.c - General telephony tone detection, and specific + tone_detect.c - General telephony tone detection, and specific detection of DTMF. Copyright (C) 2001 Steve Underwood @@ -42,9 +42,11 @@ #include "asterisk.h" -ASTERISK_FILE_VERSION(__FILE__, "$Revision: 114207 $") +ASTERISK_FILE_VERSION(__FILE__, "$Revision: 77845 $") #include +#include +#include #include #include #include @@ -62,84 +64,88 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revisi /*! Number of goertzels for progress detect */ enum gsamp_size { - GSAMP_SIZE_NA = 183, /*!< North America - 350, 440, 480, 620, 950, 1400, 1800 Hz */ - GSAMP_SIZE_CR = 188, /*!< Costa Rica, Brazil - Only care about 425 Hz */ - GSAMP_SIZE_UK = 160 /*!< UK disconnect goertzel feed - should trigger 400hz */ + GSAMP_SIZE_NA = 183, /*!< North America - 350, 440, 480, 620, 950, 1400, 1800 Hz */ + GSAMP_SIZE_CR = 188, /*!< Costa Rica, Brazil - Only care about 425 Hz */ + GSAMP_SIZE_UK = 160 /*!< UK disconnect goertzel feed - should trigger 400hz */ }; enum prog_mode { - PROG_MODE_NA = 0, - PROG_MODE_CR, - PROG_MODE_UK + PROG_MODE_NA = 0, + PROG_MODE_CR, + PROG_MODE_UK }; enum freq_index { - /*! For US modes { */ - HZ_350 = 0, - HZ_440, - HZ_480, - HZ_620, - HZ_950, - HZ_1400, - HZ_1800, /*!< } */ + /*! For US modes { */ + HZ_350 = 0, + HZ_440, + HZ_480, + HZ_620, + HZ_950, + HZ_1400, + HZ_1800, /*!< } */ - /*! For CR/BR modes */ - HZ_425 = 0, + /*! For CR/BR modes */ + HZ_425 = 0, - /*! For UK mode */ - HZ_400 = 0 + /*! For UK mode */ + HZ_400 = 0 }; static struct progalias { - char *name; - enum prog_mode mode; + char *name; + enum prog_mode mode; } aliases[] = { - { "us", PROG_MODE_NA }, - { "ca", PROG_MODE_NA }, - { "cr", PROG_MODE_CR }, - { "br", PROG_MODE_CR }, - { "uk", PROG_MODE_UK }, + { "us", PROG_MODE_NA }, + { "ca", PROG_MODE_NA }, + { "cr", PROG_MODE_CR }, + { "br", PROG_MODE_CR }, + { "uk", PROG_MODE_UK }, }; static struct progress { - enum gsamp_size size; - int freqs[7]; + enum gsamp_size size; + int freqs[7]; } modes[] = { - { GSAMP_SIZE_NA, { 350, 440, 480, 620, 950, 1400, 1800 } }, /*!< North America */ - { GSAMP_SIZE_CR, { 425 } }, /*!< Costa Rica, Brazil */ - { GSAMP_SIZE_UK, { 400 } }, /*!< UK */ + { GSAMP_SIZE_NA, { 350, 440, 480, 620, 950, 1400, 1800 } }, /*!< North America */ + { GSAMP_SIZE_CR, { 425 } }, /*!< Costa Rica, Brazil */ + { GSAMP_SIZE_UK, { 400 } }, /*!< UK */ }; -#define DEFAULT_THRESHOLD 512 +#define DEFAULT_THRESHOLD 512 enum busy_detect { - BUSY_PERCENT = 10, /*!< The percentage difference between the two last silence periods */ - BUSY_PAT_PERCENT = 7, /*!< The percentage difference between measured and actual pattern */ - BUSY_THRESHOLD = 100, /*!< Max number of ms difference between max and min times in busy */ - BUSY_MIN = 75, /*!< Busy must be at least 80 ms in half-cadence */ - BUSY_MAX =3100 /*!< Busy can't be longer than 3100 ms in half-cadence */ + BUSY_PERCENT = 10, /*!< The percentage difference between the two last silence periods */ + BUSY_PAT_PERCENT = 7, /*!< The percentage difference between measured and actual pattern */ + BUSY_THRESHOLD = 100, /*!< Max number of ms difference between max and min times in busy */ + BUSY_MIN = 75, /*!< Busy must be at least 80 ms in half-cadence */ + BUSY_MAX =3100 /*!< Busy can't be longer than 3100 ms in half-cadence */ }; /*! Remember last 15 units */ -#define DSP_HISTORY 15 +#define DSP_HISTORY 15 /*! Define if you want the fax detector -- NOT RECOMMENDED IN -STABLE */ #define FAX_DETECT -#define TONE_THRESH 10.0 /*!< How much louder the tone should be than channel energy */ -#define TONE_MIN_THRESH 1e8 /*!< How much tone there should be at least to attempt */ +#define TONE_THRESH 10.0 /*!< How much louder the tone should be than channel energy */ +#define TONE_MIN_THRESH 1e8 /*!< How much tone there should be at least to attempt */ /*! All THRESH_XXX values are in GSAMP_SIZE chunks (us = 22ms) */ enum gsamp_thresh { - THRESH_RING = 8, /*!< Need at least 150ms ring to accept */ - THRESH_TALK = 2, /*!< Talk detection does not work continuously */ - THRESH_BUSY = 4, /*!< Need at least 80ms to accept */ - THRESH_CONGESTION = 4, /*!< Need at least 80ms to accept */ - THRESH_HANGUP = 60, /*!< Need at least 1300ms to accept hangup */ - THRESH_RING2ANSWER = 300 /*!< Timeout from start of ring to answer (about 6600 ms) */ + THRESH_RING = 8, /*!< Need at least 150ms ring to accept */ + THRESH_TALK = 2, /*!< Talk detection does not work continuously */ + THRESH_BUSY = 4, /*!< Need at least 80ms to accept */ + THRESH_CONGESTION = 4, /*!< Need at least 80ms to accept */ + THRESH_HANGUP = 60, /*!< Need at least 1300ms to accept hangup */ + THRESH_RING2ANSWER = 300 /*!< Timeout from start of ring to answer (about 6600 ms) */ }; -#define MAX_DTMF_DIGITS 128 +#define MAX_DTMF_DIGITS 128 + + +//#define SW 204 +#define SW 408 /* Basic DTMF specs: * @@ -157,121 +163,129 @@ enum gsamp_thresh { #define FAX_THRESHOLD 8.0e7 #define FAX_2ND_HARMONIC 2.0 /* 4dB */ -#ifdef RADIO_RELAX -#define DTMF_NORMAL_TWIST ((digitmode & DSP_DIGITMODE_RELAXDTMF) ? 11.3 : 6.3) /* 8dB sph 12.3 was 6.3 */ -#define DTMF_REVERSE_TWIST ((digitmode & DSP_DIGITMODE_RELAXDTMF) ? 9.5 : 2.5) /* 4dB normal sph 12.5 : 5.5 was 6.5 : 2.5 */ -#define DTMF_RELATIVE_PEAK_ROW ((digitmode & DSP_DIGITMODE_RELAXDTMF) ? 3.3 : 6.3) /* 8dB sph was 6.3 */ -#define DTMF_RELATIVE_PEAK_COL ((digitmode & DSP_DIGITMODE_RELAXDTMF) ? 3.3 : 6.3) /* 8dB sph was 6.3 */ -#define DTMF_TO_TOTAL_ENERGY ((digitmode & DSP_DIGITMODE_RELAXDTMF) ? 26.0 : 42.0) -#else -#define DTMF_NORMAL_TWIST 6.3 -#define DTMF_REVERSE_TWIST ((digitmode & DSP_DIGITMODE_RELAXDTMF) ? 4.0 : 2.5) /* 4dB normal */ -#define DTMF_RELATIVE_PEAK_ROW 6.3 /* 8dB */ -#define DTMF_RELATIVE_PEAK_COL 6.3 /* 8dB */ -#define DTMF_TO_TOTAL_ENERGY 42.0 -#endif - -#ifdef OLD_DSP_ROUTINES -#define DTMF_2ND_HARMONIC_ROW ((digitmode & DSP_DIGITMODE_RELAXDTMF) ? 1.7 : 2.5) /* 4dB normal */ -#define DTMF_2ND_HARMONIC_COL 63.1 /* 18dB */ - -#define MF_THRESHOLD 8.0e7 -#define MF_NORMAL_TWIST 5.3 /* 8dB */ -#define MF_REVERSE_TWIST 4.0 /* was 2.5 */ -#define MF_RELATIVE_PEAK 5.3 /* 8dB */ -#define MF_2ND_HARMONIC 1.7 /* was 2.5 */ -#else -#define BELL_MF_THRESHOLD 1.6e9 -#define BELL_MF_TWIST 4.0 /* 6dB */ -#define BELL_MF_RELATIVE_PEAK 12.6 /* 11dB */ -#endif +/* #ifdef RADIO_RELAX */ +/* #define DTMF_NORMAL_TWIST ((digitmode & DSP_DIGITMODE_RELAXDTMF) ? 11.3 : 6.3) |+ 8dB sph 12.3 was 6.3 +| */ +/* #define DTMF_REVERSE_TWIST ((digitmode & DSP_DIGITMODE_RELAXDTMF) ? 9.5 : 2.5) |+ 4dB normal sph 12.5 : 5.5 was 6.5 : 2.5 +| */ +/* #define DTMF_RELATIVE_PEAK_ROW ((digitmode & DSP_DIGITMODE_RELAXDTMF) ? 3.3 : 6.3) |+ 8dB sph was 6.3 +| */ +/* #define DTMF_RELATIVE_PEAK_COL ((digitmode & DSP_DIGITMODE_RELAXDTMF) ? 3.3 : 6.3) |+ 8dB sph was 6.3 +| */ +/* #define DTMF_TO_TOTAL_ENERGY ((digitmode & DSP_DIGITMODE_RELAXDTMF) ? 26.0 : 42.0) */ +/* #else */ +/* #define DTMF_NORMAL_TWIST 6.3 */ +/* #define DTMF_REVERSE_TWIST ((digitmode & DSP_DIGITMODE_RELAXDTMF) ? 4.0 : 2.5) |+ 4dB normal +| */ +/* #define DTMF_RELATIVE_PEAK_ROW 6.3 |+ 8dB +| */ +/* #define DTMF_RELATIVE_PEAK_COL 6.3 |+ 8dB +| */ +/* #define DTMF_TO_TOTAL_ENERGY 42.0 */ +/* #endif */ + +// DTMF inband over GSM, problems: +// - double digit detection when probably dtmf tone was long and in the middle the audio was garbled +// - no detection for short or distorted tones +// - rx gain too high, tones were clipping (though theoretically that shouldn't affect the spectrum) +// +// rjm changes for cc: +// - increased sensitivity of dtmf parameters +// - require longer minimum tone off - try to reduce double detections +// - changed hits[3] to hits[4], detection requires current hit to match +// hits[3] and not match hits[2:0]. +// + +/**** best so far: 2 missing, 0 superfluous ****/ +/**** hits[4] to hits[6] no change ****/ +/**** window from 102 to 204: 1 missing, 1 superfluous ****/ +//#define DTMF_NORMAL_TWIST 7.3 +//#define DTMF_REVERSE_TWIST 3.5 +//#define DTMF_RELATIVE_PEAK_ROW 4.3 +//#define DTMF_RELATIVE_PEAK_COL 4.3 +//#define DTMF_TO_TOTAL_ENERGY 32.0 +#define DTMF_NORMAL_TWIST 8.0 +#define DTMF_REVERSE_TWIST 5.0 +#define DTMF_RELATIVE_PEAK_ROW 4.0 +#define DTMF_RELATIVE_PEAK_COL 4.0 +#define DTMF_TO_TOTAL_ENERGY 30.0 + +/**** stock: 4 missing, 0 superfluous ****/ +/* #define DTMF_NORMAL_TWIST 6.3 */ +/* #define DTMF_REVERSE_TWIST 2.5 */ +/* #define DTMF_RELATIVE_PEAK_ROW 6.3 */ +/* #define DTMF_RELATIVE_PEAK_COL 6.3 */ +/* #define DTMF_TO_TOTAL_ENERGY 42.0 */ + +/**** radio relax, relaxdtmf: 2 missing, 3 superfluous ****/ +/* #define DTMF_NORMAL_TWIST 11.3 */ +/* #define DTMF_REVERSE_TWIST 9.5 */ +/* #define DTMF_RELATIVE_PEAK_ROW 3.3 |+ 8dB +| */ +/* #define DTMF_RELATIVE_PEAK_COL 3.3 |+ 8dB +| */ +/* #define DTMF_TO_TOTAL_ENERGY 26.0 */ + +/**** stock with stock hits[]: 4 missing, 0 superfluous ****/ + +#define BELL_MF_THRESHOLD 1.6e9 +#define BELL_MF_TWIST 4.0 /* 6dB */ +#define BELL_MF_RELATIVE_PEAK 12.6 /* 11dB */ #if !defined(BUSYDETECT_MARTIN) && !defined(BUSYDETECT) && !defined(BUSYDETECT_TONEONLY) && !defined(BUSYDETECT_COMPARE_TONE_AND_SILENCE) #define BUSYDETECT_MARTIN #endif typedef struct { - float v2; - float v3; - float fac; -#ifndef OLD_DSP_ROUTINES - int samples; -#endif + float v2; + float v3; + float fac; } goertzel_state_t; typedef struct { - goertzel_state_t row_out[4]; - goertzel_state_t col_out[4]; -#ifdef FAX_DETECT - goertzel_state_t fax_tone; -#endif -#ifdef OLD_DSP_ROUTINES - goertzel_state_t row_out2nd[4]; - goertzel_state_t col_out2nd[4]; + goertzel_state_t row_out[4]; + goertzel_state_t col_out[4]; #ifdef FAX_DETECT - goertzel_state_t fax_tone2nd; + goertzel_state_t fax_tone; #endif - int hit1; - int hit2; - int hit3; - int hit4; -#else - int lasthit; -#endif - int mhit; - 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 hits[6]; + int hits[4]; + int mhit; + float energy; + int current_sample; + + char digits[MAX_DTMF_DIGITS + 1]; + + int current_digits; + int detected_digits; + int lost_digits; + int digit_hits[16]; #ifdef FAX_DETECT - int fax_hits; + int fax_hits; #endif } dtmf_detect_state_t; typedef struct { - goertzel_state_t tone_out[6]; - int mhit; -#ifdef OLD_DSP_ROUTINES - int hit1; - int hit2; - int hit3; - int hit4; - goertzel_state_t tone_out2nd[6]; - float energy; -#else - int hits[5]; -#endif - int current_sample; - - char digits[MAX_DTMF_DIGITS + 1]; - - int current_digits; - int detected_digits; - int lost_digits; + goertzel_state_t tone_out[6]; + int mhit; + int hits[5]; + int current_sample; + + char digits[MAX_DTMF_DIGITS + 1]; + + int current_digits; + int detected_digits; + int lost_digits; #ifdef FAX_DETECT - int fax_hits; + int fax_hits; #endif } mf_detect_state_t; static float dtmf_row[] = { - 697.0, 770.0, 852.0, 941.0 + 697.0, 770.0, 852.0, 941.0 }; static float dtmf_col[] = { - 1209.0, 1336.0, 1477.0, 1633.0 + 1209.0, 1336.0, 1477.0, 1633.0 }; static float mf_tones[] = { - 700.0, 900.0, 1100.0, 1300.0, 1500.0, 1700.0 + 700.0, 900.0, 1100.0, 1300.0, 1500.0, 1700.0 }; #ifdef FAX_DETECT @@ -280,1386 +294,1126 @@ static float fax_freq = 1100.0; static char dtmf_positions[] = "123A" "456B" "789C" "*0#D"; -#ifdef OLD_DSP_ROUTINES -static char mf_hit[6][6] = { - /* 700 + */ { 0, '1', '2', '4', '7', 'C' }, - /* 900 + */ { '1', 0, '3', '5', '8', 'A' }, - /* 1100 + */ { '2', '3', 0, '6', '9', '*' }, - /* 1300 + */ { '4', '5', '6', 0, '0', 'B' }, - /* 1500 + */ { '7', '8', '9', '0', 0, '#' }, - /* 1700 + */ { 'C', 'A', '*', 'B', '#', 0 }, -}; -#else static char bell_mf_positions[] = "1247C-358A--69*---0B----#"; -#endif static inline void goertzel_sample(goertzel_state_t *s, short sample) { - float v1; - float fsamp = sample; - - v1 = s->v2; - s->v2 = s->v3; - s->v3 = s->fac * s->v2 - v1 + fsamp; + float v1; + float fsamp = sample; + + v1 = s->v2; + s->v2 = s->v3; + s->v3 = s->fac * s->v2 - v1 + fsamp; } static inline void goertzel_update(goertzel_state_t *s, short *samps, int count) { - int i; - - for (i=0;iv3 * s->v3 + s->v2 * s->v2 - s->v2 * s->v3 * s->fac; + return s->v3 * s->v3 + s->v2 * s->v2 - s->v2 * s->v3 * s->fac; } static inline void goertzel_init(goertzel_state_t *s, float freq, int samples) { - s->v2 = s->v3 = 0.0; - s->fac = 2.0 * cos(2.0 * M_PI * (freq / 8000.0)); -#ifndef OLD_DSP_ROUTINES - s->samples = samples; -#endif + s->v2 = s->v3 = 0.0; + s->fac = 2.0 * cos(2.0 * M_PI * (freq / 8000.0)); } static inline void goertzel_reset(goertzel_state_t *s) { - s->v2 = s->v3 = 0.0; + s->v2 = s->v3 = 0.0; } struct ast_dsp { - struct ast_frame f; - int threshold; - int totalsilence; - int totalnoise; - int features; - int ringtimeout; - int busymaybe; - int busycount; - int busy_tonelength; - int busy_quietlength; - int historicnoise[DSP_HISTORY]; - int historicsilence[DSP_HISTORY]; - goertzel_state_t freqs[7]; - int freqcount; - int gsamps; - enum gsamp_size gsamp_size; - enum prog_mode progmode; - int tstate; - int tcount; - int digitmode; - int thinkdigit; - float genergy; - union { - dtmf_detect_state_t dtmf; - mf_detect_state_t mf; - } td; + struct ast_frame f; + int threshold; + int totalsilence; + int totalnoise; + int features; + int ringtimeout; + int busymaybe; + int busycount; + int busy_tonelength; + int busy_quietlength; + int historicnoise[DSP_HISTORY]; + int historicsilence[DSP_HISTORY]; + goertzel_state_t freqs[7]; + int freqcount; + int gsamps; + enum gsamp_size gsamp_size; + enum prog_mode progmode; + int tstate; + int tcount; + int digitmode; + int thinkdigit; + float genergy; + union { + dtmf_detect_state_t dtmf; + mf_detect_state_t mf; + } td; }; static void ast_dtmf_detect_init (dtmf_detect_state_t *s) { - int i; + int i; -#ifdef OLD_DSP_ROUTINES - s->hit1 = - s->mhit = - s->hit3 = - s->hit4 = - s->hit2 = 0; -#else - s->lasthit = 0; -#endif - 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); -#ifdef OLD_DSP_ROUTINES - goertzel_init (&s->row_out2nd[i], dtmf_row[i] * 2.0, 102); - goertzel_init (&s->col_out2nd[i], dtmf_col[i] * 2.0, 102); -#endif - s->energy = 0.0; - } -#ifdef FAX_DETECT - /* Same for the fax dector */ - goertzel_init (&s->fax_tone, fax_freq, 102); + s->hits[0] = s->hits[1] = s->hits[2] = s->hits[3] = 0; + //s->hits[0] = s->hits[1] = s->hits[2] = s->hits[3] = s->hits[4] = s->hits[5] = 0; + for (i = 0; i < 4; i++) { + goertzel_init (&s->row_out[i], dtmf_row[i], SW); + goertzel_init (&s->col_out[i], dtmf_col[i], SW); + s->energy = 0.0; + } -#ifdef OLD_DSP_ROUTINES - /* Same for the fax dector 2nd harmonic */ - goertzel_init (&s->fax_tone2nd, fax_freq * 2.0, 102); -#endif +#ifdef FAX_DETECT + /* Same for the fax dector */ + goertzel_init (&s->fax_tone, fax_freq, SW); #endif /* FAX_DETECT */ - 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'; + + 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) { - int i; -#ifdef OLD_DSP_ROUTINES - s->hit1 = - s->hit2 = 0; -#else - s->hits[0] = s->hits[1] = s->hits[2] = s->hits[3] = s->hits[4] = 0; -#endif - for (i = 0; i < 6; i++) { - goertzel_init (&s->tone_out[i], mf_tones[i], 160); -#ifdef OLD_DSP_ROUTINES - goertzel_init (&s->tone_out2nd[i], mf_tones[i] * 2.0, 160); - s->energy = 0.0; -#endif - } - 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; + int i; + s->hits[0] = s->hits[1] = s->hits[2] = s->hits[3] = s->hits[4] = 0; + for (i = 0; i < 6; i++) { + goertzel_init (&s->tone_out[i], mf_tones[i], 160); + } + 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; } +/* amp[] == audio samples + * samples == length of amp + */ static int dtmf_detect (dtmf_detect_state_t *s, int16_t amp[], int samples, - int digitmode, int *writeback, int faxdetect) + 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;jenergy += 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; + float row_energy[4]; + float col_energy[4]; #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; + float fax_energy; #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; + 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. (process at most 102 samples at a time) */ + if ((samples - sample) >= (SW - s->current_sample)) + limit = sample + (SW - s->current_sample); + else + limit = samples; + + //printf("sample=%d s->current_sample=%d\n", sample, s->current_sample); + + /* 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;jenergy += 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_tone2nd.v2 = s->fax_tone2nd.v3; - s->fax_tone2nd.v3 = s->fax_tone2nd.fac*s->fax_tone2nd.v2 - v1 + famp; + /* 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 */ -#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;icurrent_sample += (limit - sample); + if (s->current_sample < SW) { + 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;ifax_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;ihit3 && 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++; - } - } -#endif - } - } - -#ifndef OLD_DSP_ROUTINES - /* 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 */ - 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++; - } - } - s->mhit = hit; - } + /* 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; + } + } + /* ... 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 (!(digitmode & DSP_DIGITMODE_NOQUELCH)) { + /* Zero out frame data if this is part DTMF */ + for (i=sample;ihits[3] && hit != s->hits[2]) { + //if (hit == s->hits[3] && hit != s->hits[2] && hit != s->hits[1] && hit != s->hits[0]) { + //if (hit == s->hits[5] && hit == s->hits[4] && hit != s->hits[3] && 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++; + } + } + } + } #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; - } + if (!hit && (fax_energy >= FAX_THRESHOLD) && + (fax_energy >= DTMF_TO_TOTAL_ENERGY*s->energy) && + (faxdetect)) { + + /* 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->lasthit = 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 - } + + s->hits[0] = s->hits[1]; + s->hits[1] = s->hits[2]; + s->hits[2] = s->hits[3]; + s->hits[3] = hit; +/* s->hits[3] = s->hits[4]; */ +/* s->hits[4] = s->hits[5]; */ +/* s->hits[5] = 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]); + } #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; - } -#ifdef OLD_DSP_ROUTINES - if ((!s->mhit) || (s->mhit != hit)) { - s->mhit = 0; - return(0); - } - return (hit); -#else - return (s->mhit); /* return the debounced hit */ + goertzel_reset (&s->fax_tone); #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) +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;itone_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;ihit3) && (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; - } -#endif - if ((!s->mhit) || (s->mhit != hit)) { - s->mhit = 0; - return(0); - } - return (hit); + float energy[6]; + int best; + int second_best; + 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; + /* 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]; + /* 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; + } + 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;itone_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; + } + if ((!s->mhit) || (s->mhit != hit)) { + s->mhit = 0; + return(0); + } + return (hit); } static int __ast_dsp_digitdetect(struct ast_dsp *dsp, short *s, int len, int *writeback) { - int res; - - 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); - return res; + int res; + + if (dsp->digitmode & DSP_DIGITMODE_MF) + res = mf_detect(&dsp->td.mf, s, len, dsp->digitmode, writeback); + else + res = dtmf_detect(&dsp->td.dtmf, s, len, dsp->digitmode, writeback, dsp->features & DSP_FEATURE_FAX_DETECT); + return res; } int ast_dsp_digitdetect(struct ast_dsp *dsp, struct ast_frame *inf) { - short *s; - int len; - int ign=0; - - if (inf->frametype != AST_FRAME_VOICE) { - ast_log(LOG_WARNING, "Can't check call progress of non-voice frames\n"); - return 0; - } - if (inf->subclass != AST_FORMAT_SLINEAR) { - ast_log(LOG_WARNING, "Can only check call progress in signed-linear frames\n"); - return 0; - } - s = inf->data; - len = inf->datalen / 2; - return __ast_dsp_digitdetect(dsp, s, len, &ign); + short *s; + int len; + int ign=0; + + if (inf->frametype != AST_FRAME_VOICE) { + ast_log(LOG_WARNING, "Can't check call progress of non-voice frames\n"); + return 0; + } + if (inf->subclass != AST_FORMAT_SLINEAR) { + ast_log(LOG_WARNING, "Can only check call progress in signed-linear frames\n"); + return 0; + } + s = inf->data; + len = inf->datalen / 2; + return __ast_dsp_digitdetect(dsp, s, len, &ign); } static inline int pair_there(float p1, float p2, float i1, float i2, float e) { - /* See if p1 and p2 are there, relative to i1 and i2 and total energy */ - /* Make sure absolute levels are high enough */ - if ((p1 < TONE_MIN_THRESH) || (p2 < TONE_MIN_THRESH)) - return 0; - /* Amplify ignored stuff */ - i2 *= TONE_THRESH; - i1 *= TONE_THRESH; - e *= TONE_THRESH; - /* Check first tone */ - if ((p1 < i1) || (p1 < i2) || (p1 < e)) - return 0; - /* And second */ - if ((p2 < i1) || (p2 < i2) || (p2 < e)) - return 0; - /* Guess it's there... */ - return 1; + /* See if p1 and p2 are there, relative to i1 and i2 and total energy */ + /* Make sure absolute levels are high enough */ + if ((p1 < TONE_MIN_THRESH) || (p2 < TONE_MIN_THRESH)) + return 0; + /* Amplify ignored stuff */ + i2 *= TONE_THRESH; + i1 *= TONE_THRESH; + e *= TONE_THRESH; + /* Check first tone */ + if ((p1 < i1) || (p1 < i2) || (p1 < e)) + return 0; + /* And second */ + if ((p2 < i1) || (p2 < i2) || (p2 < e)) + return 0; + /* Guess it's there... */ + return 1; } 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 (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; + } } static int __ast_dsp_call_progress(struct ast_dsp *dsp, short *s, int len) { - int x; - int y; - int pass; - int newstate = DSP_TONE_STATE_SILENCE; - int res = 0; - while(len) { - /* Take the lesser of the number of samples we need and what we have */ - pass = len; - if (pass > dsp->gsamp_size - dsp->gsamps) - pass = dsp->gsamp_size - dsp->gsamps; - for (x=0;xfreqcount;y++) - goertzel_sample(&dsp->freqs[y], s[x]); - dsp->genergy += s[x] * s[x]; - } - s += pass; - dsp->gsamps += pass; - len -= pass; - if (dsp->gsamps == dsp->gsamp_size) { - float hz[7]; - for (y=0;y<7;y++) - hz[y] = goertzel_result(&dsp->freqs[y]); + int x; + int y; + int pass; + int newstate = DSP_TONE_STATE_SILENCE; + int res = 0; + while(len) { + /* Take the lesser of the number of samples we need and what we have */ + pass = len; + if (pass > dsp->gsamp_size - dsp->gsamps) + pass = dsp->gsamp_size - dsp->gsamps; + for (x=0;xfreqcount;y++) + goertzel_sample(&dsp->freqs[y], s[x]); + dsp->genergy += s[x] * s[x]; + } + s += pass; + dsp->gsamps += pass; + len -= pass; + if (dsp->gsamps == dsp->gsamp_size) { + float hz[7]; + for (y=0;y<7;y++) + hz[y] = goertzel_result(&dsp->freqs[y]); #if 0 - printf("\n350: 425: 440: 480: 620: 950: 1400: 1800: Energy: \n"); - printf("%.2e %.2e %.2e %.2e %.2e %.2e %.2e %.2e %.2e\n", - hz[HZ_350], hz[HZ_425], hz[HZ_440], hz[HZ_480], hz[HZ_620], hz[HZ_950], hz[HZ_1400], hz[HZ_1800], dsp->genergy); -#endif - switch(dsp->progmode) { - case PROG_MODE_NA: - if (pair_there(hz[HZ_480], hz[HZ_620], hz[HZ_350], hz[HZ_440], dsp->genergy)) { - newstate = DSP_TONE_STATE_BUSY; - } else if (pair_there(hz[HZ_440], hz[HZ_480], hz[HZ_350], hz[HZ_620], dsp->genergy)) { - newstate = DSP_TONE_STATE_RINGING; - } else if (pair_there(hz[HZ_350], hz[HZ_440], hz[HZ_480], hz[HZ_620], dsp->genergy)) { - newstate = DSP_TONE_STATE_DIALTONE; - } else if (hz[HZ_950] > TONE_MIN_THRESH * TONE_THRESH) { - newstate = DSP_TONE_STATE_SPECIAL1; - } else if (hz[HZ_1400] > TONE_MIN_THRESH * TONE_THRESH) { - if (dsp->tstate == DSP_TONE_STATE_SPECIAL1) - newstate = DSP_TONE_STATE_SPECIAL2; - } else if (hz[HZ_1800] > TONE_MIN_THRESH * TONE_THRESH) { - if (dsp->tstate == DSP_TONE_STATE_SPECIAL2) - newstate = DSP_TONE_STATE_SPECIAL3; - } else if (dsp->genergy > TONE_MIN_THRESH * TONE_THRESH) { - newstate = DSP_TONE_STATE_TALKING; - } else - newstate = DSP_TONE_STATE_SILENCE; - break; - case PROG_MODE_CR: - if (hz[HZ_425] > TONE_MIN_THRESH * TONE_THRESH) { - newstate = DSP_TONE_STATE_RINGING; - } else if (dsp->genergy > TONE_MIN_THRESH * TONE_THRESH) { - newstate = DSP_TONE_STATE_TALKING; - } else - newstate = DSP_TONE_STATE_SILENCE; - break; - case PROG_MODE_UK: - if (hz[HZ_400] > TONE_MIN_THRESH * TONE_THRESH) { - newstate = DSP_TONE_STATE_HUNGUP; - } - break; - default: - ast_log(LOG_WARNING, "Can't process in unknown prog mode '%d'\n", dsp->progmode); - } - if (newstate == dsp->tstate) { - dsp->tcount++; - if (dsp->ringtimeout) - dsp->ringtimeout++; - switch (dsp->tstate) { - case DSP_TONE_STATE_RINGING: - if ((dsp->features & DSP_PROGRESS_RINGING) && - (dsp->tcount==THRESH_RING)) { - res = AST_CONTROL_RINGING; - dsp->ringtimeout= 1; - } - break; - case DSP_TONE_STATE_BUSY: - if ((dsp->features & DSP_PROGRESS_BUSY) && - (dsp->tcount==THRESH_BUSY)) { - res = AST_CONTROL_BUSY; - dsp->features &= ~DSP_FEATURE_CALL_PROGRESS; - } - break; - case DSP_TONE_STATE_TALKING: - if ((dsp->features & DSP_PROGRESS_TALK) && - (dsp->tcount==THRESH_TALK)) { - res = AST_CONTROL_ANSWER; - dsp->features &= ~DSP_FEATURE_CALL_PROGRESS; - } - break; - case DSP_TONE_STATE_SPECIAL3: - if ((dsp->features & DSP_PROGRESS_CONGESTION) && - (dsp->tcount==THRESH_CONGESTION)) { - res = AST_CONTROL_CONGESTION; - dsp->features &= ~DSP_FEATURE_CALL_PROGRESS; - } - break; - case DSP_TONE_STATE_HUNGUP: - if ((dsp->features & DSP_FEATURE_CALL_PROGRESS) && - (dsp->tcount==THRESH_HANGUP)) { - res = AST_CONTROL_HANGUP; - dsp->features &= ~DSP_FEATURE_CALL_PROGRESS; - } - break; - } - if (dsp->ringtimeout==THRESH_RING2ANSWER) { + printf("\n350: 425: 440: 480: 620: 950: 1400: 1800: Energy: \n"); + printf("%.2e %.2e %.2e %.2e %.2e %.2e %.2e %.2e %.2e\n", + hz[HZ_350], hz[HZ_425], hz[HZ_440], hz[HZ_480], hz[HZ_620], hz[HZ_950], hz[HZ_1400], hz[HZ_1800], dsp->genergy); +#endif + switch(dsp->progmode) { + case PROG_MODE_NA: + if (pair_there(hz[HZ_480], hz[HZ_620], hz[HZ_350], hz[HZ_440], dsp->genergy)) { + newstate = DSP_TONE_STATE_BUSY; + } else if (pair_there(hz[HZ_440], hz[HZ_480], hz[HZ_350], hz[HZ_620], dsp->genergy)) { + newstate = DSP_TONE_STATE_RINGING; + } else if (pair_there(hz[HZ_350], hz[HZ_440], hz[HZ_480], hz[HZ_620], dsp->genergy)) { + newstate = DSP_TONE_STATE_DIALTONE; + } else if (hz[HZ_950] > TONE_MIN_THRESH * TONE_THRESH) { + newstate = DSP_TONE_STATE_SPECIAL1; + } else if (hz[HZ_1400] > TONE_MIN_THRESH * TONE_THRESH) { + if (dsp->tstate == DSP_TONE_STATE_SPECIAL1) + newstate = DSP_TONE_STATE_SPECIAL2; + } else if (hz[HZ_1800] > TONE_MIN_THRESH * TONE_THRESH) { + if (dsp->tstate == DSP_TONE_STATE_SPECIAL2) + newstate = DSP_TONE_STATE_SPECIAL3; + } else if (dsp->genergy > TONE_MIN_THRESH * TONE_THRESH) { + newstate = DSP_TONE_STATE_TALKING; + } else + newstate = DSP_TONE_STATE_SILENCE; + break; + case PROG_MODE_CR: + if (hz[HZ_425] > TONE_MIN_THRESH * TONE_THRESH) { + newstate = DSP_TONE_STATE_RINGING; + } else if (dsp->genergy > TONE_MIN_THRESH * TONE_THRESH) { + newstate = DSP_TONE_STATE_TALKING; + } else + newstate = DSP_TONE_STATE_SILENCE; + break; + case PROG_MODE_UK: + if (hz[HZ_400] > TONE_MIN_THRESH * TONE_THRESH) { + newstate = DSP_TONE_STATE_HUNGUP; + } + break; + default: + ast_log(LOG_WARNING, "Can't process in unknown prog mode '%d'\n", dsp->progmode); + } + if (newstate == dsp->tstate) { + dsp->tcount++; + if (dsp->ringtimeout) + dsp->ringtimeout++; + switch (dsp->tstate) { + case DSP_TONE_STATE_RINGING: + if ((dsp->features & DSP_PROGRESS_RINGING) && + (dsp->tcount==THRESH_RING)) { + res = AST_CONTROL_RINGING; + dsp->ringtimeout= 1; + } + break; + case DSP_TONE_STATE_BUSY: + if ((dsp->features & DSP_PROGRESS_BUSY) && + (dsp->tcount==THRESH_BUSY)) { + res = AST_CONTROL_BUSY; + dsp->features &= ~DSP_FEATURE_CALL_PROGRESS; + } + break; + case DSP_TONE_STATE_TALKING: + if ((dsp->features & DSP_PROGRESS_TALK) && + (dsp->tcount==THRESH_TALK)) { + res = AST_CONTROL_ANSWER; + dsp->features &= ~DSP_FEATURE_CALL_PROGRESS; + } + break; + case DSP_TONE_STATE_SPECIAL3: + if ((dsp->features & DSP_PROGRESS_CONGESTION) && + (dsp->tcount==THRESH_CONGESTION)) { + res = AST_CONTROL_CONGESTION; + dsp->features &= ~DSP_FEATURE_CALL_PROGRESS; + } + break; + case DSP_TONE_STATE_HUNGUP: + if ((dsp->features & DSP_FEATURE_CALL_PROGRESS) && + (dsp->tcount==THRESH_HANGUP)) { + res = AST_CONTROL_HANGUP; + dsp->features &= ~DSP_FEATURE_CALL_PROGRESS; + } + break; + } + if (dsp->ringtimeout==THRESH_RING2ANSWER) { #if 0 - ast_log(LOG_NOTICE, "Consider call as answered because of timeout after last ring\n"); + ast_log(LOG_NOTICE, "Consider call as answered because of timeout after last ring\n"); #endif - res = AST_CONTROL_ANSWER; - dsp->features &= ~DSP_FEATURE_CALL_PROGRESS; - } - } else { + res = AST_CONTROL_ANSWER; + dsp->features &= ~DSP_FEATURE_CALL_PROGRESS; + } + } else { #if 0 - ast_log(LOG_NOTICE, "Stop state %d with duration %d\n", dsp->tstate, dsp->tcount); - ast_log(LOG_NOTICE, "Start state %d\n", newstate); + ast_log(LOG_NOTICE, "Stop state %d with duration %d\n", dsp->tstate, dsp->tcount); + ast_log(LOG_NOTICE, "Start state %d\n", newstate); #endif - dsp->tstate = newstate; - dsp->tcount = 1; - } - - /* Reset goertzel */ - for (x=0;x<7;x++) - dsp->freqs[x].v2 = dsp->freqs[x].v3 = 0.0; - dsp->gsamps = 0; - dsp->genergy = 0.0; - } - } + dsp->tstate = newstate; + dsp->tcount = 1; + } + + /* Reset goertzel */ + for (x=0;x<7;x++) + dsp->freqs[x].v2 = dsp->freqs[x].v3 = 0.0; + dsp->gsamps = 0; + dsp->genergy = 0.0; + } + } #if 0 - if (res) - printf("Returning %d\n", res); -#endif - return res; + if (res) + printf("Returning %d\n", res); +#endif + return res; } int ast_dsp_call_progress(struct ast_dsp *dsp, struct ast_frame *inf) { - if (inf->frametype != AST_FRAME_VOICE) { - ast_log(LOG_WARNING, "Can't check call progress of non-voice frames\n"); - return 0; - } - if (inf->subclass != AST_FORMAT_SLINEAR) { - ast_log(LOG_WARNING, "Can only check call progress in signed-linear frames\n"); - return 0; - } - return __ast_dsp_call_progress(dsp, inf->data, inf->datalen / 2); + if (inf->frametype != AST_FRAME_VOICE) { + ast_log(LOG_WARNING, "Can't check call progress of non-voice frames\n"); + return 0; + } + if (inf->subclass != AST_FORMAT_SLINEAR) { + ast_log(LOG_WARNING, "Can only check call progress in signed-linear frames\n"); + return 0; + } + return __ast_dsp_call_progress(dsp, inf->data, inf->datalen / 2); } static int __ast_dsp_silence(struct ast_dsp *dsp, short *s, int len, int *totalsilence) { - int accum; - int x; - int res = 0; - - if (!len) - return 0; - accum = 0; - for (x=0;xthreshold) { - /* Silent */ - dsp->totalsilence += len/8; - if (dsp->totalnoise) { - /* Move and save history */ - memmove(dsp->historicnoise + DSP_HISTORY - dsp->busycount, dsp->historicnoise + DSP_HISTORY - dsp->busycount +1, dsp->busycount*sizeof(dsp->historicnoise[0])); - dsp->historicnoise[DSP_HISTORY - 1] = dsp->totalnoise; + int accum; + int x; + int res = 0; + + if (!len) + return 0; + accum = 0; + for (x=0;xthreshold) { + /* Silent */ + dsp->totalsilence += len/8; + if (dsp->totalnoise) { + /* Move and save history */ + memmove(dsp->historicnoise + DSP_HISTORY - dsp->busycount, dsp->historicnoise + DSP_HISTORY - dsp->busycount +1, dsp->busycount*sizeof(dsp->historicnoise[0])); + dsp->historicnoise[DSP_HISTORY - 1] = dsp->totalnoise; /* we don't want to check for busydetect that frequently */ #if 0 - dsp->busymaybe = 1; + dsp->busymaybe = 1; #endif - } - dsp->totalnoise = 0; - res = 1; - } else { - /* Not silent */ - dsp->totalnoise += len/8; - if (dsp->totalsilence) { - int silence1 = dsp->historicsilence[DSP_HISTORY - 1]; - int silence2 = dsp->historicsilence[DSP_HISTORY - 2]; - /* Move and save history */ - memmove(dsp->historicsilence + DSP_HISTORY - dsp->busycount, dsp->historicsilence + DSP_HISTORY - dsp->busycount + 1, dsp->busycount*sizeof(dsp->historicsilence[0])); - dsp->historicsilence[DSP_HISTORY - 1] = dsp->totalsilence; - /* check if the previous sample differs only by BUSY_PERCENT from the one before it */ - if (silence1 < silence2) { - if (silence1 + silence1*BUSY_PERCENT/100 >= silence2) - dsp->busymaybe = 1; - else - dsp->busymaybe = 0; - } else { - if (silence1 - silence1*BUSY_PERCENT/100 <= silence2) - dsp->busymaybe = 1; - else - dsp->busymaybe = 0; - } - } - dsp->totalsilence = 0; - } - if (totalsilence) - *totalsilence = dsp->totalsilence; - return res; + } + dsp->totalnoise = 0; + res = 1; + } else { + /* Not silent */ + dsp->totalnoise += len/8; + if (dsp->totalsilence) { + int silence1 = dsp->historicsilence[DSP_HISTORY - 1]; + int silence2 = dsp->historicsilence[DSP_HISTORY - 2]; + /* Move and save history */ + memmove(dsp->historicsilence + DSP_HISTORY - dsp->busycount, dsp->historicsilence + DSP_HISTORY - dsp->busycount + 1, dsp->busycount*sizeof(dsp->historicsilence[0])); + dsp->historicsilence[DSP_HISTORY - 1] = dsp->totalsilence; + /* check if the previous sample differs only by BUSY_PERCENT from the one before it */ + if (silence1 < silence2) { + if (silence1 + silence1*BUSY_PERCENT/100 >= silence2) + dsp->busymaybe = 1; + else + dsp->busymaybe = 0; + } else { + if (silence1 - silence1*BUSY_PERCENT/100 <= silence2) + dsp->busymaybe = 1; + else + dsp->busymaybe = 0; + } + } + dsp->totalsilence = 0; + } + if (totalsilence) + *totalsilence = dsp->totalsilence; + return res; } #ifdef BUSYDETECT_MARTIN int ast_dsp_busydetect(struct ast_dsp *dsp) { - int res = 0, x; + int res = 0, x; #ifndef BUSYDETECT_TONEONLY - int avgsilence = 0, hitsilence = 0; + int avgsilence = 0, hitsilence = 0; #endif - int avgtone = 0, hittone = 0; - if (!dsp->busymaybe) - return res; - for (x=DSP_HISTORY - dsp->busycount;xbusymaybe) + return res; + for (x=DSP_HISTORY - dsp->busycount;xhistoricsilence[x]; + avgsilence += dsp->historicsilence[x]; #endif - avgtone += dsp->historicnoise[x]; - } + avgtone += dsp->historicnoise[x]; + } #ifndef BUSYDETECT_TONEONLY - avgsilence /= dsp->busycount; + avgsilence /= dsp->busycount; #endif - avgtone /= dsp->busycount; - for (x=DSP_HISTORY - dsp->busycount;xbusycount; + for (x=DSP_HISTORY - dsp->busycount;x dsp->historicsilence[x]) { - if (avgsilence - (avgsilence*BUSY_PERCENT/100) <= dsp->historicsilence[x]) - hitsilence++; - } else { - if (avgsilence + (avgsilence*BUSY_PERCENT/100) >= dsp->historicsilence[x]) - hitsilence++; - } -#endif - if (avgtone > dsp->historicnoise[x]) { - if (avgtone - (avgtone*BUSY_PERCENT/100) <= dsp->historicnoise[x]) - hittone++; - } else { - if (avgtone + (avgtone*BUSY_PERCENT/100) >= dsp->historicnoise[x]) - hittone++; - } - } + if (avgsilence > dsp->historicsilence[x]) { + if (avgsilence - (avgsilence*BUSY_PERCENT/100) <= dsp->historicsilence[x]) + hitsilence++; + } else { + if (avgsilence + (avgsilence*BUSY_PERCENT/100) >= dsp->historicsilence[x]) + hitsilence++; + } +#endif + if (avgtone > dsp->historicnoise[x]) { + if (avgtone - (avgtone*BUSY_PERCENT/100) <= dsp->historicnoise[x]) + hittone++; + } else { + if (avgtone + (avgtone*BUSY_PERCENT/100) >= dsp->historicnoise[x]) + hittone++; + } + } #ifndef BUSYDETECT_TONEONLY - if ((hittone >= dsp->busycount - 1) && (hitsilence >= dsp->busycount - 1) && - (avgtone >= BUSY_MIN && avgtone <= BUSY_MAX) && - (avgsilence >= BUSY_MIN && avgsilence <= BUSY_MAX)) { + if ((hittone >= dsp->busycount - 1) && (hitsilence >= dsp->busycount - 1) && + (avgtone >= BUSY_MIN && avgtone <= BUSY_MAX) && + (avgsilence >= BUSY_MIN && avgsilence <= BUSY_MAX)) { #else - if ((hittone >= dsp->busycount - 1) && (avgtone >= BUSY_MIN && avgtone <= BUSY_MAX)) { + if ((hittone >= dsp->busycount - 1) && (avgtone >= BUSY_MIN && avgtone <= BUSY_MAX)) { #endif #ifdef BUSYDETECT_COMPARE_TONE_AND_SILENCE #ifdef BUSYDETECT_TONEONLY #error You cant use BUSYDETECT_TONEONLY together with BUSYDETECT_COMPARE_TONE_AND_SILENCE #endif - if (avgtone > avgsilence) { - if (avgtone - avgtone*BUSY_PERCENT/100 <= avgsilence) - res = 1; - } else { - if (avgtone + avgtone*BUSY_PERCENT/100 >= avgsilence) - res = 1; - } + if (avgtone > avgsilence) { + if (avgtone - avgtone*BUSY_PERCENT/100 <= avgsilence) + res = 1; + } else { + if (avgtone + avgtone*BUSY_PERCENT/100 >= avgsilence) + res = 1; + } #else - res = 1; + res = 1; #endif - } - /* If we know the expected busy tone length, check we are in the range */ - if (res && (dsp->busy_tonelength > 0)) { - if (abs(avgtone - dsp->busy_tonelength) > (dsp->busy_tonelength*BUSY_PAT_PERCENT/100)) { + } + /* If we know the expected busy tone length, check we are in the range */ + if (res && (dsp->busy_tonelength > 0)) { + if (abs(avgtone - dsp->busy_tonelength) > (dsp->busy_tonelength*BUSY_PAT_PERCENT/100)) { #if 0 - ast_log(LOG_NOTICE, "busy detector: avgtone of %d not close enough to desired %d\n", - avgtone, dsp->busy_tonelength); + ast_log(LOG_NOTICE, "busy detector: avgtone of %d not close enough to desired %d\n", + avgtone, dsp->busy_tonelength); #endif - res = 0; - } - } + res = 0; + } + } #ifndef BUSYDETECT_TONEONLY - /* If we know the expected busy tone silent-period length, check we are in the range */ - if (res && (dsp->busy_quietlength > 0)) { - if (abs(avgsilence - dsp->busy_quietlength) > (dsp->busy_quietlength*BUSY_PAT_PERCENT/100)) { + /* If we know the expected busy tone silent-period length, check we are in the range */ + if (res && (dsp->busy_quietlength > 0)) { + if (abs(avgsilence - dsp->busy_quietlength) > (dsp->busy_quietlength*BUSY_PAT_PERCENT/100)) { #if 0 - ast_log(LOG_NOTICE, "busy detector: avgsilence of %d not close enough to desired %d\n", - avgsilence, dsp->busy_quietlength); + ast_log(LOG_NOTICE, "busy detector: avgsilence of %d not close enough to desired %d\n", + avgsilence, dsp->busy_quietlength); #endif - res = 0; - } - } + res = 0; + } + } #endif #ifndef BUSYDETECT_TONEONLY #if 1 - if (res) - ast_log(LOG_DEBUG, "ast_dsp_busydetect detected busy, avgtone: %d, avgsilence %d\n", avgtone, avgsilence); + if (res) + ast_log(LOG_DEBUG, "ast_dsp_busydetect detected busy, avgtone: %d, avgsilence %d\n", avgtone, avgsilence); #endif #endif - return res; + return res; } #endif #ifdef BUSYDETECT int ast_dsp_busydetect(struct ast_dsp *dsp) { - int x; - int res = 0; - int max, min; + int x; + int res = 0; + int max, min; #if 0 - if (dsp->busy_hits > 5); - return 0; + if (dsp->busy_hits > 5); + return 0; #endif - if (dsp->busymaybe) { + if (dsp->busymaybe) { #if 0 - printf("Maybe busy!\n"); -#endif - dsp->busymaybe = 0; - min = 9999; - max = 0; - for (x=DSP_HISTORY - dsp->busycount;xbusymaybe = 0; + min = 9999; + max = 0; + for (x=DSP_HISTORY - dsp->busycount;xhistoricsilence[x], dsp->historicnoise[x]); -#endif - if (dsp->historicsilence[x] < min) - min = dsp->historicsilence[x]; - if (dsp->historicnoise[x] < min) - min = dsp->historicnoise[x]; - if (dsp->historicsilence[x] > max) - max = dsp->historicsilence[x]; - if (dsp->historicnoise[x] > max) - max = dsp->historicnoise[x]; - } - if ((max - min < BUSY_THRESHOLD) && (max < BUSY_MAX) && (min > BUSY_MIN)) { + printf("Silence: %d, Noise: %d\n", dsp->historicsilence[x], dsp->historicnoise[x]); +#endif + if (dsp->historicsilence[x] < min) + min = dsp->historicsilence[x]; + if (dsp->historicnoise[x] < min) + min = dsp->historicnoise[x]; + if (dsp->historicsilence[x] > max) + max = dsp->historicsilence[x]; + if (dsp->historicnoise[x] > max) + max = dsp->historicnoise[x]; + } + if ((max - min < BUSY_THRESHOLD) && (max < BUSY_MAX) && (min > BUSY_MIN)) { #if 0 - printf("Busy!\n"); -#endif - res = 1; - } + printf("Busy!\n"); +#endif + res = 1; + } #if 0 - printf("Min: %d, max: %d\n", min, max); -#endif - } - return res; + printf("Min: %d, max: %d\n", min, max); +#endif + } + return res; } #endif int ast_dsp_silence(struct ast_dsp *dsp, struct ast_frame *f, int *totalsilence) { - short *s; - int len; - - if (f->frametype != AST_FRAME_VOICE) { - ast_log(LOG_WARNING, "Can't calculate silence on a non-voice frame\n"); - return 0; - } - if (f->subclass != AST_FORMAT_SLINEAR) { - ast_log(LOG_WARNING, "Can only calculate silence on signed-linear frames :(\n"); - return 0; - } - s = f->data; - len = f->datalen/2; - return __ast_dsp_silence(dsp, s, len, totalsilence); + short *s; + int len; + + if (f->frametype != AST_FRAME_VOICE) { + ast_log(LOG_WARNING, "Can't calculate silence on a non-voice frame\n"); + return 0; + } + if (f->subclass != AST_FORMAT_SLINEAR) { + ast_log(LOG_WARNING, "Can only calculate silence on signed-linear frames :(\n"); + return 0; + } + s = f->data; + len = f->datalen/2; + return __ast_dsp_silence(dsp, s, len, totalsilence); } struct ast_frame *ast_dsp_process(struct ast_channel *chan, struct ast_dsp *dsp, struct ast_frame *af) { - int silence; - int res; - int digit; - int x; - short *shortdata; - unsigned char *odata; - int len; - int writeback = 0; + int silence; + int res; + int digit; + int x; + short *shortdata; + unsigned char *odata; + int len; + int writeback = 0; #define FIX_INF(inf) do { \ - if (writeback) { \ - switch(inf->subclass) { \ - case AST_FORMAT_SLINEAR: \ - break; \ - case AST_FORMAT_ULAW: \ - for (x=0;xframetype != AST_FRAME_VOICE) - return af; - odata = af->data; - len = af->datalen; - /* Make sure we have short data */ - switch(af->subclass) { - case AST_FORMAT_SLINEAR: - shortdata = af->data; - len = af->datalen / 2; - break; - case AST_FORMAT_ULAW: - shortdata = alloca(af->datalen * 2); - for (x = 0;x < len; x++) - shortdata[x] = AST_MULAW(odata[x]); - break; - case AST_FORMAT_ALAW: - shortdata = alloca(af->datalen * 2); - for (x = 0; x < len; x++) - shortdata[x] = AST_ALAW(odata[x]); - break; - default: - ast_log(LOG_WARNING, "Inband DTMF is not supported on codec %s. Use RFC2833\n", ast_getformatname(af->subclass)); - return af; - } - silence = __ast_dsp_silence(dsp, shortdata, len, NULL); - if ((dsp->features & DSP_FEATURE_SILENCE_SUPPRESS) && silence) { - memset(&dsp->f, 0, sizeof(dsp->f)); - dsp->f.frametype = AST_FRAME_NULL; + if (writeback) { \ + switch(inf->subclass) { \ + case AST_FORMAT_SLINEAR: \ + break; \ + case AST_FORMAT_ULAW: \ + for (x=0;xframetype != AST_FRAME_VOICE) + return af; + odata = af->data; + len = af->datalen; + /* Make sure we have short data */ + switch(af->subclass) { + case AST_FORMAT_SLINEAR: + shortdata = af->data; + len = af->datalen / 2; + break; + case AST_FORMAT_ULAW: + shortdata = alloca(af->datalen * 2); + for (x = 0;x < len; x++) + shortdata[x] = AST_MULAW(odata[x]); + break; + case AST_FORMAT_ALAW: + shortdata = alloca(af->datalen * 2); + for (x = 0; x < len; x++) + shortdata[x] = AST_ALAW(odata[x]); + break; + default: + ast_log(LOG_WARNING, "Inband DTMF is not supported on codec %s. Use RFC2833\n", ast_getformatname(af->subclass)); + return af; + } + silence = __ast_dsp_silence(dsp, shortdata, len, NULL); + if ((dsp->features & DSP_FEATURE_SILENCE_SUPPRESS) && silence) { + memset(&dsp->f, 0, sizeof(dsp->f)); + dsp->f.frametype = AST_FRAME_NULL; ast_frfree(af); ast_set_flag(&dsp->f, AST_FRFLAG_FROM_DSP); - return &dsp->f; - } - if ((dsp->features & DSP_FEATURE_BUSY_DETECT) && ast_dsp_busydetect(dsp)) { - chan->_softhangup |= AST_SOFTHANGUP_DEV; - memset(&dsp->f, 0, sizeof(dsp->f)); - dsp->f.frametype = AST_FRAME_CONTROL; - dsp->f.subclass = AST_CONTROL_BUSY; + return &dsp->f; + } + if ((dsp->features & DSP_FEATURE_BUSY_DETECT) && ast_dsp_busydetect(dsp)) { + chan->_softhangup |= AST_SOFTHANGUP_DEV; + memset(&dsp->f, 0, sizeof(dsp->f)); + dsp->f.frametype = AST_FRAME_CONTROL; + dsp->f.subclass = AST_CONTROL_BUSY; ast_frfree(af); ast_set_flag(&dsp->f, AST_FRFLAG_FROM_DSP); - return &dsp->f; - } - if ((dsp->features & DSP_FEATURE_DTMF_DETECT)) { - digit = __ast_dsp_digitdetect(dsp, shortdata, len, &writeback); + return &dsp->f; + } + if ((dsp->features & DSP_FEATURE_DTMF_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 - if (dsp->digitmode & (DSP_DIGITMODE_MUTECONF | DSP_DIGITMODE_MUTEMAX)) { - if (!dsp->thinkdigit) { - if (digit) { - /* Looks like we might have something. - * Request a conference mute for the moment */ - memset(&dsp->f, 0, sizeof(dsp->f)); - dsp->f.frametype = AST_FRAME_DTMF; - dsp->f.subclass = 'm'; - dsp->thinkdigit = 'x'; - FIX_INF(af); - if (chan) - ast_queue_frame(chan, af); - ast_frfree(af); + if (digit) + printf("Performing digit detection returned %d, digitmode is %d\n", digit, dsp->digitmode); +#endif + if (dsp->digitmode & (DSP_DIGITMODE_MUTECONF | DSP_DIGITMODE_MUTEMAX)) { + if (!dsp->thinkdigit) { + if (digit) { + /* Looks like we might have something. + * Request a conference mute for the moment */ + memset(&dsp->f, 0, sizeof(dsp->f)); + dsp->f.frametype = AST_FRAME_DTMF; + dsp->f.subclass = 'm'; + dsp->thinkdigit = 'x'; + FIX_INF(af); + if (chan) + ast_queue_frame(chan, af); + ast_frfree(af); ast_set_flag(&dsp->f, AST_FRFLAG_FROM_DSP); - return &dsp->f; - } - } else { - if (digit) { - /* Thought we saw one last time. Pretty sure we really have now */ - if ((dsp->thinkdigit != 'x') && (dsp->thinkdigit != digit)) { - /* If we found a digit, and we're changing digits, go - ahead and send this one, but DON'T stop confmute because - we're detecting something else, too... */ - memset(&dsp->f, 0, sizeof(dsp->f)); - dsp->f.frametype = AST_FRAME_DTMF_END; - dsp->f.subclass = dsp->thinkdigit; - FIX_INF(af); - if (chan) - ast_queue_frame(chan, af); - ast_frfree(af); - } else { - dsp->thinkdigit = digit; - memset(&dsp->f, 0, sizeof(dsp->f)); - dsp->f.frametype = AST_FRAME_DTMF_BEGIN; - dsp->f.subclass = dsp->thinkdigit; - FIX_INF(af); - if (chan) - ast_queue_frame(chan, af); - ast_frfree(af); - } + return &dsp->f; + } + } else { + if (digit) { + /* Thought we saw one last time. Pretty sure we really have now */ + if ((dsp->thinkdigit != 'x') && (dsp->thinkdigit != digit)) { + /* If we found a digit, and we're changing digits, go + ahead and send this one, but DON'T stop confmute because + we're detecting something else, too... */ + memset(&dsp->f, 0, sizeof(dsp->f)); + dsp->f.frametype = AST_FRAME_DTMF_END; + dsp->f.subclass = dsp->thinkdigit; + FIX_INF(af); + if (chan) + ast_queue_frame(chan, af); + ast_frfree(af); + } else { + dsp->thinkdigit = digit; + memset(&dsp->f, 0, sizeof(dsp->f)); + dsp->f.frametype = AST_FRAME_DTMF_BEGIN; + dsp->f.subclass = dsp->thinkdigit; + FIX_INF(af); + if (chan) + ast_queue_frame(chan, af); + ast_frfree(af); + } ast_set_flag(&dsp->f, AST_FRFLAG_FROM_DSP); - return &dsp->f; - } else { - memset(&dsp->f, 0, sizeof(dsp->f)); - if (dsp->thinkdigit != 'x') { - /* If we found a digit, send it now */ - dsp->f.frametype = AST_FRAME_DTMF_END; - dsp->f.subclass = dsp->thinkdigit; - dsp->thinkdigit = 0; - } else { - dsp->f.frametype = AST_FRAME_DTMF; - dsp->f.subclass = 'u'; - dsp->thinkdigit = 0; - } - FIX_INF(af); - if (chan) - ast_queue_frame(chan, af); - ast_frfree(af); + return &dsp->f; + } else { + memset(&dsp->f, 0, sizeof(dsp->f)); + if (dsp->thinkdigit != 'x') { + /* If we found a digit, send it now */ + dsp->f.frametype = AST_FRAME_DTMF_END; + dsp->f.subclass = dsp->thinkdigit; + dsp->thinkdigit = 0; + } else { + dsp->f.frametype = AST_FRAME_DTMF; + dsp->f.subclass = 'u'; + dsp->thinkdigit = 0; + } + FIX_INF(af); + if (chan) + ast_queue_frame(chan, af); + ast_frfree(af); ast_set_flag(&dsp->f, AST_FRFLAG_FROM_DSP); - 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 (!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); ast_set_flag(&dsp->f, AST_FRFLAG_FROM_DSP); - 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->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); ast_set_flag(&dsp->f, AST_FRFLAG_FROM_DSP); - return &dsp->f; - } - } - } - } - if ((dsp->features & DSP_FEATURE_CALL_PROGRESS)) { - res = __ast_dsp_call_progress(dsp, shortdata, len); - if (res) { - switch(res) { - case AST_CONTROL_ANSWER: - case AST_CONTROL_BUSY: - case AST_CONTROL_RINGING: - case AST_CONTROL_CONGESTION: - case AST_CONTROL_HANGUP: - memset(&dsp->f, 0, sizeof(dsp->f)); - dsp->f.frametype = AST_FRAME_CONTROL; - dsp->f.subclass = res; - dsp->f.src = "dsp_progress"; - if (chan) - ast_queue_frame(chan, &dsp->f); - break; - default: - ast_log(LOG_WARNING, "Don't know how to represent call progress message %d\n", res); - } - } - } - FIX_INF(af); - return af; + return &dsp->f; + } + } + } + } + if ((dsp->features & DSP_FEATURE_CALL_PROGRESS)) { + res = __ast_dsp_call_progress(dsp, shortdata, len); + if (res) { + switch(res) { + case AST_CONTROL_ANSWER: + case AST_CONTROL_BUSY: + case AST_CONTROL_RINGING: + case AST_CONTROL_CONGESTION: + case AST_CONTROL_HANGUP: + memset(&dsp->f, 0, sizeof(dsp->f)); + dsp->f.frametype = AST_FRAME_CONTROL; + dsp->f.subclass = res; + dsp->f.src = "dsp_progress"; + if (chan) + ast_queue_frame(chan, &dsp->f); + break; + default: + ast_log(LOG_WARNING, "Don't know how to represent call progress message %d\n", res); + } + } + } + FIX_INF(af); + return af; } static void ast_dsp_prog_reset(struct ast_dsp *dsp) { - int max = 0; - int x; - - dsp->gsamp_size = modes[dsp->progmode].size; - dsp->gsamps = 0; - for (x=0;xprogmode].freqs) / sizeof(modes[dsp->progmode].freqs[0]);x++) { - if (modes[dsp->progmode].freqs[x]) { - goertzel_init(&dsp->freqs[x], (float)modes[dsp->progmode].freqs[x], dsp->gsamp_size); - max = x + 1; - } - } - dsp->freqcount = max; - dsp->ringtimeout= 0; + int max = 0; + int x; + + dsp->gsamp_size = modes[dsp->progmode].size; + dsp->gsamps = 0; + for (x=0;xprogmode].freqs) / sizeof(modes[dsp->progmode].freqs[0]);x++) { + if (modes[dsp->progmode].freqs[x]) { + goertzel_init(&dsp->freqs[x], (float)modes[dsp->progmode].freqs[x], dsp->gsamp_size); + max = x + 1; + } + } + dsp->freqcount = max; + dsp->ringtimeout= 0; } struct ast_dsp *ast_dsp_new(void) { - struct ast_dsp *dsp; - - if ((dsp = ast_calloc(1, sizeof(*dsp)))) { - dsp->threshold = DEFAULT_THRESHOLD; - dsp->features = DSP_FEATURE_SILENCE_SUPPRESS; - dsp->busycount = DSP_HISTORY; - /* Initialize DTMF detector */ - ast_dtmf_detect_init(&dsp->td.dtmf); - /* Initialize initial DSP progress detect parameters */ - ast_dsp_prog_reset(dsp); - } - return dsp; + struct ast_dsp *dsp; + + if ((dsp = ast_calloc(1, sizeof(*dsp)))) { + dsp->threshold = DEFAULT_THRESHOLD; + dsp->features = DSP_FEATURE_SILENCE_SUPPRESS; + dsp->busycount = DSP_HISTORY; + /* Initialize DTMF detector */ + ast_dtmf_detect_init(&dsp->td.dtmf); + /* Initialize initial DSP progress detect parameters */ + ast_dsp_prog_reset(dsp); + } + return dsp; } void ast_dsp_set_features(struct ast_dsp *dsp, int features) { - dsp->features = features; + dsp->features = features; } void ast_dsp_free(struct ast_dsp *dsp) @@ -1675,133 +1429,114 @@ void ast_dsp_free(struct ast_dsp *dsp) return; } - free(dsp); + free(dsp); } void ast_dsp_set_threshold(struct ast_dsp *dsp, int threshold) { - dsp->threshold = threshold; + dsp->threshold = threshold; } void ast_dsp_set_busy_count(struct ast_dsp *dsp, int cadences) { - if (cadences < 4) - cadences = 4; - if (cadences > DSP_HISTORY) - cadences = DSP_HISTORY; - dsp->busycount = cadences; + if (cadences < 4) + cadences = 4; + if (cadences > DSP_HISTORY) + cadences = DSP_HISTORY; + dsp->busycount = cadences; } void ast_dsp_set_busy_pattern(struct ast_dsp *dsp, int tonelength, int quietlength) { - dsp->busy_tonelength = tonelength; - dsp->busy_quietlength = quietlength; - ast_log(LOG_DEBUG, "dsp busy pattern set to %d,%d\n", tonelength, quietlength); + dsp->busy_tonelength = tonelength; + dsp->busy_quietlength = quietlength; + ast_log(LOG_DEBUG, "dsp busy pattern set to %d,%d\n", tonelength, quietlength); } void ast_dsp_digitreset(struct ast_dsp *dsp) { - int i; - - 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; - /* Reinitialise the detector for the next block */ - for (i = 0; i < 6; i++) { - goertzel_reset(&dsp->td.mf.tone_out[i]); -#ifdef OLD_DSP_ROUTINES - goertzel_reset(&dsp->td.mf.tone_out2nd[i]); -#endif - } -#ifdef OLD_DSP_ROUTINES - dsp->td.mf.energy = 0.0; - dsp->td.mf.hit1 = dsp->td.mf.hit2 = dsp->td.mf.hit3 = dsp->td.mf.hit4 = dsp->td.mf.mhit = 0; -#else - 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; -#endif - dsp->td.mf.current_sample = 0; - } else { - memset(dsp->td.dtmf.digits, 0, sizeof(dsp->td.dtmf.digits)); - dsp->td.dtmf.current_digits = 0; - /* 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]); -#ifdef OLD_DSP_ROUTINES - goertzel_reset(&dsp->td.dtmf.row_out2nd[i]); - goertzel_reset(&dsp->td.dtmf.col_out2nd[i]); -#endif - } + int i; + + 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; + /* Reinitialise the detector for the next block */ + for (i = 0; i < 6; i++) { + goertzel_reset(&dsp->td.mf.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; + } else { + memset(dsp->td.dtmf.digits, 0, sizeof(dsp->td.dtmf.digits)); + dsp->td.dtmf.current_digits = 0; + /* 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]); + } #ifdef FAX_DETECT - goertzel_reset (&dsp->td.dtmf.fax_tone); + goertzel_reset (&dsp->td.dtmf.fax_tone); #endif -#ifdef OLD_DSP_ROUTINES -#ifdef FAX_DETECT - goertzel_reset (&dsp->td.dtmf.fax_tone2nd); -#endif - dsp->td.dtmf.hit1 = dsp->td.dtmf.hit2 = dsp->td.dtmf.hit3 = dsp->td.dtmf.hit4 = dsp->td.dtmf.mhit = 0; -#else - dsp->td.dtmf.lasthit = dsp->td.dtmf.mhit = 0; -#endif - dsp->td.dtmf.energy = 0.0; - dsp->td.dtmf.current_sample = 0; - } + dsp->td.dtmf.hits[2] = dsp->td.dtmf.hits[1] = dsp->td.dtmf.hits[0] = dsp->td.dtmf.mhit = 0; + dsp->td.dtmf.energy = 0.0; + dsp->td.dtmf.current_sample = 0; + } } void ast_dsp_reset(struct ast_dsp *dsp) { - int x; - - dsp->totalsilence = 0; - dsp->gsamps = 0; - for (x=0;x<4;x++) - dsp->freqs[x].v2 = dsp->freqs[x].v3 = 0.0; - memset(dsp->historicsilence, 0, sizeof(dsp->historicsilence)); - memset(dsp->historicnoise, 0, sizeof(dsp->historicnoise)); - dsp->ringtimeout= 0; + int x; + + dsp->totalsilence = 0; + dsp->gsamps = 0; + for (x=0;x<4;x++) + dsp->freqs[x].v2 = dsp->freqs[x].v3 = 0.0; + memset(dsp->historicsilence, 0, sizeof(dsp->historicsilence)); + memset(dsp->historicnoise, 0, sizeof(dsp->historicnoise)); + dsp->ringtimeout= 0; } int ast_dsp_digitmode(struct ast_dsp *dsp, int digitmode) { - int new; - int old; - - old = dsp->digitmode & (DSP_DIGITMODE_DTMF | DSP_DIGITMODE_MF | DSP_DIGITMODE_MUTECONF | DSP_DIGITMODE_MUTEMAX); - 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); - } - dsp->digitmode = digitmode; - return 0; + int new; + int old; + + old = dsp->digitmode & (DSP_DIGITMODE_DTMF | DSP_DIGITMODE_MF | DSP_DIGITMODE_MUTECONF | DSP_DIGITMODE_MUTEMAX); + 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); + } + dsp->digitmode = digitmode; + return 0; } int ast_dsp_set_call_progress_zone(struct ast_dsp *dsp, char *zone) { - int x; - - for (x=0;xprogmode = aliases[x].mode; - ast_dsp_prog_reset(dsp); - return 0; - } - } - return -1; + int x; + + for (x=0;xprogmode = aliases[x].mode; + ast_dsp_prog_reset(dsp); + return 0; + } + } + return -1; } int ast_dsp_get_tstate(struct ast_dsp *dsp) { - return dsp->tstate; + return dsp->tstate; } int ast_dsp_get_tcount(struct ast_dsp *dsp) { - return dsp->tcount; + return dsp->tcount; } void ast_dsp_frame_freed(struct ast_frame *fr) @@ -1817,3 +1552,44 @@ void ast_dsp_frame_freed(struct ast_fram ast_dsp_free(dsp); } + +void dtmf_test(char* file) { + + short *samples; // raw 16-bit signed linear 8Khz PCM + int len; // # of samples + int writeback = 0; // ignore + + struct ast_dsp * dsp = ast_dsp_new(); + + + int fd = open(file, 0); + if (fd < 0) { + printf("can't open file\n"); + + return; + } + + struct stat file_stat; + if (fstat(fd, &file_stat)) { + printf("can't fstat file\n"); + return; + } + + len = file_stat.st_size / 2; + samples = (short *)calloc(len, sizeof(short)); + + int rc = read(fd, (void *)samples, len*2); + if (rc < (len*2-2)) { + printf("error rc=%d\n", rc); + return; + } + + printf("%d samples\n", len); + + int digit = dtmf_detect(&dsp->td.dtmf, &samples[64], len-70, 0, &writeback, 0); + printf("dtmf_detect returned %d\n", digit); + + char buf[1024]; + ast_dsp_getdigits(dsp, buf, 100); + printf("digits=[%s]\n", buf); +} diff -urpN asterisk-1.4.22.2/main/frame.c asterisk-1.4.22.2.tcl/main/frame.c --- asterisk-1.4.22.2/main/frame.c 2008-08-22 16:23:02.000000000 -0300 +++ asterisk-1.4.22.2.tcl/main/frame.c 2009-03-14 15:30:11.000000000 -0300 @@ -200,7 +200,7 @@ int __ast_smoother_feed(struct ast_smoot } if (s->flags & AST_SMOOTHER_FLAG_G729) { if (s->len % 10) { - ast_log(LOG_NOTICE, "Dropping extra frame of G.729 since we already have a VAD frame at the end\n"); + ast_log(LOG_DEBUG, "Dropping extra frame of G.729 since we already have a VAD frame at the end\n"); return 0; } } diff -urpN asterisk-1.4.22.2/main/pbx.c asterisk-1.4.22.2.tcl/main/pbx.c --- asterisk-1.4.22.2/main/pbx.c 2008-09-08 18:02:36.000000000 -0300 +++ asterisk-1.4.22.2.tcl/main/pbx.c 2009-03-14 15:30:11.000000000 -0300 @@ -1850,15 +1850,15 @@ static int pbx_extension_helper(struct a term_color(tmp3, passdata, COLOR_BRMAGENTA, 0, sizeof(tmp3)), "in new stack"); } - manager_event(EVENT_FLAG_CALL, "Newexten", - "Channel: %s\r\n" - "Context: %s\r\n" - "Extension: %s\r\n" - "Priority: %d\r\n" - "Application: %s\r\n" - "AppData: %s\r\n" - "Uniqueid: %s\r\n", - c->name, c->context, c->exten, c->priority, app->name, passdata, c->uniqueid); +/* manager_event(EVENT_FLAG_CALL, "Newexten", */ +/* "Channel: %s\r\n" */ +/* "Context: %s\r\n" */ +/* "Extension: %s\r\n" */ +/* "Priority: %d\r\n" */ +/* "Application: %s\r\n" */ +/* "AppData: %s\r\n" */ +/* "Uniqueid: %s\r\n", */ +/* c->name, c->context, c->exten, c->priority, app->name, passdata, c->uniqueid); */ return pbx_exec(c, app, passdata); /* 0 on success, -1 on failure */ } } else if (q.swo) { /* not found here, but in another switch */ diff -urpN asterisk-1.4.22.2/Makefile asterisk-1.4.22.2.tcl/Makefile --- asterisk-1.4.22.2/Makefile 2008-09-08 17:15:42.000000000 -0300 +++ asterisk-1.4.22.2.tcl/Makefile 2009-03-14 15:30:11.000000000 -0300 @@ -26,6 +26,9 @@ # # $ ASTCFLAGS="-Werror" make +# rjm added +ASTLDFLAGS+=-lglib-2.0 + export ASTTOPDIR export ASTERISKVERSION export ASTERISKVERSIONNUM diff -urpN asterisk-1.4.22.2/res/res_config_odbc.c asterisk-1.4.22.2.tcl/res/res_config_odbc.c --- asterisk-1.4.22.2/res/res_config_odbc.c 2008-07-10 18:19:48.000000000 -0300 +++ asterisk-1.4.22.2.tcl/res/res_config_odbc.c 2009-03-14 15:30:11.000000000 -0300 @@ -85,7 +85,7 @@ static SQLHSTMT custom_prepare(struct od while ((newparam = va_arg(ap, const char *))) { newval = va_arg(ap, const char *); - SQLBindParameter(stmt, x++, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(newval), 0, (void *)newval, 0, NULL); + if (newval) SQLBindParameter(stmt, x++, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(newval), 0, (void *)newval, 0, NULL); } va_end(ap); @@ -476,6 +476,7 @@ static struct ast_config *config_odbc(co ast_build_string(&sql, &sqlleft, "SELECT cat_metric, category, var_name, var_val FROM %s ", table); ast_build_string(&sql, &sqlleft, "WHERE filename='%s' AND commented=0 ", file); + ast_build_string(&sql, &sqlleft, "AND (systemname IS NULL OR systemname='%s') ", ast_config_AST_SYSTEM_NAME); ast_build_string(&sql, &sqlleft, "ORDER BY cat_metric DESC, var_metric ASC, category, var_name "); q.sql = sqlbuf; diff -urpN asterisk-1.4.22.2/res/res_odbc.c asterisk-1.4.22.2.tcl/res/res_odbc.c --- asterisk-1.4.22.2/res/res_odbc.c 2008-08-10 21:20:38.000000000 -0300 +++ asterisk-1.4.22.2.tcl/res/res_odbc.c 2009-03-14 15:30:11.000000000 -0300 @@ -78,6 +78,10 @@ static odbc_status odbc_obj_connect(stru static odbc_status odbc_obj_disconnect(struct odbc_obj *obj); static int odbc_register_class(struct odbc_class *class, int connect); +static struct odbc_obj *ast_odbc_request_obj_int(const char *name, int check); + +static time_t fo_until = 0; /* */ +static const time_t FO_CCACHE_SEC = 80; /* cache primary odbc errors for this long before retrying the connection */ SQLHSTMT ast_odbc_prepare_and_execute(struct odbc_obj *obj, SQLHSTMT (*prepare_cb)(struct odbc_obj *obj, void *data), void *data) { @@ -93,7 +97,7 @@ SQLHSTMT ast_odbc_prepare_and_execute(st * when we disconnect, all handles become invalid for most databases. * We must therefore redo everything when we establish a new * connection. */ - stmt = prepare_cb(obj, data); + if (obj && data) stmt = prepare_cb(obj, data); if (stmt) { res = SQLExecute(stmt); @@ -398,7 +402,7 @@ int ast_odbc_backslash_is_escape(struct return obj->parent->backslash_is_escape; } -struct odbc_obj *ast_odbc_request_obj(const char *name, int check) +struct odbc_obj *ast_odbc_request_obj_int(const char *name, int check) { struct odbc_obj *obj = NULL; struct odbc_class *class; @@ -479,6 +483,80 @@ struct odbc_obj *ast_odbc_request_obj(co return obj; } +struct odbc_obj *ast_odbc_request_obj(const char *name, int check) +{ + /** + * Acquire odbc connection object, optionally checking connection validity + * first. + * + * Get obj from ast_odbc_request_obj([primary]): + * If obj != null: + * if obj->up: return obj (probable that connection is good, or nearly 100% chance connection is good if check==1) + * else: + * attempt reconnect on obj + * if obj != null && obj->up: return obj (probable that connection is good, or nearly 100% chance connection is good if check==1) + * else ast_odbc_release_obj(obj); + * Get obj from ast_odbc_request_obj([failover]): + * If obj != null: + * if obj->up: return obj (probable that connection is good) + * else: + * attempt reconnect on obj + * if obj != null && obj->up: return obj (probable that connection is good) + * else ast_odbc_release_obj(obj); + * return null + * + * + */ + + struct odbc_obj *obj = NULL; + + char name2[128] = ""; + snprintf(name2, sizeof(name2), "%s-failover", name); + + if (fo_until == 0 || fo_until < time(NULL)) { + + obj = ast_odbc_request_obj_int(name, check); + if (obj) { + if (obj->up) { + fo_until = 0; + return obj; + } + else { + ast_log(LOG_WARNING, "obj0 not up, attempting reconnect\n"); + odbc_obj_disconnect(obj); + if (odbc_obj_connect(obj)==ODBC_SUCCESS && obj->up) { + fo_until = 0; + return obj; + } + else ast_odbc_release_obj(obj); + } + } + + ast_log(LOG_WARNING, "res_odbc: primary class '%s' unavailable, trying failover class '%s'\n", name, name2); + fo_until = time(NULL) + FO_CCACHE_SEC; + } + + + obj = ast_odbc_request_obj_int(name2, check); + if (obj) { + if (obj->up) { + return obj; + } + else { + ast_log(LOG_WARNING, "obj1 not up, attempting reconnect\n"); + odbc_obj_disconnect(obj); + if (odbc_obj_connect(obj)==ODBC_SUCCESS && obj->up) { + return obj; + } + else ast_odbc_release_obj(obj); + } + } + + ast_log(LOG_WARNING, "failover failed too!\n"); + fo_until = 0; // go back to trying primary first + return NULL; +} + static odbc_status odbc_obj_disconnect(struct odbc_obj *obj) { int res;