1/* 2 * Copyright (C) 1997-2002 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 "tcl.h" 23#include "jkAudIO.h" 24#include <stdio.h> 25#include <fcntl.h> 26#include <unistd.h> 27 28extern void Snack_WriteLog(char *s); 29extern void Snack_WriteLogInt(char *s, int n); 30 31#ifndef min 32#define min(a,b) ((a)<(b)?(a):(b)) 33#define max(a,b) ((a)>(b)?(a):(b)) 34#endif 35 36#define SNACK_NUMBER_MIXERS 2 37 38struct MixerLink mixerLinks[SNACK_NUMBER_MIXERS][2]; 39 40#define OLD_AL 41 42int 43SnackAudioOpen(ADesc *A, Tcl_Interp *interp, char *device, int mode, int freq, 44 int nchannels, int encoding) 45{ 46#ifdef OLD_AL 47# define alSetWidth(A,B) ALsetwidth(A,B) 48# define alSetChannels(A,B) ALsetchannels(A,B) 49# define alGetFrameNumber(A,B) ALgetframenumber(A, (unsigned long long *)B) 50# define alGetFilled(A) ALgetfilled(A) 51# define alClosePort(A) ALcloseport(A) 52# define alFreeConfig(A) ALfreeconfig(A) 53# define alReadFrames(A,B,C) ALreadsamps(A,B,C) 54# define alWriteFrames(A,B,C) ALwritesamps(A,B,C) 55# define alSetFillPoint(A,B) ALsetfillpoint(A,B) 56# define alGetFillable(A) ALgetfillable(A) 57 58 typedef long long stamp_t; 59 60 long buf[2]; 61 A->config = ALnewconfig(); 62#else 63 ALpv pv[2]; 64 A->config = alNewConfig(); 65#endif /* OLD_AL */ 66 67 switch (encoding) { 68 case LIN16: 69 if (alSetWidth(A->config, AL_SAMPLE_16) == -1) { 70 Tcl_AppendResult(interp, "Failed alSetWidth: AL_SAMPLE_16.", NULL); 71 return TCL_ERROR; 72 } 73 A->bytesPerSample = sizeof(short); 74 break; 75 case ALAW: 76 A->bytesPerSample = sizeof(char); 77 Tcl_AppendResult(interp, "Unsupported format ALAW.", NULL); 78 return TCL_ERROR; 79 case MULAW: 80 A->bytesPerSample = sizeof(char); 81 Tcl_AppendResult(interp, "Unsupported format MULAW.", NULL); 82 return TCL_ERROR; 83 case LIN8OFFSET: 84 A->bytesPerSample = sizeof(char); 85 Tcl_AppendResult(interp, "Unsupported format LIN8OFFSET.", NULL); 86 return TCL_ERROR; 87 case LIN8: 88 if (alSetWidth(A->config, AL_SAMPLE_8) == -1) { 89 Tcl_AppendResult(interp, "Failed alSetWidth: AL_SAMPLE_8.", NULL); 90 return TCL_ERROR; 91 } 92 A->bytesPerSample = sizeof(char); 93 break; 94 } 95 96 if (alSetChannels(A->config, nchannels) == -1) { 97 Tcl_AppendResult(interp, "Failed alSetChannels.", NULL); 98 return TCL_ERROR; 99 } 100 A->nChannels = nchannels; 101 102 A->mode = mode; 103 switch (mode) { 104 case RECORD: 105 106#ifdef OLD_AL 107 buf[0] = AL_INPUT_RATE; 108 buf[1] = freq; 109 if (ALsetparams(AL_DEFAULT_DEVICE, buf, 2) == -1) { 110 Tcl_AppendResult(interp, "Failed ALsetparams.", NULL); 111 return TCL_ERROR; 112 } 113 if ((A->port = ALopenport("snack-in", "r", A->config)) == NULL) { 114 Tcl_AppendResult(interp, "Failed ALopenport.", NULL); 115 return TCL_ERROR; 116 } 117#else 118 pv[0].param = AL_MASTER_CLOCK; 119 pv[0].value.i = AL_CRYSTAL_MCLK_TYPE; 120 pv[1].param = AL_RATE; 121 pv[1].value.ll = alDoubleToFixed((double)(freq * 1.0)); 122 if (alSetParams(AL_DEFAULT_INPUT, pv, 2) == -1) { 123 printf("Error: %s\n", alGetErrorString(oserror())); 124 if (pv[0].sizeOut < 0) printf("AL_MASTER_CLOCK\n"); 125 if (pv[1].sizeOut < 0) printf("Rate invalid\n"); 126 Tcl_AppendResult(interp, "Failed alSetParams.", NULL); 127 return TCL_ERROR; 128 } 129 if ((A->port = alOpenPort("snack-in", "r", A->config)) == NULL) { 130 Tcl_AppendResult(interp, "Failed ALopenport.", NULL); 131 return TCL_ERROR; 132 } 133#endif /* OLD_AL */ 134 break; 135 136 case PLAY: 137#ifdef OLD_AL 138 buf[0] = AL_OUTPUT_RATE; 139 buf[1] = freq; 140 if (ALsetparams(AL_DEFAULT_DEVICE, buf, 2) == -1) { 141 Tcl_AppendResult(interp, "Failed ALsetparams.", NULL); 142 return TCL_ERROR; 143 } 144 if ((A->port = ALopenport("snack-out", "w", A->config)) ==NULL) { 145 Tcl_AppendResult(interp, "Failed ALopenport.", NULL); 146 return TCL_ERROR; 147 } 148#else 149 pv[0].param = AL_MASTER_CLOCK; 150 pv[0].value.i = AL_CRYSTAL_MCLK_TYPE; 151 pv[1].param = AL_RATE; 152 pv[1].value.ll = alDoubleToFixed((double)(freq * 1.0)); 153 if (alSetParams(AL_DEFAULT_OUTPUT, pv, 1) == -1) { 154 Tcl_AppendResult(interp, "Failed ALsetparams.", NULL); 155 return TCL_ERROR; 156 } 157 if ((A->port = alOpenPort("snack-out", "w", A->config)) == NULL) { 158 Tcl_AppendResult(interp, "Failed ALopenport.", NULL); 159 return TCL_ERROR; 160 } 161#endif /* OLD_AL */ 162 break; 163 } 164 165 alGetFrameNumber(A->port, (stamp_t*) &A->startfn); 166 A->count = 0; 167 168 return TCL_OK; 169} 170 171int 172SnackAudioClose(ADesc *A) 173{ 174 if (A->port != NULL) { 175 if (alGetFilled(A->port) > 0) { 176 return(-1); 177 } 178 alClosePort(A->port); 179 } 180 alFreeConfig(A->config); 181 A->count = 0; 182 183 return(0); 184} 185 186long 187SnackAudioPause(ADesc *A) 188{ 189 switch (A->mode) { 190 case RECORD: 191#ifdef OLD_AL 192 alClosePort(A->port); 193 A->port = NULL; 194#else 195#endif 196 break; 197 198 case PLAY: 199#ifdef OLD_AL 200 A->count = SnackAudioPlayed(A); 201 alClosePort(A->port); 202 A->port = NULL; 203 return(A->count); 204#else 205#endif 206 /* break;*/ 207 } 208 return(-1); 209} 210 211void 212SnackAudioResume(ADesc *A) 213{ 214 switch (A->mode) { 215 case RECORD: 216 break; 217 218 case PLAY: 219#ifdef OLD_AL 220 A->port = ALopenport("snack-out", "w", A->config); 221#else 222#endif 223 break; 224 } 225} 226 227void 228SnackAudioFlush(ADesc *A) 229{ 230 switch (A->mode) { 231 case RECORD: 232 break; 233 234 case PLAY: 235#ifdef OLD_AL 236 alClosePort(A->port); 237 A->port = NULL; 238#else 239 alDiscardFrames(A->port, 10000000); 240#endif 241 break; 242 } 243} 244 245void 246SnackAudioPost(ADesc *A) 247{ 248} 249 250int 251SnackAudioRead(ADesc *A, void *buf, int nFrames) 252{ 253 alReadFrames(A->port, buf, nFrames * A->nChannels); /* always returns 0 */ 254 return(nFrames); 255} 256 257int 258SnackAudioWrite(ADesc *A, void *buf, int nFrames) 259{ 260 alWriteFrames(A->port, buf, nFrames * A->nChannels); 261 return(nFrames); 262} 263 264int 265SnackAudioReadable(ADesc *A) 266{ 267 if (A->port == NULL) { 268 return(0); 269 } 270 return alGetFilled(A->port); 271} 272 273int 274SnackAudioWriteable(ADesc *A) 275{ 276 return alGetFillable(A->port); 277} 278 279long 280SnackAudioPlayed(ADesc *A) 281{ 282 unsigned long long fnum; 283 284 /* Needed on IRIX 6.2 */ 285 typedef long long stamp_t; 286 287 alGetFrameNumber(A->port, (stamp_t*) &fnum); 288 289 return(A->count + (int)(fnum - A->startfn)); 290} 291 292void 293SnackAudioInit() 294{ 295} 296 297void 298SnackAudioFree() 299{ 300 int i, j; 301 302 for (i = 0; i < SNACK_NUMBER_MIXERS; i++) { 303 for (j = 0; j < 2; j++) { 304 if (mixerLinks[i][j].mixer != NULL) { 305 ckfree(mixerLinks[i][j].mixer); 306 } 307 if (mixerLinks[i][j].mixerVar != NULL) { 308 ckfree(mixerLinks[i][j].mixerVar); 309 } 310 } 311 if (mixerLinks[i][0].jack != NULL) { 312 ckfree(mixerLinks[i][0].jack); 313 } 314 if (mixerLinks[i][0].jackVar != NULL) { 315 ckfree((char *)mixerLinks[i][0].jackVar); 316 } 317 } 318} 319 320void 321ASetRecGain(int gain) 322{ 323 int g = min(max(gain, 0), 100); 324 325#ifdef OLD_AL 326 long buf[4]; 327 328 g = 255 - g * 255 / 100; 329 buf[0] = AL_LEFT_INPUT_ATTEN; 330 buf[1] = g; 331 buf[2] = AL_RIGHT_INPUT_ATTEN; 332 buf[3] = g; 333 ALsetparams(AL_DEFAULT_DEVICE, buf, 4); 334 335#else 336 ALpv x[2]; 337 ALfixed gains[2]; 338 ALparamInfo pi; 339 double gmin, gmax, fg; 340 341 alGetParamInfo(AL_DEFAULT_INPUT, AL_GAIN, &pi); 342 gmax = alFixedToDouble(pi.max.ll); 343 gmin = alFixedToDouble(pi.min.ll); 344 345 x[0].param = AL_GAIN; 346 x[0].value.ptr = gains; 347 x[0].sizeIn = 2; 348 x[1].param = AL_CHANNELS; 349 350 fg = alDoubleToFixed(((double) g / 100.0) * (gmax - gmin) + gmin); 351 gains[0] = gains[1] = fg; 352#endif /* OLD_AL */ 353} 354 355void 356ASetPlayGain(int gain) 357{ 358 int g = min(max(gain, 0), 100); 359 360#ifdef OLD_AL 361 long buf[4]; 362 363 g = 255 - g * 255 / 100; 364 buf[0] = AL_LEFT_INPUT_ATTEN; 365 buf[1] = g; 366 buf[2] = AL_RIGHT_INPUT_ATTEN; 367 buf[3] = g; 368 ALsetparams(AL_DEFAULT_DEVICE, buf, 4); 369#else 370 ALpv x[2]; 371 ALfixed gains[2]; 372 ALparamInfo pi; 373 double gmin, gmax, fg; 374 375 alGetParamInfo(AL_DEFAULT_OUTPUT, AL_GAIN, &pi); 376 gmax = alFixedToDouble(pi.max.ll); 377 gmin = alFixedToDouble(pi.min.ll); 378 379 x[0].param = AL_GAIN; 380 x[0].value.ptr = gains; 381 x[0].sizeIn = 2; 382 x[1].param = AL_CHANNELS; 383 384 fg = alDoubleToFixed(((double) g / 100.0) * (gmax - gmin) + gmin); 385 gains[0] = gains[1] = fg; 386#endif /* OLD_AL */ 387} 388 389int 390AGetRecGain() 391{ 392#ifdef OLD_AL 393 int g = 0; 394 long buf[2]; 395 396 buf[0] = AL_LEFT_INPUT_ATTEN; 397 ALgetparams(AL_DEFAULT_DEVICE, buf, 2); 398 g = 100 - buf[1] * 100 / 255; 399 400#else 401 int g = 0; 402 ALpv x[2]; 403 ALfixed gain[2]; 404 ALparamInfo pi; 405 double gmin, gmax; 406 407 alGetParamInfo(AL_DEFAULT_INPUT, AL_GAIN, &pi); 408 gmax = alFixedToDouble(pi.max.ll); 409 gmin = alFixedToDouble(pi.min.ll); 410 411 x[0].param = AL_GAIN; 412 x[0].value.ptr = gain; 413 x[0].sizeIn = 2; 414 x[1].param = AL_CHANNELS; 415 416 alGetParams(AL_DEFAULT_INPUT, x, 2); 417 418 g = (int) (100.0 * (alFixedToDouble(gain[0]) - gmin) / (gmax - gmin)); 419#endif /* OLD_AL */ 420 421 return(g); 422} 423 424int 425AGetPlayGain() 426{ 427#ifdef OLD_AL 428 int g = 0; 429 long buf[2]; 430 431 buf[0] = AL_LEFT_SPEAKER_GAIN; 432 ALgetparams(AL_DEFAULT_DEVICE, buf, 2); 433 g = buf[1] * 100 / 255; 434 435#else 436 int g = 0; 437 ALpv x[2]; 438 ALfixed gain[2]; 439 ALparamInfo pi; 440 double gmin, gmax; 441 442 alGetParamInfo(AL_DEFAULT_OUTPUT, AL_GAIN, &pi); 443 gmax = alFixedToDouble(pi.max.ll); 444 gmin = alFixedToDouble(pi.min.ll); 445 446 x[0].param = AL_GAIN; 447 x[0].value.ptr = gain; 448 x[0].sizeIn = 2; 449 x[1].param = AL_CHANNELS; 450 451 alGetParams(AL_DEFAULT_INPUT, x, 2); 452 453 g = (int) (100.0 * (alFixedToDouble(gain[0]) - gmin) / (gmax - gmin)); 454#endif /* OLD_AL */ 455 456 return(g); 457} 458 459int 460SnackAudioGetEncodings(char *device) 461{ 462 return(LIN24 | LIN16); 463} 464 465void 466SnackAudioGetRates(char *device, char *buf, int n) 467{ 468 strncpy(buf, "8000 11025 16000 22050 32000 44100 48000", n); 469 buf[n-1] = '\0'; 470} 471 472int 473SnackAudioMaxNumberChannels(char *device) 474{ 475 return(16); 476} 477 478int 479SnackAudioMinNumberChannels(char *device) 480{ 481 return(1); 482} 483 484void 485SnackMixerGetInputJackLabels(char *buf, int n) 486{ 487 strncpy(buf, "Microphone \"Line In\"", n); 488 buf[n-1] = '\0'; 489} 490 491void 492SnackMixerGetOutputJackLabels(char *buf, int n) 493{ 494 strncpy(buf, "Line Headphones", n); 495 buf[n-1] = '\0'; 496} 497 498void 499SnackMixerGetInputJack(char *buf, int n) 500{ 501 strncpy(buf, "Microphone", n); 502 buf[n-1] = '\0'; 503} 504 505int 506SnackMixerSetInputJack(Tcl_Interp *interp, char *jack, CONST84 char *status) 507{ 508 return 1; 509} 510 511void 512SnackMixerGetOutputJack(char *buf, int n) 513{ 514 buf[0] = '\0'; 515} 516 517void 518SnackMixerSetOutputJack(char *jack, char *status) 519{ 520} 521 522void 523SnackMixerGetChannelLabels(char *line, char *buf, int n) 524{ 525 strncpy(buf, "Mono", n); 526 buf[n-1] = '\0'; 527} 528 529void 530SnackMixerGetVolume(char *line, int channel, char *buf, int n) 531{ 532 if (strncasecmp(line, "Record", strlen(line)) == 0) { 533 sprintf(buf, "%d", AGetRecGain()); 534 } 535 if (strncasecmp(line, "Play", strlen(line)) == 0) { 536 sprintf(buf, "%d", AGetPlayGain()); 537 } 538} 539 540void 541SnackMixerSetVolume(char *line, int channel, int volume) 542{ 543 if (strncasecmp(line, "Record", strlen(line)) == 0) { 544 ASetRecGain(volume); 545 } 546 if (strncasecmp(line, "Play", strlen(line)) == 0) { 547 ASetPlayGain(volume); 548 } 549} 550 551void 552SnackMixerLinkJacks(Tcl_Interp *interp, char *jack, Tcl_Obj *var) 553{ 554} 555 556static char * 557VolumeVarProc(ClientData clientData, Tcl_Interp *interp, CONST84 char *name1, 558 CONST84 char *name2, int flags) 559{ 560 MixerLink *mixLink = (MixerLink *) clientData; 561 CONST84 char *stringValue; 562 563 if (flags & TCL_TRACE_UNSETS) { 564 if ((flags & TCL_TRACE_DESTROYED) && !(flags & TCL_INTERP_DESTROYED)) { 565 Tcl_Obj *obj, *var; 566 char tmp[VOLBUFSIZE]; 567 568 SnackMixerGetVolume(mixLink->mixer, mixLink->channel, tmp, VOLBUFSIZE); 569 obj = Tcl_NewIntObj(atoi(tmp)); 570 var = Tcl_NewStringObj(mixLink->mixerVar, -1); 571 Tcl_ObjSetVar2(interp, var, NULL, obj, TCL_GLOBAL_ONLY | TCL_PARSE_PART1); 572 Tcl_TraceVar(interp, mixLink->mixerVar, 573 TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS, 574 VolumeVarProc, (int *)mixLink); 575 } 576 return (char *) NULL; 577 } 578 stringValue = Tcl_GetVar(interp, mixLink->mixerVar, TCL_GLOBAL_ONLY); 579 if (stringValue != NULL) { 580 SnackMixerSetVolume(mixLink->mixer, mixLink->channel, atoi(stringValue)); 581 } 582 583 return (char *) NULL; 584} 585 586void 587SnackMixerLinkVolume(Tcl_Interp *interp, char *line, int n, 588 Tcl_Obj *CONST objv[]) 589{ 590 char *mixLabels[] = { "Play", "Record" }; 591 int i, j, channel; 592 CONST84 char *value; 593 char tmp[VOLBUFSIZE]; 594 595 for (i = 0; i < SNACK_NUMBER_MIXERS; i++) { 596 if (strncasecmp(line, mixLabels[i], strlen(line)) == 0) { 597 for (j = 0; j < n; j++) { 598 if (n == 1) { 599 channel = -1; 600 } else { 601 channel = j; 602 } 603 mixerLinks[i][j].mixer = (char *)SnackStrDup(line); 604 mixerLinks[i][j].mixerVar = (char *)SnackStrDup(Tcl_GetStringFromObj(objv[j+3], NULL)); 605 mixerLinks[i][j].channel = j; 606 value = Tcl_GetVar(interp, mixerLinks[i][j].mixerVar, TCL_GLOBAL_ONLY); 607 if (value != NULL) { 608 SnackMixerSetVolume(line, channel, atoi(value)); 609 } else { 610 Tcl_Obj *obj; 611 SnackMixerGetVolume(line, channel, tmp, VOLBUFSIZE); 612 obj = Tcl_NewIntObj(atoi(tmp)); 613 Tcl_ObjSetVar2(interp, objv[j+3], NULL, obj, 614 TCL_GLOBAL_ONLY | TCL_PARSE_PART1); 615 } 616 Tcl_TraceVar(interp, mixerLinks[i][j].mixerVar, 617 TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS, 618 VolumeVarProc, (ClientData) &mixerLinks[i][j]); 619 } 620 } 621 } 622} 623 624void 625SnackMixerUpdateVars(Tcl_Interp *interp) 626{ 627 int i, j; 628 char tmp[VOLBUFSIZE]; 629 Tcl_Obj *obj, *var; 630 631 for (i = 0; i < SNACK_NUMBER_MIXERS; i++) { 632 for (j = 0; j < 2; j++) { 633 if (mixerLinks[i][j].mixerVar != NULL) { 634 SnackMixerGetVolume(mixerLinks[i][j].mixer, mixerLinks[i][j].channel, 635 tmp, VOLBUFSIZE); 636 obj = Tcl_NewIntObj(atoi(tmp)); 637 var = Tcl_NewStringObj(mixerLinks[i][j].mixerVar, -1); 638 Tcl_ObjSetVar2(interp, var, NULL, obj, TCL_GLOBAL_ONLY|TCL_PARSE_PART1); 639 } 640 } 641 } 642} 643 644void 645SnackMixerGetLineLabels(char *buf, int n) 646{ 647 strncpy(buf, "Play Record", n); 648 buf[n-1] = '\0'; 649} 650 651int 652SnackGetOutputDevices(char **arr, int n) 653{ 654 arr[0] = (char *) SnackStrDup("default"); 655 656 return 1; 657} 658 659int 660SnackGetInputDevices(char **arr, int n) 661{ 662 arr[0] = (char *) SnackStrDup("default"); 663 664 return 1; 665} 666 667int 668SnackGetMixerDevices(char **arr, int n) 669{ 670 arr[0] = (char *) SnackStrDup("default"); 671 672 return 1; 673} 674