pa_mac_core_old.c (35195B)
1 /* 2 * $Id$ 3 * pa_mac_core.c 4 * Implementation of PortAudio for Mac OS X CoreAudio 5 * 6 * PortAudio Portable Real-Time Audio Library 7 * Latest Version at: http://www.portaudio.com 8 * 9 * Authors: Ross Bencina and Phil Burk 10 * Copyright (c) 1999-2000 Ross Bencina and Phil Burk 11 * 12 * Permission is hereby granted, free of charge, to any person obtaining 13 * a copy of this software and associated documentation files 14 * (the "Software"), to deal in the Software without restriction, 15 * including without limitation the rights to use, copy, modify, merge, 16 * publish, distribute, sublicense, and/or sell copies of the Software, 17 * and to permit persons to whom the Software is furnished to do so, 18 * subject to the following conditions: 19 * 20 * The above copyright notice and this permission notice shall be 21 * included in all copies or substantial portions of the Software. 22 * 23 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 24 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 25 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 26 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR 27 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 28 * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 29 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 30 */ 31 32 /* 33 * The text above constitutes the entire PortAudio license; however, 34 * the PortAudio community also makes the following non-binding requests: 35 * 36 * Any person wishing to distribute modifications to the Software is 37 * requested to send the modifications to the original developer so that 38 * they can be incorporated into the canonical version. It is also 39 * requested that these non-binding requests be included along with the 40 * license above. 41 */ 42 43 #include <CoreAudio/CoreAudio.h> 44 #include <AudioToolbox/AudioToolbox.h> 45 #include <stdio.h> 46 #include <stdlib.h> 47 #include <math.h> 48 #include <assert.h> 49 50 #include "portaudio.h" 51 #include "pa_trace.h" 52 #include "pa_util.h" 53 #include "pa_allocation.h" 54 #include "pa_hostapi.h" 55 #include "pa_stream.h" 56 #include "pa_cpuload.h" 57 #include "pa_process.h" 58 59 // ===== constants ===== 60 61 // ===== structs ===== 62 #pragma mark structs 63 64 // PaMacCoreHostApiRepresentation - host api datastructure specific to this implementation 65 typedef struct PaMacCore_HAR 66 { 67 PaUtilHostApiRepresentation inheritedHostApiRep; 68 PaUtilStreamInterface callbackStreamInterface; 69 PaUtilStreamInterface blockingStreamInterface; 70 71 PaUtilAllocationGroup *allocations; 72 AudioDeviceID *macCoreDeviceIds; 73 } 74 PaMacCoreHostApiRepresentation; 75 76 typedef struct PaMacCore_DI 77 { 78 PaDeviceInfo inheritedDeviceInfo; 79 } 80 PaMacCoreDeviceInfo; 81 82 // PaMacCoreStream - a stream data structure specifically for this implementation 83 typedef struct PaMacCore_S 84 { 85 PaUtilStreamRepresentation streamRepresentation; 86 PaUtilCpuLoadMeasurer cpuLoadMeasurer; 87 PaUtilBufferProcessor bufferProcessor; 88 89 int primeStreamUsingCallback; 90 91 AudioDeviceID inputDevice; 92 AudioDeviceID outputDevice; 93 94 // Processing thread management -------------- 95 // HANDLE abortEvent; 96 // HANDLE processingThread; 97 // DWORD processingThreadId; 98 99 char throttleProcessingThreadOnOverload; // 0 -> don't throtte, non-0 -> throttle 100 int processingThreadPriority; 101 int highThreadPriority; 102 int throttledThreadPriority; 103 unsigned long throttledSleepMsecs; 104 105 int isStopped; 106 volatile int isActive; 107 volatile int stopProcessing; // stop thread once existing buffers have been returned 108 volatile int abortProcessing; // stop thread immediately 109 110 // DWORD allBuffersDurationMs; // used to calculate timeouts 111 } 112 PaMacCoreStream; 113 114 // Data needed by the CoreAudio callback functions 115 typedef struct PaMacCore_CD 116 { 117 PaMacCoreStream *stream; 118 PaStreamCallback *callback; 119 void *userData; 120 PaUtilConverter *inputConverter; 121 PaUtilConverter *outputConverter; 122 void *inputBuffer; 123 void *outputBuffer; 124 int inputChannelCount; 125 int outputChannelCount; 126 PaSampleFormat inputSampleFormat; 127 PaSampleFormat outputSampleFormat; 128 PaUtilTriangularDitherGenerator *ditherGenerator; 129 } 130 PaMacClientData; 131 132 // ===== CoreAudio-PortAudio bridge functions ===== 133 #pragma mark CoreAudio-PortAudio bridge functions 134 135 // Maps CoreAudio OSStatus codes to PortAudio PaError codes 136 static PaError conv_err(OSStatus error) 137 { 138 PaError result; 139 140 switch (error) { 141 case kAudioHardwareNoError: 142 result = paNoError; break; 143 case kAudioHardwareNotRunningError: 144 result = paInternalError; break; 145 case kAudioHardwareUnspecifiedError: 146 result = paInternalError; break; 147 case kAudioHardwareUnknownPropertyError: 148 result = paInternalError; break; 149 case kAudioHardwareBadPropertySizeError: 150 result = paInternalError; break; 151 case kAudioHardwareIllegalOperationError: 152 result = paInternalError; break; 153 case kAudioHardwareBadDeviceError: 154 result = paInvalidDevice; break; 155 case kAudioHardwareBadStreamError: 156 result = paBadStreamPtr; break; 157 case kAudioHardwareUnsupportedOperationError: 158 result = paInternalError; break; 159 case kAudioDeviceUnsupportedFormatError: 160 result = paSampleFormatNotSupported; break; 161 case kAudioDevicePermissionsError: 162 result = paDeviceUnavailable; break; 163 default: 164 result = paInternalError; 165 } 166 167 return result; 168 } 169 170 /* This function is unused 171 static AudioStreamBasicDescription *InitializeStreamDescription(const PaStreamParameters *parameters, double sampleRate) 172 { 173 struct AudioStreamBasicDescription *streamDescription = PaUtil_AllocateMemory(sizeof(AudioStreamBasicDescription)); 174 streamDescription->mSampleRate = sampleRate; 175 streamDescription->mFormatID = kAudioFormatLinearPCM; 176 streamDescription->mFormatFlags = 0; 177 streamDescription->mFramesPerPacket = 1; 178 179 if (parameters->sampleFormat & paNonInterleaved) { 180 streamDescription->mFormatFlags |= kLinearPCMFormatFlagIsNonInterleaved; 181 streamDescription->mChannelsPerFrame = 1; 182 streamDescription->mBytesPerFrame = Pa_GetSampleSize(parameters->sampleFormat); 183 streamDescription->mBytesPerPacket = Pa_GetSampleSize(parameters->sampleFormat); 184 } 185 else { 186 streamDescription->mChannelsPerFrame = parameters->channelCount; 187 } 188 189 streamDescription->mBytesPerFrame = Pa_GetSampleSize(parameters->sampleFormat) * streamDescription->mChannelsPerFrame; 190 streamDescription->mBytesPerPacket = streamDescription->mBytesPerFrame * streamDescription->mFramesPerPacket; 191 192 if (parameters->sampleFormat & paFloat32) { 193 streamDescription->mFormatFlags |= kLinearPCMFormatFlagIsFloat; 194 streamDescription->mBitsPerChannel = 32; 195 } 196 else if (parameters->sampleFormat & paInt32) { 197 streamDescription->mFormatFlags |= kLinearPCMFormatFlagIsSignedInteger; 198 streamDescription->mBitsPerChannel = 32; 199 } 200 else if (parameters->sampleFormat & paInt24) { 201 streamDescription->mFormatFlags |= kLinearPCMFormatFlagIsSignedInteger; 202 streamDescription->mBitsPerChannel = 24; 203 } 204 else if (parameters->sampleFormat & paInt16) { 205 streamDescription->mFormatFlags |= kLinearPCMFormatFlagIsSignedInteger; 206 streamDescription->mBitsPerChannel = 16; 207 } 208 else if (parameters->sampleFormat & paInt8) { 209 streamDescription->mFormatFlags |= kLinearPCMFormatFlagIsSignedInteger; 210 streamDescription->mBitsPerChannel = 8; 211 } 212 else if (parameters->sampleFormat & paInt32) { 213 streamDescription->mBitsPerChannel = 8; 214 } 215 216 return streamDescription; 217 } 218 */ 219 220 static PaStreamCallbackTimeInfo *InitializeTimeInfo(const AudioTimeStamp* now, const AudioTimeStamp* inputTime, const AudioTimeStamp* outputTime) 221 { 222 PaStreamCallbackTimeInfo *timeInfo = PaUtil_AllocateMemory(sizeof(PaStreamCallbackTimeInfo)); 223 224 timeInfo->inputBufferAdcTime = inputTime->mSampleTime; 225 timeInfo->currentTime = now->mSampleTime; 226 timeInfo->outputBufferDacTime = outputTime->mSampleTime; 227 228 return timeInfo; 229 } 230 231 // ===== support functions ===== 232 #pragma mark support functions 233 234 static void CleanUp(PaMacCoreHostApiRepresentation *macCoreHostApi) 235 { 236 if( macCoreHostApi->allocations ) 237 { 238 PaUtil_FreeAllAllocations( macCoreHostApi->allocations ); 239 PaUtil_DestroyAllocationGroup( macCoreHostApi->allocations ); 240 } 241 242 PaUtil_FreeMemory( macCoreHostApi ); 243 } 244 245 static PaError GetChannelInfo(PaDeviceInfo *deviceInfo, AudioDeviceID macCoreDeviceId, int isInput) 246 { 247 UInt32 propSize; 248 PaError err = paNoError; 249 UInt32 i; 250 int numChannels = 0; 251 AudioBufferList *buflist; 252 253 err = conv_err(AudioDeviceGetPropertyInfo(macCoreDeviceId, 0, isInput, kAudioDevicePropertyStreamConfiguration, &propSize, NULL)); 254 buflist = PaUtil_AllocateMemory(propSize); 255 err = conv_err(AudioDeviceGetProperty(macCoreDeviceId, 0, isInput, kAudioDevicePropertyStreamConfiguration, &propSize, buflist)); 256 if (!err) { 257 for (i = 0; i < buflist->mNumberBuffers; ++i) { 258 numChannels += buflist->mBuffers[i].mNumberChannels; 259 } 260 261 if (isInput) 262 deviceInfo->maxInputChannels = numChannels; 263 else 264 deviceInfo->maxOutputChannels = numChannels; 265 266 int frameLatency; 267 propSize = sizeof(UInt32); 268 err = conv_err(AudioDeviceGetProperty(macCoreDeviceId, 0, isInput, kAudioDevicePropertyLatency, &propSize, &frameLatency)); 269 if (!err) { 270 double secondLatency = frameLatency / deviceInfo->defaultSampleRate; 271 if (isInput) { 272 deviceInfo->defaultLowInputLatency = secondLatency; 273 deviceInfo->defaultHighInputLatency = secondLatency; 274 } 275 else { 276 deviceInfo->defaultLowOutputLatency = secondLatency; 277 deviceInfo->defaultHighOutputLatency = secondLatency; 278 } 279 } 280 } 281 PaUtil_FreeMemory(buflist); 282 283 return err; 284 } 285 286 static PaError InitializeDeviceInfo(PaMacCoreDeviceInfo *macCoreDeviceInfo, AudioDeviceID macCoreDeviceId, PaHostApiIndex hostApiIndex ) 287 { 288 PaDeviceInfo *deviceInfo = &macCoreDeviceInfo->inheritedDeviceInfo; 289 deviceInfo->structVersion = 2; 290 deviceInfo->hostApi = hostApiIndex; 291 292 PaError err = paNoError; 293 UInt32 propSize; 294 295 err = conv_err(AudioDeviceGetPropertyInfo(macCoreDeviceId, 0, 0, kAudioDevicePropertyDeviceName, &propSize, NULL)); 296 // FIXME: this allocation should be part of the allocations group 297 char *name = PaUtil_AllocateMemory(propSize); 298 err = conv_err(AudioDeviceGetProperty(macCoreDeviceId, 0, 0, kAudioDevicePropertyDeviceName, &propSize, name)); 299 if (!err) { 300 deviceInfo->name = name; 301 } 302 303 Float64 sampleRate; 304 propSize = sizeof(Float64); 305 err = conv_err(AudioDeviceGetProperty(macCoreDeviceId, 0, 0, kAudioDevicePropertyNominalSampleRate, &propSize, &sampleRate)); 306 if (!err) { 307 deviceInfo->defaultSampleRate = sampleRate; 308 } 309 310 311 // Get channel info 312 err = GetChannelInfo(deviceInfo, macCoreDeviceId, 1); 313 err = GetChannelInfo(deviceInfo, macCoreDeviceId, 0); 314 315 return err; 316 } 317 318 static PaError InitializeDeviceInfos( PaMacCoreHostApiRepresentation *macCoreHostApi, PaHostApiIndex hostApiIndex ) 319 { 320 PaError result = paNoError; 321 PaUtilHostApiRepresentation *hostApi; 322 PaMacCoreDeviceInfo *deviceInfoArray; 323 324 // initialise device counts and default devices under the assumption that there are no devices. These values are incremented below if and when devices are successfully initialized. 325 hostApi = &macCoreHostApi->inheritedHostApiRep; 326 hostApi->info.deviceCount = 0; 327 hostApi->info.defaultInputDevice = paNoDevice; 328 hostApi->info.defaultOutputDevice = paNoDevice; 329 330 UInt32 propsize; 331 AudioHardwareGetPropertyInfo(kAudioHardwarePropertyDevices, &propsize, NULL); 332 int numDevices = propsize / sizeof(AudioDeviceID); 333 hostApi->info.deviceCount = numDevices; 334 if (numDevices > 0) { 335 hostApi->deviceInfos = (PaDeviceInfo**)PaUtil_GroupAllocateMemory( 336 macCoreHostApi->allocations, sizeof(PaDeviceInfo*) * numDevices ); 337 if( !hostApi->deviceInfos ) 338 { 339 return paInsufficientMemory; 340 } 341 342 // allocate all device info structs in a contiguous block 343 deviceInfoArray = (PaMacCoreDeviceInfo*)PaUtil_GroupAllocateMemory( 344 macCoreHostApi->allocations, sizeof(PaMacCoreDeviceInfo) * numDevices ); 345 if( !deviceInfoArray ) 346 { 347 return paInsufficientMemory; 348 } 349 350 macCoreHostApi->macCoreDeviceIds = PaUtil_GroupAllocateMemory(macCoreHostApi->allocations, propsize); 351 AudioHardwareGetProperty(kAudioHardwarePropertyDevices, &propsize, macCoreHostApi->macCoreDeviceIds); 352 353 AudioDeviceID defaultInputDevice, defaultOutputDevice; 354 propsize = sizeof(AudioDeviceID); 355 AudioHardwareGetProperty(kAudioHardwarePropertyDefaultInputDevice, &propsize, &defaultInputDevice); 356 AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice, &propsize, &defaultOutputDevice); 357 358 UInt32 i; 359 for (i = 0; i < numDevices; ++i) { 360 if (macCoreHostApi->macCoreDeviceIds[i] == defaultInputDevice) { 361 hostApi->info.defaultInputDevice = i; 362 } 363 if (macCoreHostApi->macCoreDeviceIds[i] == defaultOutputDevice) { 364 hostApi->info.defaultOutputDevice = i; 365 } 366 InitializeDeviceInfo(&deviceInfoArray[i], macCoreHostApi->macCoreDeviceIds[i], hostApiIndex); 367 hostApi->deviceInfos[i] = &(deviceInfoArray[i].inheritedDeviceInfo); 368 } 369 } 370 371 return result; 372 } 373 374 static OSStatus CheckFormat(AudioDeviceID macCoreDeviceId, const PaStreamParameters *parameters, double sampleRate, int isInput) 375 { 376 UInt32 propSize = sizeof(AudioStreamBasicDescription); 377 AudioStreamBasicDescription *streamDescription = PaUtil_AllocateMemory(propSize); 378 379 streamDescription->mSampleRate = sampleRate; 380 streamDescription->mFormatID = 0; 381 streamDescription->mFormatFlags = 0; 382 streamDescription->mBytesPerPacket = 0; 383 streamDescription->mFramesPerPacket = 0; 384 streamDescription->mBytesPerFrame = 0; 385 streamDescription->mChannelsPerFrame = 0; 386 streamDescription->mBitsPerChannel = 0; 387 streamDescription->mReserved = 0; 388 389 OSStatus result = AudioDeviceGetProperty(macCoreDeviceId, 0, isInput, kAudioDevicePropertyStreamFormatSupported, &propSize, streamDescription); 390 PaUtil_FreeMemory(streamDescription); 391 return result; 392 } 393 394 static OSStatus CopyInputData(PaMacClientData* destination, const AudioBufferList *source, unsigned long frameCount) 395 { 396 int frameSpacing, channelSpacing; 397 if (destination->inputSampleFormat & paNonInterleaved) { 398 frameSpacing = 1; 399 channelSpacing = destination->inputChannelCount; 400 } 401 else { 402 frameSpacing = destination->inputChannelCount; 403 channelSpacing = 1; 404 } 405 406 AudioBuffer const *inputBuffer = &source->mBuffers[0]; 407 void *coreAudioBuffer = inputBuffer->mData; 408 void *portAudioBuffer = destination->inputBuffer; 409 UInt32 i, streamNumber, streamChannel; 410 for (i = streamNumber = streamChannel = 0; i < destination->inputChannelCount; ++i, ++streamChannel) { 411 if (streamChannel >= inputBuffer->mNumberChannels) { 412 ++streamNumber; 413 inputBuffer = &source->mBuffers[streamNumber]; 414 coreAudioBuffer = inputBuffer->mData; 415 streamChannel = 0; 416 } 417 destination->inputConverter(portAudioBuffer, frameSpacing, coreAudioBuffer, inputBuffer->mNumberChannels, frameCount, destination->ditherGenerator); 418 coreAudioBuffer += sizeof(Float32); 419 portAudioBuffer += Pa_GetSampleSize(destination->inputSampleFormat) * channelSpacing; 420 } 421 return noErr; 422 } 423 424 static OSStatus CopyOutputData(AudioBufferList* destination, PaMacClientData *source, unsigned long frameCount) 425 { 426 int frameSpacing, channelSpacing; 427 if (source->outputSampleFormat & paNonInterleaved) { 428 frameSpacing = 1; 429 channelSpacing = source->outputChannelCount; 430 } 431 else { 432 frameSpacing = source->outputChannelCount; 433 channelSpacing = 1; 434 } 435 436 AudioBuffer *outputBuffer = &destination->mBuffers[0]; 437 void *coreAudioBuffer = outputBuffer->mData; 438 void *portAudioBuffer = source->outputBuffer; 439 UInt32 i, streamNumber, streamChannel; 440 for (i = streamNumber = streamChannel = 0; i < source->outputChannelCount; ++i, ++streamChannel) { 441 if (streamChannel >= outputBuffer->mNumberChannels) { 442 ++streamNumber; 443 outputBuffer = &destination->mBuffers[streamNumber]; 444 coreAudioBuffer = outputBuffer->mData; 445 streamChannel = 0; 446 } 447 source->outputConverter(coreAudioBuffer, outputBuffer->mNumberChannels, portAudioBuffer, frameSpacing, frameCount, NULL); 448 coreAudioBuffer += sizeof(Float32); 449 portAudioBuffer += Pa_GetSampleSize(source->outputSampleFormat) * channelSpacing; 450 } 451 return noErr; 452 } 453 454 static OSStatus AudioIOProc( AudioDeviceID inDevice, 455 const AudioTimeStamp* inNow, 456 const AudioBufferList* inInputData, 457 const AudioTimeStamp* inInputTime, 458 AudioBufferList* outOutputData, 459 const AudioTimeStamp* inOutputTime, 460 void* inClientData) 461 { 462 PaMacClientData *clientData = (PaMacClientData *)inClientData; 463 PaStreamCallbackTimeInfo *timeInfo = InitializeTimeInfo(inNow, inInputTime, inOutputTime); 464 465 PaUtil_BeginCpuLoadMeasurement( &clientData->stream->cpuLoadMeasurer ); 466 467 AudioBuffer *outputBuffer = &outOutputData->mBuffers[0]; 468 unsigned long frameCount = outputBuffer->mDataByteSize / (outputBuffer->mNumberChannels * sizeof(Float32)); 469 470 if (clientData->inputBuffer) { 471 CopyInputData(clientData, inInputData, frameCount); 472 } 473 PaStreamCallbackResult result = clientData->callback(clientData->inputBuffer, clientData->outputBuffer, frameCount, timeInfo, paNoFlag, clientData->userData); 474 if (clientData->outputBuffer) { 475 CopyOutputData(outOutputData, clientData, frameCount); 476 } 477 478 PaUtil_EndCpuLoadMeasurement( &clientData->stream->cpuLoadMeasurer, frameCount ); 479 480 if (result == paComplete || result == paAbort) { 481 Pa_StopStream(clientData->stream); 482 } 483 484 PaUtil_FreeMemory( timeInfo ); 485 return noErr; 486 } 487 488 // This is not for input-only streams, this is for streams where the input device is different from the output device 489 static OSStatus AudioInputProc( AudioDeviceID inDevice, 490 const AudioTimeStamp* inNow, 491 const AudioBufferList* inInputData, 492 const AudioTimeStamp* inInputTime, 493 AudioBufferList* outOutputData, 494 const AudioTimeStamp* inOutputTime, 495 void* inClientData) 496 { 497 PaMacClientData *clientData = (PaMacClientData *)inClientData; 498 PaStreamCallbackTimeInfo *timeInfo = InitializeTimeInfo(inNow, inInputTime, inOutputTime); 499 500 PaUtil_BeginCpuLoadMeasurement( &clientData->stream->cpuLoadMeasurer ); 501 502 AudioBuffer const *inputBuffer = &inInputData->mBuffers[0]; 503 unsigned long frameCount = inputBuffer->mDataByteSize / (inputBuffer->mNumberChannels * sizeof(Float32)); 504 505 CopyInputData(clientData, inInputData, frameCount); 506 PaStreamCallbackResult result = clientData->callback(clientData->inputBuffer, clientData->outputBuffer, frameCount, timeInfo, paNoFlag, clientData->userData); 507 508 PaUtil_EndCpuLoadMeasurement( &clientData->stream->cpuLoadMeasurer, frameCount ); 509 if( result == paComplete || result == paAbort ) 510 Pa_StopStream(clientData->stream); 511 PaUtil_FreeMemory( timeInfo ); 512 return noErr; 513 } 514 515 // This is not for output-only streams, this is for streams where the input device is different from the output device 516 static OSStatus AudioOutputProc( AudioDeviceID inDevice, 517 const AudioTimeStamp* inNow, 518 const AudioBufferList* inInputData, 519 const AudioTimeStamp* inInputTime, 520 AudioBufferList* outOutputData, 521 const AudioTimeStamp* inOutputTime, 522 void* inClientData) 523 { 524 PaMacClientData *clientData = (PaMacClientData *)inClientData; 525 //PaStreamCallbackTimeInfo *timeInfo = InitializeTimeInfo(inNow, inInputTime, inOutputTime); 526 527 PaUtil_BeginCpuLoadMeasurement( &clientData->stream->cpuLoadMeasurer ); 528 529 AudioBuffer *outputBuffer = &outOutputData->mBuffers[0]; 530 unsigned long frameCount = outputBuffer->mDataByteSize / (outputBuffer->mNumberChannels * sizeof(Float32)); 531 532 //clientData->callback(NULL, clientData->outputBuffer, frameCount, timeInfo, paNoFlag, clientData->userData); 533 534 CopyOutputData(outOutputData, clientData, frameCount); 535 536 PaUtil_EndCpuLoadMeasurement( &clientData->stream->cpuLoadMeasurer, frameCount ); 537 return noErr; 538 } 539 540 static PaError SetSampleRate(AudioDeviceID device, double sampleRate, int isInput) 541 { 542 PaError result = paNoError; 543 544 double actualSampleRate; 545 UInt32 propSize = sizeof(double); 546 result = conv_err(AudioDeviceSetProperty(device, NULL, 0, isInput, kAudioDevicePropertyNominalSampleRate, propSize, &sampleRate)); 547 548 result = conv_err(AudioDeviceGetProperty(device, 0, isInput, kAudioDevicePropertyNominalSampleRate, &propSize, &actualSampleRate)); 549 550 if (result == paNoError && actualSampleRate != sampleRate) { 551 result = paInvalidSampleRate; 552 } 553 554 return result; 555 } 556 557 static PaError SetFramesPerBuffer(AudioDeviceID device, unsigned long framesPerBuffer, int isInput) 558 { 559 PaError result = paNoError; 560 UInt32 preferredFramesPerBuffer = framesPerBuffer; 561 // while (preferredFramesPerBuffer > UINT32_MAX) { 562 // preferredFramesPerBuffer /= 2; 563 // } 564 565 UInt32 actualFramesPerBuffer; 566 UInt32 propSize = sizeof(UInt32); 567 result = conv_err(AudioDeviceSetProperty(device, NULL, 0, isInput, kAudioDevicePropertyBufferFrameSize, propSize, &preferredFramesPerBuffer)); 568 569 result = conv_err(AudioDeviceGetProperty(device, 0, isInput, kAudioDevicePropertyBufferFrameSize, &propSize, &actualFramesPerBuffer)); 570 571 if (result != paNoError) { 572 // do nothing 573 } 574 else if (actualFramesPerBuffer > framesPerBuffer) { 575 result = paBufferTooSmall; 576 } 577 else if (actualFramesPerBuffer < framesPerBuffer) { 578 result = paBufferTooBig; 579 } 580 581 return result; 582 } 583 584 static PaError SetUpUnidirectionalStream(AudioDeviceID device, double sampleRate, unsigned long framesPerBuffer, int isInput) 585 { 586 PaError err = paNoError; 587 err = SetSampleRate(device, sampleRate, isInput); 588 if( err == paNoError ) 589 err = SetFramesPerBuffer(device, framesPerBuffer, isInput); 590 return err; 591 } 592 593 // ===== PortAudio functions ===== 594 #pragma mark PortAudio functions 595 596 #ifdef __cplusplus 597 extern "C" 598 { 599 #endif // __cplusplus 600 601 PaError PaMacCore_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index ); 602 603 #ifdef __cplusplus 604 } 605 #endif // __cplusplus 606 607 static void Terminate( struct PaUtilHostApiRepresentation *hostApi ) 608 { 609 PaMacCoreHostApiRepresentation *macCoreHostApi = (PaMacCoreHostApiRepresentation*)hostApi; 610 611 CleanUp(macCoreHostApi); 612 } 613 614 static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi, 615 const PaStreamParameters *inputParameters, 616 const PaStreamParameters *outputParameters, 617 double sampleRate ) 618 { 619 PaMacCoreHostApiRepresentation *macCoreHostApi = (PaMacCoreHostApiRepresentation*)hostApi; 620 PaDeviceInfo *deviceInfo; 621 622 PaError result = paNoError; 623 if (inputParameters) { 624 deviceInfo = macCoreHostApi->inheritedHostApiRep.deviceInfos[inputParameters->device]; 625 if (inputParameters->channelCount > deviceInfo->maxInputChannels) 626 result = paInvalidChannelCount; 627 else if (CheckFormat(macCoreHostApi->macCoreDeviceIds[inputParameters->device], inputParameters, sampleRate, 1) != kAudioHardwareNoError) { 628 result = paInvalidSampleRate; 629 } 630 } 631 if (outputParameters && result == paNoError) { 632 deviceInfo = macCoreHostApi->inheritedHostApiRep.deviceInfos[outputParameters->device]; 633 if (outputParameters->channelCount > deviceInfo->maxOutputChannels) 634 result = paInvalidChannelCount; 635 else if (CheckFormat(macCoreHostApi->macCoreDeviceIds[outputParameters->device], outputParameters, sampleRate, 0) != kAudioHardwareNoError) { 636 result = paInvalidSampleRate; 637 } 638 } 639 640 return result; 641 } 642 643 static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, 644 PaStream** s, 645 const PaStreamParameters *inputParameters, 646 const PaStreamParameters *outputParameters, 647 double sampleRate, 648 unsigned long framesPerBuffer, 649 PaStreamFlags streamFlags, 650 PaStreamCallback *streamCallback, 651 void *userData ) 652 { 653 PaError err = paNoError; 654 PaMacCoreHostApiRepresentation *macCoreHostApi = (PaMacCoreHostApiRepresentation *)hostApi; 655 PaMacCoreStream *stream = PaUtil_AllocateMemory(sizeof(PaMacCoreStream)); 656 stream->isActive = 0; 657 stream->isStopped = 1; 658 stream->inputDevice = kAudioDeviceUnknown; 659 stream->outputDevice = kAudioDeviceUnknown; 660 661 PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation, 662 ( (streamCallback) 663 ? &macCoreHostApi->callbackStreamInterface 664 : &macCoreHostApi->blockingStreamInterface ), 665 streamCallback, userData ); 666 PaUtil_InitializeCpuLoadMeasurer( &stream->cpuLoadMeasurer, sampleRate ); 667 668 *s = (PaStream*)stream; 669 PaMacClientData *clientData = PaUtil_AllocateMemory(sizeof(PaMacClientData)); 670 clientData->stream = stream; 671 clientData->callback = streamCallback; 672 clientData->userData = userData; 673 clientData->inputBuffer = 0; 674 clientData->outputBuffer = 0; 675 clientData->ditherGenerator = PaUtil_AllocateMemory(sizeof(PaUtilTriangularDitherGenerator)); 676 PaUtil_InitializeTriangularDitherState(clientData->ditherGenerator); 677 678 if (inputParameters != NULL) { 679 stream->inputDevice = macCoreHostApi->macCoreDeviceIds[inputParameters->device]; 680 clientData->inputConverter = PaUtil_SelectConverter(paFloat32, inputParameters->sampleFormat, streamFlags); 681 clientData->inputBuffer = PaUtil_AllocateMemory(Pa_GetSampleSize(inputParameters->sampleFormat) * framesPerBuffer * inputParameters->channelCount); 682 clientData->inputChannelCount = inputParameters->channelCount; 683 clientData->inputSampleFormat = inputParameters->sampleFormat; 684 err = SetUpUnidirectionalStream(stream->inputDevice, sampleRate, framesPerBuffer, 1); 685 } 686 687 if (err == paNoError && outputParameters != NULL) { 688 stream->outputDevice = macCoreHostApi->macCoreDeviceIds[outputParameters->device]; 689 clientData->outputConverter = PaUtil_SelectConverter(outputParameters->sampleFormat, paFloat32, streamFlags); 690 clientData->outputBuffer = PaUtil_AllocateMemory(Pa_GetSampleSize(outputParameters->sampleFormat) * framesPerBuffer * outputParameters->channelCount); 691 clientData->outputChannelCount = outputParameters->channelCount; 692 clientData->outputSampleFormat = outputParameters->sampleFormat; 693 err = SetUpUnidirectionalStream(stream->outputDevice, sampleRate, framesPerBuffer, 0); 694 } 695 696 if (inputParameters == NULL || outputParameters == NULL || stream->inputDevice == stream->outputDevice) { 697 AudioDeviceID device = (inputParameters == NULL) ? stream->outputDevice : stream->inputDevice; 698 699 AudioDeviceAddIOProc(device, AudioIOProc, clientData); 700 } 701 else { 702 // using different devices for input and output 703 AudioDeviceAddIOProc(stream->inputDevice, AudioInputProc, clientData); 704 AudioDeviceAddIOProc(stream->outputDevice, AudioOutputProc, clientData); 705 } 706 707 return err; 708 } 709 710 // Note: When CloseStream() is called, the multi-api layer ensures that the stream has already been stopped or aborted. 711 static PaError CloseStream( PaStream* s ) 712 { 713 PaError err = paNoError; 714 PaMacCoreStream *stream = (PaMacCoreStream*)s; 715 716 PaUtil_ResetCpuLoadMeasurer( &stream->cpuLoadMeasurer ); 717 718 if (stream->inputDevice != kAudioDeviceUnknown) { 719 if (stream->outputDevice == kAudioDeviceUnknown || stream->outputDevice == stream->inputDevice) { 720 err = conv_err(AudioDeviceRemoveIOProc(stream->inputDevice, AudioIOProc)); 721 } 722 else { 723 err = conv_err(AudioDeviceRemoveIOProc(stream->inputDevice, AudioInputProc)); 724 err = conv_err(AudioDeviceRemoveIOProc(stream->outputDevice, AudioOutputProc)); 725 } 726 } 727 else { 728 err = conv_err(AudioDeviceRemoveIOProc(stream->outputDevice, AudioIOProc)); 729 } 730 731 return err; 732 } 733 734 735 static PaError StartStream( PaStream *s ) 736 { 737 PaError err = paNoError; 738 PaMacCoreStream *stream = (PaMacCoreStream*)s; 739 740 if (stream->inputDevice != kAudioDeviceUnknown) { 741 if (stream->outputDevice == kAudioDeviceUnknown || stream->outputDevice == stream->inputDevice) { 742 err = conv_err(AudioDeviceStart(stream->inputDevice, AudioIOProc)); 743 } 744 else { 745 err = conv_err(AudioDeviceStart(stream->inputDevice, AudioInputProc)); 746 err = conv_err(AudioDeviceStart(stream->outputDevice, AudioOutputProc)); 747 } 748 } 749 else { 750 err = conv_err(AudioDeviceStart(stream->outputDevice, AudioIOProc)); 751 } 752 753 stream->isActive = 1; 754 stream->isStopped = 0; 755 return err; 756 } 757 758 static PaError AbortStream( PaStream *s ) 759 { 760 PaError err = paNoError; 761 PaMacCoreStream *stream = (PaMacCoreStream*)s; 762 763 if (stream->inputDevice != kAudioDeviceUnknown) { 764 if (stream->outputDevice == kAudioDeviceUnknown || stream->outputDevice == stream->inputDevice) { 765 err = conv_err(AudioDeviceStop(stream->inputDevice, AudioIOProc)); 766 } 767 else { 768 err = conv_err(AudioDeviceStop(stream->inputDevice, AudioInputProc)); 769 err = conv_err(AudioDeviceStop(stream->outputDevice, AudioOutputProc)); 770 } 771 } 772 else { 773 err = conv_err(AudioDeviceStop(stream->outputDevice, AudioIOProc)); 774 } 775 776 stream->isActive = 0; 777 stream->isStopped = 1; 778 return err; 779 } 780 781 static PaError StopStream( PaStream *s ) 782 { 783 // TODO: this should be nicer than abort 784 return AbortStream(s); 785 } 786 787 static PaError IsStreamStopped( PaStream *s ) 788 { 789 PaMacCoreStream *stream = (PaMacCoreStream*)s; 790 791 return stream->isStopped; 792 } 793 794 795 static PaError IsStreamActive( PaStream *s ) 796 { 797 PaMacCoreStream *stream = (PaMacCoreStream*)s; 798 799 return stream->isActive; 800 } 801 802 803 static PaTime GetStreamTime( PaStream *s ) 804 { 805 OSStatus err; 806 PaTime result; 807 PaMacCoreStream *stream = (PaMacCoreStream*)s; 808 809 AudioTimeStamp *timeStamp = PaUtil_AllocateMemory(sizeof(AudioTimeStamp)); 810 if (stream->inputDevice != kAudioDeviceUnknown) { 811 err = AudioDeviceGetCurrentTime(stream->inputDevice, timeStamp); 812 } 813 else { 814 err = AudioDeviceGetCurrentTime(stream->outputDevice, timeStamp); 815 } 816 817 result = err ? 0 : timeStamp->mSampleTime; 818 PaUtil_FreeMemory(timeStamp); 819 820 return result; 821 } 822 823 824 static double GetStreamCpuLoad( PaStream* s ) 825 { 826 PaMacCoreStream *stream = (PaMacCoreStream*)s; 827 828 return PaUtil_GetCpuLoad( &stream->cpuLoadMeasurer ); 829 } 830 831 832 // As separate stream interfaces are used for blocking and callback streams, the following functions can be guaranteed to only be called for blocking streams. 833 834 static PaError ReadStream( PaStream* s, 835 void *buffer, 836 unsigned long frames ) 837 { 838 return paInternalError; 839 } 840 841 842 static PaError WriteStream( PaStream* s, 843 const void *buffer, 844 unsigned long frames ) 845 { 846 return paInternalError; 847 } 848 849 850 static signed long GetStreamReadAvailable( PaStream* s ) 851 { 852 return paInternalError; 853 } 854 855 856 static signed long GetStreamWriteAvailable( PaStream* s ) 857 { 858 return paInternalError; 859 } 860 861 // HostAPI-specific initialization function 862 PaError PaMacCore_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex ) 863 { 864 PaError result = paNoError; 865 PaMacCoreHostApiRepresentation *macCoreHostApi = (PaMacCoreHostApiRepresentation *)PaUtil_AllocateMemory( sizeof(PaMacCoreHostApiRepresentation) ); 866 if( !macCoreHostApi ) 867 { 868 result = paInsufficientMemory; 869 goto error; 870 } 871 872 macCoreHostApi->allocations = PaUtil_CreateAllocationGroup(); 873 if( !macCoreHostApi->allocations ) 874 { 875 result = paInsufficientMemory; 876 goto error; 877 } 878 879 *hostApi = &macCoreHostApi->inheritedHostApiRep; 880 (*hostApi)->info.structVersion = 1; 881 (*hostApi)->info.type = paCoreAudio; 882 (*hostApi)->info.name = "CoreAudio"; 883 884 result = InitializeDeviceInfos(macCoreHostApi, hostApiIndex); 885 if (result != paNoError) { 886 goto error; 887 } 888 889 // Set up the proper callbacks to this HostApi's functions 890 (*hostApi)->Terminate = Terminate; 891 (*hostApi)->OpenStream = OpenStream; 892 (*hostApi)->IsFormatSupported = IsFormatSupported; 893 894 PaUtil_InitializeStreamInterface( &macCoreHostApi->callbackStreamInterface, CloseStream, StartStream, 895 StopStream, AbortStream, IsStreamStopped, IsStreamActive, 896 GetStreamTime, GetStreamCpuLoad, 897 PaUtil_DummyRead, PaUtil_DummyWrite, 898 PaUtil_DummyGetReadAvailable, PaUtil_DummyGetWriteAvailable ); 899 900 PaUtil_InitializeStreamInterface( &macCoreHostApi->blockingStreamInterface, CloseStream, StartStream, 901 StopStream, AbortStream, IsStreamStopped, IsStreamActive, 902 GetStreamTime, PaUtil_DummyGetCpuLoad, 903 ReadStream, WriteStream, GetStreamReadAvailable, GetStreamWriteAvailable ); 904 905 return result; 906 907 error: 908 if( macCoreHostApi ) { 909 CleanUp(macCoreHostApi); 910 } 911 912 return result; 913 }