libcm/osx/cmAudioPortOsx.c

909 lignes
26 KiB
C

//
// http://developer.apple.com/library/mac/#documentation/MusicAudio/Reference/CACoreAudioReference
//
#include <Carbon/Carbon.h>
#include "cmPrefix.h"
#include "cmGlobal.h"
#include "cmRpt.h"
#include "cmTime.h"
#include "cmAudioPort.h"
#include "cmMem.h"
#include "cmMallocDebug.h"
#include "cmAudioPortOsx.h"
#include <CoreAudio/CoreAudio.h>
#include <unistd.h> // usleap
typedef struct
{
unsigned chIdx;
unsigned chCnt;
unsigned framesPerBuf;
} cmApOsxBufRecd;
typedef struct
{
AudioDeviceID devId;
AudioDeviceIOProcID ioProcId;
unsigned devIdx;
cmApCallbackPtr_t callbackPtr;
char* mfgStr;
char* nameStr;
double srate;
unsigned inFramesPerCycle;
unsigned inChCnt;
unsigned inPktCnt;
cmApAudioPacket_t* inPktArray;
unsigned outFramesPerCycle;
unsigned outChCnt;
unsigned outPktCnt;
cmApAudioPacket_t* outPktArray;
unsigned timeOutMs;
AudioDeviceID fmtChangeDevId;
AudioObjectID fmtChangeObjId;
} cmApOsxDevRecd;
typedef struct
{
cmRpt_t* rpt;
cmApOsxDevRecd* devArray;
unsigned devCnt;
} cmApOsxRoot;
cmApOsxRoot _cmApOsxRoot = { NULL, NULL, 0 };
const char* _cmApOsxOsStatusToText( OSStatus errCode )
{
switch( errCode )
{
case kAudioHardwareNoError: return "No Error";
case kAudioHardwareNotRunningError: return "Not runing error";
case kAudioHardwareUnspecifiedError: return "Unspecified error";
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";
case kAudioDeviceUnsupportedFormatError: return "Unsupported format error";
case kAudioDevicePermissionsError: return "Permissions error";
}
return "Unknown error code";
}
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);
else
cmRptErrorf(r->rpt,"Audio Port Error: Unknown\n");
}
return kSysErrApRC;
}
#define _cmApOsxOsError( err, root ) _cmApOsxOsErrorRpt( err, root, __FUNCTION__, __FILE__, __LINE__ )
OSStatus cmApOsxSystemStreamPropertyListenerProc(
AudioObjectID audioObjId,
UInt32 channel,
const AudioObjectPropertyAddress* propertyAddr,
void* clientData)
{
cmApOsxDevRecd* drp = (cmApOsxDevRecd*)clientData;
drp->fmtChangeDevId = drp->devId;
drp->fmtChangeObjId = audioObjId;
return noErr;
}
OSStatus _cmApOsxSystemDeviceIOProc( AudioDeviceID inDevice,
const AudioTimeStamp* inNow,
const AudioBufferList* iabl,
const AudioTimeStamp* inInputTime,
AudioBufferList* oabl,
const AudioTimeStamp* inOutputTime,
void* inClientData)
{
cmApOsxDevRecd* drp = (cmApOsxDevRecd*)inClientData;
if( iabl->mNumberBuffers!=0 && iabl->mNumberBuffers != drp->inPktCnt )
return noErr;
if( oabl->mNumberBuffers!=0 && oabl->mNumberBuffers != drp->outPktCnt )
return noErr;
//assert( iabl->mNumberBuffers==0 || iabl->mNumberBuffers == drp->inPktCnt );
//assert( oabl->mNumberBuffers==0 || oabl->mNumberBuffers == drp->outPktCnt );
// setup the incoming packets (ADC->app)
const AudioBuffer* bp = iabl->mBuffers;
const AudioBuffer* ep = bp + iabl->mNumberBuffers;
cmApAudioPacket_t* pp = drp->inPktArray;
unsigned chIdx = 0;
for(; bp<ep; ++bp,++pp)
{
pp->audioBytesPtr = (float*)bp->mData;
pp->audioFramesCnt = bp->mDataByteSize / (bp->mNumberChannels * sizeof(cmApSample_t));
pp->begChIdx = chIdx;
pp->chCnt = bp->mNumberChannels;
chIdx += pp->chCnt;
}
// setup the outgoing packets (app->DAC)
bp = oabl->mBuffers;
ep = bp + oabl->mNumberBuffers;
pp = drp->outPktArray;
for(chIdx=0; bp<ep; ++bp,++pp)
{
pp->audioBytesPtr = (float*)bp->mData;
pp->audioFramesCnt = bp->mDataByteSize / (bp->mNumberChannels * sizeof(cmApSample_t));
pp->begChIdx = chIdx;
pp->chCnt = bp->mNumberChannels;
chIdx += pp->chCnt;
}
// call the app
drp->callbackPtr(
iabl->mNumberBuffers ? drp->inPktArray : NULL, iabl->mNumberBuffers,
oabl->mNumberBuffers ? drp->outPktArray : NULL, oabl->mNumberBuffers );
return noErr;
}
OSStatus _cmApOsxAllocPropertyCFString(
AudioDeviceID devId,
AudioObjectPropertySelector sel,
AudioObjectPropertyScope scope,
AudioObjectPropertyElement ele,
char** strPtrPtr )
{
CFStringRef cfStr;
UInt32 outByteCnt = sizeof(cfStr);;
OSStatus err = noErr;
AudioObjectPropertyAddress addr;
addr.mSelector = sel;
addr.mScope = scope;
addr.mElement = ele;
if((err = AudioObjectGetPropertyData(devId, &addr, 0, NULL, &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 _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;
}
// 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; i<ablp->mNumberBuffers; ++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;
}
// 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 )
{
// 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 _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; i<streamCnt; i++)
{
UInt32 byteCnt = 0;
AudioStreamBasicDescription sdr;
// get the size of the stream desc recd
addr.mSelector = kAudioDevicePropertyStreamFormat;
if((err = AudioObjectGetPropertyDataSize(drp->devId, &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( newFramesPerCycle<r.mMinimum || r.mMaximum < newFramesPerCycle )
return _cmApOsxOsError(err,&_cmApOsxRoot);
addr.mSelector = kAudioDevicePropertyBufferFrameSize;
outByteCnt = sizeof(UInt32);
// get the cur value off the param. to change
if ((err=AudioObjectGetPropertyData(drp->devId, &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;
// 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; i<p->devCnt; ++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 rc = kOkApRC;
cmApOsxRoot* p = &_cmApOsxRoot;
unsigned i;
OSStatus err;
for(i=0; i<p->devCnt; ++i)
{
cmApOsxDevRecd* drp = p->devArray + i;
unsigned j;
for(j=0; j<2; ++j)
{
AudioStreamID* streamIdArray;
unsigned streamCnt;
unsigned k;
if((err = _cmApOsxAllocateStreamIdArray(drp->devId, j==0, &streamIdArray,&streamCnt )) != noErr )
_cmApOsxOsError(err,p);
for(k=0; k<streamCnt; ++k)
{
AudioObjectPropertyAddress listenerAddr =
{
kAudioObjectPropertySelectorWildcard,
kAudioObjectPropertyScopeWildcard,
kAudioObjectPropertyElementWildcard
};
// install a stream property listener
AudioObjectRemovePropertyListener(streamIdArray[i], &listenerAddr, cmApOsxSystemStreamPropertyListenerProc, drp );
}
cmMemPtrFree(&streamIdArray);
}
if( drp->ioProcId != NULL )
if((err = AudioDeviceDestroyIOProcID( drp->devId, drp->ioProcId )) != noErr )
_cmApOsxOsError(err,p);
cmMemFree(drp->nameStr);
cmMemFree(drp->mfgStr);
cmMemFree(drp->inPktArray);
cmMemFree(drp->outPktArray);
}
cmMemFree(p->devArray);
return rc;
}
cmApRC_t cmApOsxDeviceCount()
{ return _cmApOsxRoot.devCnt; }
const char* cmApOsxDeviceLabel( unsigned devIdx )
{
assert( devIdx < _cmApOsxRoot.devCnt );
return _cmApOsxRoot.devArray[ devIdx ].nameStr;
}
unsigned cmApOsxDeviceChannelCount( unsigned devIdx, bool inputFl )
{
assert( devIdx < _cmApOsxRoot.devCnt );
return inputFl ? _cmApOsxRoot.devArray[ devIdx ].inChCnt : _cmApOsxRoot.devArray[ devIdx ].outChCnt;
}
double cmApOsxDeviceSampleRate( unsigned devIdx )
{
assert( devIdx < _cmApOsxRoot.devCnt );
return _cmApOsxRoot.devArray[ devIdx ].srate;
}
unsigned cmApOsxDeviceFramesPerCycle( unsigned devIdx, bool inputFl )
{
assert( devIdx < _cmApOsxRoot.devCnt );
return inputFl ? _cmApOsxRoot.devArray[ devIdx ].inFramesPerCycle : _cmApOsxRoot.devArray[ devIdx ].outFramesPerCycle;
}
cmApRC_t cmApOsxDeviceSetup(
unsigned devIdx,
double srate,
unsigned framesPerCycle,
cmApCallbackPtr_t callbackPtr,
void* userCbPtr )
{
assert( devIdx < _cmApOsxRoot.devCnt );
cmApOsxRoot* p = &_cmApOsxRoot;
cmApOsxDevRecd* drp = _cmApOsxRoot.devArray + devIdx;
unsigned j;
OSStatus err;
if( cmApOsxDeviceIsStarted(devIdx) )
cmApOsxDeviceStop(devIdx);
// set the sample rate
if( drp->srate != srate )
{
for(j=0; j<2; ++j )
if((err = _cmApOsxSetSampleRate(drp, j==0, srate )) != noErr )
goto errLabel;
drp->srate = srate;
}
// set the frames per cycle
for(j=0; j<2; ++j)
{
unsigned* fpcPtr = j==0 ? &drp->inFramesPerCycle : &drp->outFramesPerCycle;
if( framesPerCycle != (*fpcPtr) )
{
if((err = _cmApOsxSetFramesPerCycle(drp, j==0, framesPerCycle )) == noErr )
*fpcPtr = framesPerCycle;
else
goto errLabel;
}
}
// set the user callback data ptr in each packet on this device
for(j=0; j<2; ++j)
{
unsigned k;
cmApAudioPacket_t* packetArray = j==0 ? drp->inPktArray : drp->outPktArray;
unsigned pktCnt = j==0 ? drp->inPktCnt : drp->outPktCnt;
for(k=0; k<pktCnt; ++k)
packetArray[k].userCbPtr = userCbPtr;
}
drp->callbackPtr = callbackPtr;
// if the io
if( drp->ioProcId != NULL )
if((err = AudioDeviceDestroyIOProcID( drp->devId, drp->ioProcId )) != noErr )
_cmApOsxOsError(err,p);
// set the io proc
drp->ioProcId = NULL;
if( (err = AudioDeviceCreateIOProcID(drp->devId,_cmApOsxSystemDeviceIOProc,(void*)drp,&drp->ioProcId) ) != noErr )
{
_cmApOsxOsError(err,p);
return kSysErrApRC;
}
return kOkApRC;
errLabel:
return kSysErrApRC;
}
cmApRC_t cmApOsxDeviceStart( unsigned devIdx )
{
assert( devIdx < _cmApOsxRoot.devCnt );
OSStatus err;
if( (err = AudioDeviceStart(_cmApOsxRoot.devArray[devIdx].devId,_cmApOsxRoot.devArray[devIdx].ioProcId)) != noErr )
{
_cmApOsxOsError(err,&_cmApOsxRoot);
return kSysErrApRC;
}
return kOkApRC;
}
cmApRC_t cmApOsxDeviceStop( unsigned devIdx )
{
assert( devIdx < _cmApOsxRoot.devCnt );
OSStatus err;
if( (err = AudioDeviceStop(_cmApOsxRoot.devArray[devIdx].devId,_cmApOsxRoot.devArray[devIdx].ioProcId)) != noErr )
{
_cmApOsxOsError(err,&_cmApOsxRoot);
return kSysErrApRC;
}
return kOkApRC;
}
bool cmApOsxDeviceIsStarted( unsigned devIdx )
{
assert( devIdx < _cmApOsxRoot.devCnt );
return false;
}
void cmApOsxReport( cmRpt_t* rpt )
{
cmApOsxRoot* p = &_cmApOsxRoot;
unsigned i;
for(i=0; i<p->devCnt; ++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 )
{
unsigned i;
for(i=0; i<_cmApOsxRoot.devCnt; ++i)
{
cmApOsxDevRecd* drp = _cmApOsxRoot.devArray + i;
printf("%i in:%i out:%i %s %s\n",i,drp->inChCnt,drp->outChCnt,drp->nameStr,drp->mfgStr);
}
}
*/
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");
}