app_dictate.c 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357
  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.samgoma.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. /*** DOCUMENTATION
  41. <application name="Dictate" language="en_US">
  42. <synopsis>
  43. Virtual Dictation Machine.
  44. </synopsis>
  45. <syntax>
  46. <parameter name="base_dir" />
  47. <parameter name="filename" />
  48. </syntax>
  49. <description>
  50. <para>Start dictation machine using optional <replaceable>base_dir</replaceable> for files.</para>
  51. </description>
  52. </application>
  53. ***/
  54. static const char app[] = "Dictate";
  55. typedef enum {
  56. DFLAG_RECORD = (1 << 0),
  57. DFLAG_PLAY = (1 << 1),
  58. DFLAG_TRUNC = (1 << 2),
  59. DFLAG_PAUSE = (1 << 3),
  60. } dflags;
  61. typedef enum {
  62. DMODE_INIT,
  63. DMODE_RECORD,
  64. DMODE_PLAY
  65. } dmodes;
  66. #define ast_toggle_flag(it,flag) if(ast_test_flag(it, flag)) ast_clear_flag(it, flag); else ast_set_flag(it, flag)
  67. static int play_and_wait(struct ast_channel *chan, char *file, char *digits)
  68. {
  69. int res = -1;
  70. if (!ast_streamfile(chan, file, ast_channel_language(chan))) {
  71. res = ast_waitstream(chan, digits);
  72. }
  73. return res;
  74. }
  75. static int dictate_exec(struct ast_channel *chan, const char *data)
  76. {
  77. char *path = NULL, filein[256], *filename = "";
  78. char *parse;
  79. AST_DECLARE_APP_ARGS(args,
  80. AST_APP_ARG(base);
  81. AST_APP_ARG(filename);
  82. );
  83. char dftbase[256];
  84. char *base;
  85. struct ast_flags flags = {0};
  86. struct ast_filestream *fs;
  87. struct ast_frame *f = NULL;
  88. int ffactor = 320 * 80,
  89. res = 0,
  90. done = 0,
  91. lastop = 0,
  92. samples = 0,
  93. speed = 1,
  94. digit = 0,
  95. len = 0,
  96. maxlen = 0,
  97. mode = 0;
  98. struct ast_format oldr;
  99. ast_format_clear(&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. ast_format_copy(&oldr, ast_channel_readformat(chan));
  115. if ((res = ast_set_read_format_by_id(chan, AST_FORMAT_SLINEAR)) < 0) {
  116. ast_log(LOG_WARNING, "Unable to set to linear mode.\n");
  117. return -1;
  118. }
  119. if (ast_channel_state(chan) != AST_STATE_UP) {
  120. ast_answer(chan);
  121. }
  122. ast_safe_sleep(chan, 200);
  123. for (res = 0; !res;) {
  124. if (ast_strlen_zero(filename)) {
  125. if (ast_app_getdata(chan, "dictate/enter_filename", filein, sizeof(filein), 0) ||
  126. ast_strlen_zero(filein)) {
  127. res = -1;
  128. break;
  129. }
  130. } else {
  131. ast_copy_string(filein, filename, sizeof(filein));
  132. filename = "";
  133. }
  134. ast_mkdir(base, 0755);
  135. len = strlen(base) + strlen(filein) + 2;
  136. if (!path || len > maxlen) {
  137. path = ast_alloca(len);
  138. memset(path, 0, len);
  139. maxlen = len;
  140. } else {
  141. memset(path, 0, maxlen);
  142. }
  143. snprintf(path, len, "%s/%s", base, filein);
  144. fs = ast_writefile(path, "raw", NULL, O_CREAT|O_APPEND, 0, AST_FILE_MODE);
  145. mode = DMODE_PLAY;
  146. memset(&flags, 0, sizeof(flags));
  147. ast_set_flag(&flags, DFLAG_PAUSE);
  148. digit = play_and_wait(chan, "dictate/forhelp", AST_DIGIT_ANY);
  149. done = 0;
  150. speed = 1;
  151. res = 0;
  152. lastop = 0;
  153. samples = 0;
  154. while (!done && ((res = ast_waitfor(chan, -1)) > -1) && fs && (f = ast_read(chan))) {
  155. if (digit) {
  156. struct ast_frame fr = {AST_FRAME_DTMF, { .integer = digit } };
  157. ast_queue_frame(chan, &fr);
  158. digit = 0;
  159. }
  160. if ((f->frametype == AST_FRAME_DTMF)) {
  161. int got = 1;
  162. switch(mode) {
  163. case DMODE_PLAY:
  164. switch (f->subclass.integer) {
  165. case '1':
  166. ast_set_flag(&flags, DFLAG_PAUSE);
  167. mode = DMODE_RECORD;
  168. break;
  169. case '2':
  170. speed++;
  171. if (speed > 4) {
  172. speed = 1;
  173. }
  174. res = ast_say_number(chan, speed, AST_DIGIT_ANY, ast_channel_language(chan), NULL);
  175. break;
  176. case '7':
  177. samples -= ffactor;
  178. if(samples < 0) {
  179. samples = 0;
  180. }
  181. ast_seekstream(fs, samples, SEEK_SET);
  182. break;
  183. case '8':
  184. samples += ffactor;
  185. ast_seekstream(fs, samples, SEEK_SET);
  186. break;
  187. default:
  188. got = 0;
  189. }
  190. break;
  191. case DMODE_RECORD:
  192. switch (f->subclass.integer) {
  193. case '1':
  194. ast_set_flag(&flags, DFLAG_PAUSE);
  195. mode = DMODE_PLAY;
  196. break;
  197. case '8':
  198. ast_toggle_flag(&flags, DFLAG_TRUNC);
  199. lastop = 0;
  200. break;
  201. default:
  202. got = 0;
  203. }
  204. break;
  205. default:
  206. got = 0;
  207. }
  208. if (!got) {
  209. switch (f->subclass.integer) {
  210. case '#':
  211. done = 1;
  212. continue;
  213. break;
  214. case '*':
  215. ast_toggle_flag(&flags, DFLAG_PAUSE);
  216. if (ast_test_flag(&flags, DFLAG_PAUSE)) {
  217. digit = play_and_wait(chan, "dictate/pause", AST_DIGIT_ANY);
  218. } else {
  219. digit = play_and_wait(chan, mode == DMODE_PLAY ? "dictate/playback" : "dictate/record", AST_DIGIT_ANY);
  220. }
  221. break;
  222. case '0':
  223. ast_set_flag(&flags, DFLAG_PAUSE);
  224. digit = play_and_wait(chan, "dictate/paused", AST_DIGIT_ANY);
  225. switch(mode) {
  226. case DMODE_PLAY:
  227. digit = play_and_wait(chan, "dictate/play_help", AST_DIGIT_ANY);
  228. break;
  229. case DMODE_RECORD:
  230. digit = play_and_wait(chan, "dictate/record_help", AST_DIGIT_ANY);
  231. break;
  232. }
  233. if (digit == 0) {
  234. digit = play_and_wait(chan, "dictate/both_help", AST_DIGIT_ANY);
  235. } else if (digit < 0) {
  236. done = 1;
  237. break;
  238. }
  239. break;
  240. }
  241. }
  242. } else if (f->frametype == AST_FRAME_VOICE) {
  243. switch(mode) {
  244. struct ast_frame *fr;
  245. int x;
  246. case DMODE_PLAY:
  247. if (lastop != DMODE_PLAY) {
  248. if (ast_test_flag(&flags, DFLAG_PAUSE)) {
  249. digit = play_and_wait(chan, "dictate/playback_mode", AST_DIGIT_ANY);
  250. if (digit == 0) {
  251. digit = play_and_wait(chan, "dictate/paused", AST_DIGIT_ANY);
  252. } else if (digit < 0) {
  253. break;
  254. }
  255. }
  256. if (lastop != DFLAG_PLAY) {
  257. lastop = DFLAG_PLAY;
  258. ast_closestream(fs);
  259. if (!(fs = ast_openstream(chan, path, ast_channel_language(chan))))
  260. break;
  261. ast_seekstream(fs, samples, SEEK_SET);
  262. ast_channel_stream_set(chan, NULL);
  263. }
  264. lastop = DMODE_PLAY;
  265. }
  266. if (!ast_test_flag(&flags, DFLAG_PAUSE)) {
  267. for (x = 0; x < speed; x++) {
  268. if ((fr = ast_readframe(fs))) {
  269. ast_write(chan, fr);
  270. samples += fr->samples;
  271. ast_frfree(fr);
  272. fr = NULL;
  273. } else {
  274. samples = 0;
  275. ast_seekstream(fs, 0, SEEK_SET);
  276. }
  277. }
  278. }
  279. break;
  280. case DMODE_RECORD:
  281. if (lastop != DMODE_RECORD) {
  282. int oflags = O_CREAT | O_WRONLY;
  283. if (ast_test_flag(&flags, DFLAG_PAUSE)) {
  284. digit = play_and_wait(chan, "dictate/record_mode", AST_DIGIT_ANY);
  285. if (digit == 0) {
  286. digit = play_and_wait(chan, "dictate/paused", AST_DIGIT_ANY);
  287. } else if (digit < 0) {
  288. break;
  289. }
  290. }
  291. lastop = DMODE_RECORD;
  292. ast_closestream(fs);
  293. if ( ast_test_flag(&flags, DFLAG_TRUNC)) {
  294. oflags |= O_TRUNC;
  295. digit = play_and_wait(chan, "dictate/truncating_audio", AST_DIGIT_ANY);
  296. } else {
  297. oflags |= O_APPEND;
  298. }
  299. fs = ast_writefile(path, "raw", NULL, oflags, 0, AST_FILE_MODE);
  300. if (ast_test_flag(&flags, DFLAG_TRUNC)) {
  301. ast_seekstream(fs, 0, SEEK_SET);
  302. ast_clear_flag(&flags, DFLAG_TRUNC);
  303. } else {
  304. ast_seekstream(fs, 0, SEEK_END);
  305. }
  306. }
  307. if (!ast_test_flag(&flags, DFLAG_PAUSE)) {
  308. res = ast_writestream(fs, f);
  309. }
  310. break;
  311. }
  312. }
  313. ast_frfree(f);
  314. }
  315. }
  316. if (oldr.id) {
  317. ast_set_read_format(chan, &oldr);
  318. }
  319. return 0;
  320. }
  321. static int unload_module(void)
  322. {
  323. int res;
  324. res = ast_unregister_application(app);
  325. return res;
  326. }
  327. static int load_module(void)
  328. {
  329. return ast_register_application_xml(app, dictate_exec);
  330. }
  331. AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Virtual Dictation Machine");