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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799
  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 cmApDeviceChannelCount( unsigned devIdx, bool inputFl )
  182. {
  183. cmApDriver_t* dp;
  184. unsigned di;
  185. cmApRC_t rc;
  186. if( devIdx == cmInvalidIdx )
  187. return 0;
  188. if((rc = _cmApIndexToDev(devIdx,&dp,&di)) != kOkApRC )
  189. return rc;
  190. return dp->deviceChannelCount(di,inputFl);
  191. }
  192. double cmApDeviceSampleRate( unsigned devIdx )
  193. {
  194. cmApDriver_t* dp;
  195. unsigned di;
  196. cmApRC_t rc;
  197. if((rc = _cmApIndexToDev(devIdx,&dp,&di)) != kOkApRC )
  198. return rc;
  199. return dp->deviceSampleRate(di);
  200. }
  201. unsigned cmApDeviceFramesPerCycle( unsigned devIdx, bool inputFl )
  202. {
  203. cmApDriver_t* dp;
  204. unsigned di;
  205. cmApRC_t rc;
  206. if( devIdx == cmInvalidIdx )
  207. return 0;
  208. if((rc = _cmApIndexToDev(devIdx,&dp,&di)) != kOkApRC )
  209. return rc;
  210. return dp->deviceFramesPerCycle(di,inputFl);
  211. }
  212. cmApRC_t cmApDeviceSetup(
  213. unsigned devIdx,
  214. double srate,
  215. unsigned framesPerCycle,
  216. cmApCallbackPtr_t callbackPtr,
  217. void* userCbPtr )
  218. {
  219. cmApDriver_t* dp;
  220. unsigned di;
  221. cmApRC_t rc;
  222. if( devIdx == cmInvalidIdx )
  223. return kOkApRC;
  224. if((rc = _cmApIndexToDev(devIdx,&dp,&di)) != kOkApRC )
  225. return rc;
  226. return dp->deviceSetup(di,srate,framesPerCycle,callbackPtr,userCbPtr);
  227. }
  228. cmApRC_t cmApDeviceStart( unsigned devIdx )
  229. {
  230. cmApDriver_t* dp;
  231. unsigned di;
  232. cmApRC_t rc;
  233. if( devIdx == cmInvalidIdx )
  234. return kOkApRC;
  235. if((rc = _cmApIndexToDev(devIdx,&dp,&di)) != kOkApRC )
  236. return rc;
  237. return dp->deviceStart(di);
  238. }
  239. cmApRC_t cmApDeviceStop( unsigned devIdx )
  240. {
  241. cmApDriver_t* dp;
  242. unsigned di;
  243. cmApRC_t rc;
  244. if( devIdx == cmInvalidIdx )
  245. return kOkApRC;
  246. if((rc = _cmApIndexToDev(devIdx,&dp,&di)) != kOkApRC )
  247. return rc;
  248. return dp->deviceStop(di);
  249. }
  250. bool cmApDeviceIsStarted( unsigned devIdx )
  251. {
  252. cmApDriver_t* dp;
  253. unsigned di;
  254. cmApRC_t rc;
  255. if( devIdx == cmInvalidIdx )
  256. return false;
  257. if((rc = _cmApIndexToDev(devIdx,&dp,&di)) != kOkApRC )
  258. return rc;
  259. return dp->deviceIsStarted(di);
  260. }
  261. void cmApReport( cmRpt_t* rpt )
  262. {
  263. unsigned i,j,k;
  264. for(j=0,k=0; j<_ap->drvCnt; ++j)
  265. {
  266. cmApDriver_t* drvPtr = _ap->drvArray + j;
  267. unsigned n = drvPtr->deviceCount();
  268. for(i=0; i<n; ++i,++k)
  269. {
  270. cmRptPrintf(rpt, "%i %f in:%i (%i) out:%i (%i) %s\n",
  271. k, drvPtr->deviceSampleRate(i),
  272. drvPtr->deviceChannelCount(i,true), drvPtr->deviceFramesPerCycle(i,true),
  273. drvPtr->deviceChannelCount(i,false), drvPtr->deviceFramesPerCycle(i,false),
  274. drvPtr->deviceLabel(i));
  275. }
  276. }
  277. }
  278. /// [cmAudioPortExample]
  279. // See cmApPortTest() below for the main point of entry.
  280. // Data structure used to hold the parameters for cpApPortTest()
  281. // and the user defined data record passed to the host from the
  282. // audio port callback functions.
  283. typedef struct
  284. {
  285. unsigned bufCnt; // 2=double buffering 3=triple buffering
  286. unsigned chIdx; // first test channel
  287. unsigned chCnt; // count of channels to test
  288. unsigned framesPerCycle; // DSP frames per cycle
  289. unsigned bufFrmCnt; // count of DSP frames used by the audio buffer (bufCnt * framesPerCycle)
  290. unsigned bufSmpCnt; // count of samples used by the audio buffer (chCnt * bufFrmCnt)
  291. unsigned inDevIdx; // input device index
  292. unsigned outDevIdx; // output device index
  293. double srate; // audio sample rate
  294. unsigned meterMs; // audio meter buffer length
  295. // param's and state for cmApSynthSine()
  296. unsigned phase; // sine synth phase
  297. double frqHz; // sine synth frequency in Hz
  298. // buffer and state for cmApCopyIn/Out()
  299. cmApSample_t* buf; // buf[bufSmpCnt] - circular interleaved audio buffer
  300. unsigned bufInIdx; // next input buffer index
  301. unsigned bufOutIdx; // next output buffer index
  302. unsigned bufFullCnt; // count of full samples
  303. // debugging log data arrays
  304. unsigned logCnt; // count of elements in log[] and ilong[]
  305. char* log; // log[logCnt]
  306. unsigned* ilog; // ilog[logCnt]
  307. unsigned logIdx; // current log index
  308. unsigned cbCnt; // count the callback
  309. } cmApPortTestRecd;
  310. #ifdef NOT_DEF
  311. // The application can request any block of channels from the device. The packets are provided with the starting
  312. // device channel and channel count. This function converts device channels and channel counts to buffer
  313. // channel indexes and counts.
  314. //
  315. // Example:
  316. // input output
  317. // i,n i n
  318. // App: 0,4 0 1 2 3 -> 2 2
  319. // Pkt 2,8 2 3 4 5 6 7 8 -> 0 2
  320. //
  321. // The return value is the count of application requested channels located in this packet.
  322. //
  323. // input: *appChIdxPtr and appChCnt describe a block of device channels requested by the application.
  324. // *pktChIdxPtr and pktChCnt describe a block of device channels provided to the application
  325. //
  326. // output:*appChIdxPtr and <return value> describe a block of app buffer channels which will send/recv samples.
  327. // *pktChIdxPtr and <return value> describe a block of pkt buffer channels which will send/recv samples
  328. //
  329. unsigned _cmApDeviceToBuffer( unsigned* appChIdxPtr, unsigned appChCnt, unsigned* pktChIdxPtr, unsigned pktChCnt )
  330. {
  331. unsigned abi = *appChIdxPtr;
  332. unsigned aei = abi+appChCnt-1;
  333. unsigned pbi = *pktChIdxPtr;
  334. unsigned pei = pbi+pktChCnt-1;
  335. // if the ch's rqstd by the app do not overlap with this packet - return false.
  336. if( aei < pbi || abi > pei )
  337. return 0;
  338. // if the ch's rqstd by the app overlap with the beginning of the pkt channel block
  339. if( abi < pbi )
  340. {
  341. appChCnt -= pbi - abi;
  342. *appChIdxPtr = pbi - abi;
  343. *pktChIdxPtr = 0;
  344. }
  345. else
  346. {
  347. // the rqstd ch's begin inside the pkt channel block
  348. pktChCnt -= abi - pbi;
  349. *pktChIdxPtr = abi - pbi;
  350. *appChIdxPtr = 0;
  351. }
  352. // if the pkt channels extend beyond the rqstd ch block
  353. if( aei < pei )
  354. pktChCnt -= pei - aei;
  355. else
  356. appChCnt -= aei - pei; // the rqstd ch's extend beyond or coincide with the pkt block
  357. // the returned channel count must always be the same for both the rqstd and pkt
  358. return cmMin(appChCnt,pktChCnt);
  359. }
  360. // synthesize a sine signal into an interleaved audio buffer
  361. unsigned _cmApSynthSine( cmApPortTestRecd* r, float* p, unsigned chIdx, unsigned chCnt, unsigned frmCnt, unsigned phs, double hz )
  362. {
  363. long ph = 0;
  364. unsigned i;
  365. unsigned bufIdx = r->chIdx;
  366. unsigned bufChCnt;
  367. if( (bufChCnt = _cmApDeviceToBuffer( &bufIdx, r->chCnt, &chIdx, chCnt )) == 0)
  368. return phs;
  369. //if( r->cbCnt < 50 )
  370. // printf("ch:%i cnt:%i ch:%i cnt:%i bi:%i bcn:%i\n",r->chIdx,r->chCnt,chIdx,chCnt,bufIdx,bufChCnt);
  371. for(i=bufIdx; i<bufIdx+bufChCnt; ++i)
  372. {
  373. unsigned j;
  374. float* op = p + i;
  375. ph = phs;
  376. for(j=0; j<frmCnt; j++, op+=chCnt, ph++)
  377. {
  378. *op = (float)(0.9 * sin( 2.0 * M_PI * hz * ph / r->srate ));
  379. }
  380. }
  381. return ph;
  382. }
  383. // Copy the audio samples in the interleaved audio buffer sp[srcChCnt*srcFrameCnt]
  384. // to the internal record buffer.
  385. void _cmApCopyIn( cmApPortTestRecd* r, const cmApSample_t* sp, unsigned srcChIdx, unsigned srcChCnt, unsigned srcFrameCnt )
  386. {
  387. unsigned i,j;
  388. unsigned chCnt = cmMin(r->chCnt,srcChCnt);
  389. for(i=0; i<srcFrameCnt; ++i)
  390. {
  391. for(j=0; j<chCnt; ++j)
  392. r->buf[ r->bufInIdx + j ] = sp[ (i*srcChCnt) + j ];
  393. for(; j<r->chCnt; ++j)
  394. r->buf[ r->bufInIdx + j ] = 0;
  395. r->bufInIdx = (r->bufInIdx+r->chCnt) % r->bufFrmCnt;
  396. }
  397. //r->bufFullCnt = (r->bufFullCnt + srcFrameCnt) % r->bufFrmCnt;
  398. r->bufFullCnt += srcFrameCnt;
  399. }
  400. // Copy audio samples out of the internal record buffer into dp[dstChCnt*dstFrameCnt].
  401. void _cmApCopyOut( cmApPortTestRecd* r, cmApSample_t* dp, unsigned dstChIdx, unsigned dstChCnt, unsigned dstFrameCnt )
  402. {
  403. // if there are not enough samples available to fill the destination buffer then zero the dst buf.
  404. if( r->bufFullCnt < dstFrameCnt )
  405. {
  406. printf("Empty Output Buffer\n");
  407. memset( dp, 0, dstFrameCnt*dstChCnt*sizeof(cmApSample_t) );
  408. }
  409. else
  410. {
  411. unsigned i,j;
  412. unsigned chCnt = cmMin(dstChCnt, r->chCnt);
  413. // for each output frame
  414. for(i=0; i<dstFrameCnt; ++i)
  415. {
  416. // copy the first chCnt samples from the internal buf to the output buf
  417. for(j=0; j<chCnt; ++j)
  418. dp[ (i*dstChCnt) + j ] = r->buf[ r->bufOutIdx + j ];
  419. // zero any output ch's for which there is no internal buf channel
  420. for(; j<dstChCnt; ++j)
  421. dp[ (i*dstChCnt) + j ] = 0;
  422. // advance the internal buffer
  423. r->bufOutIdx = (r->bufOutIdx + r->chCnt) % r->bufFrmCnt;
  424. }
  425. r->bufFullCnt -= dstFrameCnt;
  426. }
  427. }
  428. // Audio port callback function called from the audio device thread.
  429. void _cmApPortCb( cmApAudioPacket_t* inPktArray, unsigned inPktCnt, cmApAudioPacket_t* outPktArray, unsigned outPktCnt )
  430. {
  431. unsigned i;
  432. // for each incoming audio packet
  433. for(i=0; i<inPktCnt; ++i)
  434. {
  435. cmApPortTestRecd* r = (cmApPortTestRecd*)inPktArray[i].userCbPtr;
  436. if( inPktArray[i].devIdx == r->inDevIdx )
  437. {
  438. // copy the incoming audio into an internal buffer where it can be picked up by _cpApCopyOut().
  439. _cmApCopyIn( r, (cmApSample_t*)inPktArray[i].audioBytesPtr, inPktArray[i].begChIdx, inPktArray[i].chCnt, inPktArray[i].audioFramesCnt );
  440. }
  441. ++r->cbCnt;
  442. //printf("i %4i in:%4i out:%4i\n",r->bufFullCnt,r->bufInIdx,r->bufOutIdx);
  443. }
  444. unsigned hold_phase = 0;
  445. // for each outgoing audio packet
  446. for(i=0; i<outPktCnt; ++i)
  447. {
  448. cmApPortTestRecd* r = (cmApPortTestRecd*)outPktArray[i].userCbPtr;
  449. if( outPktArray[i].devIdx == r->outDevIdx )
  450. {
  451. // zero the output buffer
  452. memset(outPktArray[i].audioBytesPtr,0,outPktArray[i].chCnt * outPktArray[i].audioFramesCnt * sizeof(cmApSample_t) );
  453. // if the synth is enabled
  454. if( r->synthFl )
  455. {
  456. unsigned tmp_phase = _cmApSynthSine( r, outPktArray[i].audioBytesPtr, outPktArray[i].begChIdx, outPktArray[i].chCnt, outPktArray[i].audioFramesCnt, r->phase, r->frqHz );
  457. // the phase will only change on packets that are actually used
  458. if( tmp_phase != r->phase )
  459. hold_phase = tmp_phase;
  460. }
  461. else
  462. {
  463. // copy the any audio in the internal record buffer to the playback device
  464. _cmApCopyOut( r, (cmApSample_t*)outPktArray[i].audioBytesPtr, outPktArray[i].begChIdx, outPktArray[i].chCnt, outPktArray[i].audioFramesCnt );
  465. }
  466. }
  467. r->phase = hold_phase;
  468. //printf("o %4i in:%4i out:%4i\n",r->bufFullCnt,r->bufInIdx,r->bufOutIdx);
  469. // count callbacks
  470. ++r->cbCnt;
  471. }
  472. }
  473. #endif
  474. // print the usage message for cmAudioPortTest.c
  475. void _cmApPrintUsage( cmRpt_t* rpt )
  476. {
  477. char msg[] =
  478. "cmApPortTest() command switches\n"
  479. "-r <srate> -c <chcnt> -b <bufcnt> -f <frmcnt> -i <idevidx> -o <odevidx> -t -p -h \n"
  480. "\n"
  481. "-r <srate> = sample rate\n"
  482. "-a <chidx> = first channel\n"
  483. "-c <chcnt> = audio channels\n"
  484. "-b <bufcnt> = count of buffers\n"
  485. "-f <frmcnt> = count of samples per buffer\n"
  486. "-i <idevidx> = input device index\n"
  487. "-o <odevidx> = output device index\n"
  488. "-p = print report but do not start audio devices\n"
  489. "-h = print this usage message\n";
  490. cmRptPrintf(rpt,msg);
  491. }
  492. // Get a command line option.
  493. int _cmApGetOpt( int argc, const char* argv[], const char* label, int defaultVal, bool boolFl )
  494. {
  495. int i = 0;
  496. for(; i<argc; ++i)
  497. if( strcmp(label,argv[i]) == 0 )
  498. {
  499. if(boolFl)
  500. return 1;
  501. if( i == (argc-1) )
  502. return defaultVal;
  503. return atoi(argv[i+1]);
  504. }
  505. return defaultVal;
  506. }
  507. unsigned _cmGlobalInDevIdx = 0;
  508. unsigned _cmGlobalOutDevIdx = 0;
  509. void _cmApPortCb2( cmApAudioPacket_t* inPktArray, unsigned inPktCnt, cmApAudioPacket_t* outPktArray, unsigned outPktCnt )
  510. {
  511. cmApBufInputToOutput( _cmGlobalInDevIdx, _cmGlobalOutDevIdx );
  512. cmApBufUpdate( inPktArray, inPktCnt, outPktArray, outPktCnt );
  513. }
  514. // Audio Port testing function
  515. int cmApPortTest( bool runFl, cmRpt_t* rpt, int argc, const char* argv[] )
  516. {
  517. cmApPortTestRecd r;
  518. unsigned i;
  519. int result = 0;
  520. if( _cmApGetOpt(argc,argv,"-h",0,true) )
  521. _cmApPrintUsage(rpt);
  522. runFl = _cmApGetOpt(argc,argv,"-p",!runFl,true)?false:true;
  523. r.chIdx = _cmApGetOpt(argc,argv,"-a",0,false);
  524. r.chCnt = _cmApGetOpt(argc,argv,"-c",2,false);
  525. r.bufCnt = _cmApGetOpt(argc,argv,"-b",3,false);
  526. r.framesPerCycle = _cmApGetOpt(argc,argv,"-f",512,false);
  527. r.bufFrmCnt = (r.bufCnt*r.framesPerCycle);
  528. r.bufSmpCnt = (r.chCnt * r.bufFrmCnt);
  529. r.logCnt = 100;
  530. r.meterMs = 50;
  531. cmApSample_t buf[r.bufSmpCnt];
  532. char log[r.logCnt];
  533. unsigned ilog[r.logCnt];
  534. r.inDevIdx = _cmGlobalInDevIdx = _cmApGetOpt(argc,argv,"-i",0,false);
  535. r.outDevIdx = _cmGlobalOutDevIdx = _cmApGetOpt(argc,argv,"-o",2,false);
  536. r.phase = 0;
  537. r.frqHz = 2000;
  538. r.srate = 44100;
  539. r.bufInIdx = 0;
  540. r.bufOutIdx = 0;
  541. r.bufFullCnt = 0;
  542. r.logIdx = 0;
  543. r.buf = buf;
  544. r.log = log;
  545. r.ilog = ilog;
  546. r.cbCnt = 0;
  547. 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);
  548. // allocate the non-real-time port
  549. if( cmApNrtAllocate(rpt) != kOkApRC )
  550. {
  551. cmRptPrintf(rpt,"Non-real-time system allocation failed.");
  552. result = 1;
  553. goto errLabel;
  554. }
  555. // initialize the audio device interface
  556. if( cmApInitialize(rpt) != kOkApRC )
  557. {
  558. cmRptPrintf(rpt,"Initialize failed.\n");
  559. result = 1;
  560. goto errLabel;
  561. }
  562. // report the current audio device configuration
  563. for(i=0; i<cmApDeviceCount(); ++i)
  564. {
  565. 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));
  566. }
  567. // report the current audio devices using the audio port interface function
  568. cmApReport(rpt);
  569. if( runFl )
  570. {
  571. // initialize the audio bufer
  572. cmApBufInitialize( cmApDeviceCount(), r.meterMs );
  573. // setup the buffer for the output device
  574. cmApBufSetup( r.outDevIdx, r.srate, r.framesPerCycle, r.bufCnt, cmApDeviceChannelCount(r.outDevIdx,true), r.framesPerCycle, cmApDeviceChannelCount(r.outDevIdx,false), r.framesPerCycle );
  575. // setup the buffer for the input device
  576. if( r.inDevIdx != r.outDevIdx )
  577. cmApBufSetup( r.inDevIdx, r.srate, r.framesPerCycle, r.bufCnt, cmApDeviceChannelCount(r.inDevIdx,true), r.framesPerCycle, cmApDeviceChannelCount(r.inDevIdx,false), r.framesPerCycle );
  578. // setup an output device
  579. if(cmApDeviceSetup(r.outDevIdx,r.srate,r.framesPerCycle,_cmApPortCb2,&r) != kOkApRC )
  580. cmRptPrintf(rpt,"Out device setup failed.\n");
  581. else
  582. // setup an input device
  583. if( cmApDeviceSetup(r.inDevIdx,r.srate,r.framesPerCycle,_cmApPortCb2,&r) != kOkApRC )
  584. cmRptPrintf(rpt,"In device setup failed.\n");
  585. else
  586. // start the input device
  587. if( cmApDeviceStart(r.inDevIdx) != kOkApRC )
  588. cmRptPrintf(rpt,"In device start failed.\n");
  589. else
  590. // start the output device
  591. if( cmApDeviceStart(r.outDevIdx) != kOkApRC )
  592. cmRptPrintf(rpt,"Out Device start failed.\n");
  593. cmRptPrintf(rpt,"q=quit O/o output tone, I/i input tone P/p pass\n");
  594. char c;
  595. while((c=getchar()) != 'q')
  596. {
  597. //cmApAlsaDeviceRtReport(rpt,r.outDevIdx);
  598. switch(c)
  599. {
  600. case 'i':
  601. case 'I':
  602. cmApBufEnableTone(r.inDevIdx,-1,kInApFl | (c=='I'?kEnableApFl:0));
  603. break;
  604. case 'o':
  605. case 'O':
  606. cmApBufEnableTone(r.outDevIdx,-1,kOutApFl | (c=='O'?kEnableApFl:0));
  607. break;
  608. case 'p':
  609. case 'P':
  610. cmApBufEnablePass(r.outDevIdx,-1,kOutApFl | (c=='P'?kEnableApFl:0));
  611. break;
  612. case 's':
  613. cmApBufReport(rpt);
  614. break;
  615. }
  616. }
  617. // stop the input device
  618. if( cmApDeviceIsStarted(r.inDevIdx) )
  619. if( cmApDeviceStop(r.inDevIdx) != kOkApRC )
  620. cmRptPrintf(rpt,"In device stop failed.\n");
  621. // stop the output device
  622. if( cmApDeviceIsStarted(r.outDevIdx) )
  623. if( cmApDeviceStop(r.outDevIdx) != kOkApRC )
  624. cmRptPrintf(rpt,"Out device stop failed.\n");
  625. }
  626. errLabel:
  627. // release any resources held by the audio port interface
  628. if( cmApFinalize() != kOkApRC )
  629. cmRptPrintf(rpt,"Finalize failed.\n");
  630. cmApBufFinalize();
  631. cmApNrtFree();
  632. // report the count of audio buffer callbacks
  633. cmRptPrintf(rpt,"cb count:%i\n", r.cbCnt );
  634. //for(i=0; i<_logCnt; ++i)
  635. // cmRptPrintf(rpt,"%c(%i)",_log[i],_ilog[i]);
  636. //cmRptPrintf(rpt,"\n");
  637. return result;
  638. }
  639. /// [cmAudioPortExample]