app_talkdetect.c 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259
  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>core</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. #include "asterisk/format.h"
  41. #include "asterisk/format_cache.h"
  42. /*** DOCUMENTATION
  43. <application name="BackgroundDetect" language="en_US">
  44. <synopsis>
  45. Background a file with talk detect.
  46. </synopsis>
  47. <syntax>
  48. <parameter name="filename" required="true" />
  49. <parameter name="sil">
  50. <para>If not specified, defaults to <literal>1000</literal>.</para>
  51. </parameter>
  52. <parameter name="min">
  53. <para>If not specified, defaults to <literal>100</literal>.</para>
  54. </parameter>
  55. <parameter name="max">
  56. <para>If not specified, defaults to <literal>infinity</literal>.</para>
  57. </parameter>
  58. <parameter name="analysistime">
  59. <para>If not specified, defaults to <literal>infinity</literal>.</para>
  60. </parameter>
  61. </syntax>
  62. <description>
  63. <para>Plays back <replaceable>filename</replaceable>, waiting for interruption from a given digit (the digit
  64. must start the beginning of a valid extension, or it will be ignored). During
  65. the playback of the file, audio is monitored in the receive direction, and if
  66. a period of non-silence which is greater than <replaceable>min</replaceable> ms yet less than
  67. <replaceable>max</replaceable> ms is followed by silence for at least <replaceable>sil</replaceable> ms,
  68. which occurs during the first <replaceable>analysistime</replaceable> ms, then the audio playback is
  69. aborted and processing jumps to the <replaceable>talk</replaceable> extension, if available.</para>
  70. </description>
  71. </application>
  72. ***/
  73. static char *app = "BackgroundDetect";
  74. static int background_detect_exec(struct ast_channel *chan, const char *data)
  75. {
  76. int res = 0;
  77. char *tmp;
  78. struct ast_frame *fr;
  79. int notsilent = 0;
  80. struct timeval start = { 0, 0 };
  81. struct timeval detection_start = { 0, 0 };
  82. int sil = 1000;
  83. int min = 100;
  84. int max = -1;
  85. int analysistime = -1;
  86. int continue_analysis = 1;
  87. int x;
  88. RAII_VAR(struct ast_format *, origrformat, NULL, ao2_cleanup);
  89. struct ast_dsp *dsp = NULL;
  90. AST_DECLARE_APP_ARGS(args,
  91. AST_APP_ARG(filename);
  92. AST_APP_ARG(silence);
  93. AST_APP_ARG(min);
  94. AST_APP_ARG(max);
  95. AST_APP_ARG(analysistime);
  96. );
  97. if (ast_strlen_zero(data)) {
  98. ast_log(LOG_WARNING, "BackgroundDetect requires an argument (filename)\n");
  99. return -1;
  100. }
  101. tmp = ast_strdupa(data);
  102. AST_STANDARD_APP_ARGS(args, tmp);
  103. if (!ast_strlen_zero(args.silence) && (sscanf(args.silence, "%30d", &x) == 1) && (x > 0)) {
  104. sil = x;
  105. }
  106. if (!ast_strlen_zero(args.min) && (sscanf(args.min, "%30d", &x) == 1) && (x > 0)) {
  107. min = x;
  108. }
  109. if (!ast_strlen_zero(args.max) && (sscanf(args.max, "%30d", &x) == 1) && (x > 0)) {
  110. max = x;
  111. }
  112. if (!ast_strlen_zero(args.analysistime) && (sscanf(args.analysistime, "%30d", &x) == 1) && (x > 0)) {
  113. analysistime = x;
  114. }
  115. ast_debug(1, "Preparing detect of '%s', sil=%d, min=%d, max=%d, analysistime=%d\n", args.filename, sil, min, max, analysistime);
  116. do {
  117. if (ast_channel_state(chan) != AST_STATE_UP) {
  118. if ((res = ast_answer(chan))) {
  119. break;
  120. }
  121. }
  122. origrformat = ao2_bump(ast_channel_readformat(chan));
  123. if ((ast_set_read_format(chan, ast_format_slin))) {
  124. ast_log(LOG_WARNING, "Unable to set read format to linear!\n");
  125. res = -1;
  126. break;
  127. }
  128. if (!(dsp = ast_dsp_new())) {
  129. ast_log(LOG_WARNING, "Unable to allocate DSP!\n");
  130. res = -1;
  131. break;
  132. }
  133. ast_stopstream(chan);
  134. if (ast_streamfile(chan, tmp, ast_channel_language(chan))) {
  135. ast_log(LOG_WARNING, "ast_streamfile failed on %s for %s\n", ast_channel_name(chan), (char *)data);
  136. break;
  137. }
  138. detection_start = ast_tvnow();
  139. while (ast_channel_stream(chan)) {
  140. res = ast_sched_wait(ast_channel_sched(chan));
  141. if ((res < 0) && !ast_channel_timingfunc(chan)) {
  142. res = 0;
  143. break;
  144. }
  145. if (res < 0) {
  146. res = 1000;
  147. }
  148. res = ast_waitfor(chan, res);
  149. if (res < 0) {
  150. ast_log(LOG_WARNING, "Waitfor failed on %s\n", ast_channel_name(chan));
  151. break;
  152. } else if (res > 0) {
  153. fr = ast_read(chan);
  154. if (continue_analysis && analysistime >= 0) {
  155. /* If we have a limit for the time to analyze voice
  156. * frames and the time has not expired */
  157. if (ast_tvdiff_ms(ast_tvnow(), detection_start) >= analysistime) {
  158. continue_analysis = 0;
  159. ast_verb(3, "BackgroundDetect: Talk analysis time complete on %s.\n", ast_channel_name(chan));
  160. }
  161. }
  162. if (!fr) {
  163. res = -1;
  164. break;
  165. } else if (fr->frametype == AST_FRAME_DTMF) {
  166. char t[2];
  167. t[0] = fr->subclass.integer;
  168. t[1] = '\0';
  169. if (ast_canmatch_extension(chan, ast_channel_context(chan), t, 1,
  170. S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL))) {
  171. /* They entered a valid extension, or might be anyhow */
  172. res = fr->subclass.integer;
  173. ast_frfree(fr);
  174. break;
  175. }
  176. } else if ((fr->frametype == AST_FRAME_VOICE) &&
  177. (ast_format_cmp(fr->subclass.format, ast_format_slin) == AST_FORMAT_CMP_EQUAL) && continue_analysis) {
  178. int totalsilence;
  179. int ms;
  180. res = ast_dsp_silence(dsp, fr, &totalsilence);
  181. if (res && (totalsilence > sil)) {
  182. /* We've been quiet a little while */
  183. if (notsilent) {
  184. /* We had heard some talking */
  185. ms = ast_tvdiff_ms(ast_tvnow(), start);
  186. ms -= sil;
  187. if (ms < 0)
  188. ms = 0;
  189. if ((ms > min) && ((max < 0) || (ms < max))) {
  190. char ms_str[12];
  191. ast_debug(1, "Found qualified token of %d ms\n", ms);
  192. /* Save detected talk time (in milliseconds) */
  193. snprintf(ms_str, sizeof(ms_str), "%d", ms);
  194. pbx_builtin_setvar_helper(chan, "TALK_DETECTED", ms_str);
  195. ast_goto_if_exists(chan, ast_channel_context(chan), "talk", 1);
  196. res = 0;
  197. ast_frfree(fr);
  198. break;
  199. } else {
  200. ast_debug(1, "Found unqualified token of %d ms\n", ms);
  201. }
  202. notsilent = 0;
  203. }
  204. } else {
  205. if (!notsilent) {
  206. /* Heard some audio, mark the begining of the token */
  207. start = ast_tvnow();
  208. ast_debug(1, "Start of voice token!\n");
  209. notsilent = 1;
  210. }
  211. }
  212. }
  213. ast_frfree(fr);
  214. }
  215. ast_sched_runq(ast_channel_sched(chan));
  216. }
  217. ast_stopstream(chan);
  218. } while (0);
  219. if (res > -1) {
  220. if (origrformat && ast_set_read_format(chan, origrformat)) {
  221. ast_log(LOG_WARNING, "Failed to restore read format for %s to %s\n",
  222. ast_channel_name(chan), ast_format_get_name(origrformat));
  223. }
  224. }
  225. if (dsp) {
  226. ast_dsp_free(dsp);
  227. }
  228. return res;
  229. }
  230. static int unload_module(void)
  231. {
  232. return ast_unregister_application(app);
  233. }
  234. static int load_module(void)
  235. {
  236. return ast_register_application_xml(app, background_detect_exec);
  237. }
  238. AST_MODULE_INFO_STANDARD_EXTENDED(ASTERISK_GPL_KEY, "Playback with Talk Detection");