diff --git a/osx/cmAudioPortOsx.c b/osx/cmAudioPortOsx.c index 063dff5..95cdf1e 100644 --- a/osx/cmAudioPortOsx.c +++ b/osx/cmAudioPortOsx.c @@ -45,21 +45,20 @@ typedef struct unsigned timeOutMs; AudioDeviceID fmtChangeDevId; - AudioStreamID fmtChangeStreamId; + AudioObjectID fmtChangeObjId; } cmApOsxDevRecd; typedef struct { - cmRpt_t* rpt; + cmRpt_t* rpt; cmApOsxDevRecd* devArray; - unsigned devCnt; + unsigned devCnt; } cmApOsxRoot; cmApOsxRoot _cmApOsxRoot = { NULL, NULL, 0 }; - const char* _cmApOsxOsStatusToText( OSStatus errCode ) { switch( errCode ) @@ -70,6 +69,7 @@ const char* _cmApOsxOsStatusToText( OSStatus errCode ) case kAudioHardwareUnknownPropertyError: return "Unknown property error"; case kAudioHardwareBadPropertySizeError: return "Bad property error"; case kAudioHardwareIllegalOperationError: return "Illegal operation error"; + case kAudioHardwareBadObjectError: return "Bad object error"; case kAudioHardwareBadDeviceError: return "Bad device error"; case kAudioHardwareBadStreamError: return "Bad stream error"; case kAudioHardwareUnsupportedOperationError: return "Unsupported operating error"; @@ -82,10 +82,19 @@ const char* _cmApOsxOsStatusToText( OSStatus errCode ) cmApRC_t _cmApOsxOsErrorRpt( OSStatus err, cmApOsxRoot* r, const char* func, const char* file, int line ) { + char errStr[5]; + unsigned i; + + for(i=0; i<4; ++i) + errStr[i] = (char)(err & (0x000000ff << i)); + + errStr[4] = 0; + + if( r->rpt != NULL ) { if( err != noErr ) - cmRptErrorf(r->rpt,"Audio Port Error %s in %s line:%i %s\n",_cmApOsxOsStatusToText(err),func,line,file); + cmRptErrorf(r->rpt,"Audio Port Error : %s in %s line:%i %s\n",_cmApOsxOsStatusToText(err),func,line,file); else cmRptErrorf(r->rpt,"Audio Port Error: Unknown\n"); } @@ -95,358 +104,21 @@ cmApRC_t _cmApOsxOsErrorRpt( OSStatus err, cmApOsxRoot* r, const char* func, con #define _cmApOsxOsError( err, root ) _cmApOsxOsErrorRpt( err, root, __FUNCTION__, __FILE__, __LINE__ ) -OSStatus _cmApOsxAllocDeviceCFString( AudioDeviceID devId, UInt32 ch, Boolean inputFl, AudioDevicePropertyID devPropId, char** strPtrPtr ) -{ - CFStringRef cfStr; - //Boolean outWritableFl = false; - UInt32 outByteCnt = sizeof(cfStr);; - OSStatus err = noErr; - - if((err = AudioDeviceGetProperty( devId, ch, inputFl, devPropId, &outByteCnt, &cfStr )) != noErr ) - return err; - - CFIndex cfLen = CFStringGetLength(cfStr) * 2; - - char* cStr = cmMemAllocZ( char, cfLen+1 ); - cStr[0] = 0; - - if( CFStringGetCString( cfStr, cStr, cfLen, kCFStringEncodingUTF8 ) ) - cStr[cfLen]=0; - - CFRelease(cfStr); - - *strPtrPtr = cStr; - - return noErr; -} - -// Note: the streamIdArray* allocated by this function must be released by the calling function. -OSStatus _cmApOsxAllocStreamIdArray( AudioDeviceID devId, Boolean inputFl, AudioStreamID** streamIdArrayPtr, unsigned* streamIdCntPtr ) -{ - UInt32 byteCnt = 0; - Boolean canWriteFl = false; - OSStatus err = noErr; - - *streamIdArrayPtr = NULL; - *streamIdCntPtr = 0; - - // get the length of the stream id array - if((err = AudioDeviceGetPropertyInfo( devId, 0, inputFl, kAudioDevicePropertyStreams, &byteCnt, &canWriteFl )) != noErr ) - return err; - - if( byteCnt <= 0 ) - goto doneLabel; - - // get the count of streams - *streamIdCntPtr = byteCnt / sizeof(AudioStreamID); - - // allocate the stream id array - *streamIdArrayPtr = cmMemAllocZ( AudioStreamID, *streamIdCntPtr ); - - // verify that the size of the stream id array is an integer multiple of the sizeof(AudioStreamID) - assert( *streamIdCntPtr * sizeof(AudioStreamID) == byteCnt ); - - // fill the stream id array - if((err = AudioDeviceGetProperty( devId, 0, inputFl, kAudioDevicePropertyStreams, &byteCnt, *streamIdArrayPtr )) != noErr ) - return err; - - doneLabel: - return noErr; -} - OSStatus cmApOsxSystemStreamPropertyListenerProc( - AudioStreamID inStream, - UInt32 inChannel, - AudioDevicePropertyID inPropertyID, - void* inClientData) + AudioObjectID audioObjId, + UInt32 channel, + const AudioObjectPropertyAddress* propertyAddr, + void* clientData) { - cmApOsxDevRecd* drp = (cmApOsxDevRecd*)inClientData; + cmApOsxDevRecd* drp = (cmApOsxDevRecd*)clientData; - drp->fmtChangeDevId = drp->devId; - drp->fmtChangeStreamId = inStream; + drp->fmtChangeDevId = drp->devId; + drp->fmtChangeObjId = audioObjId; return noErr; } -OSStatus _cmApOsxSetSampleRate( cmApOsxDevRecd* drp, bool inputFl, double newSampleRate ) -{ - AudioStreamID* streamIdArray = NULL; - unsigned streamCnt; - OSStatus err = noErr; - unsigned waitMs = 0; - unsigned i; - - // allocate a stream id array - if((err = _cmApOsxAllocStreamIdArray( drp->devId, inputFl, &streamIdArray, &streamCnt )) != noErr ) - return _cmApOsxOsError(err,&_cmApOsxRoot); - - // for each stream on this device - for(i=0; idevId, i, inputFl, kAudioDevicePropertyStreamFormat, &byteCnt, &canWriteFl )) != noErr ) - { - _cmApOsxOsError(err,&_cmApOsxRoot); - goto errLabel; - } - - assert( byteCnt == sizeof(sdr) ); - - // get the stream desc recd - if((err = AudioDeviceGetProperty( drp->devId, i, inputFl, kAudioDevicePropertyStreamFormat, &byteCnt, &sdr )) != noErr ) - { - _cmApOsxOsError(err,&_cmApOsxRoot); - goto errLabel; - } - - // if the format has not already been set - if( sdr.mSampleRate != newSampleRate ) - { - // change the sample rate - sdr.mSampleRate = newSampleRate; - - drp->fmtChangeDevId = -1; - drp->fmtChangeStreamId = -1; - - // attempt to change the sample rate - if((err = AudioStreamSetProperty(streamIdArray[i], NULL, 0, kAudioDevicePropertyStreamFormat, sizeof(sdr), &sdr)) != noErr ) - { - err = _cmApOsxOsError(err,&_cmApOsxRoot); - goto errLabel; - } - - // wait for confirmation - waitMs = 0; - while( drp->fmtChangeDevId != drp->devId - && drp->fmtChangeStreamId != streamIdArray[i] - && waitMs < drp->timeOutMs ) - { - const unsigned waitIncrMs = 20; - usleep(waitIncrMs*1000); - waitMs += waitIncrMs; - } - - // wait timed out - if( waitMs >= drp->timeOutMs ) - { - err = _cmApOsxOsError(kAudioHardwareUnspecifiedError,&_cmApOsxRoot); - goto errLabel; - } - else - { - // read back the format to be really sure it changed - if((err = AudioDeviceGetProperty( drp->devId, i, inputFl, kAudioDevicePropertyStreamFormat, &byteCnt, &sdr )) != noErr ) - { - _cmApOsxOsError(err,&_cmApOsxRoot); - goto errLabel; - } - - assert( sdr.mSampleRate == newSampleRate ); - } - } - - } - - errLabel: - cmMemPtrFree(&streamIdArray); - - return err; -} - -OSStatus _cmApOsxSetFramesPerCycle( cmApOsxDevRecd* drp, Boolean inputFl, unsigned newFramesPerCycle ) -{ - OSStatus err = noErr; - AudioTimeStamp when; - UInt32 ch = 0; - UInt32 devPropId = kAudioDevicePropertyBufferFrameSize; - UInt32 outByteCnt = sizeof(UInt32); - UInt32 curFramesPerCycle = 0; - - // set time stamp to zero to force the change to be immediate - when.mHostTime = 0; - when.mFlags = kAudioTimeStampHostTimeValid; - - // get the cur value off the param. to change - if((err = AudioDeviceGetProperty( drp->devId, ch, inputFl, devPropId, &outByteCnt, &curFramesPerCycle )) != noErr ) - return _cmApOsxOsError(err,&_cmApOsxRoot); - - // if the cur value is the same as the new value then there is nothing to do - if( curFramesPerCycle == newFramesPerCycle ) - return noErr; - - // attempt to set the new value - if((err = AudioDeviceSetProperty( drp->devId, &when, ch, inputFl, devPropId, sizeof(newFramesPerCycle), &newFramesPerCycle)) != noErr ) - return _cmApOsxOsError(err,&_cmApOsxRoot); - - // wait for the value to actually change - unsigned waitMs = 0; - while( waitMs < drp->timeOutMs ) - { - const unsigned waitIncrMs = 20; - usleep(waitIncrMs*1000); - - // read the parameter value - if((err = AudioDeviceGetProperty( drp->devId, ch, inputFl, devPropId, &outByteCnt, &curFramesPerCycle )) != noErr ) - return _cmApOsxOsError(err,&_cmApOsxRoot); - - // if the parameter value equals the new value then the change has taken effect - if( curFramesPerCycle == newFramesPerCycle ) - break; - - waitMs += waitIncrMs; - } - - // wait timed out - if( waitMs >= drp->timeOutMs ) - return _cmApOsxOsError(kAudioHardwareUnspecifiedError,&_cmApOsxRoot); - - return noErr; -} - - -// Note: *bufArrayPtr must be deallocated by the caller. - cmApRC_t _cmApOsxGetBufferCfg(unsigned devIdx, AudioDeviceID devId, Boolean inputFl, cmApOsxDevRecd* drp ) -{ - cmApRC_t rc = kOkApRC; - OSStatus err = noErr; - UInt32 byteCnt = 0; - Boolean canWriteFl = 0; - AudioBufferList* ablp = NULL; - unsigned streamCnt = 0; - AudioStreamID* streamIdArray = NULL; - unsigned i = 0; - unsigned chIdx = 0; - - // get the size of stream cfg buffer - if( (err = AudioDeviceGetPropertyInfo(devId,0,inputFl,kAudioDevicePropertyStreamConfiguration,&byteCnt,&canWriteFl)) != noErr ) - return _cmApOsxOsError(err,&_cmApOsxRoot); - - // allocate memory to hold the AudioBufferList - ablp = (AudioBufferList*)cmMemMallocZ(byteCnt); - - // get the audio buffer list array - if((err = AudioDeviceGetProperty(devId,0,inputFl,kAudioDevicePropertyStreamConfiguration,&byteCnt,ablp)) != noErr ) - { - rc = _cmApOsxOsError(err,&_cmApOsxRoot); - goto errLabel; - } - - // allocate a stream id array - if((err = _cmApOsxAllocStreamIdArray( devId, inputFl, &streamIdArray, &streamCnt )) != noErr ) - { - rc = _cmApOsxOsError(err,&_cmApOsxRoot); - goto errLabel; - } - - // the number of buffers and the number of frames must be the same - assert( streamCnt == ablp->mNumberBuffers); - - cmApAudioPacket_t* pktArray = cmMemAllocZ(cmApAudioPacket_t,ablp->mNumberBuffers); - - if( inputFl ) - { - drp->inPktCnt = ablp->mNumberBuffers; - drp->inPktArray = pktArray; - } - else - { - drp->outPktCnt = ablp->mNumberBuffers; - drp->outPktArray = pktArray; - } - - - for(i=0; imNumberBuffers; ++i) - { - AudioStreamBasicDescription sdr; - - // get the size of the stream desc recd - if((err = AudioDeviceGetPropertyInfo( devId, i, inputFl, kAudioDevicePropertyStreamFormat, &byteCnt, &canWriteFl )) != noErr ) - { - _cmApOsxOsError(err,&_cmApOsxRoot); - goto errLabel; - } - - assert( byteCnt == sizeof(sdr) ); - - // get the stream desc recd - if((err = AudioDeviceGetProperty( devId, i, inputFl, kAudioDevicePropertyStreamFormat, &byteCnt, &sdr )) != noErr ) - { - _cmApOsxOsError(err,&_cmApOsxRoot); - goto errLabel; - } - - // assert that the data format is packed float32 native endian - //assert( IsAudioFormatNativeEndian(sdr) ); - - // 0x6c70636d = lpcm - //printf("%s %i dev:%i sr:%f fmtId:0x%lx fmtFl:0x%lx bytesPerPkt:%li frmsPerPkt:%li bytesPerFrm:%li chsPerFrm:%li bitsPerCh:%li\n", - // inputFl?"i":"o",i,devIdx,sdr.mSampleRate,sdr.mFormatID,sdr.mFormatFlags,sdr.mBytesPerPacket,sdr.mFramesPerPacket,sdr.mBytesPerFrame,sdr.mChannelsPerFrame,sdr.mBitsPerChannel); - - - // assert that all buffers have the sample rate of the device - if( drp->srate == 0 ) - drp->srate = sdr.mSampleRate; - - assert( drp->srate == sdr.mSampleRate ); - - // install a stream property listener - AudioStreamAddPropertyListener( streamIdArray[i], - kAudioPropertyWildcardChannel, - kAudioPropertyWildcardPropertyID, - cmApOsxSystemStreamPropertyListenerProc, - drp ); - - pktArray[i].devIdx = devIdx; - pktArray[i].begChIdx = chIdx; - pktArray[i].chCnt = ablp->mBuffers[i].mNumberChannels; //sdr.mChannelsPerFrame; - pktArray[i].audioFramesCnt = ablp->mBuffers[i].mDataByteSize / (sizeof(cmApSample_t) * ablp->mBuffers[i].mNumberChannels); //sdr.mFramesPerPacket; - pktArray[i].flags = kInterleavedApFl | kFloatApFl; - pktArray[i].bitsPerSample = sizeof(cmApSample_t) * 8; - pktArray[i].audioBytesPtr = NULL; - pktArray[i].userCbPtr = NULL; // the userCbPtr value isn't availabe until the cmApOsxDeviceSetup() - - // verify that all buffers on this device have the same size - assert(i==0 || pktArray[i].audioFramesCnt == pktArray[i-1].audioFramesCnt ); - - - chIdx += ablp->mBuffers[i].mNumberChannels; //sdr.mChannelsPerFrame; - - if( inputFl ) - { - // track the total number of input channels on this device - drp->inChCnt = chIdx; - - // track the frames per cycle on this device - if( drp->inFramesPerCycle == 0 ) - drp->inFramesPerCycle = pktArray[i].audioFramesCnt; - - - assert( drp->inFramesPerCycle == pktArray[i].audioFramesCnt ); - } - else - { - drp->outChCnt = chIdx; - - if( drp->outFramesPerCycle == 0 ) - drp->outFramesPerCycle = pktArray[i].audioFramesCnt; - - assert( drp->outFramesPerCycle == pktArray[i].audioFramesCnt ); - - } - } - - errLabel: - cmMemPtrFree(&streamIdArray); - cmMemPtrFree(&ablp); - return rc; -} - - OSStatus _cmApOsxSystemDeviceIOProc( AudioDeviceID inDevice, const AudioTimeStamp* inNow, const AudioBufferList* iabl, @@ -468,9 +140,9 @@ OSStatus _cmApOsxSystemDeviceIOProc( AudioDeviceID inDevice, //assert( oabl->mNumberBuffers==0 || oabl->mNumberBuffers == drp->outPktCnt ); // setup the incoming packets (ADC->app) - AudioBuffer* bp = iabl->mBuffers; - AudioBuffer* ep = bp + iabl->mNumberBuffers; - cmApAudioPacket_t* pp = drp->inPktArray; + const AudioBuffer* bp = iabl->mBuffers; + const AudioBuffer* ep = bp + iabl->mNumberBuffers; + cmApAudioPacket_t* pp = drp->inPktArray; unsigned chIdx = 0; for(; bp 0 ); + addr.mSelector = sel; + addr.mScope = scope; + addr.mElement = ele; - // calc. the device count - _cmApOsxRoot.devCnt = outByteCnt / sizeof(AudioDeviceID); + if((err = AudioObjectGetPropertyData(devId, &addr, 0, NULL, &outByteCnt, &cfStr)) != noErr ) + return err; - assert( _cmApOsxRoot.devCnt*sizeof(AudioDeviceID) == outByteCnt ); - - // allocate space for the device id array and the device array - devIdArray = cmMemAllocZ( AudioDeviceID, _cmApOsxRoot.devCnt ); - devArray = cmMemAllocZ( cmApOsxDevRecd, _cmApOsxRoot.devCnt ); - _cmApOsxRoot.devArray = devArray; + CFIndex cfLen = CFStringGetLength(cfStr) * 2; + char* cStr = cmMemAllocZ( char, cfLen+1 ); - // get the device id array - if((err = AudioHardwareGetProperty( kAudioHardwarePropertyDevices, &outByteCnt, devIdArray )) != noErr ) + cStr[0] = 0; + + if( CFStringGetCString( cfStr, cStr, cfLen, kCFStringEncodingUTF8 ) ) + cStr[cfLen]=0; + + CFRelease(cfStr); + + *strPtrPtr = cStr; + + return noErr; +} + +// Note: the streamIdArray* allocated by this function must be released by the calling function. +OSStatus _cmApOsxAllocateStreamIdArray( AudioDeviceID devId, Boolean inputFl, AudioStreamID** streamIdArrayPtr, unsigned* streamIdCntPtr ) +{ + + UInt32 byteCnt = 0; + OSStatus err = noErr; + AudioObjectPropertyAddress addr = + { + kAudioHardwarePropertyDevices, + inputFl ? kAudioDevicePropertyScopeInput : kAudioObjectPropertyScopeOutput, + kAudioObjectPropertyElementMaster + }; + + *streamIdArrayPtr = NULL; + *streamIdCntPtr = 0; + + // get the length of the stream id array + addr.mSelector = kAudioDevicePropertyStreams; + if((err = AudioObjectGetPropertyDataSize(devId, &addr, 0, NULL, &byteCnt)) != noErr ) + return _cmApOsxOsError(err,&_cmApOsxRoot); + + if( byteCnt <= 0 ) + goto errLabel; + + // get the count of streams + *streamIdCntPtr = byteCnt / sizeof(AudioStreamID); + + // allocate the stream id array + *streamIdArrayPtr = cmMemAllocZ( AudioStreamID, *streamIdCntPtr ); + + // verify that the size of the stream id array is an integer multiple of the sizeof(AudioStreamID) + assert( *streamIdCntPtr * sizeof(AudioStreamID) == byteCnt ); + + // fill the stream id array + if ((err=AudioObjectGetPropertyData(devId, &addr, 0, NULL, &byteCnt, *streamIdArrayPtr)) != noErr ) + { + _cmApOsxOsError(err,&_cmApOsxRoot); + goto errLabel; + } + + errLabel: + return noErr; +} + + +// Note: *bufArrayPtr must be deallocated by the caller. +cmApRC_t _cmApOsxGetBufferConfig(unsigned devIdx, AudioDeviceID devId, Boolean inputFl, cmApOsxDevRecd* drp ) +{ + cmApRC_t rc = kOkApRC; + OSStatus err = noErr; + UInt32 byteCnt = 0; + AudioBufferList* ablp = NULL; + unsigned streamCnt = 0; + AudioStreamID* streamIdArray = NULL; + unsigned i = 0; + unsigned chIdx = 0; + + AudioObjectPropertyAddress addr = + { + kAudioDevicePropertyStreamConfiguration, + inputFl ? kAudioDevicePropertyScopeInput : kAudioObjectPropertyScopeOutput, + kAudioObjectPropertyElementMaster + }; + + // get the size of stream cfg buffer + if((err = AudioObjectGetPropertyDataSize(devId, &addr, 0, NULL, &byteCnt)) != noErr ) + return _cmApOsxOsError(err,&_cmApOsxRoot); + + // allocate memory to hold the AudioBufferList + ablp = (AudioBufferList*)cmMemMallocZ(byteCnt); + + // get the audio buffer list array + if ((err=AudioObjectGetPropertyData(devId, &addr, 0, NULL, &byteCnt, ablp)) != noErr ) + { + rc = _cmApOsxOsError(err,&_cmApOsxRoot); + goto errLabel; + } + + // allocate a stream id array + if((err = _cmApOsxAllocateStreamIdArray( devId, inputFl, &streamIdArray, &streamCnt )) != noErr ) { rc = _cmApOsxOsError(err,&_cmApOsxRoot); goto errLabel; } - // for each - for(i=0; i<_cmApOsxRoot.devCnt; ++i) + // the number of buffers and the number of frames must be the same + assert( streamCnt == ablp->mNumberBuffers); + + cmApAudioPacket_t* pktArray = cmMemAllocZ(cmApAudioPacket_t,ablp->mNumberBuffers); + + if( inputFl ) { - // device name - if((err = _cmApOsxAllocDeviceCFString(devIdArray[i], 0, false, kAudioDevicePropertyDeviceNameCFString, &devArray[i].nameStr )) != noErr ) + drp->inPktCnt = ablp->mNumberBuffers; + drp->inPktArray = pktArray; + } + else + { + drp->outPktCnt = ablp->mNumberBuffers; + drp->outPktArray = pktArray; + } + + + // + for(i=0; imNumberBuffers; ++i) + { + AudioStreamBasicDescription sdr; + + // get the size of the stream desc recd + addr.mSelector = kAudioDevicePropertyStreamFormat; + if((err = AudioObjectGetPropertyDataSize(devId, &addr, 0, NULL, &byteCnt)) != noErr ) { + _cmApOsxOsError(err,&_cmApOsxRoot); + goto errLabel; + } + + assert( byteCnt == sizeof(sdr) ); + + // get the stream desc recd + if((err=AudioObjectGetPropertyData(devId, &addr, 0, NULL, &byteCnt, &sdr)) != noErr ) + { rc = _cmApOsxOsError(err,&_cmApOsxRoot); goto errLabel; } - // device mfg - if((err = _cmApOsxAllocDeviceCFString(devIdArray[i], 0, false, kAudioDevicePropertyDeviceManufacturerCFString, &devArray[i].mfgStr )) != noErr ) + // assert that the data format is packed float32 native endian + // assert( IsAudioFormatNativeEndian(sdr) ); + + // 0x6c70636d = lpcm + // printf("%s %i dev:%i sr:%f fmtId:0x%lx fmtFl:0x%lx bytesPerPkt:%li frmsPerPkt:%li bytesPerFrm:%li chsPerFrm:%li bitsPerCh:%li\n", + // inputFl?"i":"o",i,devIdx,sdr.mSampleRate,sdr.mFormatID,sdr.mFormatFlags,sdr.mBytesPerPacket,sdr.mFramesPerPacket,sdr.mBytesPerFrame,sdr.mChannelsPerFrame,sdr.mBitsPerChannel); + + + // assert that all buffers have the sample rate of the device + if( drp->srate == 0 ) + drp->srate = sdr.mSampleRate; + + assert( drp->srate == sdr.mSampleRate ); + + AudioObjectPropertyAddress listenerAddr = + { + kAudioObjectPropertySelectorWildcard, + kAudioObjectPropertyScopeWildcard, + kAudioObjectPropertyElementWildcard + }; + + // install a stream property listener + AudioObjectAddPropertyListener(streamIdArray[i], &listenerAddr, cmApOsxSystemStreamPropertyListenerProc, drp ); + + + pktArray[i].devIdx = devIdx; + pktArray[i].begChIdx = chIdx; + pktArray[i].chCnt = ablp->mBuffers[i].mNumberChannels; + pktArray[i].audioFramesCnt = ablp->mBuffers[i].mDataByteSize / (sizeof(cmApSample_t) * ablp->mBuffers[i].mNumberChannels); //sdr.mFramesPerPacket; + pktArray[i].flags = kInterleavedApFl | kFloatApFl; + pktArray[i].bitsPerSample = sizeof(cmApSample_t) * 8; + pktArray[i].audioBytesPtr = NULL; + pktArray[i].userCbPtr = NULL; // the userCbPtr value isn't availabe until the cmApOsxDeviceSetup() + + // verify that all buffers on this device have the same size + assert(i==0 || pktArray[i].audioFramesCnt == pktArray[i-1].audioFramesCnt ); + + chIdx += ablp->mBuffers[i].mNumberChannels; + + if( inputFl ) { - rc = _cmApOsxOsError(err,&_cmApOsxRoot); - goto errLabel; + // track the total number of input channels on this device + drp->inChCnt = chIdx; + + // track the frames per cycle on this device + if( drp->inFramesPerCycle == 0 ) + drp->inFramesPerCycle = pktArray[i].audioFramesCnt; + + + assert( drp->inFramesPerCycle == pktArray[i].audioFramesCnt ); } + else + { + drp->outChCnt = chIdx; - // in buf array - if((rc = _cmApOsxGetBufferCfg(i,devIdArray[i], true, devArray + i )) != kOkApRC ) - goto errLabel; + if( drp->outFramesPerCycle == 0 ) + drp->outFramesPerCycle = pktArray[i].audioFramesCnt; - // out buf array - if((rc = _cmApOsxGetBufferCfg(i,devIdArray[i], false, devArray + i)) != kOkApRC ) - goto errLabel; + assert( drp->outFramesPerCycle == pktArray[i].audioFramesCnt ); - - devArray[i].devId = devIdArray[i]; - devArray[i].devIdx = i; - devArray[i].callbackPtr = NULL; - devArray[i].timeOutMs = 1000; - devArray[i].ioProcId = NULL; + } } errLabel: + cmMemPtrFree(&streamIdArray); + cmMemPtrFree(&ablp); + return rc; +} - if( devIdArray != NULL ) - cmMemFree(devIdArray); - if( rc == kOkApRC ) +OSStatus _cmApOsxSetSampleRate( cmApOsxDevRecd* drp, bool inputFl, double newSampleRate ) +{ + OSStatus err = noErr; + unsigned waitMs = 0; + AudioStreamID* streamIdArray = NULL; + AudioObjectPropertyAddress addr = + { + kAudioDevicePropertyStreamConfiguration, + inputFl ? kAudioDevicePropertyScopeInput : kAudioObjectPropertyScopeOutput, + kAudioObjectPropertyElementMaster + }; + + unsigned streamCnt; + unsigned i; + + // allocate a stream id array + if((err = _cmApOsxAllocateStreamIdArray( drp->devId, inputFl, &streamIdArray, &streamCnt )) != noErr ) + return _cmApOsxOsError(err,&_cmApOsxRoot); + + // for each stream on this device + for(i=0; idevId, &addr, 0, NULL, &byteCnt)) != noErr ) + { + _cmApOsxOsError(err,&_cmApOsxRoot); + goto errLabel; + } + + assert( byteCnt == sizeof(sdr) ); + + // get the stream desc recd + if((err=AudioObjectGetPropertyData(drp->devId, &addr, 0, NULL, &byteCnt, &sdr)) != noErr ) + { + _cmApOsxOsError(err,&_cmApOsxRoot); + goto errLabel; + } + + // if the format has not already been set + if( sdr.mSampleRate != newSampleRate ) + { + + //printf("Changing %s stream %i sample rate from %f to %f.\n",(inputFl?"input":"output"),i,sdr.mSampleRate,newSampleRate); + + // change the sample rate + sdr.mSampleRate = newSampleRate; + + drp->fmtChangeDevId = -1; + drp->fmtChangeObjId = -1; + + // attempt to change the sample rate + addr.mSelector = kAudioDevicePropertyStreamFormat; + if((err = AudioObjectSetPropertyData( streamIdArray[i], &addr, 0, NULL, sizeof(sdr), &sdr)) != noErr ) + { + err = _cmApOsxOsError(err,&_cmApOsxRoot); + goto errLabel; + } + + // wait for confirmation + waitMs = 0; + while( drp->fmtChangeDevId != drp->devId + && drp->fmtChangeObjId != streamIdArray[i] + && waitMs < drp->timeOutMs ) + { + const unsigned waitIncrMs = 20; + usleep(waitIncrMs*1000); + waitMs += waitIncrMs; + } + + // wait timed out + if( waitMs >= drp->timeOutMs ) + { + err = _cmApOsxOsError(kAudioHardwareUnspecifiedError,&_cmApOsxRoot); + goto errLabel; + } + else + { + // read back the format to be really sure it changed + addr.mSelector = kAudioDevicePropertyStreamFormat; + if((err=AudioObjectGetPropertyData(drp->devId, &addr, 0, NULL, &byteCnt, &sdr)) != noErr ) + { + _cmApOsxOsError(err,&_cmApOsxRoot); + goto errLabel; + } + + + assert( sdr.mSampleRate == newSampleRate ); + } + } + + } + + errLabel: + cmMemPtrFree(&streamIdArray); + + return err; +} + +OSStatus _cmApOsxSetFramesPerCycle( cmApOsxDevRecd* drp, Boolean inputFl, unsigned newFramesPerCycle ) +{ + OSStatus err = noErr; + AudioValueRange r; + UInt32 outByteCnt = sizeof(r); + UInt32 curFramesPerCycle = 0; + AudioObjectPropertyAddress addr = + { + kAudioDevicePropertyBufferFrameSizeRange, + inputFl ? kAudioDevicePropertyScopeInput : kAudioObjectPropertyScopeOutput, + kAudioObjectPropertyElementMaster + }; + + + // get the frame size range + if ((err=AudioObjectGetPropertyData(drp->devId, &addr, 0, NULL, &outByteCnt, &r)) != noErr ) + return _cmApOsxOsError(err,&_cmApOsxRoot); + + // verify that the requested frame size is within the acceptable frame size range + if( newFramesPerCycledevId, &addr, 0, NULL, &outByteCnt, &curFramesPerCycle)) != noErr ) + return _cmApOsxOsError(err,&_cmApOsxRoot); + + + // if the cur value is the same as the new value then there is nothing to do + if( curFramesPerCycle == newFramesPerCycle ) + return noErr; + + //printf("Changing %s frames per cycle from %i to %i.\n",(inputFl?"input":"output"),curFramesPerCycle,newFramesPerCycle); + + // attempt to set the new value + if((err = AudioObjectSetPropertyData( drp->devId, &addr, 0, NULL, sizeof(newFramesPerCycle), &newFramesPerCycle)) != noErr ) + return _cmApOsxOsError(err,&_cmApOsxRoot); + + // wait for the value to actually change + unsigned waitMs = 0; + while( waitMs < drp->timeOutMs ) + { + const unsigned waitIncrMs = 20; + usleep(waitIncrMs*1000); + + // read the parameter value + outByteCnt = sizeof(curFramesPerCycle); + if ((err=AudioObjectGetPropertyData(drp->devId, &addr, 0, NULL, &outByteCnt, &curFramesPerCycle)) != noErr ) + return _cmApOsxOsError(err,&_cmApOsxRoot); + + + // if the parameter value equals the new value then the change has taken effect + if( curFramesPerCycle == newFramesPerCycle ) + break; + + waitMs += waitIncrMs; + } + + // wait timed out + if( waitMs >= drp->timeOutMs ) + return _cmApOsxOsError(kAudioHardwareUnspecifiedError,&_cmApOsxRoot); + + return noErr; +} + + +cmApRC_t cmApOsxInitialize( cmRpt_t* rpt, unsigned baseApDevIdx ) +{ + + cmApRC_t rc = kOkApRC; + cmApOsxRoot* p = &_cmApOsxRoot; + UInt32 outByteCnt = 0; + OSStatus err = noErr; + AudioDeviceID* devIdArray = NULL; + unsigned i = 0; + AudioObjectPropertyAddress thePropAddr = + { + kAudioHardwarePropertyDevices, + kAudioObjectPropertyScopeGlobal, + kAudioObjectPropertyElementMaster + }; + + if((rc = cmApOsxFinalize()) != kOkApRC ) return rc; + p->rpt = rpt; + p->devArray = NULL; - cmApOsxFinalize(); + // get the size of the device ID array in bytes + if((err = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &thePropAddr, 0, NULL, &outByteCnt)) != noErr ) + return _cmApOsxOsError(err,&_cmApOsxRoot); + + assert( outByteCnt > 0 ); + + // calc. the device count + p->devCnt = outByteCnt / sizeof(AudioDeviceID); + + assert( p->devCnt*sizeof(AudioDeviceID) == outByteCnt ); + + // allocate space for the device id array and the device array + devIdArray = cmMemAllocZ( AudioDeviceID, p->devCnt ); + p->devArray = cmMemAllocZ( cmApOsxDevRecd, p->devCnt ); + + // get the deviceID array into devIdArray[ devCnt ] + if ((err=AudioObjectGetPropertyData(kAudioObjectSystemObject, &thePropAddr, 0, NULL, &outByteCnt, devIdArray)) != noErr ) + { + rc = _cmApOsxOsError(err,&_cmApOsxRoot); + goto errLabel; + } + + // for each device + for(i=0; idevCnt; ++i) + { + + // get device name + if(( err = _cmApOsxAllocPropertyCFString(devIdArray[i],kAudioObjectPropertyName,kAudioObjectPropertyScopeGlobal,kAudioObjectPropertyElementMaster,&p->devArray[i].nameStr)) != noErr) + { + rc = _cmApOsxOsError(err,&_cmApOsxRoot); + goto errLabel; + } + + // get manufacturer + if(( rc = _cmApOsxAllocPropertyCFString(devIdArray[i],kAudioObjectPropertyManufacturer,kAudioObjectPropertyScopeGlobal,kAudioObjectPropertyElementMaster,&p->devArray[i].mfgStr)) != noErr) + goto errLabel; + + if(( rc = _cmApOsxGetBufferConfig(i, devIdArray[i], true, p->devArray + i )) != kOkApRC ) + goto errLabel; + + if(( rc = _cmApOsxGetBufferConfig(i, devIdArray[i], false, p->devArray + i )) != kOkApRC ) + goto errLabel; + + //printf("%s %s\n",p->devArray[i].mfgStr,p->devArray[i].nameStr); + p->devArray[i].devId = devIdArray[i]; + p->devArray[i].devIdx = i; + p->devArray[i].callbackPtr = NULL; + p->devArray[i].timeOutMs = 1000; + p->devArray[i].ioProcId = NULL; + + } + + errLabel: + + cmMemFree(devIdArray); + + if( rc != noErr ) + cmApOsxFinalize(); return rc; } -cmApRC_t cmApOsxFinalize() +cmApRC_t cmApOsxFinalize() { - cmApOsxDevRecd* devArray = _cmApOsxRoot.devArray; - unsigned i = 0; - OSStatus err; + cmApRC_t rc = kOkApRC; + cmApOsxRoot* p = &_cmApOsxRoot; + unsigned i; + OSStatus err; - for(i=0; i<_cmApOsxRoot.devCnt; ++i) + for(i=0; idevCnt; ++i) { + cmApOsxDevRecd* drp = p->devArray + i; + + unsigned j; for(j=0; j<2; ++j) @@ -606,46 +678,40 @@ cmApRC_t cmApOsxFinalize() unsigned streamCnt; unsigned k; - if((err = _cmApOsxAllocStreamIdArray(devArray[i].devId, true,&streamIdArray,&streamCnt )) != noErr ) - _cmApOsxOsError(err,&_cmApOsxRoot); + if((err = _cmApOsxAllocateStreamIdArray(drp->devId, j==0, &streamIdArray,&streamCnt )) != noErr ) + _cmApOsxOsError(err,p); for(k=0; kioProcId != NULL ) + if((err = AudioDeviceDestroyIOProcID( drp->devId, drp->ioProcId )) != noErr ) + _cmApOsxOsError(err,p); + - if( devArray[i].nameStr != NULL ) - cmMemFree(devArray[i].nameStr); - - if( devArray[i].mfgStr != NULL ) - cmMemFree(devArray[i].mfgStr); - - if( devArray[i].inPktArray != NULL ) - cmMemFree( devArray[i].inPktArray ); - - if( devArray[i].outPktArray!= NULL ) - cmMemFree( devArray[i].outPktArray ); + cmMemFree(drp->nameStr); + cmMemFree(drp->mfgStr); + cmMemFree(drp->inPktArray); + cmMemFree(drp->outPktArray); } - if( devArray != NULL ) - { - cmMemFree(devArray); - _cmApOsxRoot.devArray = NULL; - _cmApOsxRoot.devCnt = 0; - } - - return kOkApRC; + cmMemFree(p->devArray); + return rc; } cmApRC_t cmApOsxDeviceCount() @@ -684,10 +750,10 @@ cmApRC_t cmApOsxDeviceSetup( { assert( devIdx < _cmApOsxRoot.devCnt ); - //cmApRC_t rc = kOkApRC; - OSStatus err; + cmApOsxRoot* p = &_cmApOsxRoot; cmApOsxDevRecd* drp = _cmApOsxRoot.devArray + devIdx; - unsigned j; + unsigned j; + OSStatus err; if( cmApOsxDeviceIsStarted(devIdx) ) cmApOsxDeviceStop(devIdx); @@ -729,20 +795,22 @@ cmApRC_t cmApOsxDeviceSetup( drp->callbackPtr = callbackPtr; + // if the io if( drp->ioProcId != NULL ) if((err = AudioDeviceDestroyIOProcID( drp->devId, drp->ioProcId )) != noErr ) - _cmApOsxOsError(err,&_cmApOsxRoot); + _cmApOsxOsError(err,p); // set the io proc drp->ioProcId = NULL; - //if( (err = AudioDeviceAddIOProc(drp->devId,_cmApOsxSystemDeviceIOProc,(void*)drp) ) != noErr ) + if( (err = AudioDeviceCreateIOProcID(drp->devId,_cmApOsxSystemDeviceIOProc,(void*)drp,&drp->ioProcId) ) != noErr ) { - _cmApOsxOsError(err,&_cmApOsxRoot); + _cmApOsxOsError(err,p); return kSysErrApRC; } + return kOkApRC; @@ -751,13 +819,11 @@ cmApRC_t cmApOsxDeviceSetup( return kSysErrApRC; } - cmApRC_t cmApOsxDeviceStart( unsigned devIdx ) { assert( devIdx < _cmApOsxRoot.devCnt ); OSStatus err; - //if( (err = AudioDeviceStart(_cmApOsxRoot.devArray[devIdx].devId,_cmApOsxSystemDeviceIOProc)) != noErr ) if( (err = AudioDeviceStart(_cmApOsxRoot.devArray[devIdx].devId,_cmApOsxRoot.devArray[devIdx].ioProcId)) != noErr ) { _cmApOsxOsError(err,&_cmApOsxRoot); @@ -773,7 +839,6 @@ cmApRC_t cmApOsxDeviceStop( unsigned devIdx ) OSStatus err; - //if( (err = AudioDeviceStop(_cmApOsxRoot.devArray[devIdx].devId,_cmApOsxSystemDeviceIOProc)) != noErr ) if( (err = AudioDeviceStop(_cmApOsxRoot.devArray[devIdx].devId,_cmApOsxRoot.devArray[devIdx].ioProcId)) != noErr ) { _cmApOsxOsError(err,&_cmApOsxRoot); @@ -783,11 +848,34 @@ cmApRC_t cmApOsxDeviceStop( unsigned devIdx ) return kOkApRC; } -bool cmApOsxDeviceIsStarted( unsigned devIdx ) +bool cmApOsxDeviceIsStarted( unsigned devIdx ) { assert( devIdx < _cmApOsxRoot.devCnt ); return false; } + + +void cmApOsxReport( cmRpt_t* rpt ) +{ + cmApOsxRoot* p = &_cmApOsxRoot; + unsigned i; + + for(i=0; idevCnt; ++i) + { + cmApOsxDevRecd* drp = p->devArray + i; + + cmRptPrintf(rpt,"in ch:%2i | out ch:%2i | started:%1i | sr:%7.1f %s %s\n", + drp->inChCnt, + drp->outChCnt, + drp->srate, + cmApOsxDeviceIsStarted( i ), + cmStringNullGuard(drp->mfgStr), + cmStringNullGuard(drp->nameStr)); + } + +} + + /* void apReport( apPrintFunc_t printFunc ) { @@ -799,3 +887,22 @@ void apReport( apPrintFunc_t printFunc ) } } */ + + +void cmApOsxTest( cmRpt_t* rpt ) +{ + printf("Start\n"); + cmApOsxInitialize(rpt,0); + + cmApOsxReport(rpt); + + if( cmApOsxDeviceSetup(2,48000.0,1024,NULL,NULL) != kOkApRC ) + printf("Setup failed.\n"); + + cmApOsxReport(rpt); + + cmApOsxFinalize(); + printf("Finish\n"); +} + +