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

cmAudioPortOsx.c 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, unsigned baseApDevIdx )
  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. */