libcm is a C development framework with an emphasis on audio signal processing applications.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

cmAudioPortOsx.c 26KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908
  1. //
  2. // http://developer.apple.com/library/mac/#documentation/MusicAudio/Reference/CACoreAudioReference
  3. //
  4. #include <Carbon/Carbon.h>
  5. #include "cmPrefix.h"
  6. #include "cmGlobal.h"
  7. #include "cmRpt.h"
  8. #include "cmTime.h"
  9. #include "cmAudioPort.h"
  10. #include "cmMem.h"
  11. #include "cmMallocDebug.h"
  12. #include "cmAudioPortOsx.h"
  13. #include <CoreAudio/CoreAudio.h>
  14. #include <unistd.h> // usleap
  15. typedef struct
  16. {
  17. unsigned chIdx;
  18. unsigned chCnt;
  19. unsigned framesPerBuf;
  20. } cmApOsxBufRecd;
  21. typedef struct
  22. {
  23. AudioDeviceID devId;
  24. AudioDeviceIOProcID ioProcId;
  25. unsigned devIdx;
  26. cmApCallbackPtr_t callbackPtr;
  27. char* mfgStr;
  28. char* nameStr;
  29. double srate;
  30. unsigned inFramesPerCycle;
  31. unsigned inChCnt;
  32. unsigned inPktCnt;
  33. cmApAudioPacket_t* inPktArray;
  34. unsigned outFramesPerCycle;
  35. unsigned outChCnt;
  36. unsigned outPktCnt;
  37. cmApAudioPacket_t* outPktArray;
  38. unsigned timeOutMs;
  39. AudioDeviceID fmtChangeDevId;
  40. AudioObjectID fmtChangeObjId;
  41. } cmApOsxDevRecd;
  42. typedef struct
  43. {
  44. cmRpt_t* rpt;
  45. cmApOsxDevRecd* devArray;
  46. unsigned devCnt;
  47. } cmApOsxRoot;
  48. cmApOsxRoot _cmApOsxRoot = { NULL, NULL, 0 };
  49. const char* _cmApOsxOsStatusToText( OSStatus errCode )
  50. {
  51. switch( errCode )
  52. {
  53. case kAudioHardwareNoError: return "No Error";
  54. case kAudioHardwareNotRunningError: return "Not runing error";
  55. case kAudioHardwareUnspecifiedError: return "Unspecified error";
  56. case kAudioHardwareUnknownPropertyError: return "Unknown property error";
  57. case kAudioHardwareBadPropertySizeError: return "Bad property error";
  58. case kAudioHardwareIllegalOperationError: return "Illegal operation error";
  59. case kAudioHardwareBadObjectError: return "Bad object error";
  60. case kAudioHardwareBadDeviceError: return "Bad device error";
  61. case kAudioHardwareBadStreamError: return "Bad stream error";
  62. case kAudioHardwareUnsupportedOperationError: return "Unsupported operating error";
  63. case kAudioDeviceUnsupportedFormatError: return "Unsupported format error";
  64. case kAudioDevicePermissionsError: return "Permissions error";
  65. }
  66. return "Unknown error code";
  67. }
  68. cmApRC_t _cmApOsxOsErrorRpt( OSStatus err, cmApOsxRoot* r, const char* func, const char* file, int line )
  69. {
  70. char errStr[5];
  71. unsigned i;
  72. for(i=0; i<4; ++i)
  73. errStr[i] = (char)(err & (0x000000ff << i));
  74. errStr[4] = 0;
  75. if( r->rpt != NULL )
  76. {
  77. if( err != noErr )
  78. cmRptErrorf(r->rpt,"Audio Port Error : %s in %s line:%i %s\n",_cmApOsxOsStatusToText(err),func,line,file);
  79. else
  80. cmRptErrorf(r->rpt,"Audio Port Error: Unknown\n");
  81. }
  82. return kSysErrApRC;
  83. }
  84. #define _cmApOsxOsError( err, root ) _cmApOsxOsErrorRpt( err, root, __FUNCTION__, __FILE__, __LINE__ )
  85. OSStatus cmApOsxSystemStreamPropertyListenerProc(
  86. AudioObjectID audioObjId,
  87. UInt32 channel,
  88. const AudioObjectPropertyAddress* propertyAddr,
  89. void* clientData)
  90. {
  91. cmApOsxDevRecd* drp = (cmApOsxDevRecd*)clientData;
  92. drp->fmtChangeDevId = drp->devId;
  93. drp->fmtChangeObjId = audioObjId;
  94. return noErr;
  95. }
  96. OSStatus _cmApOsxSystemDeviceIOProc( AudioDeviceID inDevice,
  97. const AudioTimeStamp* inNow,
  98. const AudioBufferList* iabl,
  99. const AudioTimeStamp* inInputTime,
  100. AudioBufferList* oabl,
  101. const AudioTimeStamp* inOutputTime,
  102. void* inClientData)
  103. {
  104. cmApOsxDevRecd* drp = (cmApOsxDevRecd*)inClientData;
  105. if( iabl->mNumberBuffers!=0 && iabl->mNumberBuffers != drp->inPktCnt )
  106. return noErr;
  107. if( oabl->mNumberBuffers!=0 && oabl->mNumberBuffers != drp->outPktCnt )
  108. return noErr;
  109. //assert( iabl->mNumberBuffers==0 || iabl->mNumberBuffers == drp->inPktCnt );
  110. //assert( oabl->mNumberBuffers==0 || oabl->mNumberBuffers == drp->outPktCnt );
  111. // setup the incoming packets (ADC->app)
  112. const AudioBuffer* bp = iabl->mBuffers;
  113. const AudioBuffer* ep = bp + iabl->mNumberBuffers;
  114. cmApAudioPacket_t* pp = drp->inPktArray;
  115. unsigned chIdx = 0;
  116. for(; bp<ep; ++bp,++pp)
  117. {
  118. pp->audioBytesPtr = (float*)bp->mData;
  119. pp->audioFramesCnt = bp->mDataByteSize / (bp->mNumberChannels * sizeof(cmApSample_t));
  120. pp->begChIdx = chIdx;
  121. pp->chCnt = bp->mNumberChannels;
  122. chIdx += pp->chCnt;
  123. }
  124. // setup the outgoing packets (app->DAC)
  125. bp = oabl->mBuffers;
  126. ep = bp + oabl->mNumberBuffers;
  127. pp = drp->outPktArray;
  128. for(chIdx=0; bp<ep; ++bp,++pp)
  129. {
  130. pp->audioBytesPtr = (float*)bp->mData;
  131. pp->audioFramesCnt = bp->mDataByteSize / (bp->mNumberChannels * sizeof(cmApSample_t));
  132. pp->begChIdx = chIdx;
  133. pp->chCnt = bp->mNumberChannels;
  134. chIdx += pp->chCnt;
  135. }
  136. // call the app
  137. drp->callbackPtr(
  138. iabl->mNumberBuffers ? drp->inPktArray : NULL, iabl->mNumberBuffers,
  139. oabl->mNumberBuffers ? drp->outPktArray : NULL, oabl->mNumberBuffers );
  140. return noErr;
  141. }
  142. OSStatus _cmApOsxAllocPropertyCFString(
  143. AudioDeviceID devId,
  144. AudioObjectPropertySelector sel,
  145. AudioObjectPropertyScope scope,
  146. AudioObjectPropertyElement ele,
  147. char** strPtrPtr )
  148. {
  149. CFStringRef cfStr;
  150. UInt32 outByteCnt = sizeof(cfStr);;
  151. OSStatus err = noErr;
  152. AudioObjectPropertyAddress addr;
  153. addr.mSelector = sel;
  154. addr.mScope = scope;
  155. addr.mElement = ele;
  156. if((err = AudioObjectGetPropertyData(devId, &addr, 0, NULL, &outByteCnt, &cfStr)) != noErr )
  157. return err;
  158. CFIndex cfLen = CFStringGetLength(cfStr) * 2;
  159. char* cStr = cmMemAllocZ( char, cfLen+1 );
  160. cStr[0] = 0;
  161. if( CFStringGetCString( cfStr, cStr, cfLen, kCFStringEncodingUTF8 ) )
  162. cStr[cfLen]=0;
  163. CFRelease(cfStr);
  164. *strPtrPtr = cStr;
  165. return noErr;
  166. }
  167. // Note: the streamIdArray* allocated by this function must be released by the calling function.
  168. OSStatus _cmApOsxAllocateStreamIdArray( AudioDeviceID devId, Boolean inputFl, AudioStreamID** streamIdArrayPtr, unsigned* streamIdCntPtr )
  169. {
  170. UInt32 byteCnt = 0;
  171. OSStatus err = noErr;
  172. AudioObjectPropertyAddress addr =
  173. {
  174. kAudioHardwarePropertyDevices,
  175. inputFl ? kAudioDevicePropertyScopeInput : kAudioObjectPropertyScopeOutput,
  176. kAudioObjectPropertyElementMaster
  177. };
  178. *streamIdArrayPtr = NULL;
  179. *streamIdCntPtr = 0;
  180. // get the length of the stream id array
  181. addr.mSelector = kAudioDevicePropertyStreams;
  182. if((err = AudioObjectGetPropertyDataSize(devId, &addr, 0, NULL, &byteCnt)) != noErr )
  183. return _cmApOsxOsError(err,&_cmApOsxRoot);
  184. if( byteCnt <= 0 )
  185. goto errLabel;
  186. // get the count of streams
  187. *streamIdCntPtr = byteCnt / sizeof(AudioStreamID);
  188. // allocate the stream id array
  189. *streamIdArrayPtr = cmMemAllocZ( AudioStreamID, *streamIdCntPtr );
  190. // verify that the size of the stream id array is an integer multiple of the sizeof(AudioStreamID)
  191. assert( *streamIdCntPtr * sizeof(AudioStreamID) == byteCnt );
  192. // fill the stream id array
  193. if ((err=AudioObjectGetPropertyData(devId, &addr, 0, NULL, &byteCnt, *streamIdArrayPtr)) != noErr )
  194. {
  195. _cmApOsxOsError(err,&_cmApOsxRoot);
  196. goto errLabel;
  197. }
  198. errLabel:
  199. return noErr;
  200. }
  201. // Note: *bufArrayPtr must be deallocated by the caller.
  202. cmApRC_t _cmApOsxGetBufferConfig(unsigned devIdx, AudioDeviceID devId, Boolean inputFl, cmApOsxDevRecd* drp )
  203. {
  204. cmApRC_t rc = kOkApRC;
  205. OSStatus err = noErr;
  206. UInt32 byteCnt = 0;
  207. AudioBufferList* ablp = NULL;
  208. unsigned streamCnt = 0;
  209. AudioStreamID* streamIdArray = NULL;
  210. unsigned i = 0;
  211. unsigned chIdx = 0;
  212. AudioObjectPropertyAddress addr =
  213. {
  214. kAudioDevicePropertyStreamConfiguration,
  215. inputFl ? kAudioDevicePropertyScopeInput : kAudioObjectPropertyScopeOutput,
  216. kAudioObjectPropertyElementMaster
  217. };
  218. // get the size of stream cfg buffer
  219. if((err = AudioObjectGetPropertyDataSize(devId, &addr, 0, NULL, &byteCnt)) != noErr )
  220. return _cmApOsxOsError(err,&_cmApOsxRoot);
  221. // allocate memory to hold the AudioBufferList
  222. ablp = (AudioBufferList*)cmMemMallocZ(byteCnt);
  223. // get the audio buffer list array
  224. if ((err=AudioObjectGetPropertyData(devId, &addr, 0, NULL, &byteCnt, ablp)) != noErr )
  225. {
  226. rc = _cmApOsxOsError(err,&_cmApOsxRoot);
  227. goto errLabel;
  228. }
  229. // allocate a stream id array
  230. if((err = _cmApOsxAllocateStreamIdArray( devId, inputFl, &streamIdArray, &streamCnt )) != noErr )
  231. {
  232. rc = _cmApOsxOsError(err,&_cmApOsxRoot);
  233. goto errLabel;
  234. }
  235. // the number of buffers and the number of frames must be the same
  236. assert( streamCnt == ablp->mNumberBuffers);
  237. cmApAudioPacket_t* pktArray = cmMemAllocZ(cmApAudioPacket_t,ablp->mNumberBuffers);
  238. if( inputFl )
  239. {
  240. drp->inPktCnt = ablp->mNumberBuffers;
  241. drp->inPktArray = pktArray;
  242. }
  243. else
  244. {
  245. drp->outPktCnt = ablp->mNumberBuffers;
  246. drp->outPktArray = pktArray;
  247. }
  248. //
  249. for(i=0; i<ablp->mNumberBuffers; ++i)
  250. {
  251. AudioStreamBasicDescription sdr;
  252. // get the size of the stream desc recd
  253. addr.mSelector = kAudioDevicePropertyStreamFormat;
  254. if((err = AudioObjectGetPropertyDataSize(devId, &addr, 0, NULL, &byteCnt)) != noErr )
  255. {
  256. _cmApOsxOsError(err,&_cmApOsxRoot);
  257. goto errLabel;
  258. }
  259. assert( byteCnt == sizeof(sdr) );
  260. // get the stream desc recd
  261. if((err=AudioObjectGetPropertyData(devId, &addr, 0, NULL, &byteCnt, &sdr)) != noErr )
  262. {
  263. rc = _cmApOsxOsError(err,&_cmApOsxRoot);
  264. goto errLabel;
  265. }
  266. // assert that the data format is packed float32 native endian
  267. // assert( IsAudioFormatNativeEndian(sdr) );
  268. // 0x6c70636d = lpcm
  269. // printf("%s %i dev:%i sr:%f fmtId:0x%lx fmtFl:0x%lx bytesPerPkt:%li frmsPerPkt:%li bytesPerFrm:%li chsPerFrm:%li bitsPerCh:%li\n",
  270. // inputFl?"i":"o",i,devIdx,sdr.mSampleRate,sdr.mFormatID,sdr.mFormatFlags,sdr.mBytesPerPacket,sdr.mFramesPerPacket,sdr.mBytesPerFrame,sdr.mChannelsPerFrame,sdr.mBitsPerChannel);
  271. // assert that all buffers have the sample rate of the device
  272. if( drp->srate == 0 )
  273. drp->srate = sdr.mSampleRate;
  274. assert( drp->srate == sdr.mSampleRate );
  275. AudioObjectPropertyAddress listenerAddr =
  276. {
  277. kAudioObjectPropertySelectorWildcard,
  278. kAudioObjectPropertyScopeWildcard,
  279. kAudioObjectPropertyElementWildcard
  280. };
  281. // install a stream property listener
  282. AudioObjectAddPropertyListener(streamIdArray[i], &listenerAddr, cmApOsxSystemStreamPropertyListenerProc, drp );
  283. pktArray[i].devIdx = devIdx;
  284. pktArray[i].begChIdx = chIdx;
  285. pktArray[i].chCnt = ablp->mBuffers[i].mNumberChannels;
  286. pktArray[i].audioFramesCnt = ablp->mBuffers[i].mDataByteSize / (sizeof(cmApSample_t) * ablp->mBuffers[i].mNumberChannels); //sdr.mFramesPerPacket;
  287. pktArray[i].flags = kInterleavedApFl | kFloatApFl;
  288. pktArray[i].bitsPerSample = sizeof(cmApSample_t) * 8;
  289. pktArray[i].audioBytesPtr = NULL;
  290. pktArray[i].userCbPtr = NULL; // the userCbPtr value isn't availabe until the cmApOsxDeviceSetup()
  291. // verify that all buffers on this device have the same size
  292. assert(i==0 || pktArray[i].audioFramesCnt == pktArray[i-1].audioFramesCnt );
  293. chIdx += ablp->mBuffers[i].mNumberChannels;
  294. if( inputFl )
  295. {
  296. // track the total number of input channels on this device
  297. drp->inChCnt = chIdx;
  298. // track the frames per cycle on this device
  299. if( drp->inFramesPerCycle == 0 )
  300. drp->inFramesPerCycle = pktArray[i].audioFramesCnt;
  301. assert( drp->inFramesPerCycle == pktArray[i].audioFramesCnt );
  302. }
  303. else
  304. {
  305. drp->outChCnt = chIdx;
  306. if( drp->outFramesPerCycle == 0 )
  307. drp->outFramesPerCycle = pktArray[i].audioFramesCnt;
  308. assert( drp->outFramesPerCycle == pktArray[i].audioFramesCnt );
  309. }
  310. }
  311. errLabel:
  312. cmMemPtrFree(&streamIdArray);
  313. cmMemPtrFree(&ablp);
  314. return rc;
  315. }
  316. OSStatus _cmApOsxSetSampleRate( cmApOsxDevRecd* drp, bool inputFl, double newSampleRate )
  317. {
  318. OSStatus err = noErr;
  319. unsigned waitMs = 0;
  320. AudioStreamID* streamIdArray = NULL;
  321. AudioObjectPropertyAddress addr =
  322. {
  323. kAudioDevicePropertyStreamConfiguration,
  324. inputFl ? kAudioDevicePropertyScopeInput : kAudioObjectPropertyScopeOutput,
  325. kAudioObjectPropertyElementMaster
  326. };
  327. unsigned streamCnt;
  328. unsigned i;
  329. // allocate a stream id array
  330. if((err = _cmApOsxAllocateStreamIdArray( drp->devId, inputFl, &streamIdArray, &streamCnt )) != noErr )
  331. return _cmApOsxOsError(err,&_cmApOsxRoot);
  332. // for each stream on this device
  333. for(i=0; i<streamCnt; i++)
  334. {
  335. UInt32 byteCnt = 0;
  336. AudioStreamBasicDescription sdr;
  337. // get the size of the stream desc recd
  338. addr.mSelector = kAudioDevicePropertyStreamFormat;
  339. if((err = AudioObjectGetPropertyDataSize(drp->devId, &addr, 0, NULL, &byteCnt)) != noErr )
  340. {
  341. _cmApOsxOsError(err,&_cmApOsxRoot);
  342. goto errLabel;
  343. }
  344. assert( byteCnt == sizeof(sdr) );
  345. // get the stream desc recd
  346. if((err=AudioObjectGetPropertyData(drp->devId, &addr, 0, NULL, &byteCnt, &sdr)) != noErr )
  347. {
  348. _cmApOsxOsError(err,&_cmApOsxRoot);
  349. goto errLabel;
  350. }
  351. // if the format has not already been set
  352. if( sdr.mSampleRate != newSampleRate )
  353. {
  354. //printf("Changing %s stream %i sample rate from %f to %f.\n",(inputFl?"input":"output"),i,sdr.mSampleRate,newSampleRate);
  355. // change the sample rate
  356. sdr.mSampleRate = newSampleRate;
  357. drp->fmtChangeDevId = -1;
  358. drp->fmtChangeObjId = -1;
  359. // attempt to change the sample rate
  360. addr.mSelector = kAudioDevicePropertyStreamFormat;
  361. if((err = AudioObjectSetPropertyData( streamIdArray[i], &addr, 0, NULL, sizeof(sdr), &sdr)) != noErr )
  362. {
  363. err = _cmApOsxOsError(err,&_cmApOsxRoot);
  364. goto errLabel;
  365. }
  366. // wait for confirmation
  367. waitMs = 0;
  368. while( drp->fmtChangeDevId != drp->devId
  369. && drp->fmtChangeObjId != streamIdArray[i]
  370. && waitMs < drp->timeOutMs )
  371. {
  372. const unsigned waitIncrMs = 20;
  373. usleep(waitIncrMs*1000);
  374. waitMs += waitIncrMs;
  375. }
  376. // wait timed out
  377. if( waitMs >= drp->timeOutMs )
  378. {
  379. err = _cmApOsxOsError(kAudioHardwareUnspecifiedError,&_cmApOsxRoot);
  380. goto errLabel;
  381. }
  382. else
  383. {
  384. // read back the format to be really sure it changed
  385. addr.mSelector = kAudioDevicePropertyStreamFormat;
  386. if((err=AudioObjectGetPropertyData(drp->devId, &addr, 0, NULL, &byteCnt, &sdr)) != noErr )
  387. {
  388. _cmApOsxOsError(err,&_cmApOsxRoot);
  389. goto errLabel;
  390. }
  391. assert( sdr.mSampleRate == newSampleRate );
  392. }
  393. }
  394. }
  395. errLabel:
  396. cmMemPtrFree(&streamIdArray);
  397. return err;
  398. }
  399. OSStatus _cmApOsxSetFramesPerCycle( cmApOsxDevRecd* drp, Boolean inputFl, unsigned newFramesPerCycle )
  400. {
  401. OSStatus err = noErr;
  402. AudioValueRange r;
  403. UInt32 outByteCnt = sizeof(r);
  404. UInt32 curFramesPerCycle = 0;
  405. AudioObjectPropertyAddress addr =
  406. {
  407. kAudioDevicePropertyBufferFrameSizeRange,
  408. inputFl ? kAudioDevicePropertyScopeInput : kAudioObjectPropertyScopeOutput,
  409. kAudioObjectPropertyElementMaster
  410. };
  411. // get the frame size range
  412. if ((err=AudioObjectGetPropertyData(drp->devId, &addr, 0, NULL, &outByteCnt, &r)) != noErr )
  413. return _cmApOsxOsError(err,&_cmApOsxRoot);
  414. // verify that the requested frame size is within the acceptable frame size range
  415. if( newFramesPerCycle<r.mMinimum || r.mMaximum < newFramesPerCycle )
  416. return _cmApOsxOsError(err,&_cmApOsxRoot);
  417. addr.mSelector = kAudioDevicePropertyBufferFrameSize;
  418. outByteCnt = sizeof(UInt32);
  419. // get the cur value off the param. to change
  420. if ((err=AudioObjectGetPropertyData(drp->devId, &addr, 0, NULL, &outByteCnt, &curFramesPerCycle)) != noErr )
  421. return _cmApOsxOsError(err,&_cmApOsxRoot);
  422. // if the cur value is the same as the new value then there is nothing to do
  423. if( curFramesPerCycle == newFramesPerCycle )
  424. return noErr;
  425. //printf("Changing %s frames per cycle from %i to %i.\n",(inputFl?"input":"output"),curFramesPerCycle,newFramesPerCycle);
  426. // attempt to set the new value
  427. if((err = AudioObjectSetPropertyData( drp->devId, &addr, 0, NULL, sizeof(newFramesPerCycle), &newFramesPerCycle)) != noErr )
  428. return _cmApOsxOsError(err,&_cmApOsxRoot);
  429. // wait for the value to actually change
  430. unsigned waitMs = 0;
  431. while( waitMs < drp->timeOutMs )
  432. {
  433. const unsigned waitIncrMs = 20;
  434. usleep(waitIncrMs*1000);
  435. // read the parameter value
  436. outByteCnt = sizeof(curFramesPerCycle);
  437. if ((err=AudioObjectGetPropertyData(drp->devId, &addr, 0, NULL, &outByteCnt, &curFramesPerCycle)) != noErr )
  438. return _cmApOsxOsError(err,&_cmApOsxRoot);
  439. // if the parameter value equals the new value then the change has taken effect
  440. if( curFramesPerCycle == newFramesPerCycle )
  441. break;
  442. waitMs += waitIncrMs;
  443. }
  444. // wait timed out
  445. if( waitMs >= drp->timeOutMs )
  446. return _cmApOsxOsError(kAudioHardwareUnspecifiedError,&_cmApOsxRoot);
  447. return noErr;
  448. }
  449. cmApRC_t cmApOsxInitialize( cmRpt_t* rpt, unsigned baseApDevIdx )
  450. {
  451. cmApRC_t rc = kOkApRC;
  452. cmApOsxRoot* p = &_cmApOsxRoot;
  453. UInt32 outByteCnt = 0;
  454. OSStatus err = noErr;
  455. AudioDeviceID* devIdArray = NULL;
  456. unsigned i = 0;
  457. AudioObjectPropertyAddress thePropAddr =
  458. {
  459. kAudioHardwarePropertyDevices,
  460. kAudioObjectPropertyScopeGlobal,
  461. kAudioObjectPropertyElementMaster
  462. };
  463. if((rc = cmApOsxFinalize()) != kOkApRC )
  464. return rc;
  465. p->rpt = rpt;
  466. p->devArray = NULL;
  467. // get the size of the device ID array in bytes
  468. if((err = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &thePropAddr, 0, NULL, &outByteCnt)) != noErr )
  469. return _cmApOsxOsError(err,&_cmApOsxRoot);
  470. assert( outByteCnt > 0 );
  471. // calc. the device count
  472. p->devCnt = outByteCnt / sizeof(AudioDeviceID);
  473. assert( p->devCnt*sizeof(AudioDeviceID) == outByteCnt );
  474. // allocate space for the device id array and the device array
  475. devIdArray = cmMemAllocZ( AudioDeviceID, p->devCnt );
  476. p->devArray = cmMemAllocZ( cmApOsxDevRecd, p->devCnt );
  477. // get the deviceID array into devIdArray[ devCnt ]
  478. if ((err=AudioObjectGetPropertyData(kAudioObjectSystemObject, &thePropAddr, 0, NULL, &outByteCnt, devIdArray)) != noErr )
  479. {
  480. rc = _cmApOsxOsError(err,&_cmApOsxRoot);
  481. goto errLabel;
  482. }
  483. // for each device
  484. for(i=0; i<p->devCnt; ++i)
  485. {
  486. // get device name
  487. if(( err = _cmApOsxAllocPropertyCFString(devIdArray[i],kAudioObjectPropertyName,kAudioObjectPropertyScopeGlobal,kAudioObjectPropertyElementMaster,&p->devArray[i].nameStr)) != noErr)
  488. {
  489. rc = _cmApOsxOsError(err,&_cmApOsxRoot);
  490. goto errLabel;
  491. }
  492. // get manufacturer
  493. if(( rc = _cmApOsxAllocPropertyCFString(devIdArray[i],kAudioObjectPropertyManufacturer,kAudioObjectPropertyScopeGlobal,kAudioObjectPropertyElementMaster,&p->devArray[i].mfgStr)) != noErr)
  494. goto errLabel;
  495. if(( rc = _cmApOsxGetBufferConfig(i, devIdArray[i], true, p->devArray + i )) != kOkApRC )
  496. goto errLabel;
  497. if(( rc = _cmApOsxGetBufferConfig(i, devIdArray[i], false, p->devArray + i )) != kOkApRC )
  498. goto errLabel;
  499. //printf("%s %s\n",p->devArray[i].mfgStr,p->devArray[i].nameStr);
  500. p->devArray[i].devId = devIdArray[i];
  501. p->devArray[i].devIdx = i;
  502. p->devArray[i].callbackPtr = NULL;
  503. p->devArray[i].timeOutMs = 1000;
  504. p->devArray[i].ioProcId = NULL;
  505. }
  506. errLabel:
  507. cmMemFree(devIdArray);
  508. if( rc != noErr )
  509. cmApOsxFinalize();
  510. return rc;
  511. }
  512. cmApRC_t cmApOsxFinalize()
  513. {
  514. cmApRC_t rc = kOkApRC;
  515. cmApOsxRoot* p = &_cmApOsxRoot;
  516. unsigned i;
  517. OSStatus err;
  518. for(i=0; i<p->devCnt; ++i)
  519. {
  520. cmApOsxDevRecd* drp = p->devArray + i;
  521. unsigned j;
  522. for(j=0; j<2; ++j)
  523. {
  524. AudioStreamID* streamIdArray;
  525. unsigned streamCnt;
  526. unsigned k;
  527. if((err = _cmApOsxAllocateStreamIdArray(drp->devId, j==0, &streamIdArray,&streamCnt )) != noErr )
  528. _cmApOsxOsError(err,p);
  529. for(k=0; k<streamCnt; ++k)
  530. {
  531. AudioObjectPropertyAddress listenerAddr =
  532. {
  533. kAudioObjectPropertySelectorWildcard,
  534. kAudioObjectPropertyScopeWildcard,
  535. kAudioObjectPropertyElementWildcard
  536. };
  537. // install a stream property listener
  538. AudioObjectRemovePropertyListener(streamIdArray[i], &listenerAddr, cmApOsxSystemStreamPropertyListenerProc, drp );
  539. }
  540. cmMemPtrFree(&streamIdArray);
  541. }
  542. if( drp->ioProcId != NULL )
  543. if((err = AudioDeviceDestroyIOProcID( drp->devId, drp->ioProcId )) != noErr )
  544. _cmApOsxOsError(err,p);
  545. cmMemFree(drp->nameStr);
  546. cmMemFree(drp->mfgStr);
  547. cmMemFree(drp->inPktArray);
  548. cmMemFree(drp->outPktArray);
  549. }
  550. cmMemFree(p->devArray);
  551. return rc;
  552. }
  553. cmApRC_t cmApOsxDeviceCount()
  554. { return _cmApOsxRoot.devCnt; }
  555. const char* cmApOsxDeviceLabel( unsigned devIdx )
  556. {
  557. assert( devIdx < _cmApOsxRoot.devCnt );
  558. return _cmApOsxRoot.devArray[ devIdx ].nameStr;
  559. }
  560. unsigned cmApOsxDeviceChannelCount( unsigned devIdx, bool inputFl )
  561. {
  562. assert( devIdx < _cmApOsxRoot.devCnt );
  563. return inputFl ? _cmApOsxRoot.devArray[ devIdx ].inChCnt : _cmApOsxRoot.devArray[ devIdx ].outChCnt;
  564. }
  565. double cmApOsxDeviceSampleRate( unsigned devIdx )
  566. {
  567. assert( devIdx < _cmApOsxRoot.devCnt );
  568. return _cmApOsxRoot.devArray[ devIdx ].srate;
  569. }
  570. unsigned cmApOsxDeviceFramesPerCycle( unsigned devIdx, bool inputFl )
  571. {
  572. assert( devIdx < _cmApOsxRoot.devCnt );
  573. return inputFl ? _cmApOsxRoot.devArray[ devIdx ].inFramesPerCycle : _cmApOsxRoot.devArray[ devIdx ].outFramesPerCycle;
  574. }
  575. cmApRC_t cmApOsxDeviceSetup(
  576. unsigned devIdx,
  577. double srate,
  578. unsigned framesPerCycle,
  579. cmApCallbackPtr_t callbackPtr,
  580. void* userCbPtr )
  581. {
  582. assert( devIdx < _cmApOsxRoot.devCnt );
  583. cmApOsxRoot* p = &_cmApOsxRoot;
  584. cmApOsxDevRecd* drp = _cmApOsxRoot.devArray + devIdx;
  585. unsigned j;
  586. OSStatus err;
  587. if( cmApOsxDeviceIsStarted(devIdx) )
  588. cmApOsxDeviceStop(devIdx);
  589. // set the sample rate
  590. if( drp->srate != srate )
  591. {
  592. for(j=0; j<2; ++j )
  593. if((err = _cmApOsxSetSampleRate(drp, j==0, srate )) != noErr )
  594. goto errLabel;
  595. drp->srate = srate;
  596. }
  597. // set the frames per cycle
  598. for(j=0; j<2; ++j)
  599. {
  600. unsigned* fpcPtr = j==0 ? &drp->inFramesPerCycle : &drp->outFramesPerCycle;
  601. if( framesPerCycle != (*fpcPtr) )
  602. {
  603. if((err = _cmApOsxSetFramesPerCycle(drp, j==0, framesPerCycle )) == noErr )
  604. *fpcPtr = framesPerCycle;
  605. else
  606. goto errLabel;
  607. }
  608. }
  609. // set the user callback data ptr in each packet on this device
  610. for(j=0; j<2; ++j)
  611. {
  612. unsigned k;
  613. cmApAudioPacket_t* packetArray = j==0 ? drp->inPktArray : drp->outPktArray;
  614. unsigned pktCnt = j==0 ? drp->inPktCnt : drp->outPktCnt;
  615. for(k=0; k<pktCnt; ++k)
  616. packetArray[k].userCbPtr = userCbPtr;
  617. }
  618. drp->callbackPtr = callbackPtr;
  619. // if the io
  620. if( drp->ioProcId != NULL )
  621. if((err = AudioDeviceDestroyIOProcID( drp->devId, drp->ioProcId )) != noErr )
  622. _cmApOsxOsError(err,p);
  623. // set the io proc
  624. drp->ioProcId = NULL;
  625. if( (err = AudioDeviceCreateIOProcID(drp->devId,_cmApOsxSystemDeviceIOProc,(void*)drp,&drp->ioProcId) ) != noErr )
  626. {
  627. _cmApOsxOsError(err,p);
  628. return kSysErrApRC;
  629. }
  630. return kOkApRC;
  631. errLabel:
  632. return kSysErrApRC;
  633. }
  634. cmApRC_t cmApOsxDeviceStart( unsigned devIdx )
  635. {
  636. assert( devIdx < _cmApOsxRoot.devCnt );
  637. OSStatus err;
  638. if( (err = AudioDeviceStart(_cmApOsxRoot.devArray[devIdx].devId,_cmApOsxRoot.devArray[devIdx].ioProcId)) != noErr )
  639. {
  640. _cmApOsxOsError(err,&_cmApOsxRoot);
  641. return kSysErrApRC;
  642. }
  643. return kOkApRC;
  644. }
  645. cmApRC_t cmApOsxDeviceStop( unsigned devIdx )
  646. {
  647. assert( devIdx < _cmApOsxRoot.devCnt );
  648. OSStatus err;
  649. if( (err = AudioDeviceStop(_cmApOsxRoot.devArray[devIdx].devId,_cmApOsxRoot.devArray[devIdx].ioProcId)) != noErr )
  650. {
  651. _cmApOsxOsError(err,&_cmApOsxRoot);
  652. return kSysErrApRC;
  653. }
  654. return kOkApRC;
  655. }
  656. bool cmApOsxDeviceIsStarted( unsigned devIdx )
  657. {
  658. assert( devIdx < _cmApOsxRoot.devCnt );
  659. return false;
  660. }
  661. void cmApOsxReport( cmRpt_t* rpt )
  662. {
  663. cmApOsxRoot* p = &_cmApOsxRoot;
  664. unsigned i;
  665. for(i=0; i<p->devCnt; ++i)
  666. {
  667. cmApOsxDevRecd* drp = p->devArray + i;
  668. cmRptPrintf(rpt,"in ch:%2i | out ch:%2i | started:%1i | sr:%7.1f %s %s\n",
  669. drp->inChCnt,
  670. drp->outChCnt,
  671. drp->srate,
  672. cmApOsxDeviceIsStarted( i ),
  673. cmStringNullGuard(drp->mfgStr),
  674. cmStringNullGuard(drp->nameStr));
  675. }
  676. }
  677. /*
  678. void apReport( apPrintFunc_t printFunc )
  679. {
  680. unsigned i;
  681. for(i=0; i<_cmApOsxRoot.devCnt; ++i)
  682. {
  683. cmApOsxDevRecd* drp = _cmApOsxRoot.devArray + i;
  684. printf("%i in:%i out:%i %s %s\n",i,drp->inChCnt,drp->outChCnt,drp->nameStr,drp->mfgStr);
  685. }
  686. }
  687. */
  688. void cmApOsxTest( cmRpt_t* rpt )
  689. {
  690. printf("Start\n");
  691. cmApOsxInitialize(rpt,0);
  692. cmApOsxReport(rpt);
  693. if( cmApOsxDeviceSetup(2,48000.0,1024,NULL,NULL) != kOkApRC )
  694. printf("Setup failed.\n");
  695. cmApOsxReport(rpt);
  696. cmApOsxFinalize();
  697. printf("Finish\n");
  698. }