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.

cmAudioPort.c 23KB

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