gearmulator

Emulation of classic VA synths of the late 90s/2000s that are based on Motorola 56300 family DSPs
Log | Files | Refs | Submodules | README | LICENSE

pa_mac_core_old.c (35195B)


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