1 // Copyright (C) 2006-2011 David Sugar, Tycho Softworks.
2 //
3 // This file is part of GNU ccAudio2.
4 //
5 // GNU ccAudio2 is free software: you can redistribute it and/or modify
6 // it under the terms of the GNU Lesser General Public License as published
7 // by the Free Software Foundation, either version 3 of the License, or
8 // (at your option) any later version.
9 //
10 // GNU ccAudio2 is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU Lesser General Public License for more details.
14 //
15 // You should have received a copy of the GNU Lesser General Public License
16 // along with GNU ccAudio2. If not, see <http://www.gnu.org/licenses/>.
17
19 #include <config.h>
20
22 using namespace UCOMMON_NAMESPACE;
23
24 static const char *delfile = NULL;
25 static shell::flagopt helpflag('h',"--help", _TEXT("display this list"));
26 static shell::flagopt althelp('?', NULL, NULL);
27 static shell::stringopt configdir('C', "--config", _TEXT("config directory"), "path", NULL);
28 static shell::stringopt encoding('e', "--encoding", _TEXT("audio format"), "type", "pcmu");
29 static shell::numericopt framing('f', "--framing", _TEXT("audio framing"), "msec", 20);
30 static shell::numericopt interdigit('i', "--interdigit", _TEXT("interdigit timing"), "msec", 60);
31 static shell::numericopt level('l', "--level", _TEXT("audio level"), "level", 30000);
32 static shell::numericopt maxtime('m', "--max", _TEXT("max time"), "msec", 60000);
33 static shell::numericopt offset('s', "--offset", _TEXT("offset into stream"), "msec", 0);
34 static shell::stringopt lang('L', "--lang", _TEXT("specify language"), "language", "C");
35 static shell::stringopt prefix('P', "--prefix", _TEXT("specify alternate prefix path"), "path", NULL);
36 static shell::stringopt suffix('S', "--suffix", _TEXT("audio extension"), ".ext", ".au");
37 static shell::stringopt voice('V', "--voice", _TEXT("specify voice library"), "name", "default");
38 static shell::stringopt phrasebook('B', "--phrasebook", _TEXT("specify phrasebook directory"), "path", NULL);
39 static Env::pathinfo_t ruleset;
40
41 static Tonegen *getTone(char **argv, Audio::level_t l, timeout_t framing, timeout_t interdigit)
42 {
43 Tonegen::key_t *key;
44 char *name, *locale;
45
46 if(!stricmp(*argv, "dtmf"))
47 return new DTMFDialer(*(++argv), l, framing, interdigit);
48 else if(!stricmp(*argv, "mf"))
49 return new MFDialer(*(++argv), l, framing, interdigit);
50
51 name = *(argv++);
52 locale = *(argv);
53 key = Tonegen::find(name, locale);
54 if(!key)
55 return NULL;
56
57 return new Tonegen(key, l, framing);
58 }
59
60 static const char *fname(const char *cp)
61 {
62 const char *fn = strrchr(cp, '/');
63 if(!fn)
64 fn = strrchr(cp, '\\');
65 if(fn)
66 return ++fn;
67 return cp;
68 }
69
71 {
72 Tonegen *tone;
74 Audio::info_t info, make;
75 const char *target;
76 unsigned maxframes;
77 Audio::linear_t buffer;
78 char *filename = (char *)"tones.conf";
79 char pathbuf[256];
80
81 Tonegen::load(filename);
82
83 if(*argv)
84 target = *(argv++);
85
86 if(!*argv) {
87 shell::errexit(3, "*** tonetool: %s\n",
88 _TEXT("no tone spec to use"));
89 }
90
91 if(!append)
92 make.encoding = Audio::getEncoding(*encoding);
93
94 ruleset.voices = *voice;
95
96 if(!Env::path(ruleset, target, pathbuf, sizeof(pathbuf), true))
97 shell::errexit(3, "*** tonetool: %s: %s\n",
98 target, _TEXT("invalid destination"));
99 else
100 target = pathbuf;
101
102 make.rate = Audio::rate8khz;
103
104 if(!append) {
105 remove(target);
106 output.create(target, make, *framing);
107 }
108 else {
109 output.open(target, Audio::modeWrite, *framing);
110 if(is(offset))
111 output.setPosition(*offset);
112 else
113 output.setPosition();
114 }
115
116 if(!output.is_open()) {
117 shell::errexit(3, "*** tonetool: %s: %s\n",
118 fname(target), _TEXT("cannot access"));
119 }
120
121 if(!output.is_streamable()) {
122 shell::errexit(4, "*** tonetool: %s: %s\n",
123 fname(target), _TEXT("unable to load codec"));
124 }
125
126 output.getInfo(info);
127
128 tone = getTone(argv, ((Audio::level_t)*level), info.framing, *interdigit);
129
130 if(!tone) {
131 shell::errexit(5, "*** tonetool: %s\n",
132 _TEXT("unrecognized tone spec used"));
133 }
134
135 printf("%s: ", fname(target));
136 maxframes = *maxtime / info.framing;
137
138 while(maxframes--) {
139 buffer = tone->getFrame();
140 if(!buffer) {
141 if(tone->is_complete())
142 break;
143 printf("!");
144 continue;
145 }
146 output.putMono(buffer);
147 if(tone->is_silent())
148 printf(".");
149 else
150 printf("+");
151 }
152 printf("\n");
153 output.close();
154 exit(0);
155 }
156
158 {
159 const char *locale = NULL;
160 const char *option;
161 Tonegen::key_t *key;
162 Tonegen::def_t *def;
163
164 retry:
165 option = *argv;
166
167 if(eq("--", option)) {
168 ++argv;
169 goto skip;
170 }
171
172 if(eq("--", option, 2))
173 ++option;
174
175 if(eq(option, "-locale=", 8)) {
176 locale = option + 8;
177 ++argv;
178 goto retry;
179 }
180
181 if(eq(option, "-locale")) {
182 ++argv;
183 if(!(*argv)) {
184 shell::errexit(2, "*** tonetool: -locale: %s\n",
185 _TEXT("missing argument"));
186 }
187
188 locale = *(argv++);
189 goto retry;
190 }
191
192
193 skip:
194 if(*argv && **argv == '-') {
195 shell::errexit(1, "*** tonetool: %s: %s\n",
196 *argv, _TEXT("unknown option"));
197 }
198
199 if(!*argv) {
200 shell::errexit(3, "*** tonetool: %s\n",
201 _TEXT("tone name missing"));
202 }
203
204 if(!Tonegen::load(locale)) {
205 shell::errexit(4, "*** tonetool: %s\n",
206 _TEXT("unable to load"));
207 }
208
209 while(*argv) {
210 printf("%s:", *argv);
211 key = Tonegen::find(*(argv++), locale);
212 if(!key) {
213 printf(" %s\n", _TEXT("not found"));
214 continue;
215 }
216 printf("\n");
217 def = key->first;
218 while(def) {
219 printf(" ");
220 if(def->f2 && def->f1)
221 printf("%d+%d", def->f1, def->f2);
222 else
223 printf("%d", def->f1);
224
225 if(def->duration)
226 printf(" %ldms\n", def->duration);
227 else {
228 printf(" forever\n");
229 break;
230 }
231
232 if(def->silence)
233 printf(" silence %ldms\n", def->silence);
234
235 if(def == key->last) {
236 if(def->next == key->first)
237 printf(" repeat full\n");
238 else if(def->next)
239 printf(" repeat partial\n");
240 break;
241 }
242 def = def->next;
243 }
244 }
245 exit(0);
246 }
247
249 {
250 DTMFDetect *dtmf;
252 Audio::Info info, make;
253 const char *target;
254 Audio::linear_t buffer;
255 char result[128];
256 char pathbuf[256];
257
258 ruleset.voices = *voice;
259
260 while(*argv) {
261 target = *(argv++);
262
263 if(!Env::path(ruleset, target, pathbuf, sizeof(pathbuf)))
264 shell::errexit(3, "*** tonetool: %s: %s\n",
265 target, _TEXT("invalid destination"));
266 else
267 target = pathbuf;
268
269 input.open(target, Audio::modeRead, *framing);
270
271 if(!input.is_open()) {
272 shell::errexit(3, "*** tonetool: %s: %s\n",
273 fname(target), _TEXT("cannot access"));
274 }
275
276 if(!input.is_streamable()) {
277 shell::errexit(4, "*** tonetool: %s: %s\n",
278 fname(target), _TEXT("unable to load codec"));
279 }
280
281 input.getInfo(info);
282 dtmf = new DTMFDetect;
283
284 buffer = new Audio::sample_t[input.getCount()];
285 for(;;)
286 {
287 if(!input.getMono(buffer, 1))
288 break;
289
290 dtmf->putSamples(buffer, input.getCount());
291 }
292
293 dtmf->getResult(result, sizeof(result));
294 printf("%s: %s\n", fname(target), result);
295
296 input.close();
297 delete[] buffer;
298 delete dtmf;
299 }
300 exit(0);
301 }
302
303 static void stop(void)
304 {
305 if(delfile) {
306 ::remove(delfile);
307 delfile = NULL;
308 }
309 }
310
312 {
313 shell::bind("tonetool");
314 shell args(argc, argv);
315
316 Env::tool(&args);
317
318 if(is(prefix))
319 Env::set("prefix", *prefix);
320
321 if(is(phrasebook))
322 Env::set("voices", *phrasebook);
323
324 if(is(suffix))
325 Env::set("extension", *suffix);
326
327 if(is(configdir))
328 Env::set("config", *configdir);
329
330 if(is(lang))
331 ruleset.book = Phrasebook::find(*lang);
332 else
333 ruleset.book = Phrasebook::find(NULL);
334
335 if(is(helpflag) || is(althelp)) {
336 printf("%s\n", _TEXT("Usage: tonetool [options] command [arguments...]"));
337 printf("%s\n\n", _TEXT("Tone tool operations"));
338 printf("%s\n", _TEXT("Options:"));
339 shell::help();
340 printf("\n%s\n", _TEXT("Report bugs to dyfet@gnu.org"));
341 PROGRAM_EXIT(0);
342 }
343
344 if(!args())
345 shell::errexit(1, "*** tonetool: %s\n",
346 _TEXT("no command specified"));
347
348 const char *cp = args[0];
349
350 argv = args.argv();
351 ++argv;
352
353 shell::exiting(stop);
354
355 if(eq(cp, "list"))
357 else if(eq(cp, "detect"))
359 else if(eq(cp, "create"))
361 else if(eq(cp, "append"))
363
364 shell::errexit(1, "*** tonetool: %s: %s\n",
365 cp, _TEXT("unknown option"));
366
367 PROGRAM_EXIT(0);
368 }
369
#define BAYONNE_NAMESPACE
GNU Bayonne library namespace.