libcm is a C development framework with an emphasis on audio signal processing applications.
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

cmAudioPort.c 24KB

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