app_dictate.c 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358
  1. /*
  2. * Asterisk -- An open source telephony toolkit.
  3. *
  4. * Copyright (C) 2005, Anthony Minessale II
  5. *
  6. * Anthony Minessale II <anthmct@yahoo.com>
  7. *
  8. * Donated by Sangoma Technologies <http://www.sangoma.com>
  9. *
  10. * See http://www.asterisk.org for more information about
  11. * the Asterisk project. Please do not directly contact
  12. * any of the maintainers of this project for assistance;
  13. * the project provides a web site, mailing lists and IRC
  14. * channels for your use.
  15. *
  16. * This program is free software, distributed under the terms of
  17. * the GNU General Public License Version 2. See the LICENSE file
  18. * at the top of the source tree.
  19. */
  20. /*! \file
  21. *
  22. * \brief Virtual Dictation Machine Application For Asterisk
  23. *
  24. * \author Anthony Minessale II <anthmct@yahoo.com>
  25. *
  26. * \ingroup applications
  27. */
  28. /*** MODULEINFO
  29. <support_level>extended</support_level>
  30. ***/
  31. #include "asterisk.h"
  32. ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
  33. #include <sys/stat.h>
  34. #include "asterisk/paths.h" /* use ast_config_AST_SPOOL_DIR */
  35. #include "asterisk/file.h"
  36. #include "asterisk/pbx.h"
  37. #include "asterisk/module.h"
  38. #include "asterisk/say.h"
  39. #include "asterisk/app.h"
  40. #include "asterisk/format_cache.h"
  41. /*** DOCUMENTATION
  42. <application name="Dictate" language="en_US">
  43. <synopsis>
  44. Virtual Dictation Machine.
  45. </synopsis>
  46. <syntax>
  47. <parameter name="base_dir" />
  48. <parameter name="filename" />
  49. </syntax>
  50. <description>
  51. <para>Start dictation machine using optional <replaceable>base_dir</replaceable> for files.</para>
  52. </description>
  53. </application>
  54. ***/
  55. static const char app[] = "Dictate";
  56. typedef enum {
  57. DFLAG_RECORD = (1 << 0),
  58. DFLAG_PLAY = (1 << 1),
  59. DFLAG_TRUNC = (1 << 2),
  60. DFLAG_PAUSE = (1 << 3),
  61. } dflags;
  62. typedef enum {
  63. DMODE_INIT,
  64. DMODE_RECORD,
  65. DMODE_PLAY
  66. } dmodes;
  67. #define ast_toggle_flag(it,flag) if(ast_test_flag(it, flag)) ast_clear_flag(it, flag); else ast_set_flag(it, flag)
  68. static int play_and_wait(struct ast_channel *chan, char *file, char *digits)
  69. {
  70. int res = -1;
  71. if (!ast_streamfile(chan, file, ast_channel_language(chan))) {
  72. res = ast_waitstream(chan, digits);
  73. }
  74. return res;
  75. }
  76. static int dictate_exec(struct ast_channel *chan, const char *data)
  77. {
  78. char *path = NULL, filein[256], *filename = "";
  79. char *parse;
  80. AST_DECLARE_APP_ARGS(args,
  81. AST_APP_ARG(base);
  82. AST_APP_ARG(filename);
  83. );
  84. char dftbase[256];
  85. char *base;
  86. struct ast_flags flags = {0};
  87. struct ast_filestream *fs;
  88. struct ast_frame *f = NULL;
  89. int ffactor = 320 * 80,
  90. res = 0,
  91. done = 0,
  92. lastop = 0,
  93. samples = 0,
  94. speed = 1,
  95. digit = 0,
  96. len = 0,
  97. maxlen = 0,
  98. mode = 0;
  99. struct ast_format *oldr;
  100. snprintf(dftbase, sizeof(dftbase), "%s/dictate", ast_config_AST_SPOOL_DIR);
  101. if (!ast_strlen_zero(data)) {
  102. parse = ast_strdupa(data);
  103. AST_STANDARD_APP_ARGS(args, parse);
  104. } else
  105. args.argc = 0;
  106. if (args.argc && !ast_strlen_zero(args.base)) {
  107. base = args.base;
  108. } else {
  109. base = dftbase;
  110. }
  111. if (args.argc > 1 && args.filename) {
  112. filename = args.filename;
  113. }
  114. oldr = ao2_bump(ast_channel_readformat(chan));
  115. if ((res = ast_set_read_format(chan, ast_format_slin)) < 0) {
  116. ast_log(LOG_WARNING, "Unable to set to linear mode.\n");
  117. ao2_cleanup(oldr);
  118. return -1;
  119. }
  120. if (ast_channel_state(chan) != AST_STATE_UP) {
  121. ast_answer(chan);
  122. }
  123. ast_safe_sleep(chan, 200);
  124. for (res = 0; !res;) {
  125. if (ast_strlen_zero(filename)) {
  126. if (ast_app_getdata(chan, "dictate/enter_filename", filein, sizeof(filein), 0) ||
  127. ast_strlen_zero(filein)) {
  128. res = -1;
  129. break;
  130. }
  131. } else {
  132. ast_copy_string(filein, filename, sizeof(filein));
  133. filename = "";
  134. }
  135. ast_mkdir(base, 0755);
  136. len = strlen(base) + strlen(filein) + 2;
  137. if (!path || len > maxlen) {
  138. path = ast_alloca(len);
  139. memset(path, 0, len);
  140. maxlen = len;
  141. } else {
  142. memset(path, 0, maxlen);
  143. }
  144. snprintf(path, len, "%s/%s", base, filein);
  145. fs = ast_writefile(path, "raw", NULL, O_CREAT|O_APPEND, 0, AST_FILE_MODE);
  146. mode = DMODE_PLAY;
  147. memset(&flags, 0, sizeof(flags));
  148. ast_set_flag(&flags, DFLAG_PAUSE);
  149. digit = play_and_wait(chan, "dictate/forhelp", AST_DIGIT_ANY);
  150. done = 0;
  151. speed = 1;
  152. res = 0;
  153. lastop = 0;
  154. samples = 0;
  155. while (!done && ((res = ast_waitfor(chan, -1)) > -1) && fs && (f = ast_read(chan))) {
  156. if (digit) {
  157. struct ast_frame fr = {AST_FRAME_DTMF, { .integer = digit } };
  158. ast_queue_frame(chan, &fr);
  159. digit = 0;
  160. }
  161. if (f->frametype == AST_FRAME_DTMF) {
  162. int got = 1;
  163. switch(mode) {
  164. case DMODE_PLAY:
  165. switch (f->subclass.integer) {
  166. case '1':
  167. ast_set_flag(&flags, DFLAG_PAUSE);
  168. mode = DMODE_RECORD;
  169. break;
  170. case '2':
  171. speed++;
  172. if (speed > 4) {
  173. speed = 1;
  174. }
  175. res = ast_say_number(chan, speed, AST_DIGIT_ANY, ast_channel_language(chan), NULL);
  176. break;
  177. case '7':
  178. samples -= ffactor;
  179. if(samples < 0) {
  180. samples = 0;
  181. }
  182. ast_seekstream(fs, samples, SEEK_SET);
  183. break;
  184. case '8':
  185. samples += ffactor;
  186. ast_seekstream(fs, samples, SEEK_SET);
  187. break;
  188. default:
  189. got = 0;
  190. }
  191. break;
  192. case DMODE_RECORD:
  193. switch (f->subclass.integer) {
  194. case '1':
  195. ast_set_flag(&flags, DFLAG_PAUSE);
  196. mode = DMODE_PLAY;
  197. break;
  198. case '8':
  199. ast_toggle_flag(&flags, DFLAG_TRUNC);
  200. lastop = 0;
  201. break;
  202. default:
  203. got = 0;
  204. }
  205. break;
  206. default:
  207. got = 0;
  208. }
  209. if (!got) {
  210. switch (f->subclass.integer) {
  211. case '#':
  212. done = 1;
  213. continue;
  214. break;
  215. case '*':
  216. ast_toggle_flag(&flags, DFLAG_PAUSE);
  217. if (ast_test_flag(&flags, DFLAG_PAUSE)) {
  218. digit = play_and_wait(chan, "dictate/pause", AST_DIGIT_ANY);
  219. } else {
  220. digit = play_and_wait(chan, mode == DMODE_PLAY ? "dictate/playback" : "dictate/record", AST_DIGIT_ANY);
  221. }
  222. break;
  223. case '0':
  224. ast_set_flag(&flags, DFLAG_PAUSE);
  225. digit = play_and_wait(chan, "dictate/paused", AST_DIGIT_ANY);
  226. switch(mode) {
  227. case DMODE_PLAY:
  228. digit = play_and_wait(chan, "dictate/play_help", AST_DIGIT_ANY);
  229. break;
  230. case DMODE_RECORD:
  231. digit = play_and_wait(chan, "dictate/record_help", AST_DIGIT_ANY);
  232. break;
  233. }
  234. if (digit == 0) {
  235. digit = play_and_wait(chan, "dictate/both_help", AST_DIGIT_ANY);
  236. } else if (digit < 0) {
  237. done = 1;
  238. break;
  239. }
  240. break;
  241. }
  242. }
  243. } else if (f->frametype == AST_FRAME_VOICE) {
  244. switch(mode) {
  245. struct ast_frame *fr;
  246. int x;
  247. case DMODE_PLAY:
  248. if (lastop != DMODE_PLAY) {
  249. if (ast_test_flag(&flags, DFLAG_PAUSE)) {
  250. digit = play_and_wait(chan, "dictate/playback_mode", AST_DIGIT_ANY);
  251. if (digit == 0) {
  252. digit = play_and_wait(chan, "dictate/paused", AST_DIGIT_ANY);
  253. } else if (digit < 0) {
  254. break;
  255. }
  256. }
  257. if (lastop != DFLAG_PLAY) {
  258. lastop = DFLAG_PLAY;
  259. ast_closestream(fs);
  260. if (!(fs = ast_openstream(chan, path, ast_channel_language(chan))))
  261. break;
  262. ast_seekstream(fs, samples, SEEK_SET);
  263. ast_channel_stream_set(chan, NULL);
  264. }
  265. lastop = DMODE_PLAY;
  266. }
  267. if (!ast_test_flag(&flags, DFLAG_PAUSE)) {
  268. for (x = 0; x < speed; x++) {
  269. if ((fr = ast_readframe(fs))) {
  270. ast_write(chan, fr);
  271. samples += fr->samples;
  272. ast_frfree(fr);
  273. fr = NULL;
  274. } else {
  275. samples = 0;
  276. ast_seekstream(fs, 0, SEEK_SET);
  277. }
  278. }
  279. }
  280. break;
  281. case DMODE_RECORD:
  282. if (lastop != DMODE_RECORD) {
  283. int oflags = O_CREAT | O_WRONLY;
  284. if (ast_test_flag(&flags, DFLAG_PAUSE)) {
  285. digit = play_and_wait(chan, "dictate/record_mode", AST_DIGIT_ANY);
  286. if (digit == 0) {
  287. digit = play_and_wait(chan, "dictate/paused", AST_DIGIT_ANY);
  288. } else if (digit < 0) {
  289. break;
  290. }
  291. }
  292. lastop = DMODE_RECORD;
  293. ast_closestream(fs);
  294. if ( ast_test_flag(&flags, DFLAG_TRUNC)) {
  295. oflags |= O_TRUNC;
  296. digit = play_and_wait(chan, "dictate/truncating_audio", AST_DIGIT_ANY);
  297. } else {
  298. oflags |= O_APPEND;
  299. }
  300. fs = ast_writefile(path, "raw", NULL, oflags, 0, AST_FILE_MODE);
  301. if (ast_test_flag(&flags, DFLAG_TRUNC)) {
  302. ast_seekstream(fs, 0, SEEK_SET);
  303. ast_clear_flag(&flags, DFLAG_TRUNC);
  304. } else {
  305. ast_seekstream(fs, 0, SEEK_END);
  306. }
  307. }
  308. if (!ast_test_flag(&flags, DFLAG_PAUSE)) {
  309. res = ast_writestream(fs, f);
  310. }
  311. break;
  312. }
  313. }
  314. ast_frfree(f);
  315. }
  316. }
  317. if (oldr) {
  318. ast_set_read_format(chan, oldr);
  319. ao2_ref(oldr, -1);
  320. }
  321. return 0;
  322. }
  323. static int unload_module(void)
  324. {
  325. int res;
  326. res = ast_unregister_application(app);
  327. return res;
  328. }
  329. static int load_module(void)
  330. {
  331. return ast_register_application_xml(app, dictate_exec);
  332. }
  333. AST_MODULE_INFO_STANDARD_EXTENDED(ASTERISK_GPL_KEY, "Virtual Dictation Machine");