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 30extern int rop, wop; 31extern double startDevTime; 32extern struct jkQueuedSound *soundQueue; 33extern 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 42double globalLatency = BUFSECS; 43float globalScaling = 1.0f; 44 45char defaultOutDevice[MAX_DEVICE_NAME_LENGTH]; 46char defaultInDevice[MAX_DEVICE_NAME_LENGTH]; 47 48char * 49SnackStrDup(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 60static int 61outDevicesCmd(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 79static int 80inDevicesCmd(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 98static int 99selectOutCmd(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 128static int 129selectInCmd(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 158static int 159encodingsCmd(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 168static int 169ratesCmd(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 179static int 180activeCmd(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 191static int 192play_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 209static int 210record_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 227static int 228elapsedTimeCmd(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 243static int 244currentSoundCmd(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 269static int 270playLatencyCmd(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 288static int 289scalingCmd(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 307static int 308audioPlayCmd(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 317static int 318audioStopCmd(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 342static int 343audioPauseCmd(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 353int nAudioCommands = NAUDIOCOMMANDS; 354int maxAudioCommands = MAXAUDIOCOMMANDS; 355 356CONST84 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 380audioCmd *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 401audioDelCmd *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 421int 422Snack_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 440void 441Snack_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