libcm is a C development framework with an emphasis on audio signal processing applications.
Du kannst nicht mehr als 25 Themen auswählen Themen müssen mit entweder einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.

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