libcm is a C development framework with an emphasis on audio signal processing applications.
Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.

cmAudioPortOsx.c 26KB

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