1 /*
2 * Copyright (C) 1997-2004 Kare Sjolander <kare@speech.kth.se>
3 *
4 * This file is part of the Snack Sound Toolkit.
5 * The latest version can be found at http://www.speech.kth.se/snack/
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 */
21
22 #include <stdlib.h>
23 #include <stdio.h>
24 #include <signal.h>
25 #include <math.h>
26 #include <string.h>
27 #include "tcl.h"
28 #include "snack.h"
29
30 extern int rop, wop;
31 extern double startDevTime;
32 extern struct jkQueuedSound *soundQueue;
33 extern struct jkQueuedSound *rsoundQueue;
34
35 #if defined(HPUX) || defined(MAC)
36 /* Choosing a good generic value for HP-UX is not easy */
37 # define BUFSECS 2.0
38 #else
39 # define BUFSECS 0.25
40 #endif
41
42 double globalLatency = BUFSECS;
43 float globalScaling = 1.0f;
44
45 char defaultOutDevice[MAX_DEVICE_NAME_LENGTH];
46 char defaultInDevice[MAX_DEVICE_NAME_LENGTH];
47
48 char *
SnackStrDup(const char * str)49 SnackStrDup(const char *str)
50 {
51 char *new = ckalloc(strlen(str)+1);
52
53 if (new) {
54 strcpy(new, str);
55 }
56
57 return new;
58 }
59
60 static int
outDevicesCmd(Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])61 outDevicesCmd(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
62 {
63 int i, n;
64 char *arr[MAX_NUM_DEVICES];
65 Tcl_Obj *list = Tcl_NewListObj(0, NULL);
66
67 n = SnackGetOutputDevices(arr, MAX_NUM_DEVICES);
68
69 for (i = 0; i < n; i++) {
70 Tcl_ListObjAppendElement(interp, list, Tcl_NewStringObj(arr[i], -1));
71 ckfree(arr[i]);
72 }
73
74 Tcl_SetObjResult(interp, list);
75
76 return TCL_OK;
77 }
78
79 static int
inDevicesCmd(Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])80 inDevicesCmd(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
81 {
82 int i, n;
83 char *arr[MAX_NUM_DEVICES];
84 Tcl_Obj *list = Tcl_NewListObj(0, NULL);
85
86 n = SnackGetInputDevices(arr, MAX_NUM_DEVICES);
87
88 for (i = 0; i < n; i++) {
89 Tcl_ListObjAppendElement(interp, list, Tcl_NewStringObj(arr[i], -1));
90 ckfree(arr[i]);
91 }
92
93 Tcl_SetObjResult(interp, list);
94
95 return TCL_OK;
96 }
97
98 static int
selectOutCmd(Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])99 selectOutCmd(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
100 {
101 int i, n, found = 0;
102 char *arr[MAX_NUM_DEVICES];
103 char *devstr;
104
105 n = SnackGetOutputDevices(arr, MAX_NUM_DEVICES);
106
107 if (objc == 3) {
108 devstr = Tcl_GetStringFromObj(objv[2], NULL);
109 for (i = 0; i < n; i++) {
110 if (strncmp(devstr, arr[i], strlen(devstr)) == 0 && found == 0) {
111 strcpy(defaultOutDevice, arr[i]);
112 found = 1;
113 }
114 ckfree(arr[i]);
115 }
116 if (found == 0) {
117 Tcl_AppendResult(interp, "No such device: ", devstr, (char *) NULL);
118 return TCL_ERROR;
119 }
120 } else {
121 Tcl_WrongNumArgs(interp, 1, objv, "selectOutput device");
122 return TCL_ERROR;
123 }
124
125 return TCL_OK;
126 }
127
128 static int
selectInCmd(Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])129 selectInCmd(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
130 {
131 int i, n, found = 0;
132 char *arr[MAX_NUM_DEVICES];
133 char *devstr;
134
135 n = SnackGetInputDevices(arr, MAX_NUM_DEVICES);
136
137 if (objc == 3) {
138 devstr = Tcl_GetStringFromObj(objv[2], NULL);
139 for (i = 0; i < n; i++) {
140 if (strncmp(devstr, arr[i], strlen(devstr)) == 0 && found == 0) {
141 strcpy(defaultInDevice, arr[i]);
142 found = 1;
143 }
144 ckfree(arr[i]);
145 }
146 if (found == 0) {
147 Tcl_AppendResult(interp, "No such device: ", devstr, (char *) NULL);
148 return TCL_ERROR;
149 }
150 } else {
151 Tcl_WrongNumArgs(interp, 1, objv, "selectInput device");
152 return TCL_ERROR;
153 }
154
155 return TCL_OK;
156 }
157
158 static int
encodingsCmd(Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])159 encodingsCmd(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
160 {
161 char *str = "Lin16 Mulaw Alaw Lin8offset Lin8 Lin24 Lin24packed Lin32 Float";
162
163 Tcl_SetObjResult(interp, Tcl_NewStringObj(str, -1));
164
165 return TCL_OK;
166 }
167
168 static int
ratesCmd(Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])169 ratesCmd(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
170 {
171 char tmpstr[QUERYBUFSIZE];
172
173 SnackAudioGetRates(defaultOutDevice, tmpstr, QUERYBUFSIZE);
174 Tcl_SetObjResult(interp, Tcl_NewStringObj(tmpstr, -1));
175
176 return TCL_OK;
177 }
178
179 static int
activeCmd(Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])180 activeCmd(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
181 {
182 if (wop == IDLE && rop == IDLE) {
183 Tcl_SetObjResult(interp, Tcl_NewIntObj(0));
184 } else {
185 Tcl_SetObjResult(interp, Tcl_NewIntObj(1));
186 }
187
188 return TCL_OK;
189 }
190
191 static int
play_gainCmd(Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])192 play_gainCmd(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
193 {
194 int g;
195
196 if (objc == 3) {
197 if (Tcl_GetIntFromObj(interp, objv[2], &g) != TCL_OK) return TCL_ERROR;
198 ASetPlayGain(g);
199 } else {
200 #ifdef HPUX
201 if (wop == IDLE)
202 #endif
203 Tcl_SetObjResult(interp, Tcl_NewIntObj(AGetPlayGain()));
204 }
205
206 return TCL_OK;
207 }
208
209 static int
record_gainCmd(Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])210 record_gainCmd(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
211 {
212 int g;
213
214 if (objc == 3) {
215 if (Tcl_GetIntFromObj(interp, objv[2], &g) != TCL_OK) return TCL_ERROR;
216 ASetRecGain(g);
217 } else {
218 #ifdef HPUX
219 if (rop == IDLE)
220 #endif
221 Tcl_SetObjResult(interp, Tcl_NewIntObj(AGetRecGain()));
222 }
223
224 return TCL_OK;
225 }
226
227 static int
elapsedTimeCmd(Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])228 elapsedTimeCmd(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
229 {
230 double elapsedTime = SnackCurrentTime() - startDevTime;
231
232 if (wop == IDLE && rop == IDLE) {
233 Tcl_SetObjResult(interp, Tcl_NewDoubleObj(0.0));
234 } else if (wop == PAUSED || rop == PAUSED) {
235 Tcl_SetObjResult(interp, Tcl_NewDoubleObj(startDevTime));
236 } else {
237 Tcl_SetObjResult(interp, Tcl_NewDoubleObj(elapsedTime));
238 }
239
240 return TCL_OK;
241 }
242
243 static int
currentSoundCmd(Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])244 currentSoundCmd(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
245 {
246 jkQueuedSound *p;
247 char *res;
248 Tcl_HashSearch hashSearch;
249 Tcl_HashEntry *entryPtr;
250
251 if (soundQueue == NULL) {
252 Tcl_SetObjResult(interp, Tcl_NewStringObj("", -1));
253 return TCL_OK;
254 }
255 for (p = soundQueue; p->next != NULL && p->next->status == SNACK_QS_DONE;
256 p = p->next);
257
258 entryPtr = Tcl_FirstHashEntry(p->sound->soundTable, &hashSearch);
259
260 if (p->sound != (Sound *) Tcl_GetHashValue(entryPtr)) {
261 entryPtr = Tcl_NextHashEntry(&hashSearch);
262 }
263 res = Tcl_GetHashKey(p->sound->soundTable, entryPtr);
264 Tcl_SetObjResult(interp, Tcl_NewStringObj(res, -1));
265
266 return TCL_OK;
267 }
268
269 static int
playLatencyCmd(Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])270 playLatencyCmd(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
271 {
272 double d;
273
274 if (objc == 2) {
275 Tcl_SetObjResult(interp, Tcl_NewDoubleObj(1000.0*globalLatency));
276 } else if (objc == 3) {
277 if (Tcl_GetDoubleFromObj(interp, objv[2], &d) != TCL_OK) {
278 return TCL_ERROR;
279 }
280 globalLatency = d / 1000.0;
281 } else {
282 Tcl_WrongNumArgs(interp, 1, objv, "playLatency ?milliseconds?");
283 return TCL_ERROR;
284 }
285 return TCL_OK;
286 }
287
288 static int
scalingCmd(Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])289 scalingCmd(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
290 {
291 double d = 0.0;
292
293 if (objc == 2) {
294 Tcl_SetObjResult(interp, Tcl_NewDoubleObj(globalScaling));
295 } else if (objc == 3) {
296 if (Tcl_GetDoubleFromObj(interp, objv[2], &d) != TCL_OK) {
297 return TCL_ERROR;
298 }
299 globalScaling = (float) d;
300 } else {
301 Tcl_WrongNumArgs(interp, 1, objv, "scaling ?factor?");
302 return TCL_ERROR;
303 }
304 return TCL_OK;
305 }
306
307 static int
audioPlayCmd(Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])308 audioPlayCmd(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
309 {
310 if (rop == PAUSED || wop == PAUSED) {
311 SnackPauseAudio();
312 }
313
314 return TCL_OK;
315 }
316
317 static int
audioStopCmd(Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])318 audioStopCmd(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
319 {
320 jkQueuedSound *p;
321
322 if (rop == READ || rop == PAUSED) {
323 for (p = rsoundQueue; p != NULL; p = p->next) {
324 Snack_StopSound(p->sound, interp);
325 }
326 }
327 if (wop == WRITE || wop == PAUSED) {
328 for (p = soundQueue; p != NULL; p = p->next) {
329 Snack_StopSound(p->sound, interp);
330 /*
331 * The soundQueue can be remooved during a stop, so check it
332 * otherwise p is garbage
333 */
334 if (soundQueue == NULL)
335 break;
336 }
337 }
338
339 return TCL_OK;
340 }
341
342 static int
audioPauseCmd(Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])343 audioPauseCmd(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
344 {
345 SnackPauseAudio();
346
347 return TCL_OK;
348 }
349
350 #define NAUDIOCOMMANDS 17
351 #define MAXAUDIOCOMMANDS 25
352
353 int nAudioCommands = NAUDIOCOMMANDS;
354 int maxAudioCommands = MAXAUDIOCOMMANDS;
355
356 CONST84 char *audioCmdNames[MAXAUDIOCOMMANDS] = {
357 "outputDevices",
358 "inputDevices",
359 "selectOutput",
360 "selectInput",
361 "formats",
362 "frequencies",
363 "active",
364 "play_gain",
365 "record_gain",
366 "elapsedTime",
367 "currentSound",
368 "playLatency",
369 "scaling",
370 "encodings",
371 "rates",
372 "play",
373 "stop",
374 "pause",
375 NULL
376 };
377
378 /* NOTE: NAUDIOCOMMANDS needs updating when new commands are added. */
379
380 audioCmd *audioCmdProcs[MAXAUDIOCOMMANDS] = {
381 outDevicesCmd,
382 inDevicesCmd,
383 selectOutCmd,
384 selectInCmd,
385 encodingsCmd,
386 ratesCmd,
387 activeCmd,
388 play_gainCmd,
389 record_gainCmd,
390 elapsedTimeCmd,
391 currentSoundCmd,
392 playLatencyCmd,
393 scalingCmd,
394 encodingsCmd,
395 ratesCmd,
396 audioPlayCmd,
397 audioStopCmd,
398 audioPauseCmd
399 };
400
401 audioDelCmd *audioDelCmdProcs[MAXAUDIOCOMMANDS] = {
402 NULL,
403 NULL,
404 NULL,
405 NULL,
406 NULL,
407 NULL,
408 NULL,
409 NULL,
410 NULL,
411 NULL,
412 NULL,
413 NULL,
414 NULL,
415 NULL,
416 NULL,
417 NULL,
418 NULL
419 };
420
421 int
Snack_AudioCmd(ClientData cdata,Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])422 Snack_AudioCmd(ClientData cdata, Tcl_Interp *interp, int objc,
423 Tcl_Obj *CONST objv[])
424 {
425 int index;
426
427 if (objc < 2) {
428 Tcl_WrongNumArgs(interp, 1, objv, "option ?arg?");
429 return TCL_ERROR;
430 }
431
432 if (Tcl_GetIndexFromObj(interp, objv[1], audioCmdNames, "option", 0,
433 &index) != TCL_OK) {
434 return TCL_ERROR;
435 }
436
437 return((audioCmdProcs[index])(interp, objc, objv));
438 }
439
440 void
Snack_AudioDeleteCmd(ClientData clientData)441 Snack_AudioDeleteCmd(ClientData clientData)
442 {
443 int i;
444
445 for (i = 0; i < nAudioCommands; i++) {
446 if (audioDelCmdProcs[i] != NULL) {
447 (audioDelCmdProcs[i])();
448 }
449 }
450 }
451