app_talkdetect.c 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257
  1. /*
  2. * Asterisk -- An open source telephony toolkit.
  3. *
  4. * Copyright (C) 1999 - 2005, Digium, Inc.
  5. *
  6. * Mark Spencer <markster@digium.com>
  7. *
  8. * See http://www.asterisk.org for more information about
  9. * the Asterisk project. Please do not directly contact
  10. * any of the maintainers of this project for assistance;
  11. * the project provides a web site, mailing lists and IRC
  12. * channels for your use.
  13. *
  14. * This program is free software, distributed under the terms of
  15. * the GNU General Public License Version 2. See the LICENSE file
  16. * at the top of the source tree.
  17. */
  18. /*! \file
  19. *
  20. * \brief Playback a file with audio detect
  21. *
  22. * \author Mark Spencer <markster@digium.com>
  23. *
  24. * \ingroup applications
  25. */
  26. /*** MODULEINFO
  27. <support_level>extended</support_level>
  28. ***/
  29. #include "asterisk.h"
  30. ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
  31. #include "asterisk/lock.h"
  32. #include "asterisk/file.h"
  33. #include "asterisk/channel.h"
  34. #include "asterisk/pbx.h"
  35. #include "asterisk/module.h"
  36. #include "asterisk/translate.h"
  37. #include "asterisk/utils.h"
  38. #include "asterisk/dsp.h"
  39. #include "asterisk/app.h"
  40. /*** DOCUMENTATION
  41. <application name="BackgroundDetect" language="en_US">
  42. <synopsis>
  43. Background a file with talk detect.
  44. </synopsis>
  45. <syntax>
  46. <parameter name="filename" required="true" />
  47. <parameter name="sil">
  48. <para>If not specified, defaults to <literal>1000</literal>.</para>
  49. </parameter>
  50. <parameter name="min">
  51. <para>If not specified, defaults to <literal>100</literal>.</para>
  52. </parameter>
  53. <parameter name="max">
  54. <para>If not specified, defaults to <literal>infinity</literal>.</para>
  55. </parameter>
  56. <parameter name="analysistime">
  57. <para>If not specified, defaults to <literal>infinity</literal>.</para>
  58. </parameter>
  59. </syntax>
  60. <description>
  61. <para>Plays back <replaceable>filename</replaceable>, waiting for interruption from a given digit (the digit
  62. must start the beginning of a valid extension, or it will be ignored). During
  63. the playback of the file, audio is monitored in the receive direction, and if
  64. a period of non-silence which is greater than <replaceable>min</replaceable> ms yet less than
  65. <replaceable>max</replaceable> ms is followed by silence for at least <replaceable>sil</replaceable> ms,
  66. which occurs during the first <replaceable>analysistime</replaceable> ms, then the audio playback is
  67. aborted and processing jumps to the <replaceable>talk</replaceable> extension, if available.</para>
  68. </description>
  69. </application>
  70. ***/
  71. static char *app = "BackgroundDetect";
  72. static int background_detect_exec(struct ast_channel *chan, const char *data)
  73. {
  74. int res = 0;
  75. char *tmp;
  76. struct ast_frame *fr;
  77. int notsilent = 0;
  78. struct timeval start = { 0, 0 };
  79. struct timeval detection_start = { 0, 0 };
  80. int sil = 1000;
  81. int min = 100;
  82. int max = -1;
  83. int analysistime = -1;
  84. int continue_analysis = 1;
  85. int x;
  86. int origrformat = 0;
  87. struct ast_dsp *dsp = NULL;
  88. AST_DECLARE_APP_ARGS(args,
  89. AST_APP_ARG(filename);
  90. AST_APP_ARG(silence);
  91. AST_APP_ARG(min);
  92. AST_APP_ARG(max);
  93. AST_APP_ARG(analysistime);
  94. );
  95. if (ast_strlen_zero(data)) {
  96. ast_log(LOG_WARNING, "BackgroundDetect requires an argument (filename)\n");
  97. return -1;
  98. }
  99. tmp = ast_strdupa(data);
  100. AST_STANDARD_APP_ARGS(args, tmp);
  101. if (!ast_strlen_zero(args.silence) && (sscanf(args.silence, "%30d", &x) == 1) && (x > 0)) {
  102. sil = x;
  103. }
  104. if (!ast_strlen_zero(args.min) && (sscanf(args.min, "%30d", &x) == 1) && (x > 0)) {
  105. min = x;
  106. }
  107. if (!ast_strlen_zero(args.max) && (sscanf(args.max, "%30d", &x) == 1) && (x > 0)) {
  108. max = x;
  109. }
  110. if (!ast_strlen_zero(args.analysistime) && (sscanf(args.analysistime, "%30d", &x) == 1) && (x > 0)) {
  111. analysistime = x;
  112. }
  113. ast_debug(1, "Preparing detect of '%s', sil=%d, min=%d, max=%d, analysistime=%d\n", args.filename, sil, min, max, analysistime);
  114. do {
  115. if (chan->_state != AST_STATE_UP) {
  116. if ((res = ast_answer(chan))) {
  117. break;
  118. }
  119. }
  120. origrformat = chan->readformat;
  121. if ((ast_set_read_format(chan, AST_FORMAT_SLINEAR))) {
  122. ast_log(LOG_WARNING, "Unable to set read format to linear!\n");
  123. res = -1;
  124. break;
  125. }
  126. if (!(dsp = ast_dsp_new())) {
  127. ast_log(LOG_WARNING, "Unable to allocate DSP!\n");
  128. res = -1;
  129. break;
  130. }
  131. ast_stopstream(chan);
  132. if (ast_streamfile(chan, tmp, chan->language)) {
  133. ast_log(LOG_WARNING, "ast_streamfile failed on %s for %s\n", chan->name, (char *)data);
  134. break;
  135. }
  136. detection_start = ast_tvnow();
  137. while (chan->stream) {
  138. res = ast_sched_wait(chan->sched);
  139. if ((res < 0) && !chan->timingfunc) {
  140. res = 0;
  141. break;
  142. }
  143. if (res < 0) {
  144. res = 1000;
  145. }
  146. res = ast_waitfor(chan, res);
  147. if (res < 0) {
  148. ast_log(LOG_WARNING, "Waitfor failed on %s\n", chan->name);
  149. break;
  150. } else if (res > 0) {
  151. fr = ast_read(chan);
  152. if (continue_analysis && analysistime >= 0) {
  153. /* If we have a limit for the time to analyze voice
  154. * frames and the time has not expired */
  155. if (ast_tvdiff_ms(ast_tvnow(), detection_start) >= analysistime) {
  156. continue_analysis = 0;
  157. ast_verb(3, "BackgroundDetect: Talk analysis time complete on %s.\n", chan->name);
  158. }
  159. }
  160. if (!fr) {
  161. res = -1;
  162. break;
  163. } else if (fr->frametype == AST_FRAME_DTMF) {
  164. char t[2];
  165. t[0] = fr->subclass.integer;
  166. t[1] = '\0';
  167. if (ast_canmatch_extension(chan, chan->context, t, 1,
  168. S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
  169. /* They entered a valid extension, or might be anyhow */
  170. res = fr->subclass.integer;
  171. ast_frfree(fr);
  172. break;
  173. }
  174. } else if ((fr->frametype == AST_FRAME_VOICE) && (fr->subclass.codec == AST_FORMAT_SLINEAR) && continue_analysis) {
  175. int totalsilence;
  176. int ms;
  177. res = ast_dsp_silence(dsp, fr, &totalsilence);
  178. if (res && (totalsilence > sil)) {
  179. /* We've been quiet a little while */
  180. if (notsilent) {
  181. /* We had heard some talking */
  182. ms = ast_tvdiff_ms(ast_tvnow(), start);
  183. ms -= sil;
  184. if (ms < 0)
  185. ms = 0;
  186. if ((ms > min) && ((max < 0) || (ms < max))) {
  187. char ms_str[12];
  188. ast_debug(1, "Found qualified token of %d ms\n", ms);
  189. /* Save detected talk time (in milliseconds) */
  190. snprintf(ms_str, sizeof(ms_str), "%d", ms);
  191. pbx_builtin_setvar_helper(chan, "TALK_DETECTED", ms_str);
  192. ast_goto_if_exists(chan, chan->context, "talk", 1);
  193. res = 0;
  194. ast_frfree(fr);
  195. break;
  196. } else {
  197. ast_debug(1, "Found unqualified token of %d ms\n", ms);
  198. }
  199. notsilent = 0;
  200. }
  201. } else {
  202. if (!notsilent) {
  203. /* Heard some audio, mark the begining of the token */
  204. start = ast_tvnow();
  205. ast_debug(1, "Start of voice token!\n");
  206. notsilent = 1;
  207. }
  208. }
  209. }
  210. ast_frfree(fr);
  211. }
  212. ast_sched_runq(chan->sched);
  213. }
  214. ast_stopstream(chan);
  215. } while (0);
  216. if (res > -1) {
  217. if (origrformat && ast_set_read_format(chan, origrformat)) {
  218. ast_log(LOG_WARNING, "Failed to restore read format for %s to %s\n",
  219. chan->name, ast_getformatname(origrformat));
  220. }
  221. }
  222. if (dsp) {
  223. ast_dsp_free(dsp);
  224. }
  225. return res;
  226. }
  227. static int unload_module(void)
  228. {
  229. return ast_unregister_application(app);
  230. }
  231. static int load_module(void)
  232. {
  233. return ast_register_application_xml(app, background_detect_exec);
  234. }
  235. AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Playback with Talk Detection");