- Timestamp:
- Oct 13, 2006 5:57:42 PM (18 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
pjproject/trunk/pjmedia/src/pjmedia/portaudio/pa_mac_core.c
r341 r770 1 1 /* 2 * $Id$ 3 * pa_mac_core.c 4 * Implementation of PortAudio for Mac OS X CoreAudio 5 * 2 * Implementation of the PortAudio API for Apple AUHAL 3 * 6 4 * PortAudio Portable Real-Time Audio Library 7 5 * Latest Version at: http://www.portaudio.com 8 6 * 9 * Authors: Ross Bencina and Phil Burk 10 * Copyright (c) 1999-2000 Ross Bencina and Phil Burk 7 * Written by Bjorn Roche of XO Audio LLC, from PA skeleton code. 8 * Portions copied from code by Dominic Mazzoni (who wrote a HAL implementation) 9 * 10 * Dominic's code was based on code by Phil Burk, Darren Gibbs, 11 * Gord Peters, Stephane Letz, and Greg Pfiel. 12 * 13 * The following people also deserve acknowledgements: 14 * 15 * Olivier Tristan for feedback and testing 16 * Glenn Zelniker and Z-Systems engineering for sponsoring the Blocking I/O 17 * interface. 18 * 19 * 20 * Based on the Open Source API proposed by Ross Bencina 21 * Copyright (c) 1999-2002 Ross Bencina, Phil Burk 11 22 * 12 23 * Permission is hereby granted, free of charge, to any person obtaining … … 21 32 * included in all copies or substantial portions of the Software. 22 33 * 23 * Any person wishing to distribute modifications to the Software is24 * requested to send the modifications to the original developer so that25 * they can be incorporated into the canonical version.26 *27 34 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 28 35 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF … … 32 39 * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 33 40 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 41 */ 42 43 /* 44 * The text above constitutes the entire PortAudio license; however, 45 * the PortAudio community also makes the following non-binding requests: 34 46 * 47 * Any person wishing to distribute modifications to the Software is 48 * requested to send the modifications to the original developer so that 49 * they can be incorporated into the canonical version. It is also 50 * requested that these non-binding requests be included along with the 51 * license above. 35 52 */ 36 53 37 #include <CoreAudio/CoreAudio.h> 38 #include <AudioToolbox/AudioToolbox.h> 39 #include <stdio.h> 40 #include <stdlib.h> 41 #include <math.h> 42 #include <assert.h> 43 44 #include "portaudio.h" 45 #include "pa_trace.h" 46 #include "pa_util.h" 47 #include "pa_allocation.h" 48 #include "pa_hostapi.h" 49 #include "pa_stream.h" 50 #include "pa_cpuload.h" 51 #include "pa_process.h" 52 53 // ===== constants ===== 54 55 // ===== structs ===== 56 #pragma mark structs 57 58 // PaMacCoreHostApiRepresentation - host api datastructure specific to this implementation 59 typedef struct PaMacCore_HAR 54 /** 55 @file pa_mac_core 56 @ingroup hostapi_src 57 @author Bjorn Roche 58 @brief AUHAL implementation of PortAudio 59 */ 60 61 /* FIXME: not all error conditions call PaUtil_SetLastHostErrorInfo() 62 * PaMacCore_SetError() will do this. 63 */ 64 65 #include "pa_mac_core_internal.h" 66 67 #include <string.h> /* strlen(), memcmp() etc. */ 68 69 #include "pa_mac_core.h" 70 #include "pa_mac_core_utilities.h" 71 #include "pa_mac_core_blocking.h" 72 73 74 75 /* prototypes for functions declared in this file */ 76 77 #ifdef __cplusplus 78 extern "C" 60 79 { 61 PaUtilHostApiRepresentation inheritedHostApiRep; 62 PaUtilStreamInterface callbackStreamInterface; 63 PaUtilStreamInterface blockingStreamInterface; 64 65 PaUtilAllocationGroup *allocations; 66 AudioDeviceID *macCoreDeviceIds; 80 #endif /* __cplusplus */ 81 82 PaError PaMacCore_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index ); 83 84 #ifdef __cplusplus 67 85 } 68 PaMacCoreHostApiRepresentation; 69 70 typedef struct PaMacCore_DI 86 #endif /* __cplusplus */ 87 88 #define RING_BUFFER_ADVANCE_DENOMINATOR (4) 89 90 static void Terminate( struct PaUtilHostApiRepresentation *hostApi ); 91 static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi, 92 const PaStreamParameters *inputParameters, 93 const PaStreamParameters *outputParameters, 94 double sampleRate ); 95 static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, 96 PaStream** s, 97 const PaStreamParameters *inputParameters, 98 const PaStreamParameters *outputParameters, 99 double sampleRate, 100 unsigned long framesPerBuffer, 101 PaStreamFlags streamFlags, 102 PaStreamCallback *streamCallback, 103 void *userData ); 104 static PaError CloseStream( PaStream* stream ); 105 static PaError StartStream( PaStream *stream ); 106 static PaError StopStream( PaStream *stream ); 107 static PaError AbortStream( PaStream *stream ); 108 static PaError IsStreamStopped( PaStream *s ); 109 static PaError IsStreamActive( PaStream *stream ); 110 static PaTime GetStreamTime( PaStream *stream ); 111 static void setStreamStartTime( PaStream *stream ); 112 static OSStatus AudioIOProc( void *inRefCon, 113 AudioUnitRenderActionFlags *ioActionFlags, 114 const AudioTimeStamp *inTimeStamp, 115 UInt32 inBusNumber, 116 UInt32 inNumberFrames, 117 AudioBufferList *ioData ); 118 static double GetStreamCpuLoad( PaStream* stream ); 119 120 static PaError GetChannelInfo( PaMacAUHAL *auhalHostApi, 121 PaDeviceInfo *deviceInfo, 122 AudioDeviceID macCoreDeviceId, 123 int isInput); 124 125 static PaError OpenAndSetupOneAudioUnit( 126 const PaStreamParameters *inStreamParams, 127 const PaStreamParameters *outStreamParams, 128 const unsigned long requestedFramesPerBuffer, 129 unsigned long *actualInputFramesPerBuffer, 130 unsigned long *actualOutputFramesPerBuffer, 131 const PaMacAUHAL *auhalHostApi, 132 AudioUnit *audioUnit, 133 AudioConverterRef *srConverter, 134 AudioDeviceID *audioDevice, 135 const double sampleRate, 136 void *refCon ); 137 138 /* for setting errors. */ 139 #define PA_AUHAL_SET_LAST_HOST_ERROR( errorCode, errorText ) \ 140 PaUtil_SetLastHostErrorInfo( paInDevelopment, errorCode, errorText ) 141 142 143 /*currently, this is only used in initialization, but it might be modified 144 to be used when the list of devices changes.*/ 145 static PaError gatherDeviceInfo(PaMacAUHAL *auhalHostApi) 71 146 { 72 PaDeviceInfo inheritedDeviceInfo; 147 UInt32 size; 148 UInt32 propsize; 149 VVDBUG(("gatherDeviceInfo()\n")); 150 /* -- free any previous allocations -- */ 151 if( auhalHostApi->devIds ) 152 PaUtil_GroupFreeMemory(auhalHostApi->allocations, auhalHostApi->devIds); 153 auhalHostApi->devIds = NULL; 154 155 /* -- figure out how many devices there are -- */ 156 AudioHardwareGetPropertyInfo( kAudioHardwarePropertyDevices, 157 &propsize, 158 NULL ); 159 auhalHostApi->devCount = propsize / sizeof( AudioDeviceID ); 160 161 VDBUG( ( "Found %ld device(s).\n", auhalHostApi->devCount ) ); 162 163 /* -- copy the device IDs -- */ 164 auhalHostApi->devIds = (AudioDeviceID *)PaUtil_GroupAllocateMemory( 165 auhalHostApi->allocations, 166 propsize ); 167 if( !auhalHostApi->devIds ) 168 return paInsufficientMemory; 169 AudioHardwareGetProperty( kAudioHardwarePropertyDevices, 170 &propsize, 171 auhalHostApi->devIds ); 172 #ifdef MAC_CORE_VERBOSE_DEBUG 173 { 174 int i; 175 for( i=0; i<auhalHostApi->devCount; ++i ) 176 printf( "Device %d\t: %ld\n", i, auhalHostApi->devIds[i] ); 177 } 178 #endif 179 180 size = sizeof(AudioDeviceID); 181 auhalHostApi->defaultIn = kAudioDeviceUnknown; 182 auhalHostApi->defaultOut = kAudioDeviceUnknown; 183 184 /* determine the default device. */ 185 /* I am not sure how these calls to AudioHardwareGetProperty() 186 could fail, but in case they do, we use the first available 187 device as the default. */ 188 if( 0 != AudioHardwareGetProperty(kAudioHardwarePropertyDefaultInputDevice, 189 &size, 190 &auhalHostApi->defaultIn) ) { 191 int i; 192 auhalHostApi->defaultIn = kAudioDeviceUnknown; 193 VDBUG(("Failed to get default input device from OS.")); 194 VDBUG((" I will substitute the first available input Device.")); 195 for( i=0; i<auhalHostApi->devCount; ++i ) { 196 PaDeviceInfo devInfo; 197 if( 0 != GetChannelInfo( auhalHostApi, &devInfo, 198 auhalHostApi->devIds[i], TRUE ) ) 199 if( devInfo.maxInputChannels ) { 200 auhalHostApi->defaultIn = auhalHostApi->devIds[i]; 201 break; 202 } 203 } 204 } 205 if( 0 != AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice, 206 &size, 207 &auhalHostApi->defaultOut) ) { 208 int i; 209 auhalHostApi->defaultIn = kAudioDeviceUnknown; 210 VDBUG(("Failed to get default output device from OS.")); 211 VDBUG((" I will substitute the first available output Device.")); 212 for( i=0; i<auhalHostApi->devCount; ++i ) { 213 PaDeviceInfo devInfo; 214 if( 0 != GetChannelInfo( auhalHostApi, &devInfo, 215 auhalHostApi->devIds[i], FALSE ) ) 216 if( devInfo.maxOutputChannels ) { 217 auhalHostApi->defaultOut = auhalHostApi->devIds[i]; 218 break; 219 } 220 } 221 } 222 223 VDBUG( ( "Default in : %ld\n", auhalHostApi->defaultIn ) ); 224 VDBUG( ( "Default out: %ld\n", auhalHostApi->defaultOut ) ); 225 226 return paNoError; 73 227 } 74 PaMacCoreDeviceInfo; 75 76 // PaMacCoreStream - a stream data structure specifically for this implementation 77 typedef struct PaMacCore_S 78 { 79 PaUtilStreamRepresentation streamRepresentation; 80 PaUtilCpuLoadMeasurer cpuLoadMeasurer; 81 PaUtilBufferProcessor bufferProcessor; 82 83 int primeStreamUsingCallback; 84 85 AudioDeviceID inputDevice; 86 AudioDeviceID outputDevice; 87 88 // Processing thread management -------------- 89 // HANDLE abortEvent; 90 // HANDLE processingThread; 91 // DWORD processingThreadId; 92 93 char throttleProcessingThreadOnOverload; // 0 -> don't throtte, non-0 -> throttle 94 int processingThreadPriority; 95 int highThreadPriority; 96 int throttledThreadPriority; 97 unsigned long throttledSleepMsecs; 98 99 int isStopped; 100 volatile int isActive; 101 volatile int stopProcessing; // stop thread once existing buffers have been returned 102 volatile int abortProcessing; // stop thread immediately 103 104 // DWORD allBuffersDurationMs; // used to calculate timeouts 105 } 106 PaMacCoreStream; 107 108 // Data needed by the CoreAudio callback functions 109 typedef struct PaMacCore_CD 110 { 111 PaMacCoreStream *stream; 112 PaStreamCallback *callback; 113 void *userData; 114 PaUtilConverter *inputConverter; 115 PaUtilConverter *outputConverter; 116 void *inputBuffer; 117 void *outputBuffer; 118 int inputChannelCount; 119 int outputChannelCount; 120 PaSampleFormat inputSampleFormat; 121 PaSampleFormat outputSampleFormat; 122 PaUtilTriangularDitherGenerator *ditherGenerator; 123 } 124 PaMacClientData; 125 126 // ===== CoreAudio-PortAudio bridge functions ===== 127 #pragma mark CoreAudio-PortAudio bridge functions 128 129 // Maps CoreAudio OSStatus codes to PortAudio PaError codes 130 static PaError conv_err(OSStatus error) 131 { 132 PaError result; 133 134 switch (error) { 135 case kAudioHardwareNoError: 136 result = paNoError; break; 137 case kAudioHardwareNotRunningError: 138 result = paInternalError; break; 139 case kAudioHardwareUnspecifiedError: 140 result = paInternalError; break; 141 case kAudioHardwareUnknownPropertyError: 142 result = paInternalError; break; 143 case kAudioHardwareBadPropertySizeError: 144 result = paInternalError; break; 145 case kAudioHardwareIllegalOperationError: 146 result = paInternalError; break; 147 case kAudioHardwareBadDeviceError: 148 result = paInvalidDevice; break; 149 case kAudioHardwareBadStreamError: 150 result = paBadStreamPtr; break; 151 // case kAudioHardwareUnsupportedOperationError: 152 // result = paInternalError; break; 153 case kAudioDeviceUnsupportedFormatError: 154 result = paSampleFormatNotSupported; break; 155 case kAudioDevicePermissionsError: 156 result = paDeviceUnavailable; break; 157 default: 158 result = paInternalError; 159 } 160 161 return result; 162 } 163 164 static AudioStreamBasicDescription *InitializeStreamDescription(const PaStreamParameters *parameters, double sampleRate) 165 { 166 struct AudioStreamBasicDescription *streamDescription = PaUtil_AllocateMemory(sizeof(AudioStreamBasicDescription)); 167 streamDescription->mSampleRate = sampleRate; 168 streamDescription->mFormatID = kAudioFormatLinearPCM; 169 streamDescription->mFormatFlags = 0; 170 streamDescription->mFramesPerPacket = 1; 171 172 if (parameters->sampleFormat & paNonInterleaved) { 173 streamDescription->mFormatFlags |= kLinearPCMFormatFlagIsNonInterleaved; 174 streamDescription->mChannelsPerFrame = 1; 175 streamDescription->mBytesPerFrame = Pa_GetSampleSize(parameters->sampleFormat); 176 streamDescription->mBytesPerPacket = Pa_GetSampleSize(parameters->sampleFormat); 177 } 178 else { 179 streamDescription->mChannelsPerFrame = parameters->channelCount; 180 } 181 182 streamDescription->mBytesPerFrame = Pa_GetSampleSize(parameters->sampleFormat) * streamDescription->mChannelsPerFrame; 183 streamDescription->mBytesPerPacket = streamDescription->mBytesPerFrame * streamDescription->mFramesPerPacket; 184 185 if (parameters->sampleFormat & paFloat32) { 186 streamDescription->mFormatFlags |= kLinearPCMFormatFlagIsFloat; 187 streamDescription->mBitsPerChannel = 32; 188 } 189 else if (parameters->sampleFormat & paInt32) { 190 streamDescription->mFormatFlags |= kLinearPCMFormatFlagIsSignedInteger; 191 streamDescription->mBitsPerChannel = 32; 192 } 193 else if (parameters->sampleFormat & paInt24) { 194 streamDescription->mFormatFlags |= kLinearPCMFormatFlagIsSignedInteger; 195 streamDescription->mBitsPerChannel = 24; 196 } 197 else if (parameters->sampleFormat & paInt16) { 198 streamDescription->mFormatFlags |= kLinearPCMFormatFlagIsSignedInteger; 199 streamDescription->mBitsPerChannel = 16; 200 } 201 else if (parameters->sampleFormat & paInt8) { 202 streamDescription->mFormatFlags |= kLinearPCMFormatFlagIsSignedInteger; 203 streamDescription->mBitsPerChannel = 8; 204 } 205 else if (parameters->sampleFormat & paInt32) { 206 streamDescription->mBitsPerChannel = 8; 207 } 208 209 return streamDescription; 210 } 211 212 static PaStreamCallbackTimeInfo *InitializeTimeInfo(const AudioTimeStamp* now, const AudioTimeStamp* inputTime, const AudioTimeStamp* outputTime) 213 { 214 PaStreamCallbackTimeInfo *timeInfo = PaUtil_AllocateMemory(sizeof(PaStreamCallbackTimeInfo)); 215 216 timeInfo->inputBufferAdcTime = inputTime->mSampleTime; 217 timeInfo->currentTime = now->mSampleTime; 218 timeInfo->outputBufferDacTime = outputTime->mSampleTime; 219 220 return timeInfo; 221 } 222 223 // ===== support functions ===== 224 #pragma mark support functions 225 226 static void CleanUp(PaMacCoreHostApiRepresentation *macCoreHostApi) 227 { 228 if( macCoreHostApi->allocations ) 229 { 230 PaUtil_FreeAllAllocations( macCoreHostApi->allocations ); 231 PaUtil_DestroyAllocationGroup( macCoreHostApi->allocations ); 232 } 233 234 PaUtil_FreeMemory( macCoreHostApi ); 235 } 236 237 static PaError GetChannelInfo(PaDeviceInfo *deviceInfo, AudioDeviceID macCoreDeviceId, int isInput) 228 229 static PaError GetChannelInfo( PaMacAUHAL *auhalHostApi, 230 PaDeviceInfo *deviceInfo, 231 AudioDeviceID macCoreDeviceId, 232 int isInput) 238 233 { 239 234 UInt32 propSize; … … 241 236 UInt32 i; 242 237 int numChannels = 0; 243 AudioBufferList *buflist; 244 245 err = conv_err(AudioDeviceGetPropertyInfo(macCoreDeviceId, 0, isInput, kAudioDevicePropertyStreamConfiguration, &propSize, NULL)); 238 AudioBufferList *buflist = NULL; 239 UInt32 frameLatency; 240 241 VVDBUG(("GetChannelInfo()\n")); 242 243 /* Get the number of channels from the stream configuration. 244 Fail if we can't get this. */ 245 246 err = ERR(AudioDeviceGetPropertyInfo(macCoreDeviceId, 0, isInput, kAudioDevicePropertyStreamConfiguration, &propSize, NULL)); 247 if (err) 248 return err; 249 246 250 buflist = PaUtil_AllocateMemory(propSize); 247 err = conv_err(AudioDeviceGetProperty(macCoreDeviceId, 0, isInput, kAudioDevicePropertyStreamConfiguration, &propSize, buflist)); 248 if (!err) { 249 for (i = 0; i < buflist->mNumberBuffers; ++i) { 250 numChannels += buflist->mBuffers[i].mNumberChannels; 251 if( !buflist ) 252 return paInsufficientMemory; 253 err = ERR(AudioDeviceGetProperty(macCoreDeviceId, 0, isInput, kAudioDevicePropertyStreamConfiguration, &propSize, buflist)); 254 if (err) 255 goto error; 256 257 for (i = 0; i < buflist->mNumberBuffers; ++i) 258 numChannels += buflist->mBuffers[i].mNumberChannels; 259 260 if (isInput) 261 deviceInfo->maxInputChannels = numChannels; 262 else 263 deviceInfo->maxOutputChannels = numChannels; 264 265 if (numChannels > 0) /* do not try to retrieve the latency if there is no channels. */ 266 { 267 /* Get the latency. Don't fail if we can't get this. */ 268 /* default to something reasonable */ 269 deviceInfo->defaultLowInputLatency = .01; 270 deviceInfo->defaultHighInputLatency = .10; 271 deviceInfo->defaultLowOutputLatency = .01; 272 deviceInfo->defaultHighOutputLatency = .10; 273 propSize = sizeof(UInt32); 274 err = WARNING(AudioDeviceGetProperty(macCoreDeviceId, 0, isInput, kAudioDevicePropertyLatency, &propSize, &frameLatency)); 275 if (!err) 276 { 277 /** FEEDBACK: 278 * This code was arrived at by trial and error, and some extentive, but not exhaustive 279 * testing. Sebastien Beaulieu <seb@plogue.com> has suggested using 280 * kAudioDevicePropertyLatency + kAudioDevicePropertySafetyOffset + buffer size instead. 281 * At the time this code was written, many users were reporting dropouts with audio 282 * programs that probably used this formula. This was probably 283 * around 10.4.4, and the problem is probably fixed now. So perhaps 284 * his formula should be reviewed and used. 285 * */ 286 double secondLatency = frameLatency / deviceInfo->defaultSampleRate; 287 if (isInput) 288 { 289 deviceInfo->defaultLowInputLatency = 3 * secondLatency; 290 deviceInfo->defaultHighInputLatency = 3 * 10 * secondLatency; 291 } 292 else 293 { 294 deviceInfo->defaultLowOutputLatency = 3 * secondLatency; 295 deviceInfo->defaultHighOutputLatency = 3 * 10 * secondLatency; 296 } 297 } 298 } 299 PaUtil_FreeMemory( buflist ); 300 return paNoError; 301 error: 302 PaUtil_FreeMemory( buflist ); 303 return err; 304 } 305 306 static PaError InitializeDeviceInfo( PaMacAUHAL *auhalHostApi, 307 PaDeviceInfo *deviceInfo, 308 AudioDeviceID macCoreDeviceId, 309 PaHostApiIndex hostApiIndex ) 310 { 311 Float64 sampleRate; 312 char *name; 313 PaError err = paNoError; 314 UInt32 propSize; 315 316 VVDBUG(("InitializeDeviceInfo(): macCoreDeviceId=%ld\n", macCoreDeviceId)); 317 318 memset(deviceInfo, 0, sizeof(deviceInfo)); 319 320 deviceInfo->structVersion = 2; 321 deviceInfo->hostApi = hostApiIndex; 322 323 /* Get the device name. Fail if we can't get it. */ 324 err = ERR(AudioDeviceGetPropertyInfo(macCoreDeviceId, 0, 0, kAudioDevicePropertyDeviceName, &propSize, NULL)); 325 if (err) 326 return err; 327 328 name = PaUtil_GroupAllocateMemory(auhalHostApi->allocations,propSize); 329 if ( !name ) 330 return paInsufficientMemory; 331 err = ERR(AudioDeviceGetProperty(macCoreDeviceId, 0, 0, kAudioDevicePropertyDeviceName, &propSize, name)); 332 if (err) 333 return err; 334 deviceInfo->name = name; 335 336 /* Try to get the default sample rate. Don't fail if we can't get this. */ 337 propSize = sizeof(Float64); 338 err = ERR(AudioDeviceGetProperty(macCoreDeviceId, 0, 0, kAudioDevicePropertyNominalSampleRate, &propSize, &sampleRate)); 339 if (err) 340 deviceInfo->defaultSampleRate = 0.0; 341 else 342 deviceInfo->defaultSampleRate = sampleRate; 343 344 /* Get the maximum number of input and output channels. Fail if we can't get this. */ 345 346 err = GetChannelInfo(auhalHostApi, deviceInfo, macCoreDeviceId, 1); 347 if (err) 348 return err; 349 350 err = GetChannelInfo(auhalHostApi, deviceInfo, macCoreDeviceId, 0); 351 if (err) 352 return err; 353 354 return paNoError; 355 } 356 357 PaError PaMacCore_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex ) 358 { 359 PaError result = paNoError; 360 int i; 361 PaMacAUHAL *auhalHostApi; 362 PaDeviceInfo *deviceInfoArray; 363 364 VVDBUG(("PaMacCore_Initialize(): hostApiIndex=%d\n", hostApiIndex)); 365 366 auhalHostApi = (PaMacAUHAL*)PaUtil_AllocateMemory( sizeof(PaMacAUHAL) ); 367 if( !auhalHostApi ) 368 { 369 result = paInsufficientMemory; 370 goto error; 371 } 372 373 auhalHostApi->allocations = PaUtil_CreateAllocationGroup(); 374 if( !auhalHostApi->allocations ) 375 { 376 result = paInsufficientMemory; 377 goto error; 378 } 379 380 auhalHostApi->devIds = NULL; 381 auhalHostApi->devCount = 0; 382 383 /* get the info we need about the devices */ 384 result = gatherDeviceInfo( auhalHostApi ); 385 if( result != paNoError ) 386 goto error; 387 388 *hostApi = &auhalHostApi->inheritedHostApiRep; 389 (*hostApi)->info.structVersion = 1; 390 (*hostApi)->info.type = paCoreAudio; 391 (*hostApi)->info.name = "Core Audio"; 392 393 (*hostApi)->info.defaultInputDevice = paNoDevice; 394 (*hostApi)->info.defaultOutputDevice = paNoDevice; 395 396 (*hostApi)->info.deviceCount = 0; 397 398 if( auhalHostApi->devCount > 0 ) 399 { 400 (*hostApi)->deviceInfos = (PaDeviceInfo**)PaUtil_GroupAllocateMemory( 401 auhalHostApi->allocations, sizeof(PaDeviceInfo*) * auhalHostApi->devCount); 402 if( !(*hostApi)->deviceInfos ) 403 { 404 result = paInsufficientMemory; 405 goto error; 251 406 } 252 253 if (isInput) 254 deviceInfo->maxInputChannels = numChannels; 255 else 256 deviceInfo->maxOutputChannels = numChannels; 257 258 int frameLatency; 259 propSize = sizeof(UInt32); 260 err = conv_err(AudioDeviceGetProperty(macCoreDeviceId, 0, isInput, kAudioDevicePropertyLatency, &propSize, &frameLatency)); 261 if (!err) { 262 double secondLatency = frameLatency / deviceInfo->defaultSampleRate; 263 if (isInput) { 264 deviceInfo->defaultLowInputLatency = secondLatency; 265 deviceInfo->defaultHighInputLatency = secondLatency; 407 408 /* allocate all device info structs in a contiguous block */ 409 deviceInfoArray = (PaDeviceInfo*)PaUtil_GroupAllocateMemory( 410 auhalHostApi->allocations, sizeof(PaDeviceInfo) * auhalHostApi->devCount ); 411 if( !deviceInfoArray ) 412 { 413 result = paInsufficientMemory; 414 goto error; 415 } 416 417 for( i=0; i < auhalHostApi->devCount; ++i ) 418 { 419 int err; 420 err = InitializeDeviceInfo( auhalHostApi, &deviceInfoArray[i], 421 auhalHostApi->devIds[i], 422 hostApiIndex ); 423 if (err == paNoError) 424 { /* copy some info and set the defaults */ 425 (*hostApi)->deviceInfos[(*hostApi)->info.deviceCount] = &deviceInfoArray[i]; 426 if (auhalHostApi->devIds[i] == auhalHostApi->defaultIn) 427 (*hostApi)->info.defaultInputDevice = (*hostApi)->info.deviceCount; 428 if (auhalHostApi->devIds[i] == auhalHostApi->defaultOut) 429 (*hostApi)->info.defaultOutputDevice = (*hostApi)->info.deviceCount; 430 (*hostApi)->info.deviceCount++; 266 431 } 267 else { 268 deviceInfo->defaultLowOutputLatency = secondLatency; 269 deviceInfo->defaultHighOutputLatency = secondLatency; 432 else 433 { /* there was an error. we need to shift the devices down, so we ignore this one */ 434 int j; 435 auhalHostApi->devCount--; 436 for( j=i; j<auhalHostApi->devCount; ++j ) 437 auhalHostApi->devIds[j] = auhalHostApi->devIds[j+1]; 438 i--; 270 439 } 271 440 } 272 441 } 273 PaUtil_FreeMemory(buflist); 274 275 return err; 276 } 277 278 static PaError InitializeDeviceInfo(PaMacCoreDeviceInfo *macCoreDeviceInfo, AudioDeviceID macCoreDeviceId, PaHostApiIndex hostApiIndex ) 279 { 280 PaDeviceInfo *deviceInfo = &macCoreDeviceInfo->inheritedDeviceInfo; 281 deviceInfo->structVersion = 2; 282 deviceInfo->hostApi = hostApiIndex; 283 284 PaError err = paNoError; 285 UInt32 propSize; 286 287 err = conv_err(AudioDeviceGetPropertyInfo(macCoreDeviceId, 0, 0, kAudioDevicePropertyDeviceName, &propSize, NULL)); 288 // FIXME: this allocation should be part of the allocations group 289 char *name = PaUtil_AllocateMemory(propSize); 290 err = conv_err(AudioDeviceGetProperty(macCoreDeviceId, 0, 0, kAudioDevicePropertyDeviceName, &propSize, name)); 291 if (!err) { 292 deviceInfo->name = name; 293 } 294 295 Float64 sampleRate; 296 propSize = sizeof(Float64); 297 err = conv_err(AudioDeviceGetProperty(macCoreDeviceId, 0, 0, kAudioDevicePropertyNominalSampleRate, &propSize, &sampleRate)); 298 if (!err) { 299 deviceInfo->defaultSampleRate = sampleRate; 300 } 301 302 303 // Get channel info 304 err = GetChannelInfo(deviceInfo, macCoreDeviceId, 1); 305 err = GetChannelInfo(deviceInfo, macCoreDeviceId, 0); 306 307 return err; 308 } 309 310 static PaError InitializeDeviceInfos( PaMacCoreHostApiRepresentation *macCoreHostApi, PaHostApiIndex hostApiIndex ) 311 { 312 PaError result = paNoError; 313 PaUtilHostApiRepresentation *hostApi; 314 PaMacCoreDeviceInfo *deviceInfoArray; 315 316 // 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. 317 hostApi = &macCoreHostApi->inheritedHostApiRep; 318 hostApi->info.deviceCount = 0; 319 hostApi->info.defaultInputDevice = paNoDevice; 320 hostApi->info.defaultOutputDevice = paNoDevice; 321 322 UInt32 propsize; 323 AudioHardwareGetPropertyInfo(kAudioHardwarePropertyDevices, &propsize, NULL); 324 int numDevices = propsize / sizeof(AudioDeviceID); 325 hostApi->info.deviceCount = numDevices; 326 if (numDevices > 0) { 327 hostApi->deviceInfos = (PaDeviceInfo**)PaUtil_GroupAllocateMemory( 328 macCoreHostApi->allocations, sizeof(PaDeviceInfo*) * numDevices ); 329 if( !hostApi->deviceInfos ) 442 443 (*hostApi)->Terminate = Terminate; 444 (*hostApi)->OpenStream = OpenStream; 445 (*hostApi)->IsFormatSupported = IsFormatSupported; 446 447 PaUtil_InitializeStreamInterface( &auhalHostApi->callbackStreamInterface, 448 CloseStream, StartStream, 449 StopStream, AbortStream, IsStreamStopped, 450 IsStreamActive, 451 GetStreamTime, GetStreamCpuLoad, 452 PaUtil_DummyRead, PaUtil_DummyWrite, 453 PaUtil_DummyGetReadAvailable, 454 PaUtil_DummyGetWriteAvailable ); 455 456 PaUtil_InitializeStreamInterface( &auhalHostApi->blockingStreamInterface, 457 CloseStream, StartStream, 458 StopStream, AbortStream, IsStreamStopped, 459 IsStreamActive, 460 GetStreamTime, PaUtil_DummyGetCpuLoad, 461 ReadStream, WriteStream, 462 GetStreamReadAvailable, 463 GetStreamWriteAvailable ); 464 465 return result; 466 467 error: 468 if( auhalHostApi ) 469 { 470 if( auhalHostApi->allocations ) 330 471 { 331 return paInsufficientMemory; 472 PaUtil_FreeAllAllocations( auhalHostApi->allocations ); 473 PaUtil_DestroyAllocationGroup( auhalHostApi->allocations ); 332 474 } 333 334 // allocate all device info structs in a contiguous block 335 deviceInfoArray = (PaMacCoreDeviceInfo*)PaUtil_GroupAllocateMemory( 336 macCoreHostApi->allocations, sizeof(PaMacCoreDeviceInfo) * numDevices ); 337 if( !deviceInfoArray ) 338 { 339 return paInsufficientMemory; 340 } 341 342 macCoreHostApi->macCoreDeviceIds = PaUtil_GroupAllocateMemory(macCoreHostApi->allocations, propsize); 343 AudioHardwareGetProperty(kAudioHardwarePropertyDevices, &propsize, macCoreHostApi->macCoreDeviceIds); 344 345 AudioDeviceID defaultInputDevice, defaultOutputDevice; 346 propsize = sizeof(AudioDeviceID); 347 AudioHardwareGetProperty(kAudioHardwarePropertyDefaultInputDevice, &propsize, &defaultInputDevice); 348 AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice, &propsize, &defaultOutputDevice); 349 350 UInt32 i; 351 for (i = 0; i < numDevices; ++i) { 352 if (macCoreHostApi->macCoreDeviceIds[i] == defaultInputDevice) { 353 hostApi->info.defaultInputDevice = i; 354 } 355 if (macCoreHostApi->macCoreDeviceIds[i] == defaultOutputDevice) { 356 hostApi->info.defaultOutputDevice = i; 357 } 358 InitializeDeviceInfo(&deviceInfoArray[i], macCoreHostApi->macCoreDeviceIds[i], hostApiIndex); 359 hostApi->deviceInfos[i] = &(deviceInfoArray[i].inheritedDeviceInfo); 360 } 361 } 362 475 476 PaUtil_FreeMemory( auhalHostApi ); 477 } 363 478 return result; 364 479 } 365 480 366 static OSStatus CheckFormat(AudioDeviceID macCoreDeviceId, const PaStreamParameters *parameters, double sampleRate, int isInput)367 {368 UInt32 propSize = sizeof(AudioStreamBasicDescription);369 AudioStreamBasicDescription *streamDescription = PaUtil_AllocateMemory(propSize);370 371 streamDescription->mSampleRate = sampleRate;372 streamDescription->mFormatID = 0;373 streamDescription->mFormatFlags = 0;374 streamDescription->mBytesPerPacket = 0;375 streamDescription->mFramesPerPacket = 0;376 streamDescription->mBytesPerFrame = 0;377 streamDescription->mChannelsPerFrame = 0;378 streamDescription->mBitsPerChannel = 0;379 streamDescription->mReserved = 0;380 381 OSStatus result = AudioDeviceGetProperty(macCoreDeviceId, 0, isInput, kAudioDevicePropertyStreamFormatSupported, &propSize, streamDescription);382 PaUtil_FreeMemory(streamDescription);383 return result;384 }385 386 static OSStatus CopyInputData(PaMacClientData* destination, const AudioBufferList *source, unsigned long frameCount)387 {388 int frameSpacing, channelSpacing;389 if (destination->inputSampleFormat & paNonInterleaved) {390 frameSpacing = 1;391 channelSpacing = destination->inputChannelCount;392 }393 else {394 frameSpacing = destination->inputChannelCount;395 channelSpacing = 1;396 }397 398 AudioBuffer const *inputBuffer = &source->mBuffers[0];399 void *coreAudioBuffer = inputBuffer->mData;400 void *portAudioBuffer = destination->inputBuffer;401 UInt32 i, streamNumber, streamChannel;402 for (i = streamNumber = streamChannel = 0; i < destination->inputChannelCount; ++i, ++streamChannel) {403 if (streamChannel >= inputBuffer->mNumberChannels) {404 ++streamNumber;405 inputBuffer = &source->mBuffers[streamNumber];406 coreAudioBuffer = inputBuffer->mData;407 streamChannel = 0;408 }409 destination->inputConverter(portAudioBuffer, frameSpacing, coreAudioBuffer, inputBuffer->mNumberChannels, frameCount, destination->ditherGenerator);410 coreAudioBuffer += sizeof(Float32);411 portAudioBuffer += Pa_GetSampleSize(destination->inputSampleFormat) * channelSpacing;412 }413 return 0;414 }415 416 static OSStatus CopyOutputData(AudioBufferList* destination, PaMacClientData *source, unsigned long frameCount)417 {418 int frameSpacing, channelSpacing;419 if (source->outputSampleFormat & paNonInterleaved) {420 frameSpacing = 1;421 channelSpacing = source->outputChannelCount;422 }423 else {424 frameSpacing = source->outputChannelCount;425 channelSpacing = 1;426 }427 428 AudioBuffer *outputBuffer = &destination->mBuffers[0];429 void *coreAudioBuffer = outputBuffer->mData;430 void *portAudioBuffer = source->outputBuffer;431 UInt32 i, streamNumber, streamChannel;432 for (i = streamNumber = streamChannel = 0; i < source->outputChannelCount; ++i, ++streamChannel) {433 if (streamChannel >= outputBuffer->mNumberChannels) {434 ++streamNumber;435 outputBuffer = &destination->mBuffers[streamNumber];436 coreAudioBuffer = outputBuffer->mData;437 streamChannel = 0;438 }439 source->outputConverter(coreAudioBuffer, outputBuffer->mNumberChannels, portAudioBuffer, frameSpacing, frameCount, NULL);440 coreAudioBuffer += sizeof(Float32);441 portAudioBuffer += Pa_GetSampleSize(source->outputSampleFormat) * channelSpacing;442 }443 return 0;444 }445 446 static OSStatus AudioIOProc( AudioDeviceID inDevice,447 const AudioTimeStamp* inNow,448 const AudioBufferList* inInputData,449 const AudioTimeStamp* inInputTime,450 AudioBufferList* outOutputData,451 const AudioTimeStamp* inOutputTime,452 void* inClientData)453 {454 PaMacClientData *clientData = (PaMacClientData *)inClientData;455 PaStreamCallbackTimeInfo *timeInfo = InitializeTimeInfo(inNow, inInputTime, inOutputTime);456 457 PaUtil_BeginCpuLoadMeasurement( &clientData->stream->cpuLoadMeasurer );458 459 AudioBuffer *outputBuffer = &outOutputData->mBuffers[0];460 unsigned long frameCount = outputBuffer->mDataByteSize / (outputBuffer->mNumberChannels * sizeof(Float32));461 462 if (clientData->inputBuffer) {463 CopyInputData(clientData, inInputData, frameCount);464 }465 PaStreamCallbackResult result = clientData->callback(clientData->inputBuffer, clientData->outputBuffer, frameCount, timeInfo, paNoFlag, clientData->userData);466 if (clientData->outputBuffer) {467 CopyOutputData(outOutputData, clientData, frameCount);468 }469 470 PaUtil_EndCpuLoadMeasurement( &clientData->stream->cpuLoadMeasurer, frameCount );471 472 if (result == paComplete || result == paAbort) {473 Pa_StopStream(clientData->stream);474 }475 return 0;476 }477 478 // This is not for input-only streams, this is for streams where the input device is different from the output device479 // TODO: This needs to store the output data in a buffer, to be written to the device the next time AudioOutputProc is called480 static OSStatus AudioInputProc( AudioDeviceID inDevice,481 const AudioTimeStamp* inNow,482 const AudioBufferList* inInputData,483 const AudioTimeStamp* inInputTime,484 AudioBufferList* outOutputData,485 const AudioTimeStamp* inOutputTime,486 void* inClientData)487 {488 PaMacClientData *clientData = (PaMacClientData *)inClientData;489 PaStreamCallbackTimeInfo *timeInfo = InitializeTimeInfo(inNow, inInputTime, inOutputTime);490 491 PaUtil_BeginCpuLoadMeasurement( &clientData->stream->cpuLoadMeasurer );492 493 AudioBuffer const *inputBuffer = &inInputData->mBuffers[0];494 unsigned long frameCount = inputBuffer->mDataByteSize / (inputBuffer->mNumberChannels * sizeof(Float32));495 496 CopyInputData(clientData, inInputData, frameCount);497 clientData->callback(clientData->inputBuffer, NULL, frameCount, timeInfo, paNoFlag, clientData->userData);498 499 PaUtil_EndCpuLoadMeasurement( &clientData->stream->cpuLoadMeasurer, frameCount );500 return 0;501 }502 503 // This is not for output-only streams, this is for streams where the input device is different from the output device504 static OSStatus AudioOutputProc( AudioDeviceID inDevice,505 const AudioTimeStamp* inNow,506 const AudioBufferList* inInputData,507 const AudioTimeStamp* inInputTime,508 AudioBufferList* outOutputData,509 const AudioTimeStamp* inOutputTime,510 void* inClientData)511 {512 PaMacClientData *clientData = (PaMacClientData *)inClientData;513 PaStreamCallbackTimeInfo *timeInfo = InitializeTimeInfo(inNow, inInputTime, inOutputTime);514 515 PaUtil_BeginCpuLoadMeasurement( &clientData->stream->cpuLoadMeasurer );516 517 AudioBuffer *outputBuffer = &outOutputData->mBuffers[0];518 unsigned long frameCount = outputBuffer->mDataByteSize / (outputBuffer->mNumberChannels * sizeof(Float32));519 520 clientData->callback(NULL, clientData->outputBuffer, frameCount, timeInfo, paNoFlag, clientData->userData);521 522 CopyOutputData(outOutputData, clientData, frameCount);523 524 PaUtil_EndCpuLoadMeasurement( &clientData->stream->cpuLoadMeasurer, frameCount );525 return 0;526 }527 528 static PaError SetSampleRate(AudioDeviceID device, double sampleRate, int isInput)529 {530 PaError result = paNoError;531 532 double actualSampleRate;533 UInt32 propSize = sizeof(double);534 result = conv_err(AudioDeviceSetProperty(device, NULL, 0, isInput, kAudioDevicePropertyNominalSampleRate, propSize, &sampleRate));535 536 result = conv_err(AudioDeviceGetProperty(device, 0, isInput, kAudioDevicePropertyNominalSampleRate, &propSize, &actualSampleRate));537 538 if (result == paNoError && actualSampleRate != sampleRate) {539 result = paInvalidSampleRate;540 }541 542 return result;543 }544 545 static PaError SetFramesPerBuffer(AudioDeviceID device, unsigned long framesPerBuffer, int isInput)546 {547 PaError result = paNoError;548 UInt32 preferredFramesPerBuffer = framesPerBuffer;549 // while (preferredFramesPerBuffer > UINT32_MAX) {550 // preferredFramesPerBuffer /= 2;551 // }552 553 UInt32 actualFramesPerBuffer;554 UInt32 propSize = sizeof(UInt32);555 result = conv_err(AudioDeviceSetProperty(device, NULL, 0, isInput, kAudioDevicePropertyBufferFrameSize, propSize, &preferredFramesPerBuffer));556 557 result = conv_err(AudioDeviceGetProperty(device, 0, isInput, kAudioDevicePropertyBufferFrameSize, &propSize, &actualFramesPerBuffer));558 559 if (result != paNoError) {560 // do nothing561 }562 else if (actualFramesPerBuffer > framesPerBuffer) {563 result = paBufferTooSmall;564 }565 else if (actualFramesPerBuffer < framesPerBuffer) {566 result = paBufferTooBig;567 }568 569 return result;570 }571 572 static PaError SetUpUnidirectionalStream(AudioDeviceID device, double sampleRate, unsigned long framesPerBuffer, int isInput)573 {574 PaError err = paNoError;575 err = SetSampleRate(device, sampleRate, isInput);576 if( err == paNoError )577 err = SetFramesPerBuffer(device, framesPerBuffer, isInput);578 return err;579 }580 581 // ===== PortAudio functions =====582 #pragma mark PortAudio functions583 584 #ifdef __cplusplus585 extern "C"586 {587 #endif // __cplusplus588 589 PaError PaMacCore_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );590 591 #ifdef __cplusplus592 }593 #endif // __cplusplus594 481 595 482 static void Terminate( struct PaUtilHostApiRepresentation *hostApi ) 596 483 { 597 PaMacCoreHostApiRepresentation *macCoreHostApi = (PaMacCoreHostApiRepresentation*)hostApi; 598 599 CleanUp(macCoreHostApi); 484 PaMacAUHAL *auhalHostApi = (PaMacAUHAL*)hostApi; 485 486 VVDBUG(("Terminate()\n")); 487 488 /* 489 IMPLEMENT ME: 490 - clean up any resources not handled by the allocation group 491 TODO: Double check that everything is handled by alloc group 492 */ 493 494 if( auhalHostApi->allocations ) 495 { 496 PaUtil_FreeAllAllocations( auhalHostApi->allocations ); 497 PaUtil_DestroyAllocationGroup( auhalHostApi->allocations ); 498 } 499 500 PaUtil_FreeMemory( auhalHostApi ); 600 501 } 502 601 503 602 504 static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi, … … 605 507 double sampleRate ) 606 508 { 607 PaMacCoreHostApiRepresentation *macCoreHostApi = (PaMacCoreHostApiRepresentation*)hostApi; 608 PaDeviceInfo *deviceInfo; 609 610 PaError result = paNoError; 611 if (inputParameters) { 612 deviceInfo = macCoreHostApi->inheritedHostApiRep.deviceInfos[inputParameters->device]; 613 if (inputParameters->channelCount > deviceInfo->maxInputChannels) 614 result = paInvalidChannelCount; 615 else if (CheckFormat(macCoreHostApi->macCoreDeviceIds[inputParameters->device], inputParameters, sampleRate, 1) != kAudioHardwareNoError) { 616 result = paInvalidSampleRate; 617 } 618 } 619 if (outputParameters && result == paNoError) { 620 deviceInfo = macCoreHostApi->inheritedHostApiRep.deviceInfos[outputParameters->device]; 621 if (outputParameters->channelCount > deviceInfo->maxOutputChannels) 622 result = paInvalidChannelCount; 623 else if (CheckFormat(macCoreHostApi->macCoreDeviceIds[outputParameters->device], outputParameters, sampleRate, 0) != kAudioHardwareNoError) { 624 result = paInvalidSampleRate; 625 } 626 } 627 628 return result; 509 int inputChannelCount, outputChannelCount; 510 PaSampleFormat inputSampleFormat, outputSampleFormat; 511 512 VVDBUG(("IsFormatSupported(): in chan=%d, in fmt=%ld, out chan=%d, out fmt=%ld sampleRate=%g\n", 513 inputParameters ? inputParameters->channelCount : -1, 514 inputParameters ? inputParameters->sampleFormat : -1, 515 outputParameters ? outputParameters->channelCount : -1, 516 outputParameters ? outputParameters->sampleFormat : -1, 517 (float) sampleRate )); 518 519 /** These first checks are standard PA checks. We do some fancier checks 520 later. */ 521 if( inputParameters ) 522 { 523 inputChannelCount = inputParameters->channelCount; 524 inputSampleFormat = inputParameters->sampleFormat; 525 526 /* all standard sample formats are supported by the buffer adapter, 527 this implementation doesn't support any custom sample formats */ 528 if( inputSampleFormat & paCustomFormat ) 529 return paSampleFormatNotSupported; 530 531 /* unless alternate device specification is supported, reject the use of 532 paUseHostApiSpecificDeviceSpecification */ 533 534 if( inputParameters->device == paUseHostApiSpecificDeviceSpecification ) 535 return paInvalidDevice; 536 537 /* check that input device can support inputChannelCount */ 538 if( inputChannelCount > hostApi->deviceInfos[ inputParameters->device ]->maxInputChannels ) 539 return paInvalidChannelCount; 540 } 541 else 542 { 543 inputChannelCount = 0; 544 } 545 546 if( outputParameters ) 547 { 548 outputChannelCount = outputParameters->channelCount; 549 outputSampleFormat = outputParameters->sampleFormat; 550 551 /* all standard sample formats are supported by the buffer adapter, 552 this implementation doesn't support any custom sample formats */ 553 if( outputSampleFormat & paCustomFormat ) 554 return paSampleFormatNotSupported; 555 556 /* unless alternate device specification is supported, reject the use of 557 paUseHostApiSpecificDeviceSpecification */ 558 559 if( outputParameters->device == paUseHostApiSpecificDeviceSpecification ) 560 return paInvalidDevice; 561 562 /* check that output device can support outputChannelCount */ 563 if( outputChannelCount > hostApi->deviceInfos[ outputParameters->device ]->maxOutputChannels ) 564 return paInvalidChannelCount; 565 566 } 567 else 568 { 569 outputChannelCount = 0; 570 } 571 572 /* FEEDBACK */ 573 /* I think the only way to check a given format SR combo is */ 574 /* to try opening it. This could be disruptive, is that Okay? */ 575 /* The alternative is to just read off available sample rates, */ 576 /* but this will not work %100 of the time (eg, a device that */ 577 /* supports N output at one rate but only N/2 at a higher rate.)*/ 578 579 /* The following code opens the device with the requested parameters to 580 see if it works. */ 581 { 582 PaError err; 583 PaStream *s; 584 err = OpenStream( hostApi, &s, inputParameters, outputParameters, 585 sampleRate, 1024, 0, (PaStreamCallback *)1, NULL ); 586 if( err != paNoError && err != paInvalidSampleRate ) 587 DBUG( ( "OpenStream @ %g returned: %d: %s\n", 588 (float) sampleRate, err, Pa_GetErrorText( err ) ) ); 589 if( err ) 590 return err; 591 err = CloseStream( s ); 592 if( err ) { 593 /* FEEDBACK: is this more serious? should we assert? */ 594 DBUG( ( "WARNING: could not close Stream. %d: %s\n", 595 err, Pa_GetErrorText( err ) ) ); 596 } 597 } 598 599 return paFormatIsSupported; 629 600 } 630 601 602 static PaError OpenAndSetupOneAudioUnit( 603 const PaStreamParameters *inStreamParams, 604 const PaStreamParameters *outStreamParams, 605 const unsigned long requestedFramesPerBuffer, 606 unsigned long *actualInputFramesPerBuffer, 607 unsigned long *actualOutputFramesPerBuffer, 608 const PaMacAUHAL *auhalHostApi, 609 AudioUnit *audioUnit, 610 AudioConverterRef *srConverter, 611 AudioDeviceID *audioDevice, 612 const double sampleRate, 613 void *refCon ) 614 { 615 ComponentDescription desc; 616 Component comp; 617 /*An Apple TN suggests using CAStreamBasicDescription, but that is C++*/ 618 AudioStreamBasicDescription desiredFormat; 619 OSErr result = noErr; 620 PaError paResult = paNoError; 621 int line = 0; 622 UInt32 callbackKey; 623 AURenderCallbackStruct rcbs; 624 unsigned long macInputStreamFlags = paMacCorePlayNice; 625 unsigned long macOutputStreamFlags = paMacCorePlayNice; 626 627 VVDBUG(("OpenAndSetupOneAudioUnit(): in chan=%d, in fmt=%ld, out chan=%d, out fmt=%ld, requestedFramesPerBuffer=%ld\n", 628 inStreamParams ? inStreamParams->channelCount : -1, 629 inStreamParams ? inStreamParams->sampleFormat : -1, 630 outStreamParams ? outStreamParams->channelCount : -1, 631 outStreamParams ? outStreamParams->sampleFormat : -1, 632 requestedFramesPerBuffer )); 633 634 /* -- handle the degenerate case -- */ 635 if( !inStreamParams && !outStreamParams ) { 636 *audioUnit = NULL; 637 *audioDevice = kAudioDeviceUnknown; 638 return paNoError; 639 } 640 641 /* -- get the user's api specific info, if they set any -- */ 642 if( inStreamParams && inStreamParams->hostApiSpecificStreamInfo ) 643 macInputStreamFlags= 644 ((paMacCoreStreamInfo*)inStreamParams->hostApiSpecificStreamInfo) 645 ->flags; 646 if( outStreamParams && outStreamParams->hostApiSpecificStreamInfo ) 647 macOutputStreamFlags= 648 ((paMacCoreStreamInfo*)outStreamParams->hostApiSpecificStreamInfo) 649 ->flags; 650 /* Override user's flags here, if desired for testing. */ 651 652 /* 653 * The HAL AU is a Mac OS style "component". 654 * the first few steps deal with that. 655 * Later steps work on a combination of Mac OS 656 * components and the slightly lower level 657 * HAL. 658 */ 659 660 /* -- describe the output type AudioUnit -- */ 661 /* Note: for the default AudioUnit, we could use the 662 * componentSubType value kAudioUnitSubType_DefaultOutput; 663 * but I don't think that's relevant here. 664 */ 665 desc.componentType = kAudioUnitType_Output; 666 desc.componentSubType = kAudioUnitSubType_HALOutput; 667 desc.componentManufacturer = kAudioUnitManufacturer_Apple; 668 desc.componentFlags = 0; 669 desc.componentFlagsMask = 0; 670 /* -- find the component -- */ 671 comp = FindNextComponent( NULL, &desc ); 672 if( !comp ) 673 { 674 DBUG( ( "AUHAL component not found." ) ); 675 *audioUnit = NULL; 676 *audioDevice = kAudioDeviceUnknown; 677 return paUnanticipatedHostError; 678 } 679 /* -- open it -- */ 680 result = OpenAComponent( comp, audioUnit ); 681 if( result ) 682 { 683 DBUG( ( "Failed to open AUHAL component." ) ); 684 *audioUnit = NULL; 685 *audioDevice = kAudioDeviceUnknown; 686 return ERR( result ); 687 } 688 /* -- prepare a little error handling logic / hackery -- */ 689 #define ERR_WRAP(mac_err) do { result = mac_err ; line = __LINE__ ; if ( result != noErr ) goto error ; } while(0) 690 691 /* -- if there is input, we have to explicitly enable input -- */ 692 if( inStreamParams ) 693 { 694 UInt32 enableIO; 695 enableIO = 1; 696 ERR_WRAP( AudioUnitSetProperty( *audioUnit, 697 kAudioOutputUnitProperty_EnableIO, 698 kAudioUnitScope_Input, 699 INPUT_ELEMENT, 700 &enableIO, 701 sizeof(enableIO) ) ); 702 } 703 /* -- if there is no output, we must explicitly disable output -- */ 704 if( !outStreamParams ) 705 { 706 UInt32 enableIO; 707 enableIO = 0; 708 ERR_WRAP( AudioUnitSetProperty( *audioUnit, 709 kAudioOutputUnitProperty_EnableIO, 710 kAudioUnitScope_Output, 711 OUTPUT_ELEMENT, 712 &enableIO, 713 sizeof(enableIO) ) ); 714 } 715 /* -- set the devices -- */ 716 /* make sure input and output are the same device if we are doing input and 717 output. */ 718 if( inStreamParams && outStreamParams ) 719 assert( outStreamParams->device == inStreamParams->device ); 720 if( inStreamParams ) 721 { 722 *audioDevice = auhalHostApi->devIds[inStreamParams->device] ; 723 ERR_WRAP( AudioUnitSetProperty( *audioUnit, 724 kAudioOutputUnitProperty_CurrentDevice, 725 kAudioUnitScope_Global, 726 INPUT_ELEMENT, 727 audioDevice, 728 sizeof(AudioDeviceID) ) ); 729 } 730 if( outStreamParams ) 731 { 732 *audioDevice = auhalHostApi->devIds[outStreamParams->device] ; 733 ERR_WRAP( AudioUnitSetProperty( *audioUnit, 734 kAudioOutputUnitProperty_CurrentDevice, 735 kAudioUnitScope_Global, 736 OUTPUT_ELEMENT, 737 audioDevice, 738 sizeof(AudioDeviceID) ) ); 739 } 740 741 /* -- set format -- */ 742 bzero( &desiredFormat, sizeof(desiredFormat) ); 743 desiredFormat.mFormatID = kAudioFormatLinearPCM ; 744 desiredFormat.mFormatFlags = kAudioFormatFlagsNativeFloatPacked; 745 desiredFormat.mFramesPerPacket = 1; 746 desiredFormat.mBitsPerChannel = sizeof( float ) * 8; 747 748 result = 0; 749 /* set device format first, but only touch the device if the user asked */ 750 if( inStreamParams ) { 751 /*The callback never calls back if we don't set the FPB */ 752 /*This seems wierd, because I would think setting anything on the device 753 would be disruptive.*/ 754 paResult = setBestFramesPerBuffer( *audioDevice, FALSE, 755 requestedFramesPerBuffer, 756 actualInputFramesPerBuffer ); 757 if( paResult ) goto error; 758 if( macInputStreamFlags & paMacCore_ChangeDeviceParameters ) { 759 bool requireExact; 760 requireExact=macInputStreamFlags&paMacCore_FailIfConversionRequired; 761 paResult = setBestSampleRateForDevice( *audioDevice, FALSE, 762 requireExact, sampleRate ); 763 if( paResult ) goto error; 764 } 765 if( actualInputFramesPerBuffer && actualOutputFramesPerBuffer ) 766 *actualOutputFramesPerBuffer = *actualInputFramesPerBuffer ; 767 } 768 if( outStreamParams && !inStreamParams ) { 769 /*The callback never calls back if we don't set the FPB */ 770 /*This seems wierd, because I would think setting anything on the device 771 would be disruptive.*/ 772 paResult = setBestFramesPerBuffer( *audioDevice, TRUE, 773 requestedFramesPerBuffer, 774 actualOutputFramesPerBuffer ); 775 if( paResult ) goto error; 776 if( macOutputStreamFlags & paMacCore_ChangeDeviceParameters ) { 777 bool requireExact; 778 requireExact=macOutputStreamFlags&paMacCore_FailIfConversionRequired; 779 paResult = setBestSampleRateForDevice( *audioDevice, TRUE, 780 requireExact, sampleRate ); 781 if( paResult ) goto error; 782 } 783 } 784 785 /* -- set the quality of the output converter -- */ 786 if( outStreamParams ) { 787 UInt32 value = kAudioConverterQuality_Max; 788 switch( macOutputStreamFlags & 0x0700 ) { 789 case 0x0100: /*paMacCore_ConversionQualityMin:*/ 790 value=kRenderQuality_Min; 791 break; 792 case 0x0200: /*paMacCore_ConversionQualityLow:*/ 793 value=kRenderQuality_Low; 794 break; 795 case 0x0300: /*paMacCore_ConversionQualityMedium:*/ 796 value=kRenderQuality_Medium; 797 break; 798 case 0x0400: /*paMacCore_ConversionQualityHigh:*/ 799 value=kRenderQuality_High; 800 break; 801 } 802 ERR_WRAP( AudioUnitSetProperty( *audioUnit, 803 kAudioUnitProperty_RenderQuality, 804 kAudioUnitScope_Global, 805 OUTPUT_ELEMENT, 806 &value, 807 sizeof(value) ) ); 808 } 809 /* now set the format on the Audio Units. */ 810 if( outStreamParams ) 811 { 812 desiredFormat.mSampleRate =sampleRate; 813 desiredFormat.mBytesPerPacket=sizeof(float)*outStreamParams->channelCount; 814 desiredFormat.mBytesPerFrame =sizeof(float)*outStreamParams->channelCount; 815 desiredFormat.mChannelsPerFrame = outStreamParams->channelCount; 816 ERR_WRAP( AudioUnitSetProperty( *audioUnit, 817 kAudioUnitProperty_StreamFormat, 818 kAudioUnitScope_Input, 819 OUTPUT_ELEMENT, 820 &desiredFormat, 821 sizeof(AudioStreamBasicDescription) ) ); 822 } 823 if( inStreamParams ) 824 { 825 AudioStreamBasicDescription sourceFormat; 826 UInt32 size = sizeof( AudioStreamBasicDescription ); 827 828 /* keep the sample rate of the device, or we confuse AUHAL */ 829 ERR_WRAP( AudioUnitGetProperty( *audioUnit, 830 kAudioUnitProperty_StreamFormat, 831 kAudioUnitScope_Input, 832 INPUT_ELEMENT, 833 &sourceFormat, 834 &size ) ); 835 desiredFormat.mSampleRate = sourceFormat.mSampleRate; 836 desiredFormat.mBytesPerPacket=sizeof(float)*inStreamParams->channelCount; 837 desiredFormat.mBytesPerFrame =sizeof(float)*inStreamParams->channelCount; 838 desiredFormat.mChannelsPerFrame = inStreamParams->channelCount; 839 ERR_WRAP( AudioUnitSetProperty( *audioUnit, 840 kAudioUnitProperty_StreamFormat, 841 kAudioUnitScope_Output, 842 INPUT_ELEMENT, 843 &desiredFormat, 844 sizeof(AudioStreamBasicDescription) ) ); 845 } 846 /* set the maximumFramesPerSlice */ 847 /* not doing this causes real problems 848 (eg. the callback might not be called). The idea of setting both this 849 and the frames per buffer on the device is that we'll be most likely 850 to actually get the frame size we requested in the callback with the 851 minimum latency. */ 852 if( outStreamParams ) { 853 UInt32 size = sizeof( *actualOutputFramesPerBuffer ); 854 ERR_WRAP( AudioUnitSetProperty( *audioUnit, 855 kAudioUnitProperty_MaximumFramesPerSlice, 856 kAudioUnitScope_Input, 857 OUTPUT_ELEMENT, 858 actualOutputFramesPerBuffer, 859 sizeof(unsigned long) ) ); 860 ERR_WRAP( AudioUnitGetProperty( *audioUnit, 861 kAudioUnitProperty_MaximumFramesPerSlice, 862 kAudioUnitScope_Global, 863 OUTPUT_ELEMENT, 864 actualOutputFramesPerBuffer, 865 &size ) ); 866 } 867 if( inStreamParams ) { 868 /*UInt32 size = sizeof( *actualInputFramesPerBuffer );*/ 869 ERR_WRAP( AudioUnitSetProperty( *audioUnit, 870 kAudioUnitProperty_MaximumFramesPerSlice, 871 kAudioUnitScope_Output, 872 INPUT_ELEMENT, 873 actualInputFramesPerBuffer, 874 sizeof(unsigned long) ) ); 875 /* Don't know why this causes problems 876 ERR_WRAP( AudioUnitGetProperty( *audioUnit, 877 kAudioUnitProperty_MaximumFramesPerSlice, 878 kAudioUnitScope_Global, //Output, 879 INPUT_ELEMENT, 880 actualInputFramesPerBuffer, 881 &size ) ); 882 */ 883 } 884 885 /* -- if we have input, we may need to setup an SR converter -- */ 886 /* even if we got the sample rate we asked for, we need to do 887 the conversion in case another program changes the underlying SR. */ 888 /* FIXME: I think we need to monitor stream and change the converter if the incoming format changes. */ 889 if( inStreamParams ) { 890 AudioStreamBasicDescription desiredFormat; 891 AudioStreamBasicDescription sourceFormat; 892 UInt32 sourceSize = sizeof( sourceFormat ); 893 bzero( &desiredFormat, sizeof(desiredFormat) ); 894 desiredFormat.mSampleRate = sampleRate; 895 desiredFormat.mFormatID = kAudioFormatLinearPCM ; 896 desiredFormat.mFormatFlags = kAudioFormatFlagsNativeFloatPacked; 897 desiredFormat.mFramesPerPacket = 1; 898 desiredFormat.mBitsPerChannel = sizeof( float ) * 8; 899 desiredFormat.mBytesPerPacket=sizeof(float)*inStreamParams->channelCount; 900 desiredFormat.mBytesPerFrame =sizeof(float)*inStreamParams->channelCount; 901 desiredFormat.mChannelsPerFrame = inStreamParams->channelCount; 902 903 /* get the source format */ 904 ERR_WRAP( AudioUnitGetProperty( 905 *audioUnit, 906 kAudioUnitProperty_StreamFormat, 907 kAudioUnitScope_Output, 908 INPUT_ELEMENT, 909 &sourceFormat, 910 &sourceSize ) ); 911 912 if( desiredFormat.mSampleRate != sourceFormat.mSampleRate ) 913 { 914 UInt32 value = kAudioConverterQuality_Max; 915 switch( macInputStreamFlags & 0x0700 ) { 916 case 0x0100: /*paMacCore_ConversionQualityMin:*/ 917 value=kAudioConverterQuality_Min; 918 break; 919 case 0x0200: /*paMacCore_ConversionQualityLow:*/ 920 value=kAudioConverterQuality_Low; 921 break; 922 case 0x0300: /*paMacCore_ConversionQualityMedium:*/ 923 value=kAudioConverterQuality_Medium; 924 break; 925 case 0x0400: /*paMacCore_ConversionQualityHigh:*/ 926 value=kAudioConverterQuality_High; 927 break; 928 } 929 VDBUG(( "Creating sample rate converter for input" 930 " to convert from %g to %g\n", 931 (float)sourceFormat.mSampleRate, 932 (float)desiredFormat.mSampleRate ) ); 933 /* create our converter */ 934 ERR_WRAP( AudioConverterNew( 935 &sourceFormat, 936 &desiredFormat, 937 srConverter ) ); 938 /* Set quality */ 939 ERR_WRAP( AudioConverterSetProperty( 940 *srConverter, 941 kAudioConverterSampleRateConverterQuality, 942 sizeof( value ), 943 &value ) ); 944 } 945 } 946 /* -- set IOProc (callback) -- */ 947 callbackKey = outStreamParams ? kAudioUnitProperty_SetRenderCallback 948 : kAudioOutputUnitProperty_SetInputCallback ; 949 rcbs.inputProc = AudioIOProc; 950 rcbs.inputProcRefCon = refCon; 951 ERR_WRAP( AudioUnitSetProperty( 952 *audioUnit, 953 callbackKey, 954 kAudioUnitScope_Output, 955 outStreamParams ? OUTPUT_ELEMENT : INPUT_ELEMENT, 956 &rcbs, 957 sizeof(rcbs)) ); 958 959 if( inStreamParams && outStreamParams && *srConverter ) 960 ERR_WRAP( AudioUnitSetProperty( 961 *audioUnit, 962 kAudioOutputUnitProperty_SetInputCallback, 963 kAudioUnitScope_Output, 964 INPUT_ELEMENT, 965 &rcbs, 966 sizeof(rcbs)) ); 967 968 /*IMPLEMENTME: may need to worry about channel mapping.*/ 969 970 /* initialize the audio unit */ 971 ERR_WRAP( AudioUnitInitialize(*audioUnit) ); 972 973 if( inStreamParams && outStreamParams ) 974 VDBUG( ("Opened device %ld for input and output.\n", *audioDevice ) ); 975 else if( inStreamParams ) 976 VDBUG( ("Opened device %ld for input.\n", *audioDevice ) ); 977 else if( outStreamParams ) 978 VDBUG( ("Opened device %ld for output.\n", *audioDevice ) ); 979 return paNoError; 980 #undef ERR_WRAP 981 982 error: 983 CloseComponent( *audioUnit ); 984 *audioUnit = NULL; 985 if( result ) 986 return PaMacCore_SetError( result, line, 1 ); 987 return paResult; 988 } 989 990 /* see pa_hostapi.h for a list of validity guarantees made about OpenStream parameters */ 631 991 static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, 632 992 PaStream** s, … … 639 999 void *userData ) 640 1000 { 641 PaError err = paNoError; 642 PaMacCoreHostApiRepresentation *macCoreHostApi = (PaMacCoreHostApiRepresentation *)hostApi; 643 PaMacCoreStream *stream = PaUtil_AllocateMemory(sizeof(PaMacCoreStream)); 644 stream->isActive = 0; 645 stream->isStopped = 1; 646 stream->inputDevice = kAudioDeviceUnknown; 647 stream->outputDevice = kAudioDeviceUnknown; 648 649 PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation, 650 ( (streamCallback) 651 ? &macCoreHostApi->callbackStreamInterface 652 : &macCoreHostApi->blockingStreamInterface ), 653 streamCallback, userData ); 1001 PaError result = paNoError; 1002 PaMacAUHAL *auhalHostApi = (PaMacAUHAL*)hostApi; 1003 PaMacCoreStream *stream = 0; 1004 int inputChannelCount, outputChannelCount; 1005 PaSampleFormat inputSampleFormat, outputSampleFormat; 1006 PaSampleFormat hostInputSampleFormat, hostOutputSampleFormat; 1007 VVDBUG(("OpenStream(): in chan=%d, in fmt=%ld, out chan=%d, out fmt=%ld SR=%g, FPB=%ld\n", 1008 inputParameters ? inputParameters->channelCount : -1, 1009 inputParameters ? inputParameters->sampleFormat : -1, 1010 outputParameters ? outputParameters->channelCount : -1, 1011 outputParameters ? outputParameters->sampleFormat : -1, 1012 (float) sampleRate, 1013 framesPerBuffer )); 1014 VDBUG( ("Opening Stream.\n") ); 1015 1016 /*These first few bits of code are from paSkeleton with few modifications.*/ 1017 if( inputParameters ) 1018 { 1019 inputChannelCount = inputParameters->channelCount; 1020 inputSampleFormat = inputParameters->sampleFormat; 1021 1022 /* unless alternate device specification is supported, reject the use of 1023 paUseHostApiSpecificDeviceSpecification */ 1024 1025 if( inputParameters->device == paUseHostApiSpecificDeviceSpecification ) 1026 return paInvalidDevice; 1027 1028 /* check that input device can support inputChannelCount */ 1029 if( inputChannelCount > hostApi->deviceInfos[ inputParameters->device ]->maxInputChannels ) 1030 return paInvalidChannelCount; 1031 1032 /* Host supports interleaved float32 */ 1033 hostInputSampleFormat = paFloat32; 1034 } 1035 else 1036 { 1037 inputChannelCount = 0; 1038 inputSampleFormat = hostInputSampleFormat = paFloat32; /* Surpress 'uninitialised var' warnings. */ 1039 } 1040 1041 if( outputParameters ) 1042 { 1043 outputChannelCount = outputParameters->channelCount; 1044 outputSampleFormat = outputParameters->sampleFormat; 1045 1046 /* unless alternate device specification is supported, reject the use of 1047 paUseHostApiSpecificDeviceSpecification */ 1048 1049 if( outputParameters->device == paUseHostApiSpecificDeviceSpecification ) 1050 return paInvalidDevice; 1051 1052 /* check that output device can support inputChannelCount */ 1053 if( outputChannelCount > hostApi->deviceInfos[ outputParameters->device ]->maxOutputChannels ) 1054 return paInvalidChannelCount; 1055 1056 /* Host supports interleaved float32 */ 1057 hostOutputSampleFormat = paFloat32; 1058 } 1059 else 1060 { 1061 outputChannelCount = 0; 1062 outputSampleFormat = hostOutputSampleFormat = paFloat32; /* Surpress 'uninitialized var' warnings. */ 1063 } 1064 1065 /* validate platform specific flags */ 1066 if( (streamFlags & paPlatformSpecificFlags) != 0 ) 1067 return paInvalidFlag; /* unexpected platform specific flag */ 1068 1069 stream = (PaMacCoreStream*)PaUtil_AllocateMemory( sizeof(PaMacCoreStream) ); 1070 if( !stream ) 1071 { 1072 result = paInsufficientMemory; 1073 goto error; 1074 } 1075 1076 /* If we fail after this point, we my be left in a bad state, with 1077 some data structures setup and others not. So, first thing we 1078 do is initialize everything so that if we fail, we know what hasn't 1079 been touched. 1080 */ 1081 1082 stream->inputAudioBufferList.mBuffers[0].mData = NULL; 1083 stream->inputRingBuffer.buffer = NULL; 1084 bzero( &stream->blio, sizeof( PaMacBlio ) ); 1085 /* 1086 stream->blio.inputRingBuffer.buffer = NULL; 1087 stream->blio.outputRingBuffer.buffer = NULL; 1088 stream->blio.inputSampleFormat = inputParameters?inputParameters->sampleFormat:0; 1089 stream->blio.inputSampleSize = computeSampleSizeFromFormat(stream->blio.inputSampleFormat); 1090 stream->blio.outputSampleFormat=outputParameters?outputParameters->sampleFormat:0; 1091 stream->blio.outputSampleSize = computeSampleSizeFromFormat(stream->blio.outputSampleFormat); 1092 */ 1093 stream->inputSRConverter = NULL; 1094 stream->inputUnit = NULL; 1095 stream->outputUnit = NULL; 1096 stream->inputFramesPerBuffer = 0; 1097 stream->outputFramesPerBuffer = 0; 1098 stream->bufferProcessorIsInitialized = FALSE; 1099 1100 /* assert( streamCallback ) ; */ /* only callback mode is implemented */ 1101 if( streamCallback ) 1102 { 1103 PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation, 1104 &auhalHostApi->callbackStreamInterface, 1105 streamCallback, userData ); 1106 } 1107 else 1108 { 1109 PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation, 1110 &auhalHostApi->blockingStreamInterface, 1111 BlioCallback, &stream->blio ); 1112 } 1113 654 1114 PaUtil_InitializeCpuLoadMeasurer( &stream->cpuLoadMeasurer, sampleRate ); 655 1115 1116 /* -- handle paFramesPerBufferUnspecified -- */ 1117 if( framesPerBuffer == paFramesPerBufferUnspecified ) { 1118 long requested = 64; 1119 if( inputParameters ) 1120 requested = MAX( requested, inputParameters->suggestedLatency * sampleRate / 2 ); 1121 if( outputParameters ) 1122 requested = MAX( requested, outputParameters->suggestedLatency *sampleRate / 2 ); 1123 VDBUG( ("Block Size unspecified. Based on Latency, the user wants a Block Size near: %ld.\n", 1124 requested ) ); 1125 if( requested <= 64 ) { 1126 /*requested a realtively low latency. make sure this is in range of devices */ 1127 /*try to get the device's min natural buffer size and use that (but no smaller than 64).*/ 1128 AudioValueRange audioRange; 1129 size_t size = sizeof( audioRange ); 1130 if( inputParameters ) { 1131 WARNING( result = AudioDeviceGetProperty( auhalHostApi->devIds[inputParameters->device], 1132 0, 1133 false, 1134 kAudioDevicePropertyBufferFrameSizeRange, 1135 &size, &audioRange ) ); 1136 if( result ) 1137 requested = MAX( requested, audioRange.mMinimum ); 1138 } 1139 if( outputParameters ) { 1140 WARNING( result = AudioDeviceGetProperty( auhalHostApi->devIds[outputParameters->device], 1141 0, 1142 false, 1143 kAudioDevicePropertyBufferFrameSizeRange, 1144 &size, &audioRange ) ); 1145 if( result ) 1146 requested = MAX( requested, audioRange.mMinimum ); 1147 } 1148 } else { 1149 /* requested a realtively high latency. make sure this is in range of devices */ 1150 /*try to get the device's max natural buffer size and use that (but no larger than 1024).*/ 1151 AudioValueRange audioRange; 1152 size_t size = sizeof( audioRange ); 1153 requested = MIN( requested, 1024 ); 1154 if( inputParameters ) { 1155 WARNING( result = AudioDeviceGetProperty( auhalHostApi->devIds[inputParameters->device], 1156 0, 1157 false, 1158 kAudioDevicePropertyBufferFrameSizeRange, 1159 &size, &audioRange ) ); 1160 if( result ) 1161 requested = MIN( requested, audioRange.mMaximum ); 1162 } 1163 if( outputParameters ) { 1164 WARNING( result = AudioDeviceGetProperty( auhalHostApi->devIds[outputParameters->device], 1165 0, 1166 false, 1167 kAudioDevicePropertyBufferFrameSizeRange, 1168 &size, &audioRange ) ); 1169 if( result ) 1170 requested = MIN( requested, audioRange.mMaximum ); 1171 } 1172 } 1173 /* -- double check ranges -- */ 1174 if( requested > 1024 ) requested = 1024; 1175 if( requested < 64 ) requested = 64; 1176 VDBUG(("After querying hardware, setting block size to %ld.\n", requested)); 1177 framesPerBuffer = requested; 1178 } 1179 1180 /* -- Now we actually open and setup streams. -- */ 1181 if( inputParameters && outputParameters && outputParameters->device == inputParameters->device ) 1182 { /* full duplex. One device. */ 1183 result = OpenAndSetupOneAudioUnit( inputParameters, 1184 outputParameters, 1185 framesPerBuffer, 1186 &(stream->inputFramesPerBuffer), 1187 &(stream->outputFramesPerBuffer), 1188 auhalHostApi, 1189 &(stream->inputUnit), 1190 &(stream->inputSRConverter), 1191 &(stream->inputDevice), 1192 sampleRate, 1193 stream ); 1194 stream->outputUnit = stream->inputUnit; 1195 stream->outputDevice = stream->inputDevice; 1196 if( result != paNoError ) 1197 goto error; 1198 } 1199 else 1200 { /* full duplex, different devices OR simplex */ 1201 result = OpenAndSetupOneAudioUnit( NULL, 1202 outputParameters, 1203 framesPerBuffer, 1204 NULL, 1205 &(stream->outputFramesPerBuffer), 1206 auhalHostApi, 1207 &(stream->outputUnit), 1208 NULL, 1209 &(stream->outputDevice), 1210 sampleRate, 1211 stream ); 1212 if( result != paNoError ) 1213 goto error; 1214 result = OpenAndSetupOneAudioUnit( inputParameters, 1215 NULL, 1216 framesPerBuffer, 1217 &(stream->inputFramesPerBuffer), 1218 NULL, 1219 auhalHostApi, 1220 &(stream->inputUnit), 1221 &(stream->inputSRConverter), 1222 &(stream->inputDevice), 1223 sampleRate, 1224 stream ); 1225 if( result != paNoError ) 1226 goto error; 1227 } 1228 1229 if( stream->inputUnit ) { 1230 const size_t szfl = sizeof(float); 1231 /* setup the AudioBufferList used for input */ 1232 bzero( &stream->inputAudioBufferList, sizeof( AudioBufferList ) ); 1233 stream->inputAudioBufferList.mNumberBuffers = 1; 1234 stream->inputAudioBufferList.mBuffers[0].mNumberChannels 1235 = inputChannelCount; 1236 stream->inputAudioBufferList.mBuffers[0].mDataByteSize 1237 = stream->inputFramesPerBuffer*inputChannelCount*szfl; 1238 stream->inputAudioBufferList.mBuffers[0].mData 1239 = (float *) calloc( 1240 stream->inputFramesPerBuffer*inputChannelCount, 1241 szfl ); 1242 if( !stream->inputAudioBufferList.mBuffers[0].mData ) 1243 { 1244 result = paInsufficientMemory; 1245 goto error; 1246 } 1247 1248 /* 1249 * If input and output devs are different or we are doing SR conversion, 1250 * we also need a 1251 * ring buffer to store inpt data while waiting for output 1252 * data. 1253 */ 1254 if( (stream->outputUnit && stream->inputUnit != stream->outputUnit) 1255 || stream->inputSRConverter ) 1256 { 1257 /* May want the ringSize ot initial position in 1258 ring buffer to depend somewhat on sample rate change */ 1259 1260 void *data; 1261 long ringSize; 1262 1263 ringSize = computeRingBufferSize( inputParameters, 1264 outputParameters, 1265 stream->inputFramesPerBuffer, 1266 stream->outputFramesPerBuffer, 1267 sampleRate ); 1268 /*ringSize <<= 4; *//*16x bigger, for testing */ 1269 1270 1271 /*now, we need to allocate memory for the ring buffer*/ 1272 data = calloc( ringSize, szfl ); 1273 if( !data ) 1274 { 1275 result = paInsufficientMemory; 1276 goto error; 1277 } 1278 1279 /* now we can initialize the ring buffer */ 1280 assert( 0 == 1281 RingBuffer_Init( &stream->inputRingBuffer, 1282 ringSize*szfl, data ) ); 1283 /* advance the read point a little, so we are reading from the 1284 middle of the buffer */ 1285 if( stream->outputUnit ) 1286 RingBuffer_AdvanceWriteIndex( &stream->inputRingBuffer, ringSize*szfl / RING_BUFFER_ADVANCE_DENOMINATOR ); 1287 } 1288 } 1289 1290 /* -- initialize Blio Buffer Processors -- */ 1291 if( !streamCallback ) 1292 { 1293 long ringSize; 1294 1295 ringSize = computeRingBufferSize( inputParameters, 1296 outputParameters, 1297 stream->inputFramesPerBuffer, 1298 stream->outputFramesPerBuffer, 1299 sampleRate ); 1300 result = initializeBlioRingBuffers( &stream->blio, 1301 inputParameters?inputParameters->sampleFormat:0 , 1302 outputParameters?outputParameters->sampleFormat:0 , 1303 MAX(stream->inputFramesPerBuffer,stream->outputFramesPerBuffer), 1304 ringSize, 1305 inputParameters?inputChannelCount:0 , 1306 outputParameters?outputChannelCount:0 ) ; 1307 if( result != paNoError ) 1308 goto error; 1309 } 1310 1311 /* -- initialize Buffer Processor -- */ 1312 { 1313 unsigned long maxHostFrames = stream->inputFramesPerBuffer; 1314 if( stream->outputFramesPerBuffer > maxHostFrames ) 1315 maxHostFrames = stream->outputFramesPerBuffer; 1316 result = PaUtil_InitializeBufferProcessor( &stream->bufferProcessor, 1317 inputChannelCount, inputSampleFormat, 1318 hostInputSampleFormat, 1319 outputChannelCount, outputSampleFormat, 1320 hostOutputSampleFormat, 1321 sampleRate, 1322 streamFlags, 1323 framesPerBuffer, 1324 /* If sample rate conversion takes place, the buffer size 1325 will not be known. */ 1326 maxHostFrames, 1327 stream->inputSRConverter 1328 ? paUtilUnknownHostBufferSize 1329 : paUtilBoundedHostBufferSize, 1330 streamCallback ? streamCallback : BlioCallback, 1331 streamCallback ? userData : &stream->blio ); 1332 if( result != paNoError ) 1333 goto error; 1334 } 1335 stream->bufferProcessorIsInitialized = TRUE; 1336 1337 /* 1338 IMPLEMENT ME: initialise the following fields with estimated or actual 1339 values. 1340 I think this is okay the way it is br 12/1/05 1341 maybe need to change input latency estimate if IO devs differ 1342 */ 1343 stream->streamRepresentation.streamInfo.inputLatency = 1344 PaUtil_GetBufferProcessorInputLatency(&stream->bufferProcessor)/sampleRate; 1345 stream->streamRepresentation.streamInfo.outputLatency = 1346 PaUtil_GetBufferProcessorOutputLatency(&stream->bufferProcessor)/sampleRate; 1347 stream->streamRepresentation.streamInfo.sampleRate = sampleRate; 1348 1349 stream->sampleRate = sampleRate; 1350 stream->outDeviceSampleRate = 0; 1351 if( stream->outputUnit ) { 1352 Float64 rate; 1353 UInt32 size = sizeof( rate ); 1354 result = ERR( AudioDeviceGetProperty( stream->outputDevice, 1355 0, 1356 FALSE, 1357 kAudioDevicePropertyNominalSampleRate, 1358 &size, &rate ) ); 1359 if( result ) 1360 goto error; 1361 stream->outDeviceSampleRate = rate; 1362 } 1363 stream->inDeviceSampleRate = 0; 1364 if( stream->inputUnit ) { 1365 Float64 rate; 1366 UInt32 size = sizeof( rate ); 1367 result = ERR( AudioDeviceGetProperty( stream->inputDevice, 1368 0, 1369 TRUE, 1370 kAudioDevicePropertyNominalSampleRate, 1371 &size, &rate ) ); 1372 if( result ) 1373 goto error; 1374 stream->inDeviceSampleRate = rate; 1375 } 1376 stream->userInChan = inputChannelCount; 1377 stream->userOutChan = outputChannelCount; 1378 1379 stream->isTimeSet = FALSE; 1380 stream->state = STOPPED; 1381 stream->xrunFlags = 0; 1382 656 1383 *s = (PaStream*)stream; 657 PaMacClientData *clientData = PaUtil_AllocateMemory(sizeof(PaMacClientData)); 658 clientData->stream = stream; 659 clientData->callback = streamCallback; 660 clientData->userData = userData; 661 clientData->inputBuffer = 0; 662 clientData->outputBuffer = 0; 663 clientData->ditherGenerator = PaUtil_AllocateMemory(sizeof(PaUtilTriangularDitherGenerator)); 664 PaUtil_InitializeTriangularDitherState(clientData->ditherGenerator); 665 666 if (inputParameters != NULL) { 667 stream->inputDevice = macCoreHostApi->macCoreDeviceIds[inputParameters->device]; 668 clientData->inputConverter = PaUtil_SelectConverter(paFloat32, inputParameters->sampleFormat, streamFlags); 669 clientData->inputBuffer = PaUtil_AllocateMemory(Pa_GetSampleSize(inputParameters->sampleFormat) * framesPerBuffer * inputParameters->channelCount); 670 clientData->inputChannelCount = inputParameters->channelCount; 671 clientData->inputSampleFormat = inputParameters->sampleFormat; 672 err = SetUpUnidirectionalStream(stream->inputDevice, sampleRate, framesPerBuffer, 1); 673 } 674 675 if (err == paNoError && outputParameters != NULL) { 676 stream->outputDevice = macCoreHostApi->macCoreDeviceIds[outputParameters->device]; 677 clientData->outputConverter = PaUtil_SelectConverter(outputParameters->sampleFormat, paFloat32, streamFlags); 678 clientData->outputBuffer = PaUtil_AllocateMemory(Pa_GetSampleSize(outputParameters->sampleFormat) * framesPerBuffer * outputParameters->channelCount); 679 clientData->outputChannelCount = outputParameters->channelCount; 680 clientData->outputSampleFormat = outputParameters->sampleFormat; 681 err = SetUpUnidirectionalStream(stream->outputDevice, sampleRate, framesPerBuffer, 0); 682 } 683 684 if (inputParameters == NULL || outputParameters == NULL || stream->inputDevice == stream->outputDevice) { 685 AudioDeviceID device = (inputParameters == NULL) ? stream->outputDevice : stream->inputDevice; 686 687 AudioDeviceAddIOProc(device, AudioIOProc, clientData); 688 } 689 else { 690 // using different devices for input and output 691 AudioDeviceAddIOProc(stream->inputDevice, AudioInputProc, clientData); 692 AudioDeviceAddIOProc(stream->outputDevice, AudioOutputProc, clientData); 693 } 694 695 return err; 1384 1385 return result; 1386 1387 error: 1388 CloseStream( stream ); 1389 return result; 696 1390 } 697 1391 698 // Note: When CloseStream() is called, the multi-api layer ensures that the stream has already been stopped or aborted. 1392 PaTime GetStreamTime( PaStream *s ) 1393 { 1394 /* FIXME: I am not at all sure this timing info stuff is right. 1395 patest_sine_time reports negative latencies, which is wierd.*/ 1396 PaMacCoreStream *stream = (PaMacCoreStream*)s; 1397 AudioTimeStamp timeStamp; 1398 1399 VVDBUG(("GetStreamTime()\n")); 1400 1401 if ( !stream->isTimeSet ) 1402 return (PaTime)0; 1403 1404 if ( stream->outputDevice ) { 1405 AudioDeviceGetCurrentTime( stream->outputDevice, &timeStamp); 1406 return (PaTime)(timeStamp.mSampleTime - stream->startTime.mSampleTime)/stream->outDeviceSampleRate; 1407 } else if ( stream->inputDevice ) { 1408 AudioDeviceGetCurrentTime( stream->inputDevice, &timeStamp); 1409 return (PaTime)(timeStamp.mSampleTime - stream->startTime.mSampleTime)/stream->inDeviceSampleRate; 1410 } else { 1411 return (PaTime)0; 1412 } 1413 } 1414 1415 static void setStreamStartTime( PaStream *stream ) 1416 { 1417 /* FIXME: I am not at all sure this timing info stuff is right. 1418 patest_sine_time reports negative latencies, which is wierd.*/ 1419 PaMacCoreStream *s = (PaMacCoreStream *) stream; 1420 VVDBUG(("setStreamStartTime()\n")); 1421 if( s->outputDevice ) 1422 AudioDeviceGetCurrentTime( s->outputDevice, &s->startTime); 1423 else if( s->inputDevice ) 1424 AudioDeviceGetCurrentTime( s->inputDevice, &s->startTime); 1425 else 1426 bzero( &s->startTime, sizeof( s->startTime ) ); 1427 1428 //FIXME: we need a memory barier here 1429 1430 s->isTimeSet = TRUE; 1431 } 1432 1433 1434 static PaTime TimeStampToSecs(PaMacCoreStream *stream, const AudioTimeStamp* timeStamp) 1435 { 1436 VVDBUG(("TimeStampToSecs()\n")); 1437 //printf( "ATS: %lu, %g, %g\n", timeStamp->mFlags, timeStamp->mSampleTime, timeStamp->mRateScalar ); 1438 if (timeStamp->mFlags & kAudioTimeStampSampleTimeValid) 1439 return (timeStamp->mSampleTime / stream->sampleRate); 1440 else 1441 return 0; 1442 } 1443 1444 #define RING_BUFFER_EMPTY (1000) 1445 1446 static OSStatus ringBufferIOProc( AudioConverterRef inAudioConverter, 1447 UInt32*ioDataSize, 1448 void** outData, 1449 void*inUserData ) 1450 { 1451 void *dummyData; 1452 long dummySize; 1453 RingBuffer *rb = (RingBuffer *) inUserData; 1454 1455 VVDBUG(("ringBufferIOProc()\n")); 1456 1457 assert( sizeof( UInt32 ) == sizeof( long ) ); 1458 if( RingBuffer_GetReadAvailable( rb ) == 0 ) { 1459 *outData = NULL; 1460 *ioDataSize = 0; 1461 return RING_BUFFER_EMPTY; 1462 } 1463 RingBuffer_GetReadRegions( rb, *ioDataSize, 1464 outData, (long *)ioDataSize, 1465 &dummyData, &dummySize ); 1466 1467 assert( *ioDataSize ); 1468 RingBuffer_AdvanceReadIndex( rb, *ioDataSize ); 1469 1470 return noErr; 1471 } 1472 1473 /* 1474 * Called by the AudioUnit API to process audio from the sound card. 1475 * This is where the magic happens. 1476 */ 1477 /* FEEDBACK: there is a lot of redundant code here because of how all the cases differ. This makes it hard to maintain, so if there are suggestinos for cleaning it up, I'm all ears. */ 1478 static OSStatus AudioIOProc( void *inRefCon, 1479 AudioUnitRenderActionFlags *ioActionFlags, 1480 const AudioTimeStamp *inTimeStamp, 1481 UInt32 inBusNumber, 1482 UInt32 inNumberFrames, 1483 AudioBufferList *ioData ) 1484 { 1485 unsigned long framesProcessed = 0; 1486 PaStreamCallbackTimeInfo timeInfo = {0,0,0}; 1487 PaMacCoreStream *stream = (PaMacCoreStream*)inRefCon; 1488 const bool isRender = inBusNumber == OUTPUT_ELEMENT; 1489 int callbackResult = paContinue ; 1490 1491 VVDBUG(("AudioIOProc()\n")); 1492 1493 PaUtil_BeginCpuLoadMeasurement( &stream->cpuLoadMeasurer ); 1494 1495 /* -----------------------------------------------------------------*\ 1496 This output may be useful for debugging, 1497 But printing durring the callback is a bad enough idea that 1498 this is not enabled by enableing the usual debugging calls. 1499 \* -----------------------------------------------------------------*/ 1500 /* 1501 static int renderCount = 0; 1502 static int inputCount = 0; 1503 printf( "------------------- starting reder/input\n" ); 1504 if( isRender ) 1505 printf("Render callback (%d):\t", ++renderCount); 1506 else 1507 printf("Input callback (%d):\t", ++inputCount); 1508 printf( "Call totals: %d (input), %d (render)\n", inputCount, renderCount ); 1509 1510 printf( "--- inBusNumber: %lu\n", inBusNumber ); 1511 printf( "--- inNumberFrames: %lu\n", inNumberFrames ); 1512 printf( "--- %x ioData\n", (unsigned) ioData ); 1513 if( ioData ) 1514 { 1515 int i=0; 1516 printf( "--- ioData.mNumBuffers %lu: \n", ioData->mNumberBuffers ); 1517 for( i=0; i<ioData->mNumberBuffers; ++i ) 1518 printf( "--- ioData buffer %d size: %lu.\n", i, ioData->mBuffers[i].mDataByteSize ); 1519 } 1520 ----------------------------------------------------------------- */ 1521 1522 if( !stream->isTimeSet ) 1523 setStreamStartTime( stream ); 1524 1525 if( isRender ) { 1526 AudioTimeStamp currentTime; 1527 timeInfo.outputBufferDacTime = TimeStampToSecs(stream, inTimeStamp); 1528 AudioDeviceGetCurrentTime(stream->outputDevice, ¤tTime); 1529 timeInfo.currentTime = TimeStampToSecs(stream, ¤tTime); 1530 } 1531 if( isRender && stream->inputUnit == stream->outputUnit ) 1532 timeInfo.inputBufferAdcTime = TimeStampToSecs(stream, inTimeStamp); 1533 if( !isRender ) { 1534 AudioTimeStamp currentTime; 1535 timeInfo.inputBufferAdcTime = TimeStampToSecs(stream, inTimeStamp); 1536 AudioDeviceGetCurrentTime(stream->inputDevice, ¤tTime); 1537 timeInfo.currentTime = TimeStampToSecs(stream, ¤tTime); 1538 } 1539 1540 //printf( "---%g, %g, %g\n", timeInfo.inputBufferAdcTime, timeInfo.currentTime, timeInfo.outputBufferDacTime ); 1541 1542 if( isRender && stream->inputUnit == stream->outputUnit 1543 && !stream->inputSRConverter ) 1544 { 1545 /* --------- Full Duplex, One Device, no SR Conversion ------- 1546 * 1547 * This is the lowest latency case, and also the simplest. 1548 * Input data and output data are available at the same time. 1549 * we do not use the input SR converter or the input ring buffer. 1550 * 1551 */ 1552 OSErr err = 0; 1553 unsigned long frames; 1554 1555 /* -- start processing -- */ 1556 PaUtil_BeginBufferProcessing( &(stream->bufferProcessor), 1557 &timeInfo, 1558 stream->xrunFlags ); 1559 stream->xrunFlags = 0; 1560 1561 /* -- compute frames. do some checks -- */ 1562 assert( ioData->mNumberBuffers == 1 ); 1563 assert( ioData->mBuffers[0].mNumberChannels == stream->userOutChan ); 1564 frames = ioData->mBuffers[0].mDataByteSize; 1565 frames /= sizeof( float ) * ioData->mBuffers[0].mNumberChannels; 1566 /* -- copy and process input data -- */ 1567 err= AudioUnitRender(stream->inputUnit, 1568 ioActionFlags, 1569 inTimeStamp, 1570 INPUT_ELEMENT, 1571 inNumberFrames, 1572 &stream->inputAudioBufferList ); 1573 /* FEEDBACK: I'm not sure what to do when this call fails */ 1574 assert( !err ); 1575 1576 PaUtil_SetInputFrameCount( &(stream->bufferProcessor), frames ); 1577 PaUtil_SetInterleavedInputChannels( &(stream->bufferProcessor), 1578 0, 1579 stream->inputAudioBufferList.mBuffers[0].mData, 1580 stream->inputAudioBufferList.mBuffers[0].mNumberChannels); 1581 /* -- Copy and process output data -- */ 1582 PaUtil_SetOutputFrameCount( &(stream->bufferProcessor), frames ); 1583 PaUtil_SetInterleavedOutputChannels( &(stream->bufferProcessor), 1584 0, 1585 ioData->mBuffers[0].mData, 1586 ioData->mBuffers[0].mNumberChannels); 1587 /* -- complete processing -- */ 1588 framesProcessed = 1589 PaUtil_EndBufferProcessing( &(stream->bufferProcessor), 1590 &callbackResult ); 1591 } 1592 else if( isRender ) 1593 { 1594 /* -------- Output Side of Full Duplex (Separate Devices or SR Conversion) 1595 * -- OR Simplex Output 1596 * 1597 * This case handles output data as in the full duplex case, 1598 * and, if there is input data, reads it off the ring buffer 1599 * and into the PA buffer processor. If sample rate conversion 1600 * is required on input, that is done here as well. 1601 */ 1602 unsigned long frames; 1603 1604 /* Sometimes, when stopping a duplex stream we get erroneous 1605 xrun flags, so if this is our last run, clear the flags. */ 1606 int xrunFlags = stream->xrunFlags; 1607 /* 1608 if( xrunFlags & paInputUnderflow ) 1609 printf( "input underflow.\n" ); 1610 if( xrunFlags & paInputOverflow ) 1611 printf( "input overflow.\n" ); 1612 */ 1613 if( stream->state == STOPPING || stream->state == CALLBACK_STOPPED ) 1614 xrunFlags = 0; 1615 1616 /* -- start processing -- */ 1617 PaUtil_BeginBufferProcessing( &(stream->bufferProcessor), 1618 &timeInfo, 1619 xrunFlags ); 1620 stream->xrunFlags = 0; /* FEEDBACK: we only send flags to Buf Proc once */ 1621 1622 /* -- Copy and process output data -- */ 1623 assert( ioData->mNumberBuffers == 1 ); 1624 frames = ioData->mBuffers[0].mDataByteSize; 1625 frames /= sizeof( float ) * ioData->mBuffers[0].mNumberChannels; 1626 assert( ioData->mBuffers[0].mNumberChannels == stream->userOutChan ); 1627 PaUtil_SetOutputFrameCount( &(stream->bufferProcessor), frames ); 1628 PaUtil_SetInterleavedOutputChannels( &(stream->bufferProcessor), 1629 0, 1630 ioData->mBuffers[0].mData, 1631 ioData->mBuffers[0].mNumberChannels); 1632 1633 /* -- copy and process input data, and complete processing -- */ 1634 if( stream->inputUnit ) { 1635 const int flsz = sizeof( float ); 1636 /* Here, we read the data out of the ring buffer, through the 1637 audio converter. */ 1638 int inChan = stream->inputAudioBufferList.mBuffers[0].mNumberChannels; 1639 if( stream->inputSRConverter ) 1640 { 1641 OSStatus err; 1642 UInt32 size; 1643 float data[ inChan * frames ]; 1644 size = sizeof( data ); 1645 err = AudioConverterFillBuffer( 1646 stream->inputSRConverter, 1647 ringBufferIOProc, 1648 &stream->inputRingBuffer, 1649 &size, 1650 (void *)&data ); 1651 if( err == RING_BUFFER_EMPTY ) 1652 { /*the ring buffer callback underflowed */ 1653 err = 0; 1654 bzero( ((char *)data) + size, sizeof(data)-size ); 1655 stream->xrunFlags |= paInputUnderflow; 1656 } 1657 ERR( err ); 1658 assert( !err ); 1659 1660 PaUtil_SetInputFrameCount( &(stream->bufferProcessor), frames ); 1661 PaUtil_SetInterleavedInputChannels( &(stream->bufferProcessor), 1662 0, 1663 data, 1664 inChan ); 1665 framesProcessed = 1666 PaUtil_EndBufferProcessing( &(stream->bufferProcessor), 1667 &callbackResult ); 1668 } 1669 else 1670 { 1671 /* Without the AudioConverter is actually a bit more complex 1672 because we have to do a little buffer processing that the 1673 AudioConverter would otherwise handle for us. */ 1674 void *data1, *data2; 1675 long size1, size2; 1676 RingBuffer_GetReadRegions( &stream->inputRingBuffer, 1677 inChan*frames*flsz, 1678 &data1, &size1, 1679 &data2, &size2 ); 1680 if( size1 / ( flsz * inChan ) == frames ) { 1681 /* simplest case: all in first buffer */ 1682 PaUtil_SetInputFrameCount( &(stream->bufferProcessor), frames ); 1683 PaUtil_SetInterleavedInputChannels( &(stream->bufferProcessor), 1684 0, 1685 data1, 1686 inChan ); 1687 framesProcessed = 1688 PaUtil_EndBufferProcessing( &(stream->bufferProcessor), 1689 &callbackResult ); 1690 RingBuffer_AdvanceReadIndex(&stream->inputRingBuffer, size1 ); 1691 } else if( ( size1 + size2 ) / ( flsz * inChan ) < frames ) { 1692 /*we underflowed. take what data we can, zero the rest.*/ 1693 float data[frames*inChan]; 1694 if( size1 ) 1695 memcpy( data, data1, size1 ); 1696 if( size2 ) 1697 memcpy( data+size1, data2, size2 ); 1698 bzero( data+size1+size2, frames*flsz*inChan - size1 - size2 ); 1699 1700 PaUtil_SetInputFrameCount( &(stream->bufferProcessor), frames ); 1701 PaUtil_SetInterleavedInputChannels( &(stream->bufferProcessor), 1702 0, 1703 data, 1704 inChan ); 1705 framesProcessed = 1706 PaUtil_EndBufferProcessing( &(stream->bufferProcessor), 1707 &callbackResult ); 1708 RingBuffer_AdvanceReadIndex( &stream->inputRingBuffer, 1709 size1+size2 ); 1710 /* flag underflow */ 1711 stream->xrunFlags |= paInputUnderflow; 1712 } else { 1713 /*we got all the data, but split between buffers*/ 1714 PaUtil_SetInputFrameCount( &(stream->bufferProcessor), 1715 size1 / ( flsz * inChan ) ); 1716 PaUtil_SetInterleavedInputChannels( &(stream->bufferProcessor), 1717 0, 1718 data1, 1719 inChan ); 1720 PaUtil_Set2ndInputFrameCount( &(stream->bufferProcessor), 1721 size2 / ( flsz * inChan ) ); 1722 PaUtil_Set2ndInterleavedInputChannels( &(stream->bufferProcessor), 1723 0, 1724 data2, 1725 inChan ); 1726 framesProcessed = 1727 PaUtil_EndBufferProcessing( &(stream->bufferProcessor), 1728 &callbackResult ); 1729 RingBuffer_AdvanceReadIndex(&stream->inputRingBuffer, size1+size2 ); 1730 } 1731 } 1732 } else { 1733 framesProcessed = 1734 PaUtil_EndBufferProcessing( &(stream->bufferProcessor), 1735 &callbackResult ); 1736 } 1737 1738 } 1739 else 1740 { 1741 /* ------------------ Input 1742 * 1743 * First, we read off the audio data and put it in the ring buffer. 1744 * if this is an input-only stream, we need to process it more, 1745 * otherwise, we let the output case deal with it. 1746 */ 1747 OSErr err = 0; 1748 int chan = stream->inputAudioBufferList.mBuffers[0].mNumberChannels ; 1749 /* FIXME: looping here may not actually be necessary, but it was something I tried in testing. */ 1750 do { 1751 err= AudioUnitRender(stream->inputUnit, 1752 ioActionFlags, 1753 inTimeStamp, 1754 INPUT_ELEMENT, 1755 inNumberFrames, 1756 &stream->inputAudioBufferList ); 1757 if( err == -10874 ) 1758 inNumberFrames /= 2; 1759 } while( err == -10874 && inNumberFrames > 1 ); 1760 /* FEEDBACK: I'm not sure what to do when this call fails */ 1761 ERR( err ); 1762 assert( !err ); 1763 if( stream->inputSRConverter || stream->outputUnit ) 1764 { 1765 /* If this is duplex or we use a converter, put the data 1766 into the ring buffer. */ 1767 long bytesIn, bytesOut; 1768 bytesIn = sizeof( float ) * inNumberFrames * chan; 1769 bytesOut = RingBuffer_Write( &stream->inputRingBuffer, 1770 stream->inputAudioBufferList.mBuffers[0].mData, 1771 bytesIn ); 1772 if( bytesIn != bytesOut ) 1773 stream->xrunFlags |= paInputOverflow ; 1774 } 1775 else 1776 { 1777 /* for simplex input w/o SR conversion, 1778 just pop the data into the buffer processor.*/ 1779 PaUtil_BeginBufferProcessing( &(stream->bufferProcessor), 1780 &timeInfo, 1781 stream->xrunFlags ); 1782 stream->xrunFlags = 0; 1783 1784 PaUtil_SetInputFrameCount( &(stream->bufferProcessor), inNumberFrames); 1785 PaUtil_SetInterleavedInputChannels( &(stream->bufferProcessor), 1786 0, 1787 stream->inputAudioBufferList.mBuffers[0].mData, 1788 chan ); 1789 framesProcessed = 1790 PaUtil_EndBufferProcessing( &(stream->bufferProcessor), 1791 &callbackResult ); 1792 } 1793 if( !stream->outputUnit && stream->inputSRConverter ) 1794 { 1795 /* ------------------ Simplex Input w/ SR Conversion 1796 * 1797 * if this is a simplex input stream, we need to read off the buffer, 1798 * do our sample rate conversion and pass the results to the buffer 1799 * processor. 1800 * The logic here is complicated somewhat by the fact that we don't 1801 * know how much data is available, so we loop on reasonably sized 1802 * chunks, and let the BufferProcessor deal with the rest. 1803 * 1804 */ 1805 /*This might be too big or small depending on SR conversion*/ 1806 float data[ chan * inNumberFrames ]; 1807 OSStatus err; 1808 do 1809 { /*Run the buffer processor until we are out of data*/ 1810 UInt32 size; 1811 long f; 1812 1813 size = sizeof( data ); 1814 err = AudioConverterFillBuffer( 1815 stream->inputSRConverter, 1816 ringBufferIOProc, 1817 &stream->inputRingBuffer, 1818 &size, 1819 (void *)data ); 1820 if( err != RING_BUFFER_EMPTY ) 1821 ERR( err ); 1822 assert( err == 0 || err == RING_BUFFER_EMPTY ); 1823 1824 f = size / ( chan * sizeof(float) ); 1825 PaUtil_SetInputFrameCount( &(stream->bufferProcessor), f ); 1826 if( f ) 1827 { 1828 PaUtil_BeginBufferProcessing( &(stream->bufferProcessor), 1829 &timeInfo, 1830 stream->xrunFlags ); 1831 stream->xrunFlags = 0; 1832 1833 PaUtil_SetInterleavedInputChannels( &(stream->bufferProcessor), 1834 0, 1835 data, 1836 chan ); 1837 framesProcessed = 1838 PaUtil_EndBufferProcessing( &(stream->bufferProcessor), 1839 &callbackResult ); 1840 } 1841 } while( callbackResult == paContinue && !err ); 1842 } 1843 } 1844 1845 switch( callbackResult ) 1846 { 1847 case paContinue: break; 1848 case paComplete: 1849 case paAbort: 1850 stream->isTimeSet = FALSE; 1851 stream->state = CALLBACK_STOPPED ; 1852 if( stream->outputUnit ) 1853 AudioOutputUnitStop(stream->outputUnit); 1854 if( stream->inputUnit ) 1855 AudioOutputUnitStop(stream->inputUnit); 1856 break; 1857 } 1858 1859 PaUtil_EndCpuLoadMeasurement( &stream->cpuLoadMeasurer, framesProcessed ); 1860 return noErr; 1861 } 1862 1863 1864 /* 1865 When CloseStream() is called, the multi-api layer ensures that 1866 the stream has already been stopped or aborted. 1867 */ 699 1868 static PaError CloseStream( PaStream* s ) 700 1869 { 701 PaError err = paNoError; 1870 /* This may be called from a failed OpenStream. 1871 Therefore, each piece of info is treated seperately. */ 1872 PaError result = paNoError; 702 1873 PaMacCoreStream *stream = (PaMacCoreStream*)s; 703 1874 704 PaUtil_ResetCpuLoadMeasurer( &stream->cpuLoadMeasurer ); 705 706 if (stream->inputDevice != kAudioDeviceUnknown) { 707 if (stream->outputDevice == kAudioDeviceUnknown || stream->outputDevice == stream->inputDevice) { 708 err = conv_err(AudioDeviceRemoveIOProc(stream->inputDevice, AudioIOProc)); 709 } 710 else { 711 err = conv_err(AudioDeviceRemoveIOProc(stream->inputDevice, AudioInputProc)); 712 err = conv_err(AudioDeviceRemoveIOProc(stream->outputDevice, AudioOutputProc)); 713 } 714 } 715 else { 716 err = conv_err(AudioDeviceRemoveIOProc(stream->outputDevice, AudioIOProc)); 717 } 718 719 return err; 1875 VVDBUG(("CloseStream()\n")); 1876 VDBUG( ( "Closing stream.\n" ) ); 1877 1878 if( stream ) { 1879 if( stream->outputUnit && stream->outputUnit != stream->inputUnit ) { 1880 AudioUnitUninitialize( stream->outputUnit ); 1881 CloseComponent( stream->outputUnit ); 1882 } 1883 stream->outputUnit = NULL; 1884 if( stream->inputUnit ) 1885 { 1886 AudioUnitUninitialize( stream->inputUnit ); 1887 CloseComponent( stream->inputUnit ); 1888 stream->inputUnit = NULL; 1889 } 1890 if( stream->inputRingBuffer.buffer ) 1891 free( (void *) stream->inputRingBuffer.buffer ); 1892 stream->inputRingBuffer.buffer = NULL; 1893 /*TODO: is there more that needs to be done on error 1894 from AudioConverterDispose?*/ 1895 if( stream->inputSRConverter ) 1896 ERR( AudioConverterDispose( stream->inputSRConverter ) ); 1897 stream->inputSRConverter = NULL; 1898 if( stream->inputAudioBufferList.mBuffers[0].mData ) 1899 free( stream->inputAudioBufferList.mBuffers[0].mData ); 1900 stream->inputAudioBufferList.mBuffers[0].mData = NULL; 1901 1902 result = destroyBlioRingBuffers( &stream->blio ); 1903 if( result ) 1904 return result; 1905 if( stream->bufferProcessorIsInitialized ) 1906 PaUtil_TerminateBufferProcessor( &stream->bufferProcessor ); 1907 PaUtil_TerminateStreamRepresentation( &stream->streamRepresentation ); 1908 PaUtil_FreeMemory( stream ); 1909 } 1910 1911 return result; 720 1912 } 721 1913 … … 723 1915 static PaError StartStream( PaStream *s ) 724 1916 { 725 PaError err = paNoError;726 1917 PaMacCoreStream *stream = (PaMacCoreStream*)s; 727 728 if (stream->inputDevice != kAudioDeviceUnknown) { 729 if (stream->outputDevice == kAudioDeviceUnknown || stream->outputDevice == stream->inputDevice) { 730 err = conv_err(AudioDeviceStart(stream->inputDevice, AudioIOProc)); 731 } 732 else { 733 err = conv_err(AudioDeviceStart(stream->inputDevice, AudioInputProc)); 734 err = conv_err(AudioDeviceStart(stream->outputDevice, AudioOutputProc)); 735 } 736 } 737 else { 738 err = conv_err(AudioDeviceStart(stream->outputDevice, AudioIOProc)); 739 } 740 741 stream->isActive = 1; 742 stream->isStopped = 0; 743 return err; 1918 OSErr result = noErr; 1919 VVDBUG(("StartStream()\n")); 1920 VDBUG( ( "Starting stream.\n" ) ); 1921 1922 #define ERR_WRAP(mac_err) do { result = mac_err ; if ( result != noErr ) return ERR(result) ; } while(0) 1923 1924 /*FIXME: maybe want to do this on close/abort for faster start? */ 1925 PaUtil_ResetBufferProcessor( &stream->bufferProcessor ); 1926 if( stream->inputSRConverter ) 1927 ERR_WRAP( AudioConverterReset( stream->inputSRConverter ) ); 1928 1929 /* -- start -- */ 1930 stream->state = ACTIVE; 1931 if( stream->inputUnit ) { 1932 ERR_WRAP( AudioOutputUnitStart(stream->inputUnit) ); 1933 } 1934 if( stream->outputUnit && stream->outputUnit != stream->inputUnit ) { 1935 ERR_WRAP( AudioOutputUnitStart(stream->outputUnit) ); 1936 } 1937 1938 //setStreamStartTime( stream ); 1939 //stream->isTimeSet = TRUE; 1940 1941 return paNoError; 1942 #undef ERR_WRAP 1943 } 1944 1945 1946 static PaError StopStream( PaStream *s ) 1947 { 1948 PaMacCoreStream *stream = (PaMacCoreStream*)s; 1949 OSErr result = noErr; 1950 PaError paErr; 1951 VVDBUG(("StopStream()\n")); 1952 1953 VDBUG( ("Waiting for BLIO.\n") ); 1954 waitUntilBlioWriteBufferIsFlushed( &stream->blio ); 1955 VDBUG( ( "Stopping stream.\n" ) ); 1956 1957 stream->isTimeSet = FALSE; 1958 stream->state = STOPPING; 1959 1960 #define ERR_WRAP(mac_err) do { result = mac_err ; if ( result != noErr ) return ERR(result) ; } while(0) 1961 /* -- stop and reset -- */ 1962 if( stream->inputUnit == stream->outputUnit && stream->inputUnit ) 1963 { 1964 ERR_WRAP( AudioOutputUnitStop(stream->inputUnit) ); 1965 ERR_WRAP( AudioUnitReset(stream->inputUnit, kAudioUnitScope_Global, 1) ); 1966 ERR_WRAP( AudioUnitReset(stream->inputUnit, kAudioUnitScope_Global, 0) ); 1967 } 1968 else 1969 { 1970 if( stream->inputUnit ) 1971 { 1972 ERR_WRAP(AudioOutputUnitStop(stream->inputUnit) ); 1973 ERR_WRAP(AudioUnitReset(stream->inputUnit,kAudioUnitScope_Global,1)); 1974 } 1975 if( stream->outputUnit ) 1976 { 1977 ERR_WRAP(AudioOutputUnitStop(stream->outputUnit)); 1978 ERR_WRAP(AudioUnitReset(stream->outputUnit,kAudioUnitScope_Global,0)); 1979 } 1980 } 1981 if( stream->inputRingBuffer.buffer ) { 1982 RingBuffer_Flush( &stream->inputRingBuffer ); 1983 bzero( (void *)stream->inputRingBuffer.buffer, 1984 stream->inputRingBuffer.bufferSize ); 1985 /* advance the write point a little, so we are reading from the 1986 middle of the buffer. We'll need extra at the end because 1987 testing has shown that this helps. */ 1988 if( stream->outputUnit ) 1989 RingBuffer_AdvanceWriteIndex( &stream->inputRingBuffer, 1990 stream->inputRingBuffer.bufferSize 1991 / RING_BUFFER_ADVANCE_DENOMINATOR ); 1992 } 1993 1994 stream->xrunFlags = 0; 1995 stream->state = STOPPED; 1996 1997 paErr = resetBlioRingBuffers( &stream->blio ); 1998 if( paErr ) 1999 return paErr; 2000 2001 /* 2002 //stream->isTimeSet = FALSE; 2003 */ 2004 2005 VDBUG( ( "Stream Stopped.\n" ) ); 2006 return paNoError; 2007 #undef ERR_WRAP 744 2008 } 745 2009 746 2010 static PaError AbortStream( PaStream *s ) 747 2011 { 748 PaError err = paNoError; 749 PaMacCoreStream *stream = (PaMacCoreStream*)s; 750 751 if (stream->inputDevice != kAudioDeviceUnknown) { 752 if (stream->outputDevice == kAudioDeviceUnknown || stream->outputDevice == stream->inputDevice) { 753 err = conv_err(AudioDeviceStop(stream->inputDevice, AudioIOProc)); 754 } 755 else { 756 err = conv_err(AudioDeviceStop(stream->inputDevice, AudioInputProc)); 757 err = conv_err(AudioDeviceStop(stream->outputDevice, AudioOutputProc)); 758 } 759 } 760 else { 761 err = conv_err(AudioDeviceStop(stream->outputDevice, AudioIOProc)); 762 } 763 764 stream->isActive = 0; 765 stream->isStopped = 1; 766 return err; 767 } 768 769 static PaError StopStream( PaStream *s ) 770 { 771 // TODO: this should be nicer than abort 772 return AbortStream(s); 2012 VVDBUG(("AbortStream()->StopStream()\n")); 2013 VDBUG( ( "Aborting stream.\n" ) ); 2014 /* We have nothing faster than StopStream. */ 2015 return StopStream(s); 773 2016 } 2017 774 2018 775 2019 static PaError IsStreamStopped( PaStream *s ) 776 2020 { 777 2021 PaMacCoreStream *stream = (PaMacCoreStream*)s; 778 779 return stream->isStopped; 2022 VVDBUG(("IsStreamStopped()\n")); 2023 2024 return stream->state == STOPPED ? 1 : 0; 780 2025 } 781 2026 … … 784 2029 { 785 2030 PaMacCoreStream *stream = (PaMacCoreStream*)s; 786 787 return stream->isActive; 788 } 789 790 791 static PaTime GetStreamTime( PaStream *s ) 792 { 793 OSStatus err; 794 PaTime result; 795 PaMacCoreStream *stream = (PaMacCoreStream*)s; 796 797 AudioTimeStamp *timeStamp = PaUtil_AllocateMemory(sizeof(AudioTimeStamp)); 798 if (stream->inputDevice != kAudioDeviceUnknown) { 799 err = AudioDeviceGetCurrentTime(stream->inputDevice, timeStamp); 800 } 801 else { 802 err = AudioDeviceGetCurrentTime(stream->outputDevice, timeStamp); 803 } 804 805 result = err ? 0 : timeStamp->mSampleTime; 806 PaUtil_FreeMemory(timeStamp); 807 808 return result; 2031 VVDBUG(("IsStreamActive()\n")); 2032 return ( stream->state == ACTIVE || stream->state == STOPPING ); 809 2033 } 810 2034 … … 813 2037 { 814 2038 PaMacCoreStream *stream = (PaMacCoreStream*)s; 815 2039 VVDBUG(("GetStreamCpuLoad()\n")); 2040 816 2041 return PaUtil_GetCpuLoad( &stream->cpuLoadMeasurer ); 817 2042 } 818 819 820 // As separate stream interfaces are used for blocking and callback streams, the following functions can be guaranteed to only be called for blocking streams.821 822 static PaError ReadStream( PaStream* s,823 void *buffer,824 unsigned long frames )825 {826 return paInternalError;827 }828 829 830 static PaError WriteStream( PaStream* s,831 const void *buffer,832 unsigned long frames )833 {834 return paInternalError;835 }836 837 838 static signed long GetStreamReadAvailable( PaStream* s )839 {840 return paInternalError;841 }842 843 844 static signed long GetStreamWriteAvailable( PaStream* s )845 {846 return paInternalError;847 }848 849 // HostAPI-specific initialization function850 PaError PaMacCore_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex )851 {852 PaError result = paNoError;853 PaMacCoreHostApiRepresentation *macCoreHostApi = (PaMacCoreHostApiRepresentation *)PaUtil_AllocateMemory( sizeof(PaMacCoreHostApiRepresentation) );854 if( !macCoreHostApi )855 {856 result = paInsufficientMemory;857 goto error;858 }859 860 macCoreHostApi->allocations = PaUtil_CreateAllocationGroup();861 if( !macCoreHostApi->allocations )862 {863 result = paInsufficientMemory;864 goto error;865 }866 867 *hostApi = &macCoreHostApi->inheritedHostApiRep;868 (*hostApi)->info.structVersion = 1;869 (*hostApi)->info.type = paCoreAudio;870 (*hostApi)->info.name = "CoreAudio";871 872 result = InitializeDeviceInfos(macCoreHostApi, hostApiIndex);873 if (result != paNoError) {874 goto error;875 }876 877 // Set up the proper callbacks to this HostApi's functions878 (*hostApi)->Terminate = Terminate;879 (*hostApi)->OpenStream = OpenStream;880 (*hostApi)->IsFormatSupported = IsFormatSupported;881 882 PaUtil_InitializeStreamInterface( &macCoreHostApi->callbackStreamInterface, CloseStream, StartStream,883 StopStream, AbortStream, IsStreamStopped, IsStreamActive,884 GetStreamTime, GetStreamCpuLoad,885 PaUtil_DummyRead, PaUtil_DummyWrite,886 PaUtil_DummyGetReadAvailable, PaUtil_DummyGetWriteAvailable );887 888 PaUtil_InitializeStreamInterface( &macCoreHostApi->blockingStreamInterface, CloseStream, StartStream,889 StopStream, AbortStream, IsStreamStopped, IsStreamActive,890 GetStreamTime, PaUtil_DummyGetCpuLoad,891 ReadStream, WriteStream, GetStreamReadAvailable, GetStreamWriteAvailable );892 893 return result;894 895 error:896 if( macCoreHostApi ) {897 CleanUp(macCoreHostApi);898 }899 900 return result;901 }
Note: See TracChangeset
for help on using the changeset viewer.