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

cmAudioPort.c 24KB

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