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 23KB

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