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 "tcl.h" 23#include "jkAudIO.h" 24#include "jkSound.h" 25#include <stdio.h> 26#include <fcntl.h> 27#include <unistd.h> 28#include <sys/ioctl.h> 29#ifdef __NetBSD__ 30 #include <soundcard.h> 31#else /* OSS default */ 32 #include <sys/soundcard.h> 33#endif 34#include <string.h> 35#include <ctype.h> 36#include <stdlib.h> 37#include <glob.h> 38#define DEVICE_NAME "/dev/dsp" 39#define MIXER_NAME "/dev/mixer" 40static char *defaultDeviceName = DEVICE_NAME; 41extern void Snack_WriteLog(char *s); 42extern void Snack_WriteLogInt(char *s, int n); 43 44#ifndef min 45#define min(a,b) ((a)<(b)?(a):(b)) 46#define max(a,b) ((a)>(b)?(a):(b)) 47#endif 48 49static int mfd = 0; 50 51static struct MixerLink mixerLinks[SOUND_MIXER_NRDEVICES][2]; 52 53static int littleEndian = 0; 54 55static int minNumChan = 1; 56 57int 58SnackAudioOpen(ADesc *A, Tcl_Interp *interp, char *device, int mode, int freq, 59 int nchannels, int encoding) 60{ 61 int format; 62 int nformat; 63 int channels; 64 int speed; 65 int mask; 66 /* int frag = 0x7fff0010;*/ 67 68 if (A->debug > 1) Snack_WriteLog(" Enter SnackAudioOpen\n"); 69 70 if (device == NULL) { 71 device = defaultDeviceName; 72 } 73 if (strlen(device) == 0) { 74 device = defaultDeviceName; 75 } 76 77 /* Test if the device is not locked by another process. This is 78 * just a crude workaround to avoid complete lockup of snack. It is 79 * not perfect since it is theoretically possible that another program 80 * locks the device between the close() and open() calls below. */ 81 A->afd = open(device, O_WRONLY|O_NONBLOCK); 82 if(A->afd == -1) { 83 Tcl_AppendResult(interp, "Could not gain access to ", device, " for writing.",NULL); 84 return TCL_ERROR; 85 } 86 close(A->afd); 87 88 A->mode = mode; 89 switch (mode) { 90 case RECORD: 91 if ((A->afd = open(device, O_RDONLY, 0)) == -1) { 92 Tcl_AppendResult(interp, "Could not open ", device, " for read.", 93 NULL); 94 return TCL_ERROR; 95 } 96 break; 97 98 case PLAY: 99 if ((A->afd = open(device, O_WRONLY, 0)) == -1) { 100 Tcl_AppendResult(interp, "Could not open ", device, " for write.", 101 NULL); 102 return TCL_ERROR; 103 } 104 break; 105 } 106 107 fcntl(A->afd, F_SETFD, FD_CLOEXEC); 108 109 /* if (ioctl(A->afd, SNDCTL_DSP_SETFRAGMENT, &frag)) return(-1);*/ 110 111 if (ioctl(A->afd, SNDCTL_DSP_GETFMTS, &mask) == -1) { 112 close(A->afd); 113 Tcl_AppendResult(interp, "Failed getting formats.", NULL); 114 return TCL_ERROR; 115 } 116 117 A->convert = 0; 118 119 switch (encoding) { 120 case LIN16: 121 if (littleEndian) { 122 format = AFMT_S16_LE; 123 } else { 124 format = AFMT_S16_BE; 125 } 126 A->bytesPerSample = sizeof(short); 127 break; 128#ifdef AFMT_S32_LE 129 case LIN24: 130 if (littleEndian) { 131 format = AFMT_S32_LE; 132 } else { 133 format = AFMT_S32_BE; 134 } 135 A->bytesPerSample = sizeof(int); 136 break; 137#endif 138 case ALAW: 139 if (mask & AFMT_A_LAW) { 140 format = AFMT_A_LAW; 141 A->bytesPerSample = sizeof(char); 142 } else { 143 if (littleEndian) { 144 format = AFMT_S16_LE; 145 } else { 146 format = AFMT_S16_BE; 147 } 148 A->bytesPerSample = sizeof(short); 149 A->convert = ALAW; 150 } 151 break; 152 case MULAW: 153 if (mask & AFMT_MU_LAW) { 154 format = AFMT_MU_LAW; 155 A->bytesPerSample = sizeof(char); 156 } else { 157 if (littleEndian) { 158 format = AFMT_S16_LE; 159 } else { 160 format = AFMT_S16_BE; 161 } 162 A->bytesPerSample = sizeof(short); 163 A->convert = MULAW; 164 } 165 break; 166 case LIN8OFFSET: 167 format = AFMT_U8; 168 A->bytesPerSample = sizeof(char); 169 break; 170 case LIN8: 171 format = AFMT_S8; 172 A->bytesPerSample = sizeof(char); 173 break; 174 } 175 176 nformat = format; 177 if (ioctl(A->afd, SNDCTL_DSP_SETFMT, &format) == -1 178 || format != nformat) { 179 close(A->afd); 180 Tcl_AppendResult(interp, "Failed setting format.", NULL); 181 return TCL_ERROR; 182 } 183 184 A->nChannels = nchannels; 185 channels = nchannels; 186 if (ioctl(A->afd, SNDCTL_DSP_CHANNELS, &channels) == -1 187 || channels != nchannels) { 188 close(A->afd); 189 Tcl_AppendResult(interp, "Failed setting number of channels.", NULL); 190 return TCL_ERROR; 191 } 192 193 speed = freq; 194 if (ioctl(A->afd, SNDCTL_DSP_SPEED, &speed) == -1 195 || abs(speed - freq) > freq / 100) { 196 close(A->afd); 197 Tcl_AppendResult(interp, "Failed setting sample frequency.", NULL); 198 return TCL_ERROR; 199 } 200 201 /* A->count = 0;*/ 202 A->frag_size = 0; 203 if (ioctl(A->afd, SNDCTL_DSP_GETBLKSIZE, &A->frag_size) == -1) { 204 close(A->afd); 205 Tcl_AppendResult(interp, "Failed getting fragment size.", NULL); 206 return TCL_ERROR; 207 } 208 /* printf("Frag size: %d\n", A->frag_size);*/ 209 A->time = SnackCurrentTime(); 210 A->timep = 0.0; 211 A->freq = freq; 212 A->warm = 0; 213 214 if (A->debug > 1) Snack_WriteLogInt(" Exit SnackAudioOpen", A->frag_size); 215 216 return TCL_OK; 217} 218 219int 220SnackAudioClose(ADesc *A) 221{ 222 if (A->debug > 1) Snack_WriteLog(" Enter SnackAudioClose\n"); 223 224 /* A->count = 0;*/ 225 226 close(A->afd); 227 228 if (A->debug > 1) Snack_WriteLog(" Exit SnackAudioClose\n"); 229 230 return(0); 231} 232 233long 234SnackAudioPause(ADesc *A) 235{ 236 long res = SnackAudioPlayed(A); 237 238 A->timep = SnackCurrentTime(); 239 ioctl(A->afd, SNDCTL_DSP_RESET, 0); 240 241 return(res); 242} 243 244void 245SnackAudioResume(ADesc *A) 246{ 247 A->time = A->time + SnackCurrentTime() - A->timep; 248} 249 250void 251SnackAudioFlush(ADesc *A) 252{ 253 if (A->mode == RECORD) { 254 } else { 255 ioctl(A->afd, SNDCTL_DSP_RESET, 0); 256 } 257} 258 259static char zeroBlock[16]; 260 261void 262SnackAudioPost(ADesc *A) 263{ 264 if (A->debug > 1) Snack_WriteLog(" Enter SnackAudioPost\n"); 265 266 if (A->warm == 1) { 267 int i; 268 for (i = 0; i < A->frag_size / (A->bytesPerSample * A->nChannels); i++) { 269 write(A->afd, zeroBlock, A->bytesPerSample * A->nChannels); 270 } 271 A->warm = 2; 272 ioctl(A->afd, SNDCTL_DSP_POST, 0); 273 } 274 275 if (A->debug > 1) Snack_WriteLog(" Exit SnackAudioPost\n"); 276} 277 278int 279SnackAudioRead(ADesc *A, void *buf, int nFrames) 280{ 281 int n = 2; 282 283 if (A->debug > 1) Snack_WriteLogInt(" Enter SnackAudioRead", nFrames); 284 285 286 while (nFrames > n * 2) n *= 2; 287 nFrames = n; 288 289 if (A->convert) { 290 int n = 0, i, res; 291 short s[2]; 292 293 for (i = 0; i < nFrames * A->nChannels; i += A->nChannels) { 294 res = read(A->afd, &s, A->nChannels * sizeof(short)); 295 if (res <= 0) return(n / (A->bytesPerSample * A->nChannels)); 296 if (A->convert == ALAW) { 297 ((unsigned char *)buf)[i] = Snack_Lin2Alaw(s[0]); 298 if (A->nChannels == 2) { 299 ((unsigned char *)buf)[i+1] = Snack_Lin2Alaw(s[1]); 300 } 301 } else { 302 ((unsigned char *)buf)[i] = Snack_Lin2Mulaw(s[0]); 303 if (A->nChannels == 2) { 304 ((unsigned char *)buf)[i+1] = Snack_Lin2Mulaw(s[1]); 305 } 306 } 307 n += res; 308 } 309 310 return(n / (A->bytesPerSample * A->nChannels)); 311 } else { 312 int n = read(A->afd, (unsigned char *)buf, nFrames * A->bytesPerSample * A->nChannels); 313 314 if (n > 0) n /= (A->bytesPerSample * A->nChannels); 315 316 if (A->debug > 1) Snack_WriteLogInt(" Exit SnackAudioRead", n); 317 318 return(n); 319 } 320} 321 322int 323SnackAudioWrite(ADesc *A, void *buf, int nFrames) 324{ 325 if (A->warm == 0) A->warm = 1; 326 327 if (A->convert) { 328 int n = 0, i, res; 329 short s; 330 331 for (i = 0; i < nFrames * A->nChannels; i++) { 332 if (A->convert == ALAW) { 333 s = Snack_Alaw2Lin(((unsigned char *)buf)[i]); 334 } else { 335 s = Snack_Mulaw2Lin(((unsigned char *)buf)[i]); 336 } 337 res = write(A->afd, &s, sizeof(short)); 338 if (res <= 0) return(n / (A->bytesPerSample * A->nChannels)); 339 n += res; 340 } 341 342 return(n / (A->bytesPerSample * A->nChannels)); 343 } else { 344 int n = write(A->afd, buf, nFrames * A->bytesPerSample * A->nChannels); 345 if (n > 0) n /= (A->bytesPerSample * A->nChannels); 346 347 return(n); 348 } 349} 350 351int 352SnackAudioReadable(ADesc *A) 353{ 354 audio_buf_info info; 355 356 if (A->debug > 1) Snack_WriteLog(" Enter SnackAudioReadable\n"); 357 358 ioctl(A->afd, SNDCTL_DSP_GETISPACE, &info); 359 if (info.bytes > 60*44100*4) info.bytes = 0; 360 if (A->debug > 1) Snack_WriteLogInt(" Exit SnackAudioReadable", info.bytes); 361 362 return (info.bytes / (A->bytesPerSample * A->nChannels)); 363} 364 365int 366SnackAudioWriteable(ADesc *A) 367{ 368 audio_buf_info info; 369 370 ioctl(A->afd, SNDCTL_DSP_GETOSPACE, &info); 371 372 return (info.bytes / (A->bytesPerSample * A->nChannels)); 373} 374 375long 376SnackAudioPlayed(ADesc *A) 377{ 378 /* 379 count_info info; 380 int res = 0; 381 382 if (A->warm) { 383 ioctl(A->afd, SNDCTL_DSP_GETOPTR, &info); 384 if (info.bytes > 0 || A->warm == 2) { 385 res = (A->freq * (SnackCurrentTime() - A->time) +.5); 386 A->warm = 2; 387 } 388 } 389 */ 390 long res; 391 392 res = (A->freq * (SnackCurrentTime() - A->time) +.5); 393 394 return(res); 395} 396 397void 398SnackAudioInit() 399{ 400 union { 401 char c[sizeof(short)]; 402 short s; 403 } order; 404 int afd, format, channels, nchannels; 405 /* 406 int i, n; 407 char *arr[MAX_NUM_DEVICES]; 408 */ 409 410 /* Compute the byte order of this machine. */ 411 412 order.s = 1; 413 if (order.c[0] == 1) { 414 littleEndian = 1; 415 } 416 417 if ((mfd = open(MIXER_NAME, O_RDWR, 0)) == -1) { 418 fprintf(stderr, "Unable to open mixer %s\n", MIXER_NAME); 419 } 420 /* 421 n = SnackGetOutputDevices(arr, MAX_NUM_DEVICES); 422 for (i = 0; i < n; i++) { 423 printf("Trying %s %d\n",arr[i], open(arr[i], O_WRONLY, 0)); 424 if ((afd = open(arr[i], O_WRONLY, 0)) != -1) { 425 defaultDeviceName = arr[i]; 426 printf("accepting %s %d\n",defaultDeviceName,afd); 427 break; 428 } 429 } 430 */ 431 432 if ((afd = open(defaultDeviceName, O_WRONLY, 0)) == -1) { 433 defaultDeviceName = "/dev/sound/dsp"; 434 if ((afd = open(defaultDeviceName, O_WRONLY, 0)) == -1) { 435 return; 436 } 437 } 438 close(afd); 439 440 /* Determine minimum number of channels supported. */ 441 442 if ((afd = open(defaultDeviceName, O_WRONLY, 0)) == -1) { 443 return; 444 } 445 446 if (littleEndian) { 447 format = AFMT_S16_LE; 448 } else { 449 format = AFMT_S16_BE; 450 } 451 if (ioctl(afd, SNDCTL_DSP_SETFMT, &format) == -1) { 452 close(afd); 453 return; 454 } 455 channels = nchannels = 1; 456 if (ioctl(afd, SNDCTL_DSP_CHANNELS, &channels) == -1 457 || channels != nchannels) { 458 minNumChan = channels; 459 } 460 close(afd); 461} 462 463void 464SnackAudioFree() 465{ 466 int i, j; 467 468 for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { 469 for (j = 0; j < 2; j++) { 470 if (mixerLinks[i][j].mixer != NULL) { 471 ckfree(mixerLinks[i][j].mixer); 472 } 473 if (mixerLinks[i][j].mixerVar != NULL) { 474 ckfree(mixerLinks[i][j].mixerVar); 475 } 476 } 477 if (mixerLinks[i][0].jack != NULL) { 478 ckfree(mixerLinks[i][0].jack); 479 } 480 if (mixerLinks[i][0].jackVar != NULL) { 481 ckfree((char *)mixerLinks[i][0].jackVar); 482 } 483 } 484 485 close(mfd); 486} 487 488void 489ASetRecGain(int gain) 490{ 491 int g = min(max(gain, 0), 100); 492 int recsrc = 0; 493 494 g = g * 256 + g; 495 ioctl(mfd, SOUND_MIXER_READ_RECSRC, &recsrc); 496 if (recsrc & SOUND_MASK_LINE) { 497 ioctl(mfd, SOUND_MIXER_WRITE_LINE, &g); 498 } else { 499 ioctl(mfd, SOUND_MIXER_WRITE_MIC, &g); 500 } 501} 502 503void 504ASetPlayGain(int gain) 505{ 506 int g = min(max(gain, 0), 100); 507 int pcm_gain = 25700; 508 509 g = g * 256 + g; 510 ioctl(mfd, SOUND_MIXER_WRITE_VOLUME, &g); 511 ioctl(mfd, SOUND_MIXER_WRITE_PCM, &pcm_gain); 512} 513 514int 515AGetRecGain() 516{ 517 int g = 0, left, right, recsrc = 0; 518 519 ioctl(mfd, SOUND_MIXER_READ_RECSRC, &recsrc); 520 if (recsrc & SOUND_MASK_LINE) { 521 ioctl(mfd, SOUND_MIXER_READ_LINE, &g); 522 } else { 523 ioctl(mfd, SOUND_MIXER_READ_MIC, &g); 524 } 525 left = g & 0xff; 526 right = (g & 0xff00) / 256; 527 g = (left + right) / 2; 528 529 return(g); 530} 531 532int 533AGetPlayGain() 534{ 535 int g = 0, left, right; 536 537 ioctl(mfd, SOUND_MIXER_READ_VOLUME, &g); 538 left = g & 0xff; 539 right = (g & 0xff00) / 256; 540 g = (left + right) / 2; 541 542 return(g); 543} 544 545int 546SnackAudioGetEncodings(char *device) 547{ 548 int afd, mask; 549 550 if ((afd = open(DEVICE_NAME, O_WRONLY, 0)) == -1) { 551 return(0); 552 } 553 if (ioctl(afd, SNDCTL_DSP_GETFMTS, &mask) == -1) { 554 return(0); 555 } 556 close(afd); 557 558 if (mask & AFMT_S16_LE || mask & AFMT_S16_BE) { 559 return(LIN16); 560 } else { 561 return(0); 562 } 563} 564 565void 566SnackAudioGetRates(char *device, char *buf, int n) 567{ 568 int afd, freq, pos= 0, i; 569 int f[] = { 8000, 11025, 16000, 22050, 32000, 44100, 48000, 96000 }; 570 571 if ((afd = open(DEVICE_NAME, O_WRONLY, 0)) == -1) { 572 buf[0] = '\0'; 573 return; 574 } 575 for (i = 0; i < 8; i++) { 576 freq = f[i]; 577 if (ioctl(afd, SNDCTL_DSP_SPEED, &freq) == -1) break; 578 if (abs(f[i] - freq) > freq / 100) continue; 579 pos += sprintf(&buf[pos], "%d ", freq); 580 } 581 close(afd); 582} 583 584int 585SnackAudioMaxNumberChannels(char *device) 586{ 587 return(2); 588} 589 590int 591SnackAudioMinNumberChannels(char *device) 592{ 593 return(minNumChan); 594} 595 596void 597SnackMixerGetInputJackLabels(char *buf, int n) 598{ 599 char *jackLabels[SOUND_MIXER_NRDEVICES] = SOUND_DEVICE_LABELS; 600 int i, recMask, pos = 0; 601 602 if (mfd != -1) { 603 ioctl(mfd, SOUND_MIXER_READ_RECMASK, &recMask); 604 for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { 605 if ((1 << i) & recMask) { 606 pos += sprintf(&buf[pos], "%s", jackLabels[i]); 607 pos += sprintf(&buf[pos], " "); 608 } 609 } 610 } else { 611 buf[0] = '\0'; 612 } 613 buf[n-1] = '\0'; 614} 615 616void 617SnackMixerGetOutputJackLabels(char *buf, int n) 618{ 619 buf[0] = '\0'; 620} 621 622void 623SnackMixerGetInputJack(char *buf, int n) 624{ 625 char *jackLabels[SOUND_MIXER_NRDEVICES] = SOUND_DEVICE_LABELS; 626 int i, recSrc = 0, pos = 0; 627 628 ioctl(mfd, SOUND_MIXER_READ_RECSRC, &recSrc); 629 for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { 630 if ((1 << i) & recSrc) { 631 pos += sprintf(&buf[pos], "%s", jackLabels[i]); 632 while (isspace(buf[pos-1])) pos--; 633 pos += sprintf(&buf[pos], " "); 634 } 635 } 636 if(isspace(buf[pos-1])) pos--; 637 buf[pos] = '\0'; 638 /*printf("SnackMixerGetInputJack %x, %s\n", recSrc, buf);*/ 639} 640 641int 642SnackMixerSetInputJack(Tcl_Interp *interp, char *jack, CONST84 char *status) 643{ 644 char *jackLabels[SOUND_MIXER_NRDEVICES] = SOUND_DEVICE_LABELS; 645 int i, recSrc = 0, currSrc; 646 647 for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { 648 if (strncasecmp(jack, jackLabels[i], strlen(jack)) == 0) { 649 recSrc = 1 << i; 650 break; 651 } 652 } 653 654 ioctl(mfd, SOUND_MIXER_READ_RECSRC, &currSrc); 655 656/* printf("SnackMixerSetInputJack1 %x %s %s\n", currSrc, jack, status);*/ 657 658 if (strcmp(status, "1") == 0) { 659 recSrc |= currSrc; 660 } else { 661 recSrc = (currSrc & ~recSrc); 662 } 663/* printf("SnackMixerSetInputJack2 %x\n", recSrc);*/ 664 665 if (ioctl(mfd, SOUND_MIXER_WRITE_RECSRC, &recSrc) == -1) { 666 return 1; 667 } else { 668 ioctl(mfd, SOUND_MIXER_READ_RECSRC, &recSrc); 669/* printf("SnackMixerSetInputJack3 %x\n", recSrc);*/ 670 return 0; 671 } 672 return 1; 673} 674 675void 676SnackMixerGetOutputJack(char *buf, int n) 677{ 678 buf[0] = '\0'; 679} 680 681void 682SnackMixerSetOutputJack(char *jack, char *status) 683{ 684} 685 686static int dontTrace = 0; 687 688static char * 689JackVarProc(ClientData clientData, Tcl_Interp *interp, CONST84 char *name1, 690 CONST84 char *name2, int flags) 691{ 692 MixerLink *mixLink = (MixerLink *) clientData; 693 char *jackLabels[SOUND_MIXER_NRDEVICES] = SOUND_DEVICE_LABELS; 694 int i, recSrc = 0, status = 0; 695 CONST84 char *stringValue; 696 Tcl_Obj *obj, *var; 697 698 if (dontTrace) return (char *) NULL; 699 700 ioctl(mfd, SOUND_MIXER_READ_RECSRC, &recSrc); 701/*printf("JackVarProc %x %s %s\n", recSrc, name1, name2);*/ 702 if (flags & TCL_TRACE_UNSETS) { 703 if ((flags & TCL_TRACE_DESTROYED) && !(flags & TCL_INTERP_DESTROYED)) { 704 for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { 705 if (strncasecmp(mixLink->jack, jackLabels[i], strlen(mixLink->jack)) 706 == 0) { 707 if ((1 << i) & recSrc) { 708 status = 1; 709 } else { 710 status = 0; 711 } 712 break; 713 } 714 } 715 obj = Tcl_NewIntObj(status); 716 var = Tcl_NewStringObj(mixLink->jackVar, -1); 717 Tcl_ObjSetVar2(interp, var, NULL, obj, TCL_GLOBAL_ONLY | TCL_PARSE_PART1); 718 Tcl_TraceVar(interp, mixLink->jackVar, 719 TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS, 720 JackVarProc, mixLink); 721 } 722 return (char *) NULL; 723 } 724 725 stringValue = Tcl_GetVar(interp, mixLink->jackVar, TCL_GLOBAL_ONLY); 726 if (stringValue != NULL) { 727 SnackMixerSetInputJack(interp, mixLink->jack, stringValue); 728 } 729 730 ioctl(mfd, SOUND_MIXER_READ_RECSRC, &recSrc); 731/*printf("JackVarProc2 %x\n", recSrc);*/ 732 dontTrace = 1; 733 for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { 734 if (mixerLinks[i][0].jackVar != NULL) { 735 if ((1 << i) & recSrc) { 736 status = 1; 737 } else { 738 status = 0; 739 } 740 obj = Tcl_NewIntObj(status); 741 var = Tcl_NewStringObj(mixerLinks[i][0].jackVar, -1); 742 Tcl_ObjSetVar2(interp, var, NULL, obj, TCL_GLOBAL_ONLY |TCL_PARSE_PART1); 743 } 744 } 745 dontTrace = 0; 746 747 return (char *) NULL; 748} 749 750void 751SnackMixerLinkJacks(Tcl_Interp *interp, char *jack, Tcl_Obj *var) 752{ 753 char *jackLabels[SOUND_MIXER_NRDEVICES] = SOUND_DEVICE_LABELS; 754 int i, recSrc = 0, status; 755 CONST84 char *value; 756 757 ioctl(mfd, SOUND_MIXER_READ_RECSRC, &recSrc); 758 759 for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { 760 if (strncasecmp(jack, jackLabels[i], strlen(jack)) == 0) { 761 if ((1 << i) & recSrc) { 762 status = 1; 763 } else { 764 status = 0; 765 } 766 mixerLinks[i][0].jack = SnackStrDup(jack); 767 mixerLinks[i][0].jackVar = SnackStrDup(Tcl_GetStringFromObj(var, NULL)); 768 value = Tcl_GetVar(interp, mixerLinks[i][0].jackVar, TCL_GLOBAL_ONLY); 769 if (value != NULL) { 770 SnackMixerSetInputJack(interp, mixerLinks[i][0].jack, value); 771 } else { 772 Tcl_Obj *obj = Tcl_NewIntObj(status); 773 Tcl_ObjSetVar2(interp, var, NULL, obj, 774 TCL_GLOBAL_ONLY | TCL_PARSE_PART1); 775 776 } 777 Tcl_TraceVar(interp, mixerLinks[i][0].jackVar, 778 TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS, 779 JackVarProc, (ClientData) &mixerLinks[i][0]); 780 break; 781 } 782 } 783} 784 785void 786SnackMixerGetChannelLabels(char *line, char *buf, int n) 787{ 788 char *mixLabels[SOUND_MIXER_NRDEVICES] = SOUND_DEVICE_LABELS; 789 int i, devMask; 790 791 ioctl(mfd, SOUND_MIXER_READ_STEREODEVS, &devMask); 792 for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { 793 if (strncasecmp(line, mixLabels[i], strlen(line)) == 0) { 794 if (devMask & (1 << i)) { 795 sprintf(buf, "Left Right"); 796 } else { 797 sprintf(buf, "Mono"); 798 } 799 break; 800 } 801 } 802} 803 804void 805SnackMixerGetVolume(char *line, int channel, char *buf, int n) 806{ 807 char *mixLabels[SOUND_MIXER_NRDEVICES] = SOUND_DEVICE_LABELS; 808 int i, vol = 0, devMask, isStereo = 0, left, right; 809 810 buf[0] = '\0'; 811 812 for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { 813 if (strncasecmp(line, mixLabels[i], strlen(line)) == 0) { 814 ioctl(mfd, MIXER_READ(i), &vol); 815 ioctl(mfd, SOUND_MIXER_READ_STEREODEVS, &devMask); 816 if (devMask & (1 << i)) { 817 isStereo = 1; 818 } 819 break; 820 } 821 } 822 left = vol & 0xff; 823 right = (vol & 0xff00) >> 8; 824 if (isStereo) { 825 if (channel == 0) { 826 sprintf(buf, "%d", left); 827 } else if (channel == 1) { 828 sprintf(buf, "%d", right); 829 } else if (channel == -1) { 830 sprintf(buf, "%d", (left + right)/2); 831 } 832 } else { 833 sprintf(buf, "%d", left); 834 } 835} 836 837void 838SnackMixerSetVolume(char *line, int channel, int volume) 839{ 840 char *mixLabels[SOUND_MIXER_NRDEVICES] = SOUND_DEVICE_LABELS; 841 int tmp = min(max(volume, 0), 100), i, oldVol = 0; 842 int vol = (tmp << 8) + tmp; 843 844 if (channel == 0) { 845 vol = tmp; 846 } 847 if (channel == 1) { 848 vol = tmp << 8; 849 } 850 851 for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { 852 if (strncasecmp(line, mixLabels[i], strlen(line)) == 0) { 853 ioctl(mfd, MIXER_READ(i), &oldVol); 854 if (channel == 0) { 855 vol = (oldVol & 0xff00) | (vol & 0x00ff); 856 } 857 if (channel == 1) { 858 vol = (vol & 0xff00) | (oldVol & 0x00ff); 859 } 860 ioctl(mfd, MIXER_WRITE(i), &vol); 861 break; 862 } 863 } 864} 865 866static char * 867VolumeVarProc(ClientData clientData, Tcl_Interp *interp, CONST84 char *name1, 868 CONST84 char *name2, int flags) 869{ 870 MixerLink *mixLink = (MixerLink *) clientData; 871 CONST84 char *stringValue; 872 873 if (flags & TCL_TRACE_UNSETS) { 874 if ((flags & TCL_TRACE_DESTROYED) && !(flags & TCL_INTERP_DESTROYED)) { 875 Tcl_Obj *obj, *var; 876 char tmp[VOLBUFSIZE]; 877 878 SnackMixerGetVolume(mixLink->mixer, mixLink->channel, tmp, VOLBUFSIZE); 879 obj = Tcl_NewIntObj(atoi(tmp)); 880 var = Tcl_NewStringObj(mixLink->mixerVar, -1); 881 Tcl_ObjSetVar2(interp, var, NULL, obj, TCL_GLOBAL_ONLY | TCL_PARSE_PART1); 882 Tcl_TraceVar(interp, mixLink->mixerVar, 883 TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS, 884 VolumeVarProc, mixLink); 885 } 886 return (char *) NULL; 887 } 888 889 stringValue = Tcl_GetVar(interp, mixLink->mixerVar, TCL_GLOBAL_ONLY); 890 if (stringValue != NULL) { 891 SnackMixerSetVolume(mixLink->mixer, mixLink->channel, atoi(stringValue)); 892 } 893 894 return (char *) NULL; 895} 896 897void 898SnackMixerLinkVolume(Tcl_Interp *interp, char *line, int n, 899 Tcl_Obj *CONST objv[]) 900{ 901 char *mixLabels[SOUND_MIXER_NRDEVICES] = SOUND_DEVICE_LABELS; 902 int i, j, channel; 903 CONST84 char *value; 904 char tmp[VOLBUFSIZE]; 905 906 for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { 907 if (strncasecmp(line, mixLabels[i], strlen(line)) == 0) { 908 for (j = 0; j < n; j++) { 909 if (n == 1) { 910 channel = -1; 911 } else { 912 channel = j; 913 } 914 mixerLinks[i][j].mixer = SnackStrDup(line); 915 mixerLinks[i][j].mixerVar = SnackStrDup(Tcl_GetStringFromObj(objv[j+3],NULL)); 916 mixerLinks[i][j].channel = j; 917 value = Tcl_GetVar(interp, mixerLinks[i][j].mixerVar, TCL_GLOBAL_ONLY); 918 if (value != NULL) { 919 SnackMixerSetVolume(line, channel, atoi(value)); 920 } else { 921 Tcl_Obj *obj; 922 SnackMixerGetVolume(line, channel, tmp, VOLBUFSIZE); 923 obj = Tcl_NewIntObj(atoi(tmp)); 924 Tcl_ObjSetVar2(interp, objv[j+3], NULL, obj, 925 TCL_GLOBAL_ONLY | TCL_PARSE_PART1); 926 } 927 Tcl_TraceVar(interp, mixerLinks[i][j].mixerVar, 928 TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS, 929 VolumeVarProc, (ClientData) &mixerLinks[i][j]); 930 } 931 } 932 } 933} 934 935void 936SnackMixerUpdateVars(Tcl_Interp *interp) 937{ 938 int i, j, recSrc, status; 939 char tmp[VOLBUFSIZE]; 940 Tcl_Obj *obj, *var; 941 942 ioctl(mfd, SOUND_MIXER_READ_RECSRC, &recSrc); 943 for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { 944 for (j = 0; j < 2; j++) { 945 if (mixerLinks[i][j].mixerVar != NULL) { 946 SnackMixerGetVolume(mixerLinks[i][j].mixer, mixerLinks[i][j].channel, 947 tmp, VOLBUFSIZE); 948 obj = Tcl_NewIntObj(atoi(tmp)); 949 var = Tcl_NewStringObj(mixerLinks[i][j].mixerVar, -1); 950 Tcl_ObjSetVar2(interp, var, NULL, obj, TCL_GLOBAL_ONLY|TCL_PARSE_PART1); 951 } 952 } 953 if (mixerLinks[i][0].jackVar != NULL) { 954 if ((1 << i) & recSrc) { 955 status = 1; 956 } else { 957 status = 0; 958 } 959 obj = Tcl_NewIntObj(status); 960 var = Tcl_NewStringObj(mixerLinks[i][0].jackVar, -1); 961 Tcl_ObjSetVar2(interp, var, NULL, obj, TCL_GLOBAL_ONLY | TCL_PARSE_PART1); 962 } 963 } 964} 965 966void 967SnackMixerGetLineLabels(char *buf, int n) 968{ 969 char *mixLabels[SOUND_MIXER_NRDEVICES] = SOUND_DEVICE_LABELS; 970 int i, devMask, pos = 0; 971 972 if (mfd != -1) { 973 ioctl(mfd, SOUND_MIXER_READ_DEVMASK, &devMask); 974 for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { 975 if ((1 << i) & devMask && pos < n-8) { 976 pos += sprintf(&buf[pos], "%s", mixLabels[i]); 977 pos += sprintf(&buf[pos], " "); 978 } 979 } 980 } else { 981 buf[0] = '\0'; 982 } 983 buf[n-1] = '\0'; 984} 985 986int 987SnackGetOutputDevices(char **arr, int n) 988{ 989 return SnackGetInputDevices(arr, n); 990} 991 992int 993SnackGetInputDevices(char **arr, int n) 994{ 995 int i, j = 0; 996 glob_t globt; 997 998 glob("/dev/dsp*", 0, NULL, &globt); 999 glob("/dev/audio*", GLOB_APPEND, NULL, &globt); 1000 glob("/dev/sound/dsp*", GLOB_APPEND, NULL, &globt); 1001 glob("/dev/sound/audio*", GLOB_APPEND, NULL, &globt); 1002 1003 for (i = 0; i < globt.gl_pathc; i++) { 1004 if (j < n) { 1005 arr[j++] = (char *) SnackStrDup(globt.gl_pathv[i]); 1006 } 1007 } 1008 globfree(&globt); 1009 1010 return(j); 1011} 1012 1013int 1014SnackGetMixerDevices(char **arr, int n) 1015{ 1016 int i, j = 0; 1017 glob_t globt; 1018 1019 glob("/dev/mixer*", 0, NULL, &globt); 1020 glob("/dev/sound/mixer*", GLOB_APPEND, NULL, &globt); 1021 1022 for (i = 0; i < globt.gl_pathc; i++) { 1023 if (j < n) { 1024 arr[j++] = (char *) SnackStrDup(globt.gl_pathv[i]); 1025 } 1026 } 1027 globfree(&globt); 1028 1029 return(j); 1030} 1031