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

cmAudioPort.c 24KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826
  1. #include "cmPrefix.h"
  2. #include "cmGlobal.h"
  3. #include "cmRpt.h"
  4. #include "cmErr.h"
  5. #include "cmCtx.h"
  6. #include "cmMem.h"
  7. #include "cmMallocDebug.h"
  8. #include "cmTime.h"
  9. #include "cmAudioPort.h"
  10. #include "cmApBuf.h" // only needed for cmApBufTest().
  11. #include "cmAudioPortFile.h"
  12. #include "cmAudioAggDev.h"
  13. #include "cmAudioNrtDev.h"
  14. #ifdef OS_LINUX
  15. #include "linux/cmAudioPortAlsa.h"
  16. #endif
  17. #ifdef OS_OSX
  18. #include "osx/cmAudioPortOsx.h"
  19. #endif
  20. typedef struct
  21. {
  22. unsigned begDevIdx;
  23. unsigned endDevIdx;
  24. cmApRC_t (*initialize)( cmRpt_t* rpt, unsigned baseApDevIdx );
  25. cmApRC_t (*finalize)();
  26. cmApRC_t (*deviceCount)();
  27. const char* (*deviceLabel)( unsigned devIdx );
  28. unsigned (*deviceChannelCount)( unsigned devIdx, bool inputFl );
  29. double (*deviceSampleRate)( unsigned devIdx );
  30. unsigned (*deviceFramesPerCycle)( unsigned devIdx, bool inputFl );
  31. cmApRC_t (*deviceSetup)( unsigned devIdx, double sr, unsigned frmPerCycle, cmApCallbackPtr_t cb, void* cbData );
  32. cmApRC_t (*deviceStart)( unsigned devIdx );
  33. cmApRC_t (*deviceStop)( unsigned devIdx );
  34. bool (*deviceIsStarted)( unsigned devIdx );
  35. } cmApDriver_t;
  36. typedef struct
  37. {
  38. cmErr_t err;
  39. cmApDriver_t* drvArray;
  40. unsigned drvCnt;
  41. unsigned devCnt;
  42. } cmAp_t;
  43. cmAp_t* _ap = NULL;
  44. cmApRC_t _cmApIndexToDev( unsigned devIdx, cmApDriver_t** drvPtrPtr, unsigned* devIdxPtr )
  45. {
  46. assert( drvPtrPtr != NULL && devIdxPtr != NULL );
  47. *drvPtrPtr = NULL;
  48. *devIdxPtr = cmInvalidIdx;
  49. unsigned i;
  50. for(i=0; i<_ap->drvCnt; ++i)
  51. if( _ap->drvArray[i].begDevIdx != cmInvalidIdx )
  52. if( (_ap->drvArray[i].begDevIdx <= devIdx) && (devIdx <= _ap->drvArray[i].endDevIdx) )
  53. {
  54. *drvPtrPtr = _ap->drvArray + i;
  55. *devIdxPtr = devIdx - _ap->drvArray[i].begDevIdx;
  56. return kOkApRC;
  57. }
  58. return cmErrMsg(&_ap->err,kInvalidDevIdApRC,"The audio port device index %i is not valid.",devIdx);
  59. }
  60. cmApRC_t cmApInitialize( cmRpt_t* rpt )
  61. {
  62. cmApRC_t rc = kOkApRC;
  63. if((rc = cmApFinalize()) != kOkApRC )
  64. return rc;
  65. _ap = cmMemAllocZ(cmAp_t,1);
  66. cmErrSetup(&_ap->err,rpt,"Audio Port Driver");
  67. _ap->drvCnt = 4;
  68. _ap->drvArray = cmMemAllocZ(cmApDriver_t,_ap->drvCnt);
  69. cmApDriver_t* dp = _ap->drvArray;
  70. #ifdef OS_OSX
  71. dp->initialize = cmApOsxInitialize;
  72. dp->finalize = cmApOsxFinalize;
  73. dp->deviceCount = cmApOsxDeviceCount;
  74. dp->deviceLabel = cmApOsxDeviceLabel;
  75. dp->deviceChannelCount = cmApOsxDeviceChannelCount;
  76. dp->deviceSampleRate = cmApOsxDeviceSampleRate;
  77. dp->deviceFramesPerCycle = cmApOsxDeviceFramesPerCycle;
  78. dp->deviceSetup = cmApOsxDeviceSetup;
  79. dp->deviceStart = cmApOsxDeviceStart;
  80. dp->deviceStop = cmApOsxDeviceStop;
  81. dp->deviceIsStarted = cmApOsxDeviceIsStarted;
  82. #endif
  83. #ifdef OS_LINUX
  84. dp->initialize = cmApAlsaInitialize;
  85. dp->finalize = cmApAlsaFinalize;
  86. dp->deviceCount = cmApAlsaDeviceCount;
  87. dp->deviceLabel = cmApAlsaDeviceLabel;
  88. dp->deviceChannelCount = cmApAlsaDeviceChannelCount;
  89. dp->deviceSampleRate = cmApAlsaDeviceSampleRate;
  90. dp->deviceFramesPerCycle = cmApAlsaDeviceFramesPerCycle;
  91. dp->deviceSetup = cmApAlsaDeviceSetup;
  92. dp->deviceStart = cmApAlsaDeviceStart;
  93. dp->deviceStop = cmApAlsaDeviceStop;
  94. dp->deviceIsStarted = cmApAlsaDeviceIsStarted;
  95. #endif
  96. dp = _ap->drvArray + 1;
  97. dp->initialize = cmApFileInitialize;
  98. dp->finalize = cmApFileFinalize;
  99. dp->deviceCount = cmApFileDeviceCount;
  100. dp->deviceLabel = cmApFileDeviceLabel;
  101. dp->deviceChannelCount = cmApFileDeviceChannelCount;
  102. dp->deviceSampleRate = cmApFileDeviceSampleRate;
  103. dp->deviceFramesPerCycle = cmApFileDeviceFramesPerCycle;
  104. dp->deviceSetup = cmApFileDeviceSetup;
  105. dp->deviceStart = cmApFileDeviceStart;
  106. dp->deviceStop = cmApFileDeviceStop;
  107. dp->deviceIsStarted = cmApFileDeviceIsStarted;
  108. dp = _ap->drvArray + 2;
  109. dp->initialize = cmApAggInitialize;
  110. dp->finalize = cmApAggFinalize;
  111. dp->deviceCount = cmApAggDeviceCount;
  112. dp->deviceLabel = cmApAggDeviceLabel;
  113. dp->deviceChannelCount = cmApAggDeviceChannelCount;
  114. dp->deviceSampleRate = cmApAggDeviceSampleRate;
  115. dp->deviceFramesPerCycle = cmApAggDeviceFramesPerCycle;
  116. dp->deviceSetup = cmApAggDeviceSetup;
  117. dp->deviceStart = cmApAggDeviceStart;
  118. dp->deviceStop = cmApAggDeviceStop;
  119. dp->deviceIsStarted = cmApAggDeviceIsStarted;
  120. dp = _ap->drvArray + 3;
  121. dp->initialize = cmApNrtInitialize;
  122. dp->finalize = cmApNrtFinalize;
  123. dp->deviceCount = cmApNrtDeviceCount;
  124. dp->deviceLabel = cmApNrtDeviceLabel;
  125. dp->deviceChannelCount = cmApNrtDeviceChannelCount;
  126. dp->deviceSampleRate = cmApNrtDeviceSampleRate;
  127. dp->deviceFramesPerCycle = cmApNrtDeviceFramesPerCycle;
  128. dp->deviceSetup = cmApNrtDeviceSetup;
  129. dp->deviceStart = cmApNrtDeviceStart;
  130. dp->deviceStop = cmApNrtDeviceStop;
  131. dp->deviceIsStarted = cmApNrtDeviceIsStarted;
  132. _ap->devCnt = 0;
  133. unsigned i;
  134. for(i=0; i<_ap->drvCnt; ++i)
  135. {
  136. unsigned dn;
  137. cmApRC_t rc0;
  138. _ap->drvArray[i].begDevIdx = cmInvalidIdx;
  139. _ap->drvArray[i].endDevIdx = cmInvalidIdx;
  140. if((rc0 = _ap->drvArray[i].initialize(rpt,_ap->devCnt)) != kOkApRC )
  141. {
  142. rc = rc0;
  143. continue;
  144. }
  145. if((dn = _ap->drvArray[i].deviceCount()) > 0)
  146. {
  147. _ap->drvArray[i].begDevIdx = _ap->devCnt;
  148. _ap->drvArray[i].endDevIdx = _ap->devCnt + dn - 1;
  149. _ap->devCnt += dn;
  150. }
  151. }
  152. if( rc != kOkApRC )
  153. cmApFinalize();
  154. return rc;
  155. }
  156. cmApRC_t cmApFinalize()
  157. {
  158. cmApRC_t rc=kOkApRC;
  159. cmApRC_t rc0 = kOkApRC;
  160. unsigned i;
  161. if( _ap == NULL )
  162. return kOkApRC;
  163. for(i=0; i<_ap->drvCnt; ++i)
  164. {
  165. if((rc0 = _ap->drvArray[i].finalize()) != kOkApRC )
  166. rc = rc0;
  167. }
  168. cmMemPtrFree(&_ap->drvArray);
  169. cmMemPtrFree(&_ap);
  170. return rc;
  171. }
  172. unsigned cmApDeviceCount()
  173. { return _ap->devCnt; }
  174. const char* cmApDeviceLabel( unsigned devIdx )
  175. {
  176. cmApDriver_t* dp = NULL;
  177. unsigned di = cmInvalidIdx;
  178. cmApRC_t rc;
  179. if( devIdx == cmInvalidIdx )
  180. return NULL;
  181. if((rc = _cmApIndexToDev(devIdx,&dp,&di)) != kOkApRC )
  182. return cmStringNullGuard(NULL);
  183. return dp->deviceLabel(di);
  184. }
  185. unsigned cmApDeviceLabelToIndex( const cmChar_t* label )
  186. {
  187. unsigned n = cmApDeviceCount();
  188. unsigned i;
  189. for(i=0; i<n; ++i)
  190. {
  191. const cmChar_t* s = cmApDeviceLabel(i);
  192. if( s!=NULL && strcmp(s,label)==0)
  193. return i;
  194. }
  195. return cmInvalidIdx;
  196. }
  197. unsigned cmApDeviceChannelCount( unsigned devIdx, bool inputFl )
  198. {
  199. cmApDriver_t* dp = NULL;
  200. unsigned di = cmInvalidIdx;
  201. cmApRC_t rc;
  202. if( devIdx == cmInvalidIdx )
  203. return 0;
  204. if((rc = _cmApIndexToDev(devIdx,&dp,&di)) != kOkApRC )
  205. return rc;
  206. return dp->deviceChannelCount(di,inputFl);
  207. }
  208. double cmApDeviceSampleRate( unsigned devIdx )
  209. {
  210. cmApDriver_t* dp = NULL;
  211. unsigned di = cmInvalidIdx;
  212. cmApRC_t rc;
  213. if((rc = _cmApIndexToDev(devIdx,&dp,&di)) != kOkApRC )
  214. return rc;
  215. return dp->deviceSampleRate(di);
  216. }
  217. unsigned cmApDeviceFramesPerCycle( unsigned devIdx, bool inputFl )
  218. {
  219. cmApDriver_t* dp = NULL;
  220. unsigned di = cmInvalidIdx;
  221. cmApRC_t rc;
  222. if( devIdx == cmInvalidIdx )
  223. return 0;
  224. if((rc = _cmApIndexToDev(devIdx,&dp,&di)) != kOkApRC )
  225. return rc;
  226. return dp->deviceFramesPerCycle(di,inputFl);
  227. }
  228. cmApRC_t cmApDeviceSetup(
  229. unsigned devIdx,
  230. double srate,
  231. unsigned framesPerCycle,
  232. cmApCallbackPtr_t callbackPtr,
  233. void* userCbPtr )
  234. {
  235. cmApDriver_t* dp;
  236. unsigned di;
  237. cmApRC_t rc;
  238. if( devIdx == cmInvalidIdx )
  239. return kOkApRC;
  240. if((rc = _cmApIndexToDev(devIdx,&dp,&di)) != kOkApRC )
  241. return rc;
  242. return dp->deviceSetup(di,srate,framesPerCycle,callbackPtr,userCbPtr);
  243. }
  244. cmApRC_t cmApDeviceStart( unsigned devIdx )
  245. {
  246. cmApDriver_t* dp;
  247. unsigned di;
  248. cmApRC_t rc;
  249. if( devIdx == cmInvalidIdx )
  250. return kOkApRC;
  251. if((rc = _cmApIndexToDev(devIdx,&dp,&di)) != kOkApRC )
  252. return rc;
  253. return dp->deviceStart(di);
  254. }
  255. cmApRC_t cmApDeviceStop( unsigned devIdx )
  256. {
  257. cmApDriver_t* dp;
  258. unsigned di;
  259. cmApRC_t rc;
  260. if( devIdx == cmInvalidIdx )
  261. return kOkApRC;
  262. if((rc = _cmApIndexToDev(devIdx,&dp,&di)) != kOkApRC )
  263. return rc;
  264. return dp->deviceStop(di);
  265. }
  266. bool cmApDeviceIsStarted( unsigned devIdx )
  267. {
  268. cmApDriver_t* dp;
  269. unsigned di;
  270. cmApRC_t rc;
  271. if( devIdx == cmInvalidIdx )
  272. return false;
  273. if((rc = _cmApIndexToDev(devIdx,&dp,&di)) != kOkApRC )
  274. return rc;
  275. return dp->deviceIsStarted(di);
  276. }
  277. void cmApReport( cmRpt_t* rpt )
  278. {
  279. unsigned i,j,k;
  280. for(j=0,k=0; j<_ap->drvCnt; ++j)
  281. {
  282. cmApDriver_t* drvPtr = _ap->drvArray + j;
  283. unsigned n = drvPtr->deviceCount();
  284. for(i=0; i<n; ++i,++k)
  285. {
  286. cmRptPrintf(rpt, "%i %f in:%i (%i) out:%i (%i) %s\n",
  287. k, drvPtr->deviceSampleRate(i),
  288. drvPtr->deviceChannelCount(i,true), drvPtr->deviceFramesPerCycle(i,true),
  289. drvPtr->deviceChannelCount(i,false), drvPtr->deviceFramesPerCycle(i,false),
  290. drvPtr->deviceLabel(i));
  291. }
  292. }
  293. }
  294. /// [cmAudioPortExample]
  295. // See cmApPortTest() below for the main point of entry.
  296. // Data structure used to hold the parameters for cpApPortTest()
  297. // and the user defined data record passed to the host from the
  298. // audio port callback functions.
  299. typedef struct
  300. {
  301. unsigned bufCnt; // 2=double buffering 3=triple buffering
  302. unsigned chIdx; // first test channel
  303. unsigned chCnt; // count of channels to test
  304. unsigned framesPerCycle; // DSP frames per cycle
  305. unsigned bufFrmCnt; // count of DSP frames used by the audio buffer (bufCnt * framesPerCycle)
  306. unsigned bufSmpCnt; // count of samples used by the audio buffer (chCnt * bufFrmCnt)
  307. unsigned inDevIdx; // input device index
  308. unsigned outDevIdx; // output device index
  309. double srate; // audio sample rate
  310. unsigned meterMs; // audio meter buffer length
  311. // param's and state for cmApSynthSine()
  312. unsigned phase; // sine synth phase
  313. double frqHz; // sine synth frequency in Hz
  314. // buffer and state for cmApCopyIn/Out()
  315. cmApSample_t* buf; // buf[bufSmpCnt] - circular interleaved audio buffer
  316. unsigned bufInIdx; // next input buffer index
  317. unsigned bufOutIdx; // next output buffer index
  318. unsigned bufFullCnt; // count of full samples
  319. // debugging log data arrays
  320. unsigned logCnt; // count of elements in log[] and ilong[]
  321. char* log; // log[logCnt]
  322. unsigned* ilog; // ilog[logCnt]
  323. unsigned logIdx; // current log index
  324. unsigned cbCnt; // count the callback
  325. } cmApPortTestRecd;
  326. #ifdef NOT_DEF
  327. // The application can request any block of channels from the device. The packets are provided with the starting
  328. // device channel and channel count. This function converts device channels and channel counts to buffer
  329. // channel indexes and counts.
  330. //
  331. // Example:
  332. // input output
  333. // i,n i n
  334. // App: 0,4 0 1 2 3 -> 2 2
  335. // Pkt 2,8 2 3 4 5 6 7 8 -> 0 2
  336. //
  337. // The return value is the count of application requested channels located in this packet.
  338. //
  339. // input: *appChIdxPtr and appChCnt describe a block of device channels requested by the application.
  340. // *pktChIdxPtr and pktChCnt describe a block of device channels provided to the application
  341. //
  342. // output:*appChIdxPtr and <return value> describe a block of app buffer channels which will send/recv samples.
  343. // *pktChIdxPtr and <return value> describe a block of pkt buffer channels which will send/recv samples
  344. //
  345. unsigned _cmApDeviceToBuffer( unsigned* appChIdxPtr, unsigned appChCnt, unsigned* pktChIdxPtr, unsigned pktChCnt )
  346. {
  347. unsigned abi = *appChIdxPtr;
  348. unsigned aei = abi+appChCnt-1;
  349. unsigned pbi = *pktChIdxPtr;
  350. unsigned pei = pbi+pktChCnt-1;
  351. // if the ch's rqstd by the app do not overlap with this packet - return false.
  352. if( aei < pbi || abi > pei )
  353. return 0;
  354. // if the ch's rqstd by the app overlap with the beginning of the pkt channel block
  355. if( abi < pbi )
  356. {
  357. appChCnt -= pbi - abi;
  358. *appChIdxPtr = pbi - abi;
  359. *pktChIdxPtr = 0;
  360. }
  361. else
  362. {
  363. // the rqstd ch's begin inside the pkt channel block
  364. pktChCnt -= abi - pbi;
  365. *pktChIdxPtr = abi - pbi;
  366. *appChIdxPtr = 0;
  367. }
  368. // if the pkt channels extend beyond the rqstd ch block
  369. if( aei < pei )
  370. pktChCnt -= pei - aei;
  371. else
  372. appChCnt -= aei - pei; // the rqstd ch's extend beyond or coincide with the pkt block
  373. // the returned channel count must always be the same for both the rqstd and pkt
  374. return cmMin(appChCnt,pktChCnt);
  375. }
  376. // synthesize a sine signal into an interleaved audio buffer
  377. unsigned _cmApSynthSine( cmApPortTestRecd* r, float* p, unsigned chIdx, unsigned chCnt, unsigned frmCnt, unsigned phs, double hz )
  378. {
  379. long ph = 0;
  380. unsigned i;
  381. unsigned bufIdx = r->chIdx;
  382. unsigned bufChCnt;
  383. if( (bufChCnt = _cmApDeviceToBuffer( &bufIdx, r->chCnt, &chIdx, chCnt )) == 0)
  384. return phs;
  385. //if( r->cbCnt < 50 )
  386. // printf("ch:%i cnt:%i ch:%i cnt:%i bi:%i bcn:%i\n",r->chIdx,r->chCnt,chIdx,chCnt,bufIdx,bufChCnt);
  387. for(i=bufIdx; i<bufIdx+bufChCnt; ++i)
  388. {
  389. unsigned j;
  390. float* op = p + i;
  391. ph = phs;
  392. for(j=0; j<frmCnt; j++, op+=chCnt, ph++)
  393. {
  394. *op = (float)(0.9 * sin( 2.0 * M_PI * hz * ph / r->srate ));
  395. }
  396. }
  397. return ph;
  398. }
  399. // Copy the audio samples in the interleaved audio buffer sp[srcChCnt*srcFrameCnt]
  400. // to the internal record buffer.
  401. void _cmApCopyIn( cmApPortTestRecd* r, const cmApSample_t* sp, unsigned srcChIdx, unsigned srcChCnt, unsigned srcFrameCnt )
  402. {
  403. unsigned i,j;
  404. unsigned chCnt = cmMin(r->chCnt,srcChCnt);
  405. for(i=0; i<srcFrameCnt; ++i)
  406. {
  407. for(j=0; j<chCnt; ++j)
  408. r->buf[ r->bufInIdx + j ] = sp[ (i*srcChCnt) + j ];
  409. for(; j<r->chCnt; ++j)
  410. r->buf[ r->bufInIdx + j ] = 0;
  411. r->bufInIdx = (r->bufInIdx+r->chCnt) % r->bufFrmCnt;
  412. }
  413. //r->bufFullCnt = (r->bufFullCnt + srcFrameCnt) % r->bufFrmCnt;
  414. r->bufFullCnt += srcFrameCnt;
  415. }
  416. // Copy audio samples out of the internal record buffer into dp[dstChCnt*dstFrameCnt].
  417. void _cmApCopyOut( cmApPortTestRecd* r, cmApSample_t* dp, unsigned dstChIdx, unsigned dstChCnt, unsigned dstFrameCnt )
  418. {
  419. // if there are not enough samples available to fill the destination buffer then zero the dst buf.
  420. if( r->bufFullCnt < dstFrameCnt )
  421. {
  422. printf("Empty Output Buffer\n");
  423. memset( dp, 0, dstFrameCnt*dstChCnt*sizeof(cmApSample_t) );
  424. }
  425. else
  426. {
  427. unsigned i,j;
  428. unsigned chCnt = cmMin(dstChCnt, r->chCnt);
  429. // for each output frame
  430. for(i=0; i<dstFrameCnt; ++i)
  431. {
  432. // copy the first chCnt samples from the internal buf to the output buf
  433. for(j=0; j<chCnt; ++j)
  434. dp[ (i*dstChCnt) + j ] = r->buf[ r->bufOutIdx + j ];
  435. // zero any output ch's for which there is no internal buf channel
  436. for(; j<dstChCnt; ++j)
  437. dp[ (i*dstChCnt) + j ] = 0;
  438. // advance the internal buffer
  439. r->bufOutIdx = (r->bufOutIdx + r->chCnt) % r->bufFrmCnt;
  440. }
  441. r->bufFullCnt -= dstFrameCnt;
  442. }
  443. }
  444. // Audio port callback function called from the audio device thread.
  445. void _cmApPortCb( cmApAudioPacket_t* inPktArray, unsigned inPktCnt, cmApAudioPacket_t* outPktArray, unsigned outPktCnt )
  446. {
  447. unsigned i;
  448. // for each incoming audio packet
  449. for(i=0; i<inPktCnt; ++i)
  450. {
  451. cmApPortTestRecd* r = (cmApPortTestRecd*)inPktArray[i].userCbPtr;
  452. if( inPktArray[i].devIdx == r->inDevIdx )
  453. {
  454. // copy the incoming audio into an internal buffer where it can be picked up by _cpApCopyOut().
  455. _cmApCopyIn( r, (cmApSample_t*)inPktArray[i].audioBytesPtr, inPktArray[i].begChIdx, inPktArray[i].chCnt, inPktArray[i].audioFramesCnt );
  456. }
  457. ++r->cbCnt;
  458. //printf("i %4i in:%4i out:%4i\n",r->bufFullCnt,r->bufInIdx,r->bufOutIdx);
  459. }
  460. unsigned hold_phase = 0;
  461. // for each outgoing audio packet
  462. for(i=0; i<outPktCnt; ++i)
  463. {
  464. cmApPortTestRecd* r = (cmApPortTestRecd*)outPktArray[i].userCbPtr;
  465. if( outPktArray[i].devIdx == r->outDevIdx )
  466. {
  467. // zero the output buffer
  468. memset(outPktArray[i].audioBytesPtr,0,outPktArray[i].chCnt * outPktArray[i].audioFramesCnt * sizeof(cmApSample_t) );
  469. // if the synth is enabled
  470. if( r->synthFl )
  471. {
  472. unsigned tmp_phase = _cmApSynthSine( r, outPktArray[i].audioBytesPtr, outPktArray[i].begChIdx, outPktArray[i].chCnt, outPktArray[i].audioFramesCnt, r->phase, r->frqHz );
  473. // the phase will only change on packets that are actually used
  474. if( tmp_phase != r->phase )
  475. hold_phase = tmp_phase;
  476. }
  477. else
  478. {
  479. // copy the any audio in the internal record buffer to the playback device
  480. _cmApCopyOut( r, (cmApSample_t*)outPktArray[i].audioBytesPtr, outPktArray[i].begChIdx, outPktArray[i].chCnt, outPktArray[i].audioFramesCnt );
  481. }
  482. }
  483. r->phase = hold_phase;
  484. //printf("o %4i in:%4i out:%4i\n",r->bufFullCnt,r->bufInIdx,r->bufOutIdx);
  485. // count callbacks
  486. ++r->cbCnt;
  487. }
  488. }
  489. #endif
  490. // print the usage message for cmAudioPortTest.c
  491. void _cmApPrintUsage( cmRpt_t* rpt )
  492. {
  493. char msg[] =
  494. "cmApPortTest() command switches\n"
  495. "-r <srate> -c <chcnt> -b <bufcnt> -f <frmcnt> -i <idevidx> -o <odevidx> -t -p -h \n"
  496. "\n"
  497. "-r <srate> = sample rate\n"
  498. "-a <chidx> = first channel\n"
  499. "-c <chcnt> = audio channels\n"
  500. "-b <bufcnt> = count of buffers\n"
  501. "-f <frmcnt> = count of samples per buffer\n"
  502. "-i <idevidx> = input device index\n"
  503. "-o <odevidx> = output device index\n"
  504. "-p = print report but do not start audio devices\n"
  505. "-h = print this usage message\n";
  506. cmRptPrintf(rpt,msg);
  507. }
  508. // Get a command line option.
  509. int _cmApGetOpt( int argc, const char* argv[], const char* label, int defaultVal, bool boolFl )
  510. {
  511. int i = 0;
  512. for(; i<argc; ++i)
  513. if( strcmp(label,argv[i]) == 0 )
  514. {
  515. if(boolFl)
  516. return 1;
  517. if( i == (argc-1) )
  518. return defaultVal;
  519. return atoi(argv[i+1]);
  520. }
  521. return defaultVal;
  522. }
  523. unsigned _cmGlobalInDevIdx = 0;
  524. unsigned _cmGlobalOutDevIdx = 0;
  525. void _cmApPortCb2( cmApAudioPacket_t* inPktArray, unsigned inPktCnt, cmApAudioPacket_t* outPktArray, unsigned outPktCnt )
  526. {
  527. cmApBufInputToOutput( _cmGlobalInDevIdx, _cmGlobalOutDevIdx );
  528. cmApBufUpdate( inPktArray, inPktCnt, outPktArray, outPktCnt );
  529. }
  530. // Audio Port testing function
  531. int cmApPortTest( bool runFl, cmRpt_t* rpt, int argc, const char* argv[] )
  532. {
  533. cmApPortTestRecd r;
  534. unsigned i;
  535. int result = 0;
  536. if( _cmApGetOpt(argc,argv,"-h",0,true) )
  537. _cmApPrintUsage(rpt);
  538. runFl = _cmApGetOpt(argc,argv,"-p",!runFl,true)?false:true;
  539. r.chIdx = _cmApGetOpt(argc,argv,"-a",0,false);
  540. r.chCnt = _cmApGetOpt(argc,argv,"-c",2,false);
  541. r.bufCnt = _cmApGetOpt(argc,argv,"-b",3,false);
  542. r.framesPerCycle = _cmApGetOpt(argc,argv,"-f",512,false);
  543. r.bufFrmCnt = (r.bufCnt*r.framesPerCycle);
  544. r.bufSmpCnt = (r.chCnt * r.bufFrmCnt);
  545. r.logCnt = 100;
  546. r.meterMs = 50;
  547. cmApSample_t buf[r.bufSmpCnt];
  548. char log[r.logCnt];
  549. unsigned ilog[r.logCnt];
  550. r.inDevIdx = _cmGlobalInDevIdx = _cmApGetOpt(argc,argv,"-i",0,false);
  551. r.outDevIdx = _cmGlobalOutDevIdx = _cmApGetOpt(argc,argv,"-o",2,false);
  552. r.phase = 0;
  553. r.frqHz = 2000;
  554. r.srate = 44100;
  555. r.bufInIdx = 0;
  556. r.bufOutIdx = 0;
  557. r.bufFullCnt = 0;
  558. r.logIdx = 0;
  559. r.buf = buf;
  560. r.log = log;
  561. r.ilog = ilog;
  562. r.cbCnt = 0;
  563. cmRptPrintf(rpt,"%s in:%i out:%i chidx:%i chs:%i bufs=%i frm=%i rate=%f\n",runFl?"exec":"rpt",r.inDevIdx,r.outDevIdx,r.chIdx,r.chCnt,r.bufCnt,r.framesPerCycle,r.srate);
  564. if( cmApFileAllocate(rpt) != kOkApRC )
  565. {
  566. cmRptPrintf(rpt,"Audio port file allocation failed.");
  567. result = -1;
  568. goto errLabel;
  569. }
  570. // allocate the non-real-time port
  571. if( cmApNrtAllocate(rpt) != kOkApRC )
  572. {
  573. cmRptPrintf(rpt,"Non-real-time system allocation failed.");
  574. result = 1;
  575. goto errLabel;
  576. }
  577. // initialize the audio device interface
  578. if( cmApInitialize(rpt) != kOkApRC )
  579. {
  580. cmRptPrintf(rpt,"Initialize failed.\n");
  581. result = 1;
  582. goto errLabel;
  583. }
  584. // report the current audio device configuration
  585. for(i=0; i<cmApDeviceCount(); ++i)
  586. {
  587. cmRptPrintf(rpt,"%i [in: chs=%i frames=%i] [out: chs=%i frames=%i] srate:%f %s\n",i,cmApDeviceChannelCount(i,true),cmApDeviceFramesPerCycle(i,true),cmApDeviceChannelCount(i,false),cmApDeviceFramesPerCycle(i,false),cmApDeviceSampleRate(i),cmApDeviceLabel(i));
  588. }
  589. // report the current audio devices using the audio port interface function
  590. cmApReport(rpt);
  591. if( runFl )
  592. {
  593. // initialize the audio bufer
  594. cmApBufInitialize( cmApDeviceCount(), r.meterMs );
  595. // setup the buffer for the output device
  596. cmApBufSetup( r.outDevIdx, r.srate, r.framesPerCycle, r.bufCnt, cmApDeviceChannelCount(r.outDevIdx,true), r.framesPerCycle, cmApDeviceChannelCount(r.outDevIdx,false), r.framesPerCycle );
  597. // setup the buffer for the input device
  598. if( r.inDevIdx != r.outDevIdx )
  599. cmApBufSetup( r.inDevIdx, r.srate, r.framesPerCycle, r.bufCnt, cmApDeviceChannelCount(r.inDevIdx,true), r.framesPerCycle, cmApDeviceChannelCount(r.inDevIdx,false), r.framesPerCycle );
  600. // setup an output device
  601. if(cmApDeviceSetup(r.outDevIdx,r.srate,r.framesPerCycle,_cmApPortCb2,&r) != kOkApRC )
  602. cmRptPrintf(rpt,"Out device setup failed.\n");
  603. else
  604. // setup an input device
  605. if( cmApDeviceSetup(r.inDevIdx,r.srate,r.framesPerCycle,_cmApPortCb2,&r) != kOkApRC )
  606. cmRptPrintf(rpt,"In device setup failed.\n");
  607. else
  608. // start the input device
  609. if( cmApDeviceStart(r.inDevIdx) != kOkApRC )
  610. cmRptPrintf(rpt,"In device start failed.\n");
  611. else
  612. // start the output device
  613. if( cmApDeviceStart(r.outDevIdx) != kOkApRC )
  614. cmRptPrintf(rpt,"Out Device start failed.\n");
  615. cmRptPrintf(rpt,"q=quit O/o output tone, I/i input tone P/p pass\n");
  616. char c;
  617. while((c=getchar()) != 'q')
  618. {
  619. //cmApAlsaDeviceRtReport(rpt,r.outDevIdx);
  620. switch(c)
  621. {
  622. case 'i':
  623. case 'I':
  624. cmApBufEnableTone(r.inDevIdx,-1,kInApFl | (c=='I'?kEnableApFl:0));
  625. break;
  626. case 'o':
  627. case 'O':
  628. cmApBufEnableTone(r.outDevIdx,-1,kOutApFl | (c=='O'?kEnableApFl:0));
  629. break;
  630. case 'p':
  631. case 'P':
  632. cmApBufEnablePass(r.outDevIdx,-1,kOutApFl | (c=='P'?kEnableApFl:0));
  633. break;
  634. case 's':
  635. cmApBufReport(rpt);
  636. break;
  637. }
  638. }
  639. // stop the input device
  640. if( cmApDeviceIsStarted(r.inDevIdx) )
  641. if( cmApDeviceStop(r.inDevIdx) != kOkApRC )
  642. cmRptPrintf(rpt,"In device stop failed.\n");
  643. // stop the output device
  644. if( cmApDeviceIsStarted(r.outDevIdx) )
  645. if( cmApDeviceStop(r.outDevIdx) != kOkApRC )
  646. cmRptPrintf(rpt,"Out device stop failed.\n");
  647. }
  648. errLabel:
  649. // release any resources held by the audio port interface
  650. if( cmApFinalize() != kOkApRC )
  651. cmRptPrintf(rpt,"Finalize failed.\n");
  652. cmApBufFinalize();
  653. cmApNrtFree();
  654. cmApFileFree();
  655. // report the count of audio buffer callbacks
  656. cmRptPrintf(rpt,"cb count:%i\n", r.cbCnt );
  657. //for(i=0; i<_logCnt; ++i)
  658. // cmRptPrintf(rpt,"%c(%i)",_log[i],_ilog[i]);
  659. //cmRptPrintf(rpt,"\n");
  660. return result;
  661. }
  662. /// [cmAudioPortExample]