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 "jkSound.h" 25#include <stdio.h> 26#include <fcntl.h> 27#include <unistd.h> 28#include <stdlib.h> 29 30static char *audioDev; 31#define DEF_AUDIO "/dev/audio" 32#define ENV_AUDIO "AUDIODEV" 33 34extern void Snack_WriteLog(char *s); 35extern void Snack_WriteLogInt(char *s, int n); 36 37#ifndef min 38#define min(a,b) ((a)<(b)?(a):(b)) 39#define max(a,b) ((a)>(b)?(a):(b)) 40#endif 41 42int ctlfd = 0; 43 44#define SNACK_NUMBER_MIXERS 2 45#define SNACK_NUMBER_JACKS 5 46#define SNACK_NUMBER_OUTJACKS 3 47 48struct MixerLink mixerLinks[SNACK_NUMBER_JACKS][2]; 49 50int 51SnackAudioOpen(ADesc *A, Tcl_Interp *interp, char *device, int mode, int freq, 52 int nchannels, int encoding) 53{ 54 ioctl(ctlfd, AUDIO_GETINFO, &A->ainfo); 55 56 A->mode = mode; 57 switch (mode) { 58 case RECORD: 59 if ((A->afd = open(audioDev, O_RDONLY, 0)) < 0) { 60 Tcl_AppendResult(interp, "Couldn't open ", audioDev, " for read.", 61 NULL); 62 return TCL_ERROR; 63 } 64 break; 65 66 case PLAY: 67 if ((A->afd = open(audioDev, O_WRONLY, 0)) < 0) { 68 Tcl_AppendResult(interp, "Couldn't open ", audioDev, " for write.", 69 NULL); 70 return TCL_ERROR; 71 } 72 fcntl(A->afd, F_SETFL, O_NONBLOCK); 73 break; 74 } 75 76 fcntl(A->afd, F_SETFD, FD_CLOEXEC); 77 78 A->ainfo.play.sample_rate = freq; 79 A->ainfo.play.channels = nchannels; 80 A->ainfo.record.sample_rate = freq; 81 A->ainfo.record.channels = nchannels; 82 A->nChannels = nchannels; 83 A->convert = 0; 84 A->convBuf = NULL; 85 A->convSize = 0; 86 87 switch (encoding) { 88 case LIN16: 89 A->ainfo.play.encoding = AUDIO_ENCODING_LINEAR; 90 A->ainfo.record.encoding = AUDIO_ENCODING_LINEAR; 91 A->ainfo.play.precision = 16; 92 A->ainfo.record.precision = 16; 93 A->bytesPerSample = sizeof(short); 94 break; 95 case ALAW: 96 if (nchannels == 1) { 97 A->ainfo.play.encoding = AUDIO_ENCODING_ALAW; 98 A->ainfo.record.encoding = AUDIO_ENCODING_ALAW; 99 A->ainfo.play.precision = 8; 100 A->ainfo.record.precision = 8; 101 A->bytesPerSample = sizeof(char); 102 } else { 103 A->ainfo.play.encoding = AUDIO_ENCODING_LINEAR; 104 A->ainfo.record.encoding = AUDIO_ENCODING_LINEAR; 105 A->ainfo.play.precision = 16; 106 A->ainfo.record.precision = 16; 107 A->bytesPerSample = sizeof(short); 108 A->convert = ALAW; 109 } 110 break; 111 case MULAW: 112 if (nchannels == 1) { 113 A->ainfo.play.encoding = AUDIO_ENCODING_ULAW; 114 A->ainfo.record.encoding = AUDIO_ENCODING_ULAW; 115 A->ainfo.play.precision = 8; 116 A->ainfo.record.precision = 8; 117 A->bytesPerSample = sizeof(char); 118 } else { 119 A->ainfo.play.encoding = AUDIO_ENCODING_LINEAR; 120 A->ainfo.record.encoding = AUDIO_ENCODING_LINEAR; 121 A->ainfo.play.precision = 16; 122 A->ainfo.record.precision = 16; 123 A->bytesPerSample = sizeof(short); 124 A->convert = MULAW; 125 } 126 break; 127 case LIN8OFFSET: 128 A->ainfo.play.encoding = AUDIO_ENCODING_LINEAR8; 129 A->ainfo.record.encoding = AUDIO_ENCODING_LINEAR8; 130 A->ainfo.play.precision = 8; 131 A->ainfo.record.precision = 8; 132 A->bytesPerSample = sizeof(char); 133 break; 134 } 135 136 if (ioctl(A->afd, AUDIO_SETINFO, &A->ainfo) < 0) { 137 Tcl_AppendResult(interp, "Unssupported audio format.", NULL); 138 return TCL_ERROR; 139 } 140 A->time = SnackCurrentTime(); 141 A->timep = 0.0; 142 A->freq = freq; 143 144 return TCL_OK; 145} 146 147int 148SnackAudioClose(ADesc *A) 149{ 150 close(A->afd); 151 ckfree(A->convBuf); 152 153 return(0); 154} 155 156long 157SnackAudioPause(ADesc *A) 158{ 159 /* int count;*/ 160 long res = SnackAudioPlayed(A); 161 162 AUDIO_INITINFO(&A->ainfo); 163 164 switch (A->mode) { 165 case RECORD: 166 A->ainfo.record.pause = 1; 167 ioctl(A->afd, AUDIO_SETINFO, &A->ainfo); 168 return(-1); 169 break; 170 171 case PLAY: 172 /* count = SnackAudioPlayed(A);*/ 173 A->ainfo.play.pause = 1; 174 ioctl(A->afd, AUDIO_SETINFO, &A->ainfo); 175 SnackAudioFlush(A); 176 /* return(count);*/ 177 A->timep = SnackCurrentTime(); 178 179 return(res); 180 break; 181 } 182} 183 184void 185SnackAudioResume(ADesc *A) 186{ 187 AUDIO_INITINFO(&A->ainfo); 188 189 switch (A->mode) { 190 case RECORD: 191 A->ainfo.record.pause = 0; 192 break; 193 194 case PLAY: 195 A->ainfo.play.pause = 0; 196 A->time = A->time + SnackCurrentTime() - A->timep; 197 break; 198 } 199 ioctl(A->afd, AUDIO_SETINFO, &A->ainfo); 200} 201 202void 203SnackAudioFlush(ADesc *A) 204{ 205 if (A->mode == RECORD) { 206 ioctl(A->afd, I_FLUSH, FLUSHR); 207 } else { 208 ioctl(A->afd, I_FLUSH, FLUSHW); 209 } 210} 211 212void 213SnackAudioPost(ADesc *A) 214{ 215} 216 217int 218SnackAudioRead(ADesc *A, void *buf, int nFrames) 219{ 220 if (nFrames == 0) return(0); 221 222 if (A->convert) { 223 int n = 0, i, res; 224 short s[2]; 225 226 for (i = 0; i < nFrames * A->nChannels; i += 2) { 227 res = read(A->afd, &s, 2*sizeof(short)); 228 if (res <= 0) return(n / (A->bytesPerSample * A->nChannels)); 229 if (A->convert == ALAW) { 230 ((unsigned char *)buf)[i] = Snack_Lin2Alaw(s[0]); 231 ((unsigned char *)buf)[i+1] = Snack_Lin2Alaw(s[1]); 232 } else { 233 ((unsigned char *)buf)[i] = Snack_Lin2Mulaw(s[0]); 234 ((unsigned char *)buf)[i+1] = Snack_Lin2Mulaw(s[1]); 235 } 236 n += res; 237 } 238 239 return(n / (A->bytesPerSample * A->nChannels)); 240 } else { 241 int n = read(A->afd, buf, nFrames * A->bytesPerSample * A->nChannels); 242 if (n > 0) n /= (A->bytesPerSample * A->nChannels); 243 return(n); 244 } 245} 246 247int 248SnackAudioWrite(ADesc *A, void *buf, int nFrames) 249{ 250 if (A->convert) { 251 int n = 0, i, res; 252 253 if (nFrames * A->bytesPerSample * A->nChannels > A->convSize) { 254 A->convSize = nFrames * A->bytesPerSample * A->nChannels; 255 if ((A->convBuf = (short *) ckrealloc(A->convBuf, A->convSize)) == NULL) { 256 return(-1); 257 } 258 } 259 A->convSize = nFrames * A->bytesPerSample * A->nChannels; 260 for (i = 0; i < nFrames * A->nChannels; i += 2) { 261 if (A->convert == ALAW) { 262 A->convBuf[i] = Snack_Alaw2Lin(((unsigned char *)buf)[i]); 263 A->convBuf[i+1] = Snack_Alaw2Lin(((unsigned char *)buf)[i+1]); 264 } else { 265 A->convBuf[i] = Snack_Mulaw2Lin(((unsigned char *)buf)[i]); 266 A->convBuf[i+1] = Snack_Mulaw2Lin(((unsigned char *)buf)[i+1]); 267 } 268 } 269 n = write(A->afd, A->convBuf, nFrames * A->bytesPerSample * A->nChannels); 270 if (n > 0) n /= (A->bytesPerSample * A->nChannels); 271 return(n); 272 } else { 273 int n = write(A->afd, buf, nFrames * A->bytesPerSample * A->nChannels); 274 if (n > 0) n /= (A->bytesPerSample * A->nChannels); 275 return(n); 276 } 277} 278 279int 280SnackAudioReadable(ADesc *A) 281{ 282 int NBytes = 0; 283 284 ioctl(A->afd, FIONREAD, &NBytes); 285 return(NBytes / (A->bytesPerSample * A->nChannels)); 286} 287 288int 289SnackAudioWriteable(ADesc *A) 290{ 291 return -1; 292} 293 294long 295SnackAudioPlayed(ADesc *A) 296{ 297 /* ioctl(A->afd, AUDIO_GETINFO, &A->ainfo); 298 299 return(A->ainfo.play.samples);*/ 300 long res; 301 302 res = (A->freq * (SnackCurrentTime() - A->time) +.5); 303 return(res); 304} 305 306void 307SnackAudioInit() 308{ 309 char *audioCtl; 310 311 audioDev = getenv(ENV_AUDIO); /* try environment variable first */ 312 if (!audioDev) 313 audioDev = DEF_AUDIO; /* take default */ 314 315 audioCtl = ckalloc(strlen(audioDev) + 4); 316 if (audioCtl) { 317 strcpy(audioCtl, audioDev); 318 strcat(audioCtl, "ctl"); 319 if ((ctlfd = open(audioCtl, O_RDWR)) < 0) { 320 fprintf(stderr, "Unable to open %s\n", audioCtl); 321 } 322 ckfree(audioCtl); 323 } 324} 325 326void 327SnackAudioFree() 328{ 329 int i, j; 330 331 for (i = 0; i < SNACK_NUMBER_MIXERS; i++) { 332 for (j = 0; j < 2; j++) { 333 if (mixerLinks[i][j].mixer != NULL) { 334 ckfree(mixerLinks[i][j].mixer); 335 } 336 if (mixerLinks[i][j].mixerVar != NULL) { 337 ckfree(mixerLinks[i][j].mixerVar); 338 } 339 } 340 if (mixerLinks[i][0].jack != NULL) { 341 ckfree(mixerLinks[i][0].jack); 342 } 343 if (mixerLinks[i][0].jackVar != NULL) { 344 ckfree((char *)mixerLinks[i][0].jackVar); 345 } 346 } 347 348 if (ctlfd > 0) close(ctlfd); 349} 350 351void 352ASetRecGain(int gain) 353{ 354 int g = min(max(gain, 0), 100); 355 356 audio_info_t info; 357 358 AUDIO_INITINFO(&info); 359 info.record.gain = (int) (g * AUDIO_MAX_GAIN / 100.0 + 0.5); 360 ioctl(ctlfd, AUDIO_SETINFO, &info); 361} 362 363void 364ASetPlayGain(int gain) 365{ 366 int g = min(max(gain, 0), 100); 367 368 audio_info_t info; 369 370 AUDIO_INITINFO(&info); 371 info.play.gain = (int) (g * AUDIO_MAX_GAIN / 100.0 + 0.5); 372 ioctl(ctlfd, AUDIO_SETINFO, &info); 373} 374 375int 376AGetRecGain() 377{ 378 int g = 0; 379 audio_info_t info; 380 381 ioctl(ctlfd, AUDIO_GETINFO, &info); 382 g = (int) (info.record.gain * 100.0 / AUDIO_MAX_GAIN + 0.5); 383 384 return(g); 385} 386 387int 388AGetPlayGain() 389{ 390 int g = 0; 391 audio_info_t info; 392 393 ioctl(ctlfd, AUDIO_GETINFO, &info); 394 g = (int) (info.play.gain * 100.0 / AUDIO_MAX_GAIN + 0.5); 395 396 return(g); 397} 398 399int 400SnackAudioGetEncodings(char *device) 401{ 402 return(LIN16); 403} 404 405void 406SnackAudioGetRates(char *device, char *buf, int n) 407{ 408 strncpy(buf, "8000 11025 16000 22050 32000 44100 48000", n); 409 buf[n-1] = '\0'; 410} 411 412int 413SnackAudioMaxNumberChannels(char *device) 414{ 415 return(2); 416} 417 418int 419SnackAudioMinNumberChannels(char *device) 420{ 421 return(1); 422} 423 424void 425SnackMixerGetInputJackLabels(char *buf, int n) 426{ 427 strncpy(buf, "Microphone \"Line In\"", n); 428 buf[n-1] = '\0'; 429} 430 431void 432SnackMixerGetOutputJackLabels(char *buf, int n) 433{ 434 strncpy(buf, "Speaker \"Line Out\" Headphones", n); 435 buf[n-1] = '\0'; 436} 437 438void 439SnackMixerGetInputJack(char *buf, int n) 440{ 441 audio_info_t info; 442 int pos = 0; 443 444 ioctl(ctlfd, AUDIO_GETINFO, &info); 445 446 if (info.record.port & AUDIO_LINE_IN) { 447 pos += (int) sprintf(&buf[pos], "\"Line In\" "); 448 } 449 if (info.record.port & AUDIO_MICROPHONE) { 450 pos += (int) sprintf(&buf[pos], "Microphone"); 451 } 452} 453 454int 455SnackMixerSetInputJack(Tcl_Interp *interp, char *jack, CONST84 char *status) 456{ 457 audio_info_t info; 458 int jackLen = strlen(jack), start = 0, end = 0, mask; 459 460 ioctl(ctlfd, AUDIO_GETINFO, &info); 461 462 while(end < jackLen) { 463 while ((end < jackLen) && (!isspace(jack[end]))) end++; 464 if (strncasecmp(&jack[start], "Microphone", end - start) == 0) { 465 mask = AUDIO_MICROPHONE; 466 } else if (strncasecmp(&jack[start], "Line In", end - start) == 0) { 467 mask = AUDIO_LINE_IN; 468 } 469 if (strcmp(status, "0") == 0) { 470 info.record.port = (~mask) & 0x03; 471 } else { 472 info.record.port = mask; 473 } 474 while ((end < jackLen) && (isspace(jack[end]))) end++; 475 start = end; 476 } 477 478 ioctl(ctlfd, AUDIO_SETINFO, &info); 479 480 return 0; 481} 482 483void 484SnackMixerGetOutputJack(char *buf, int n) 485{ 486 audio_info_t info; 487 int pos = 0; 488 489 ioctl(ctlfd, AUDIO_GETINFO, &info); 490 491 if (info.play.port & AUDIO_SPEAKER) { 492 pos += (int) sprintf(&buf[pos], "Speaker "); 493 } 494 if (info.play.port & AUDIO_HEADPHONE) { 495 pos += (int) sprintf(&buf[pos], "Headphones "); 496 } 497 if (info.play.port & AUDIO_LINE_OUT) { 498 pos += (int) sprintf(&buf[pos], "\"Line Out\""); 499 } 500} 501 502void 503SnackMixerSetOutputJack(char *jack, char *status) 504{ 505 audio_info_t info; 506 int jackLen = strlen(jack), start = 0, end = 0, mask; 507 508 ioctl(ctlfd, AUDIO_GETINFO, &info); 509 510 while(end < jackLen) { 511 while ((end < jackLen) && (!isspace(jack[end]))) end++; 512 if (strncasecmp(&jack[start], "Speaker", end - start) == 0) { 513 mask = AUDIO_SPEAKER; 514 } else if (strncasecmp(&jack[start], "Line Out", end - start) == 0) { 515 mask = AUDIO_LINE_OUT; 516 } else if (strncasecmp(&jack[start], "Headphones", end - start) == 0) { 517 mask = AUDIO_HEADPHONE; 518 } 519 if (strcmp(status, "0") == 0) { 520 info.play.port &= ~mask; 521 } else { 522 info.play.port |= mask; 523 } 524 while ((end < jackLen) && (isspace(jack[end]))) end++; 525 start = end; 526 } 527 528 ioctl(ctlfd, AUDIO_SETINFO, &info); 529} 530 531static char * 532JackVarProc(ClientData clientData, Tcl_Interp *interp, CONST84 char *name1, 533 CONST84 char *name2, int flags) 534{ 535 MixerLink *mixLink = (MixerLink *) clientData; 536 int i, j, status = 0; 537 CONST84 char *stringValue; 538 char *jackLabels[] = { "Speaker", "Line Out", "Headphones", 539 "Microphone", "Line In" }; 540 Tcl_Obj *obj, *var; 541 audio_info_t info; 542 543 ioctl(ctlfd, AUDIO_GETINFO, &info); 544 545 for (i = 0; i < SNACK_NUMBER_JACKS; i++) { 546 if (strncasecmp(mixLink->jack, jackLabels[i], strlen(mixLink->jack)) 547 == 0) { 548 break; 549 } 550 } 551 if ((i == 0 && info.play.port & AUDIO_SPEAKER) || 552 (i == 1 && info.play.port & AUDIO_LINE_OUT) || 553 (i == 2 && info.play.port & AUDIO_HEADPHONE) || 554 (i == 3 && info.record.port & AUDIO_MICROPHONE) || 555 (i == 4 && info.record.port & AUDIO_LINE_IN)) { 556 status = 1; 557 } 558 559 if (flags & TCL_TRACE_UNSETS) { 560 if ((flags & TCL_TRACE_DESTROYED) && !(flags & TCL_INTERP_DESTROYED)) { 561 obj = Tcl_NewIntObj(status); 562 var = Tcl_NewStringObj(mixLink->jackVar, -1); 563 Tcl_ObjSetVar2(interp, var, NULL, obj, TCL_GLOBAL_ONLY | TCL_PARSE_PART1); 564 Tcl_TraceVar(interp, mixLink->jackVar, 565 TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS, 566 JackVarProc, mixLink); 567 } 568 return (char *) NULL; 569 } 570 571 stringValue = Tcl_GetVar(interp, mixLink->jackVar, TCL_GLOBAL_ONLY); 572 if (stringValue != NULL) { 573 if (i < SNACK_NUMBER_OUTJACKS) { 574 SnackMixerSetOutputJack(mixLink->jack, stringValue); 575 } else { 576 SnackMixerSetInputJack(interp, mixLink->jack, stringValue); 577 } 578 } 579 580 ioctl(ctlfd, AUDIO_GETINFO, &info); 581 582 for (j = SNACK_NUMBER_OUTJACKS; j < SNACK_NUMBER_JACKS; j++) { 583 if ((j == 3 && info.record.port & AUDIO_MICROPHONE) || 584 (j == 4 && info.record.port & AUDIO_LINE_IN)) { 585 status = 1; 586 } else { 587 status = 0; 588 } 589 if (i == j) continue; 590 obj = Tcl_NewIntObj(status); 591 var = Tcl_NewStringObj(mixerLinks[j][0].jackVar, -1); 592 Tcl_ObjSetVar2(interp, var, NULL, obj, TCL_GLOBAL_ONLY |TCL_PARSE_PART1); 593 } 594 595 return (char *) NULL; 596} 597 598void 599SnackMixerLinkJacks(Tcl_Interp *interp, char *jack, Tcl_Obj *var) 600{ 601 char *jackLabels[] = { "Speaker", "Line Out", "Headphones", 602 "Microphone", "Line In" }; 603 int i, status = 0; 604 CONST84 char *value; 605 audio_info_t info; 606 607 ioctl(ctlfd, AUDIO_GETINFO, &info); 608 609 for (i = 0; i < SNACK_NUMBER_JACKS; i++) { 610 if (strncasecmp(jack, jackLabels[i], strlen(jack)) == 0) { 611 if ((i == 0 && info.play.port & AUDIO_SPEAKER) || 612 (i == 1 && info.play.port & AUDIO_LINE_OUT) || 613 (i == 2 && info.play.port & AUDIO_HEADPHONE) || 614 (i == 3 && info.record.port & AUDIO_MICROPHONE) || 615 (i == 4 && info.record.port & AUDIO_LINE_IN)) { 616 status = 1; 617 } 618 mixerLinks[i][0].jack = (char *)SnackStrDup(jack); 619 mixerLinks[i][0].jackVar = (char *)SnackStrDup(Tcl_GetStringFromObj(var, NULL)); 620 value = Tcl_GetVar(interp, mixerLinks[i][0].jackVar, TCL_GLOBAL_ONLY); 621 if (value != NULL) { 622 if (i < SNACK_NUMBER_OUTJACKS) { 623 SnackMixerSetOutputJack(mixerLinks[i][0].jack, value); 624 } else { 625 SnackMixerSetInputJack(interp, mixerLinks[i][0].jack, value); 626 } 627 } else { 628 Tcl_Obj *obj = Tcl_NewIntObj(status); 629 Tcl_ObjSetVar2(interp, var, NULL, obj, 630 TCL_GLOBAL_ONLY | TCL_PARSE_PART1); 631 } 632 Tcl_TraceVar(interp, mixerLinks[i][0].jackVar, 633 TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS, 634 JackVarProc, (ClientData) &mixerLinks[i][0]); 635 break; 636 } 637 } 638} 639 640void 641SnackMixerGetChannelLabels(char *line, char *buf, int n) 642{ 643 strncpy(buf, "Mono", n); 644 buf[n-1] = '\0'; 645} 646 647void 648SnackMixerGetVolume(char *line, int channel, char *buf, int n) 649{ 650 if (strncasecmp(line, "Record", strlen(line)) == 0) { 651 sprintf(buf, "%d", AGetRecGain()); 652 } 653 if (strncasecmp(line, "Play", strlen(line)) == 0) { 654 sprintf(buf, "%d", AGetPlayGain()); 655 } 656} 657 658void 659SnackMixerSetVolume(char *line, int channel, int volume) 660{ 661 if (strncasecmp(line, "Record", strlen(line)) == 0) { 662 ASetRecGain(volume); 663 } 664 if (strncasecmp(line, "Play", strlen(line)) == 0) { 665 ASetPlayGain(volume); 666 } 667} 668 669static char * 670VolumeVarProc(ClientData clientData, Tcl_Interp *interp, CONST84 char *name1, 671 CONST84 char *name2, int flags) 672{ 673 MixerLink *mixLink = (MixerLink *) clientData; 674 CONST84 char *stringValue; 675 676 if (flags & TCL_TRACE_UNSETS) { 677 if ((flags & TCL_TRACE_DESTROYED) && !(flags & TCL_INTERP_DESTROYED)) { 678 Tcl_Obj *obj, *var; 679 char tmp[VOLBUFSIZE]; 680 681 SnackMixerGetVolume(mixLink->mixer, mixLink->channel, tmp, VOLBUFSIZE); 682 obj = Tcl_NewIntObj(atoi(tmp)); 683 var = Tcl_NewStringObj(mixLink->mixerVar, -1); 684 Tcl_ObjSetVar2(interp, var, NULL, obj, TCL_GLOBAL_ONLY | TCL_PARSE_PART1); 685 Tcl_TraceVar(interp, mixLink->mixerVar, 686 TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS, 687 VolumeVarProc, mixLink); 688 } 689 return (char *) NULL; 690 } 691 stringValue = Tcl_GetVar(interp, mixLink->mixerVar, TCL_GLOBAL_ONLY); 692 if (stringValue != NULL) { 693 SnackMixerSetVolume(mixLink->mixer, mixLink->channel, atoi(stringValue)); 694 } 695 696 return (char *) NULL; 697} 698 699void 700SnackMixerLinkVolume(Tcl_Interp *interp, char *line, int n, 701 Tcl_Obj *CONST objv[]) 702{ 703 char *mixLabels[] = { "Play", "Record" }; 704 int i, j, channel; 705 CONST84 char *value; 706 char tmp[VOLBUFSIZE]; 707 708 for (i = 0; i < SNACK_NUMBER_MIXERS; i++) { 709 if (strncasecmp(line, mixLabels[i], strlen(line)) == 0) { 710 for (j = 0; j < n; j++) { 711 if (n == 1) { 712 channel = -1; 713 } else { 714 channel = j; 715 } 716 mixerLinks[i][j].mixer = (char *)SnackStrDup(line); 717 mixerLinks[i][j].mixerVar = (char *)SnackStrDup(Tcl_GetStringFromObj(objv[j+3], NULL)); 718 mixerLinks[i][j].channel = j; 719 value = Tcl_GetVar(interp, mixerLinks[i][j].mixerVar, TCL_GLOBAL_ONLY); 720 if (value != NULL) { 721 SnackMixerSetVolume(line, channel, atoi(value)); 722 } else { 723 Tcl_Obj *obj; 724 SnackMixerGetVolume(line, channel, tmp, VOLBUFSIZE); 725 obj = Tcl_NewIntObj(atoi(tmp)); 726 Tcl_ObjSetVar2(interp, objv[j+3], NULL, obj, 727 TCL_GLOBAL_ONLY | TCL_PARSE_PART1); 728 } 729 Tcl_TraceVar(interp, mixerLinks[i][j].mixerVar, 730 TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS, 731 VolumeVarProc, (ClientData) &mixerLinks[i][j]); 732 } 733 } 734 } 735} 736 737void 738SnackMixerUpdateVars(Tcl_Interp *interp) 739{ 740 int i, j, status = 0; 741 char tmp[VOLBUFSIZE]; 742 Tcl_Obj *obj, *var; 743 audio_info_t info; 744 745 ioctl(ctlfd, AUDIO_GETINFO, &info); 746 747 for (i = 0; i < SNACK_NUMBER_JACKS; i++) { 748 for (j = 0; j < 2; j++) { 749 if (mixerLinks[i][j].mixerVar != NULL) { 750 SnackMixerGetVolume(mixerLinks[i][j].mixer, mixerLinks[i][j].channel, 751 tmp, VOLBUFSIZE); 752 obj = Tcl_NewIntObj(atoi(tmp)); 753 var = Tcl_NewStringObj(mixerLinks[i][j].mixerVar, -1); 754 Tcl_ObjSetVar2(interp, var, NULL, obj, TCL_GLOBAL_ONLY|TCL_PARSE_PART1); 755 } 756 } 757 if ((i == 0 && info.play.port & AUDIO_SPEAKER) || 758 (i == 1 && info.play.port & AUDIO_LINE_OUT) || 759 (i == 2 && info.play.port & AUDIO_HEADPHONE) || 760 (i == 3 && info.record.port & AUDIO_MICROPHONE) || 761 (i == 4 && info.record.port & AUDIO_LINE_IN)) { 762 status = 1; 763 } else { 764 status = 0; 765 } 766 767 if (mixerLinks[i][j].jackVar != NULL) { 768 obj = Tcl_NewIntObj(status); 769 var = Tcl_NewStringObj(mixerLinks[i][0].jackVar, -1); 770 Tcl_ObjSetVar2(interp, var, NULL, obj, TCL_GLOBAL_ONLY |TCL_PARSE_PART1); 771 } 772 } 773} 774 775void 776SnackMixerGetLineLabels(char *buf, int n) 777{ 778 strncpy(buf, "Play Record", n); 779 buf[n-1] = '\0'; 780} 781 782int 783SnackGetOutputDevices(char **arr, int n) 784{ 785 arr[0] = (char *) SnackStrDup("default"); 786 787 return 1; 788} 789 790int 791SnackGetInputDevices(char **arr, int n) 792{ 793 arr[0] = (char *) SnackStrDup("default"); 794 795 return 1; 796} 797 798int 799SnackGetMixerDevices(char **arr, int n) 800{ 801 arr[0] = (char *) SnackStrDup("default"); 802 803 return 1; 804} 805