1/* 2 * Copyright (C) 2003-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 <stdio.h> 25#include <fcntl.h> 26#include <unistd.h> 27#include <CoreServices/CoreServices.h> 28#include <CoreAudio/AudioHardware.h> 29 30extern void Snack_WriteLog(char *s); 31extern void Snack_WriteLogInt(char *s, int n); 32 33#ifndef min 34#define min(a,b) ((a)<(b)?(a):(b)) 35#define max(a,b) ((a)>(b)?(a):(b)) 36#endif 37 38#define SNACK_NUMBER_MIXERS 1 39 40struct MixerLink mixerLinks[SNACK_NUMBER_MIXERS][2]; 41 42#define BUFLEN (44100*2) 43static short otmp[BUFLEN]; 44static float itmp[BUFLEN]; 45static int usageCount = 0; 46static float rate; 47static ADesc *AO = NULL; 48static ADesc *AI = NULL; 49 50OSStatus 51appIOProc(AudioDeviceID inDevice, const AudioTimeStamp* inNow, 52 const AudioBufferList* inInputData, const AudioTimeStamp* inInputTime, 53 AudioBufferList* outOutputData, const AudioTimeStamp* inOutputTime, 54 void* adesc) 55{ 56 ADesc *A = adesc; 57 int i; 58 int numFrames = A->deviceBufferSize / A->deviceFormat.mBytesPerFrame; 59 float *out = outOutputData->mBuffers[0].mData; 60 float *in = inInputData->mBuffers[0].mData; 61 62 /* printf("w %d r %d %d %d\n", A->wpos, A->rpos, numFrames*2, A->nChannels);*/ 63 if (AO != NULL && AO->mode == PLAY) { 64 for (i = 0; i < numFrames*A->nChannels; ++i) { 65 *out++ = (float) (otmp[(A->rpos*A->nChannels + i) % BUFLEN] / 32768.0); 66 if (A->nChannels == 1) { 67 *out++ = (float) (otmp[(A->rpos*A->nChannels + i) % BUFLEN] / 32768.0); 68 } 69 } 70 A->rpos = (A->rpos + numFrames) % (BUFLEN/A->nChannels); 71 } 72 /* printf("appIOProc wpos %d %d %d\n", 73 A->wpos,numFrames,inInputData->mNumberBuffers);*/ 74 if (AI != NULL && AI->mode == RECORD) { 75 if (A->wpos + numFrames < BUFLEN/2) { 76 memcpy(&itmp[A->wpos*2], in, numFrames*2*sizeof(float)); 77 A->wpos += numFrames; 78 } else { 79 memcpy(&itmp[A->wpos*2], in, (BUFLEN/2 - A->wpos)*2*sizeof(float)); 80 memcpy(itmp, &in[(BUFLEN/2 - A->wpos)*2], 81 (numFrames-(BUFLEN/2-A->wpos))*2*sizeof(float)); 82 A->wpos = (A->wpos + numFrames) % (BUFLEN/2); 83 } 84 A->tot += numFrames; 85 } 86 87 return (kAudioHardwareNoError); 88} 89 90int 91SnackAudioOpen(ADesc *A, Tcl_Interp *interp, char *device, int mode, int freq, 92 int nchannels, int encoding) 93{ 94 OSStatus err = kAudioHardwareNoError; 95 UInt32 count = sizeof(AudioDeviceID); 96 UInt32 bufferSize; 97 AudioStreamBasicDescription format; 98 99 if (mode == PLAY) { 100 AO = A; 101 A->time = SnackCurrentTime(); 102 A->wpos = 0; 103 A->rpos = 0; 104 A->tot = 0; 105 } else { 106 AI = A; 107 A->rpos = 0; 108 A->wpos = 0; 109 A->tot = 0; 110 } 111 if (usageCount == 1) { 112 usageCount = 2; 113 return TCL_OK; 114 } 115 116 err = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice, 117 &count, (void *) &A->device); 118 119 count = sizeof(bufferSize); 120 err = AudioDeviceGetProperty(A->device, 0, false, 121 kAudioDevicePropertyBufferSize, 122 &count, &bufferSize); 123 124 count = sizeof(format); 125 err = AudioDeviceGetProperty(A->device, 0, false, 126 kAudioDevicePropertyStreamFormat, 127 &count, &format); 128 129 if ((err = AudioDeviceAddIOProc(A->device, appIOProc, (void *)A)) 130 != -kAudioHardwareNoError) { 131 Tcl_AppendResult(interp, "AudioDeviceAddIOProc failed", NULL); 132 return TCL_ERROR; 133 } 134 135 if ((err = AudioDeviceStart(A->device, appIOProc)) 136 != -kAudioHardwareNoError) { 137 Tcl_AppendResult(interp, "AudioDeviceStart failed", NULL); 138 return TCL_ERROR; 139 } 140 141 A->deviceBufferSize = bufferSize; 142 A->deviceFormat.mBytesPerFrame = format.mBytesPerFrame; 143 A->nChannels = nchannels; 144 A->mode = mode; 145 A->encoding = encoding; 146 rate = (float) freq; 147 usageCount = 1; 148 149 switch (encoding) { 150 case LIN24: 151 A->bytesPerSample = sizeof(int); 152 break; 153 case LIN16: 154 A->bytesPerSample = sizeof(short); 155 break; 156 } 157 158 return TCL_OK; 159} 160 161int 162SnackAudioClose(ADesc *A) 163{ 164 OSStatus err = kAudioHardwareNoError; 165 166 if (A->mode == PLAY) { 167 AO = NULL; 168 } else { 169 AI = NULL; 170 } 171 if (usageCount == 2) { 172 usageCount = 1; 173 return(0); 174 } 175 usageCount = 0; 176 177 /* printf("SnackAudioClose\n");*/ 178 179 if ((err = AudioDeviceStop(A->device, appIOProc)) 180 != -kAudioHardwareNoError) { 181 /* printf("AudioDeviceStop failed\n");*/ 182 return TCL_ERROR; 183 } 184 185 if ((err = AudioDeviceRemoveIOProc(A->device, appIOProc)) 186 != -kAudioHardwareNoError) { 187 /* printf("AudioDeviceRemoveIOProc failed\n");*/ 188 return TCL_ERROR; 189 } 190 191 return(0); 192} 193 194long 195SnackAudioPause(ADesc *A) 196{ 197 return(-1); 198} 199 200void 201SnackAudioResume(ADesc *A) 202{ 203} 204 205void 206SnackAudioFlush(ADesc *A) 207{ 208} 209 210void 211SnackAudioPost(ADesc *A) 212{ 213 if (A->mode == PLAY) { 214 int i; 215 216 for (i = A->wpos*A->nChannels; i < BUFLEN; i++) otmp[i] = 0; 217 for (i = 0; i < A->rpos*A->nChannels; i++) otmp[i] = 0; 218 } 219} 220 221int 222SnackAudioRead(ADesc *A, void *buf, int nFrames) 223{ 224 int i, c; 225 float frac = rate / 44100.0f; 226 int tot = (int) (frac * A->tot); 227 228 /* printf("SnackAudioRead frames %d rpos %d tot %d\n", nFrames, A->rpos, A->tot);*/ 229 230 if (nFrames > tot) { 231 nFrames = tot; 232 } 233 for (c = 0; c < A->nChannels; c++) { 234 for (i = 0; i < nFrames; i++) { 235 int ij, pos; 236 float smp1 = 0.0, smp2, f, dj; 237 238 dj = i / frac; 239 ij = (int) dj; 240 f = dj - ij; 241 pos = ij * 2 + c; 242 switch (A->encoding) { 243 case LIN24: 244 case LIN24PACKED: 245 smp1 = (8388607.0*itmp[(A->rpos*2 + pos) % (BUFLEN)]); 246 smp2 = (8388607.0*itmp[(A->rpos*2 + pos + A->nChannels)%(BUFLEN)]); 247 ((int *)buf)[i * A->nChannels + c] = smp1 * (1.0f - f) + smp2 * f; 248 break; 249 case LIN32: 250 case SNACK_FLOAT: 251 smp1 = (2147483647.0*itmp[(A->rpos*2 + pos) % (BUFLEN)]); 252 smp2 = (2147483647.0*itmp[(A->rpos*2 + pos + A->nChannels)%(BUFLEN)]); 253 ((int *)buf)[i * A->nChannels + c] = smp1 * (1.0f - f) + smp2 * f; 254 break; 255 case LIN16: 256 case MULAW: 257 case ALAW: 258 smp1 = (short) (32767.0*itmp[(A->rpos*2 + pos) % (BUFLEN)]); 259 smp2 = (short) (32767.0*itmp[(A->rpos*2 + pos + A->nChannels)%(BUFLEN)]); 260 ((short *)buf)[i * A->nChannels + c] = smp1 * (1.0f - f) + smp2 * f; 261 break; 262 } 263 } 264 } 265 A->rpos = (A->rpos + (int)(nFrames/frac)) % (BUFLEN/2); 266 A->tot -= (int) (nFrames/frac); 267 268 return(nFrames); 269} 270 271int 272SnackAudioWrite(ADesc *A, void *buf, int nFrames) 273{ 274 /* printf("SnackAudioWrite %d frames %d (%d %d)\n", A->wpos, nFrames,nFrames*4,&otmp[A->wpos*2]);*/ 275 276 if (A->wpos + nFrames < BUFLEN/A->nChannels) { 277 memcpy(&otmp[A->wpos*A->nChannels], buf, nFrames*A->nChannels*2); 278 A->wpos += nFrames; 279 } else { 280 memcpy(&otmp[A->wpos*A->nChannels], buf, (BUFLEN/A->nChannels - A->wpos)*A->nChannels*2); 281 memcpy(otmp, &((short *)buf)[(BUFLEN/A->nChannels - A->wpos)*A->nChannels],(nFrames-(BUFLEN/A->nChannels-A->wpos))*A->nChannels*2); 282 A->wpos = (A->wpos + nFrames) % (BUFLEN/A->nChannels); 283 } 284 285 return(nFrames); 286} 287 288int 289SnackAudioReadable(ADesc *A) 290{ 291 return((int) (A->tot * rate / 44100.0f)); 292} 293 294int 295SnackAudioWriteable(ADesc *A) 296{ 297 return -1; 298} 299 300long 301SnackAudioPlayed(ADesc *A) 302{ 303 long res; 304 305 res = (int) (44100 * (SnackCurrentTime() - A->time) +.5); 306 307 /* printf("SnackAudioPlayed %d\n", res);*/ 308 309 return(res); 310} 311 312void 313SnackAudioInit() 314{ 315 /* 316 OSStatus err = noErr; 317 UInt32 count, bufferSize; 318 AudioDeviceID device = kAudioDeviceUnknown; 319 AudioStreamBasicDescription format; 320 321 count = sizeof(device); 322 err = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice, 323 &count, (void *) &device); 324 fprintf(stderr, "kAudioHardwarePropertyDefaultOutputDevice %d\n", err); 325 if (err != noErr) goto Bail; 326 327 count = sizeof(bufferSize); 328 err = AudioDeviceGetProperty(device, 0, false, 329 kAudioDevicePropertyBufferSize, 330 &count, &bufferSize); 331 fprintf(stderr, "kAudioDevicePropertyBufferSize %d %d\n", err, bufferSize); 332 if (err != noErr) goto Bail; 333 334 count = sizeof(format); 335 err = AudioDeviceGetProperty(device, 0, false, 336 kAudioDevicePropertyStreamFormat, 337 &count, &format); 338 fprintf(stderr, "kAudioDevicePropertyStreamFormat %d\n", err); 339 fprintf(stderr, "sampleRate %g\n", format.mSampleRate); 340 fprintf(stderr, "mFormatFlags %08X\n", format.mFormatFlags); 341 fprintf(stderr, "mBytesPerPacket %d\n", format.mBytesPerPacket); 342 fprintf(stderr, "mFramesPerPacket %d\n", format.mFramesPerPacket); 343 fprintf(stderr, "mChannelsPerFrame %d\n", format.mChannelsPerFrame); 344 fprintf(stderr, "mBytesPerFrame %d\n", format.mBytesPerFrame); 345 fprintf(stderr, "mBitsPerChannel %d\n", format.mBitsPerChannel); 346 fprintf(stderr, "mFormatID != %d %d %d\n", format.mFormatID != kAudioFormatLinearPCM, format.mFormatID, kAudioFormatLinearPCM); 347 if (err != kAudioHardwareNoError) goto Bail;*/ 348 /* FailWithAction(format.mFormatID != kAudioFormatLinearPCM, err = paramErr, Bail);*/ 349 /* 350 memset(&format, 0, sizeof(AudioStreamBasicDescription)); 351 format.mSampleRate = 44100.0; 352 err = AudioDeviceSetProperty(device, 0, 0, 0, 353 kAudioDevicePropertyStreamFormat, 354 sizeof(format), &format); 355 fprintf(stderr, "kAudioDevicePropertyStreamFormat %d\n", err); 356 357 memset(&format, 0, sizeof(AudioStreamBasicDescription)); 358 format.mChannelsPerFrame = 2; 359 err = AudioDeviceSetProperty(device, 0, 0, 0, 360 kAudioDevicePropertyStreamFormat, 361 sizeof(format), &format); 362 fprintf(stderr, "kAudioDevicePropertyStreamFormat %d\n", err); 363 */ 364 /* 365 Bail: 366 fprintf(stderr, "done\n");*/ 367} 368 369void 370SnackAudioFree() 371{ 372 int i, j; 373 374 for (i = 0; i < SNACK_NUMBER_MIXERS; i++) { 375 for (j = 0; j < 2; j++) { 376 if (mixerLinks[i][j].mixer != NULL) { 377 ckfree(mixerLinks[i][j].mixer); 378 } 379 if (mixerLinks[i][j].mixerVar != NULL) { 380 ckfree(mixerLinks[i][j].mixerVar); 381 } 382 } 383 if (mixerLinks[i][0].jack != NULL) { 384 ckfree(mixerLinks[i][0].jack); 385 } 386 if (mixerLinks[i][0].jackVar != NULL) { 387 ckfree((char *)mixerLinks[i][0].jackVar); 388 } 389 } 390} 391 392void 393ASetRecGain(int gain) 394{ 395 int g = min(max(gain, 0), 100); 396} 397 398void 399ASetPlayGain(int gain) 400{ 401 int g = min(max(gain, 0), 100); 402} 403 404int 405AGetRecGain() 406{ 407 int g = 0; 408 409 return(g); 410} 411 412int 413AGetPlayGain() 414{ 415 int g = 0; 416 417 return(g); 418} 419 420int 421SnackAudioGetEncodings(char *device) 422{ 423 return(LIN16); 424} 425 426void 427SnackAudioGetRates(char *device, char *buf, int n) 428{ 429 strncpy(buf, "8000 11025 16000 22050 32000 44100 48000 96000", n); 430 buf[n-1] = '\0'; 431} 432 433int 434SnackAudioMaxNumberChannels(char *device) 435{ 436 return(64); 437} 438 439int 440SnackAudioMinNumberChannels(char *device) 441{ 442 return(1); 443} 444 445void 446SnackMixerGetInputJackLabels(char *buf, int n) 447{ 448 buf[0] = '\0'; 449} 450 451void 452SnackMixerGetOutputJackLabels(char *buf, int n) 453{ 454 buf[0] = '\0'; 455} 456 457void 458SnackMixerGetInputJack(char *buf, int n) 459{ 460 buf[0] = '\0'; 461} 462 463int 464SnackMixerSetInputJack(Tcl_Interp *interp, char *jack, CONST84 char *status) 465{ 466 return 1; 467} 468 469void 470SnackMixerGetOutputJack(char *buf, int n) 471{ 472 buf[0] = '\0'; 473} 474 475void 476SnackMixerSetOutputJack(char *jack, char *status) 477{ 478} 479 480void 481SnackMixerGetChannelLabels(char *line, char *buf, int n) 482{ 483 strncpy(buf, "Mono", n); 484 buf[n-1] = '\0'; 485} 486 487void 488SnackMixerGetVolume(char *line, int channel, char *buf, int n) 489{ 490 if (strncasecmp(line, "Play", strlen(line)) == 0) { 491 sprintf(buf, "%d", AGetPlayGain()); 492 } 493} 494 495void 496SnackMixerSetVolume(char *line, int channel, int volume) 497{ 498 if (strncasecmp(line, "Play", strlen(line)) == 0) { 499 ASetPlayGain(volume); 500 } 501} 502 503void 504SnackMixerLinkJacks(Tcl_Interp *interp, char *jack, Tcl_Obj *var) 505{ 506} 507 508static char * 509VolumeVarProc(ClientData clientData, Tcl_Interp *interp, CONST84 char *name1, 510 CONST84 char *name2, int flags) 511{ 512 MixerLink *mixLink = (MixerLink *) clientData; 513 CONST84 char *stringValue; 514 515 if (flags & TCL_TRACE_UNSETS) { 516 if ((flags & TCL_TRACE_DESTROYED) && !(flags & TCL_INTERP_DESTROYED)) { 517 Tcl_Obj *obj, *var; 518 char tmp[VOLBUFSIZE]; 519 520 SnackMixerGetVolume(mixLink->mixer, mixLink->channel, tmp, VOLBUFSIZE); 521 obj = Tcl_NewIntObj(atoi(tmp)); 522 var = Tcl_NewStringObj(mixLink->mixerVar, -1); 523 Tcl_ObjSetVar2(interp, var, NULL, obj, TCL_GLOBAL_ONLY | TCL_PARSE_PART1); 524 Tcl_TraceVar(interp, mixLink->mixerVar, 525 TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS, 526 VolumeVarProc, (int *)mixLink); 527 } 528 return (char *) NULL; 529 } 530 stringValue = Tcl_GetVar(interp, mixLink->mixerVar, TCL_GLOBAL_ONLY); 531 if (stringValue != NULL) { 532 SnackMixerSetVolume(mixLink->mixer, mixLink->channel, atoi(stringValue)); 533 } 534 535 return (char *) NULL; 536} 537 538void 539SnackMixerLinkVolume(Tcl_Interp *interp, char *line, int n, 540 Tcl_Obj *CONST objv[]) 541{ 542 char *mixLabels[] = { "Play" }; 543 int i, j, channel; 544 CONST84 char *value; 545 char tmp[VOLBUFSIZE]; 546 547 for (i = 0; i < SNACK_NUMBER_MIXERS; i++) { 548 if (strncasecmp(line, mixLabels[i], strlen(line)) == 0) { 549 for (j = 0; j < n; j++) { 550 if (n == 1) { 551 channel = -1; 552 } else { 553 channel = j; 554 } 555 mixerLinks[i][j].mixer = (char *)SnackStrDup(line); 556 mixerLinks[i][j].mixerVar = (char *)SnackStrDup(Tcl_GetStringFromObj(objv[j+3], NULL)); 557 mixerLinks[i][j].channel = j; 558 value = Tcl_GetVar(interp, mixerLinks[i][j].mixerVar, TCL_GLOBAL_ONLY); 559 if (value != NULL) { 560 SnackMixerSetVolume(line, channel, atoi(value)); 561 } else { 562 Tcl_Obj *obj; 563 SnackMixerGetVolume(line, channel, tmp, VOLBUFSIZE); 564 obj = Tcl_NewIntObj(atoi(tmp)); 565 Tcl_ObjSetVar2(interp, objv[j+3], NULL, obj, 566 TCL_GLOBAL_ONLY | TCL_PARSE_PART1); 567 } 568 Tcl_TraceVar(interp, mixerLinks[i][j].mixerVar, 569 TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS, 570 VolumeVarProc, (ClientData) &mixerLinks[i][j]); 571 } 572 } 573 } 574} 575 576void 577SnackMixerUpdateVars(Tcl_Interp *interp) 578{ 579 int i, j; 580 char tmp[VOLBUFSIZE]; 581 Tcl_Obj *obj, *var; 582 583 for (i = 0; i < SNACK_NUMBER_MIXERS; i++) { 584 for (j = 0; j < 2; j++) { 585 if (mixerLinks[i][j].mixerVar != NULL) { 586 SnackMixerGetVolume(mixerLinks[i][j].mixer, mixerLinks[i][j].channel, 587 tmp, VOLBUFSIZE); 588 obj = Tcl_NewIntObj(atoi(tmp)); 589 var = Tcl_NewStringObj(mixerLinks[i][j].mixerVar, -1); 590 Tcl_ObjSetVar2(interp, var, NULL, obj, TCL_GLOBAL_ONLY|TCL_PARSE_PART1); 591 } 592 } 593 } 594} 595 596void 597SnackMixerGetLineLabels(char *buf, int n) 598{ 599 strncpy(buf, "Play", n); 600 buf[n-1] = '\0'; 601} 602 603int 604SnackGetOutputDevices(char **arr, int n) 605{ 606 arr[0] = (char *) SnackStrDup("default"); 607 608 return 1; 609} 610 611int 612SnackGetInputDevices(char **arr, int n) 613{ 614 arr[0] = (char *) SnackStrDup("default"); 615 616 return 1; 617} 618 619int 620SnackGetMixerDevices(char **arr, int n) 621{ 622 arr[0] = (char *) SnackStrDup("default"); 623 624 return 1; 625} 626