libcm is a C development framework with an emphasis on audio signal processing applications.
Você não pode selecionar mais de 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.

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. }