/* * Asterisk -- A telephony toolkit for Linux. * * Copyright (C) 1999-2004, Digium, Inc. * * Mark Spencer * * MachineDetect Applicaiton by Ben Hencke * Version 1.01 2005-02-11 - attempts to work correctly when blocking on ast_waitfor() * for VOIP channels that remove silence. * * Largely based on WaitForSilence by: * * WaitForSilence Application by David C. Troy * Version 1.00 2004-01-29 * * This program is free software, distributed under the terms of * the GNU General Public License */ #include #include #include #include #include #include #include #include #include #include #include static char *tdesc = "Machine Detect"; static char *app = "MachineDetect"; static char *synopsis = "Detects answering machines and waits for silence"; static char *descrip = "MachineDetect(x|y|z) Waits for answering machine greetings to finish and sets a flag.\n" " Waits for 'x' milliseconds of silence, 'y' times.\n" " ${MACHINE} will be set to 1 if 'z' miliseconds of total (non-continuous)\n" " audio noise is detected.\n" " It will not clear the flag, so it is possible to run through several\n" " calls to the application with different detection settings in sequence\n" "Examples:\n" " - MachineDetect(700,2,2200) will detect most machines. (default)\n" " - MachineDetect(500,3,2200) will get through a \"hello, hello, hello\"\n"; STANDARD_LOCAL_USER; LOCAL_USER_DECL; static int do_waiting(struct ast_channel *chan, int duration, int *totalnonsilence) { struct ast_frame *f; int totalsilence = 0; int dspsilence = 0; int gotsilence = 0; static int silencethreshold = 40; int rfmt = 0; int res = 0; struct ast_dsp *sildet; /* silence detector dsp */ struct timeval starttime,endtime; int toduration, newduration, diff; rfmt = chan->readformat; /* Set to linear mode */ res = ast_set_read_format(chan, AST_FORMAT_SLINEAR); if (res < 0) { ast_log(LOG_WARNING, "Unable to set to linear mode, giving up\n"); return -1; } sildet = ast_dsp_new(); /* Create the silence detector */ if (!sildet) { ast_log(LOG_WARNING, "Unable to create silence detector :(\n"); return -1; } ast_dsp_set_threshold(sildet, silencethreshold); //wait for the channel up to duration miliseconds // Await silence... f = NULL; toduration = duration; for(;;) { //if we dont get the channel, then we can assume it is silence strpped and just blocks on the read gettimeofday(&starttime,0); res = ast_waitfor(chan, toduration); //see if it timed out (ie we got all the silence we needed) if (res <= 0) { ast_verbose(VERBOSE_PREFIX_3 "Bailing out, timeout on ast_waitfor(chan, %d). Must have got the silence we needed\n", duration); gotsilence = 1; break; } //if we didnt time out, and chan is avail //then calc diff silence to check for and run the dsp gettimeofday(&endtime,0); //calc diff in ms endtime.tv_usec -= starttime.tv_usec; endtime.tv_sec -= starttime.tv_sec; if (endtime.tv_usec < 0) { endtime.tv_usec += 1000000; endtime.tv_sec--; } diff = (endtime.tv_sec*1000 + endtime.tv_usec/1000); newduration = duration - diff; //read this packet f = ast_read(chan); if (!f) break; if (f->frametype == AST_FRAME_VOICE ) { //&& f->subclass == AST_FORMAT_SLINEAR) { //FIXME is this needed? dspsilence = 0; ast_dsp_silence(sildet, f, &dspsilence); if (dspsilence) { //dsp silence is accumulated silence totalsilence = dspsilence; //reduce the next timeout to account for the transmitted silence toduration -= dspsilence; } else { //reset, and wait for more silence totalsilence = 0; toduration = duration; *totalnonsilence += f->samples/8; //samples is in 8000hz. a 160 sample = 20ms } //ast_verbose(VERBOSE_PREFIX_3 "dspsilence: %d, newduration: %d\n cur frame: %d", dspsilence, newduration, f->samples); if (totalsilence >= newduration) { ast_verbose(VERBOSE_PREFIX_3 "exiting with %d silence > %d required\n", totalsilence, newduration); // Ended happily with silence ast_frfree(f); gotsilence = 1; break; } } ast_frfree(f); } ast_dsp_free(sildet); return gotsilence; } static int machinedetect_exec(struct ast_channel *chan, void *data) { int res=1; struct localuser *u; int duration = 700; int iterations = 2, i; int totalnonsilence = 0; int machinetime = 2200; res = ast_answer(chan); // Answer the channel if (data) { if (sscanf(data, "%d|%d|%d", &duration, &iterations, &machinetime) != 3) { ast_log(LOG_WARNING, "Using default value of 2200ms for machine threshold\n"); } else if (sscanf(data, "%d|%d", &duration, &iterations) != 2) { ast_log(LOG_WARNING, "Using default value of 2 iterations\n"); } else if (sscanf(data, "%d", &duration) != 1) { ast_log(LOG_WARNING, "Using default silence value of 700ms\n"); } } ast_verbose(VERBOSE_PREFIX_3 "Waiting %d time(s) for %d ms silence. Machine threshhold is %d\n", iterations, duration, machinetime); LOCAL_USER_ADD(u); res = 1; for ( i=0; (i= machinetime) { ast_verbose(VERBOSE_PREFIX_3 "Detected answering machine. Threshhold is %d and got %d\n",machinetime, totalnonsilence); pbx_builtin_setvar_helper (chan, "MACHINE", "1"); } else { ast_verbose(VERBOSE_PREFIX_3 "Detected person. Threshhold is %d and got %d\n",machinetime, totalnonsilence); } LOCAL_USER_REMOVE(u); if (res > 0) res = 0; return res; } int unload_module(void) { STANDARD_HANGUP_LOCALUSERS; return ast_unregister_application(app); } int load_module(void) { return ast_register_application(app, machinedetect_exec, synopsis, descrip); } char *description(void) { return tdesc; } int usecount(void) { int res; STANDARD_USECOUNT(res); return res; } char *key() { return ASTERISK_GPL_KEY; }