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.

cmAudioPort.c 24KB

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