libcm is a C development framework with an emphasis on audio signal processing applications.
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

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. */