app_talkdetect.c 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258
  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. struct ast_format origrformat;
  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. ast_format_clear(&origrformat);
  96. if (ast_strlen_zero(data)) {
  97. ast_log(LOG_WARNING, "BackgroundDetect requires an argument (filename)\n");
  98. return -1;
  99. }
  100. tmp = ast_strdupa(data);
  101. AST_STANDARD_APP_ARGS(args, tmp);
  102. if (!ast_strlen_zero(args.silence) && (sscanf(args.silence, "%30d", &x) == 1) && (x > 0)) {
  103. sil = x;
  104. }
  105. if (!ast_strlen_zero(args.min) && (sscanf(args.min, "%30d", &x) == 1) && (x > 0)) {
  106. min = x;
  107. }
  108. if (!ast_strlen_zero(args.max) && (sscanf(args.max, "%30d", &x) == 1) && (x > 0)) {
  109. max = x;
  110. }
  111. if (!ast_strlen_zero(args.analysistime) && (sscanf(args.analysistime, "%30d", &x) == 1) && (x > 0)) {
  112. analysistime = x;
  113. }
  114. ast_debug(1, "Preparing detect of '%s', sil=%d, min=%d, max=%d, analysistime=%d\n", args.filename, sil, min, max, analysistime);
  115. do {
  116. if (ast_channel_state(chan) != AST_STATE_UP) {
  117. if ((res = ast_answer(chan))) {
  118. break;
  119. }
  120. }
  121. ast_format_copy(&origrformat, ast_channel_readformat(chan));
  122. if ((ast_set_read_format_by_id(chan, AST_FORMAT_SLINEAR))) {
  123. ast_log(LOG_WARNING, "Unable to set read format to linear!\n");
  124. res = -1;
  125. break;
  126. }
  127. if (!(dsp = ast_dsp_new())) {
  128. ast_log(LOG_WARNING, "Unable to allocate DSP!\n");
  129. res = -1;
  130. break;
  131. }
  132. ast_stopstream(chan);
  133. if (ast_streamfile(chan, tmp, ast_channel_language(chan))) {
  134. ast_log(LOG_WARNING, "ast_streamfile failed on %s for %s\n", ast_channel_name(chan), (char *)data);
  135. break;
  136. }
  137. detection_start = ast_tvnow();
  138. while (ast_channel_stream(chan)) {
  139. res = ast_sched_wait(ast_channel_sched(chan));
  140. if ((res < 0) && !ast_channel_timingfunc(chan)) {
  141. res = 0;
  142. break;
  143. }
  144. if (res < 0) {
  145. res = 1000;
  146. }
  147. res = ast_waitfor(chan, res);
  148. if (res < 0) {
  149. ast_log(LOG_WARNING, "Waitfor failed on %s\n", ast_channel_name(chan));
  150. break;
  151. } else if (res > 0) {
  152. fr = ast_read(chan);
  153. if (continue_analysis && analysistime >= 0) {
  154. /* If we have a limit for the time to analyze voice
  155. * frames and the time has not expired */
  156. if (ast_tvdiff_ms(ast_tvnow(), detection_start) >= analysistime) {
  157. continue_analysis = 0;
  158. ast_verb(3, "BackgroundDetect: Talk analysis time complete on %s.\n", ast_channel_name(chan));
  159. }
  160. }
  161. if (!fr) {
  162. res = -1;
  163. break;
  164. } else if (fr->frametype == AST_FRAME_DTMF) {
  165. char t[2];
  166. t[0] = fr->subclass.integer;
  167. t[1] = '\0';
  168. if (ast_canmatch_extension(chan, ast_channel_context(chan), t, 1,
  169. S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL))) {
  170. /* They entered a valid extension, or might be anyhow */
  171. res = fr->subclass.integer;
  172. ast_frfree(fr);
  173. break;
  174. }
  175. } else if ((fr->frametype == AST_FRAME_VOICE) && (fr->subclass.format.id == AST_FORMAT_SLINEAR) && continue_analysis) {
  176. int totalsilence;
  177. int ms;
  178. res = ast_dsp_silence(dsp, fr, &totalsilence);
  179. if (res && (totalsilence > sil)) {
  180. /* We've been quiet a little while */
  181. if (notsilent) {
  182. /* We had heard some talking */
  183. ms = ast_tvdiff_ms(ast_tvnow(), start);
  184. ms -= sil;
  185. if (ms < 0)
  186. ms = 0;
  187. if ((ms > min) && ((max < 0) || (ms < max))) {
  188. char ms_str[12];
  189. ast_debug(1, "Found qualified token of %d ms\n", ms);
  190. /* Save detected talk time (in milliseconds) */
  191. snprintf(ms_str, sizeof(ms_str), "%d", ms);
  192. pbx_builtin_setvar_helper(chan, "TALK_DETECTED", ms_str);
  193. ast_goto_if_exists(chan, ast_channel_context(chan), "talk", 1);
  194. res = 0;
  195. ast_frfree(fr);
  196. break;
  197. } else {
  198. ast_debug(1, "Found unqualified token of %d ms\n", ms);
  199. }
  200. notsilent = 0;
  201. }
  202. } else {
  203. if (!notsilent) {
  204. /* Heard some audio, mark the begining of the token */
  205. start = ast_tvnow();
  206. ast_debug(1, "Start of voice token!\n");
  207. notsilent = 1;
  208. }
  209. }
  210. }
  211. ast_frfree(fr);
  212. }
  213. ast_sched_runq(ast_channel_sched(chan));
  214. }
  215. ast_stopstream(chan);
  216. } while (0);
  217. if (res > -1) {
  218. if (origrformat.id && ast_set_read_format(chan, &origrformat)) {
  219. ast_log(LOG_WARNING, "Failed to restore read format for %s to %s\n",
  220. ast_channel_name(chan), ast_getformatname(&origrformat));
  221. }
  222. }
  223. if (dsp) {
  224. ast_dsp_free(dsp);
  225. }
  226. return res;
  227. }
  228. static int unload_module(void)
  229. {
  230. return ast_unregister_application(app);
  231. }
  232. static int load_module(void)
  233. {
  234. return ast_register_application_xml(app, background_detect_exec);
  235. }
  236. AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Playback with Talk Detection");