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