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

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