libcm is a C development framework with an emphasis on audio signal processing applications.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

cmAudioAggDev.c 27KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978
  1. //| Copyright: (C) 2009-2020 Kevin Larke <contact AT larke DOT org>
  2. //| License: GNU GPL version 3.0 or above. See the accompanying LICENSE file.
  3. #include "cmGlobal.h"
  4. #include "cmRpt.h"
  5. #include "cmErr.h"
  6. #include "cmCtx.h"
  7. #include "cmMem.h"
  8. #include "cmMallocDebug.h"
  9. #include "cmTime.h"
  10. #include "cmAudioPort.h"
  11. #include "cmAudioPortFile.h"
  12. #include "cmAudioNrtDev.h"
  13. #include "cmAudioAggDev.h"
  14. #include "cmThread.h" // cmThUIntIncr()
  15. #include "cmApBuf.h" // only needed for cmApBufTest().
  16. enum
  17. {
  18. kBufArrayCnt = 2
  19. };
  20. struct cmApAgg_str;
  21. typedef struct
  22. {
  23. unsigned physDevIdx;
  24. struct cmApAgg_str* ap;
  25. unsigned iChIdx;
  26. unsigned iChCnt;
  27. unsigned oChIdx;
  28. unsigned oChCnt;
  29. } cmApAggDev_t;
  30. typedef struct cmApAgg_str
  31. {
  32. cmChar_t* label; // agg. device label
  33. unsigned aggDevIdx; // agg. device index
  34. unsigned sysDevIdx; // system device index
  35. unsigned devCnt; // count of phys devices
  36. cmApAggDev_t* devArray; // devArray[ devCnt ] - physical device array
  37. unsigned iChCnt; // sum of phys device input channels
  38. unsigned oChCnt; // sum of phys device output channels
  39. double srate; // agg. dev sample rate
  40. unsigned framesPerCycle; // agg. dev frames per cycle
  41. unsigned flags; // kAgInFl | kAgOutFl
  42. cmApCallbackPtr_t cbFunc; // client supplied callback func
  43. void* cbArg; // client supplied callback func arg.
  44. bool startedFl; // true if the agg. device is started
  45. struct cmApAgg_str* link; // _cmAg.list link
  46. } cmApAgg_t;
  47. typedef struct
  48. {
  49. cmErr_t err;
  50. cmApAgg_t* list;
  51. } cmApAggMain_t;
  52. cmApAggMain_t _cmAg;
  53. void _cmApAggCb( cmApAudioPacket_t* inPktArray, unsigned inPktCnt, cmApAudioPacket_t* outPktArray, unsigned outPktCnt )
  54. {
  55. unsigned i;
  56. cmApAudioPacket_t pkt;
  57. for(i=0; i<inPktCnt; ++i)
  58. {
  59. cmApAggDev_t* dp = (cmApAggDev_t*)inPktArray[i].userCbPtr;
  60. pkt = inPktArray[i];
  61. pkt.devIdx = dp->ap->sysDevIdx;
  62. pkt.begChIdx = dp->iChIdx;
  63. pkt.userCbPtr = dp->ap->cbArg;
  64. dp->ap->cbFunc( &pkt, 1, NULL, 0 );
  65. }
  66. for(i=0; i<outPktCnt; ++i)
  67. {
  68. cmApAggDev_t* dp = (cmApAggDev_t*)outPktArray[i].userCbPtr;
  69. pkt = outPktArray[i];
  70. pkt.devIdx = dp->ap->sysDevIdx;
  71. pkt.begChIdx = dp->oChIdx;
  72. pkt.userCbPtr = dp->ap->cbArg;
  73. dp->ap->cbFunc( NULL, 0, &pkt, 1 );
  74. }
  75. }
  76. void _cmApAgDeleteAggDev( cmApAgg_t* ap )
  77. {
  78. cmApAgg_t* cp = _cmAg.list;
  79. cmApAgg_t* pp = NULL;
  80. while( cp != NULL )
  81. {
  82. if( cp == ap )
  83. {
  84. if( pp == NULL )
  85. _cmAg.list = cp->link;
  86. else
  87. pp->link = cp->link;
  88. cmMemFree(ap->label);
  89. cmMemFree(ap->devArray);
  90. cmMemFree(ap);
  91. return;
  92. }
  93. pp = cp;
  94. cp = cp->link;
  95. }
  96. }
  97. cmAgRC_t cmApAggAllocate( cmRpt_t* rpt )
  98. {
  99. cmAgRC_t rc = kOkAgRC;
  100. cmErrSetup(&_cmAg.err,rpt,"cmAudioAggDev");
  101. _cmAg.list = NULL;
  102. return rc;
  103. }
  104. cmAgRC_t cmApAggFree()
  105. {
  106. cmAgRC_t rc = kOkAgRC;
  107. while( _cmAg.list != NULL )
  108. _cmApAgDeleteAggDev(_cmAg.list );
  109. return rc;
  110. }
  111. cmAgRC_t cmApAggInitialize( cmRpt_t* rpt, unsigned baseApDevIdx )
  112. {
  113. cmApAgg_t* ap = _cmAg.list;
  114. unsigned i;
  115. assert( baseApDevIdx == cmApDeviceCount() );
  116. for(i=0; ap!=NULL; ap=ap->link,++i)
  117. {
  118. ap->sysDevIdx = cmApDeviceCount() + i;
  119. ap->iChCnt = 0;
  120. ap->oChCnt = 0;
  121. unsigned i;
  122. for(i=0; i<ap->devCnt; ++i)
  123. {
  124. ap->devArray[i].iChIdx = ap->iChCnt;
  125. ap->devArray[i].oChIdx = ap->oChCnt;
  126. ap->devArray[i].iChCnt = cmApDeviceChannelCount(ap->devArray[i].physDevIdx,true);
  127. ap->devArray[i].oChCnt = cmApDeviceChannelCount(ap->devArray[i].physDevIdx,false);
  128. ap->iChCnt += ap->devArray[i].iChCnt;
  129. ap->oChCnt += ap->devArray[i].oChCnt;
  130. }
  131. }
  132. return kOkAgRC;
  133. }
  134. cmAgRC_t cmApAggFinalize()
  135. { return kOkAgRC; }
  136. cmAgRC_t cmApAggCreateDevice(
  137. const cmChar_t* label,
  138. unsigned devCnt,
  139. const unsigned physDevIdxArray[],
  140. unsigned flags )
  141. {
  142. cmAgRC_t rc = kOkAgRC;
  143. unsigned i;
  144. if( devCnt < 2 )
  145. return cmErrMsg(&_cmAg.err,kMustAggTwoAgRC,"Cannot aggregate less than two devices.");
  146. /*
  147. for(i=0; i<devCnt; ++i)
  148. {
  149. unsigned physDevIdx = physDevIdxArray[i];
  150. if( cmApAggIsDeviceAggregated(physDevIdx) )
  151. return cmErrMsg(&_cmAg.err,kDevAlreadyAggAgRC,"The physical device associated with index '%i' ('%s') has already been assigned to another aggregated device.",physDevIdx,cmStringNullGuard(cmApDeviceLabel(physDevIdx)));
  152. if( cmApDeviceIsStarted(physDevIdx) )
  153. return cmErrMsg(&_cmAg.err,kCantUseStartedDevAgRC,"The physical device associated with index '%i' ('%s') cannot be aggregated while it is running.",physDevIdx,cmStringNullGuard(cmApDeviceLabel(physDevIdx)));
  154. }
  155. */
  156. cmApAgg_t* ap = cmMemAllocZ(cmApAgg_t,1);
  157. ap->label = cmMemAllocStr(label==NULL?"Aggregated Device":label);
  158. ap->devArray = cmMemAllocZ(cmApAggDev_t,devCnt);
  159. ap->aggDevIdx = cmApAggDeviceCount();
  160. ap->sysDevIdx = cmInvalidIdx;
  161. ap->devCnt = devCnt;
  162. ap->iChCnt = 0;
  163. ap->oChCnt = 0;
  164. for(i=0; i<devCnt; ++i)
  165. {
  166. ap->devArray[i].ap = ap;
  167. ap->devArray[i].physDevIdx = physDevIdxArray[i];
  168. }
  169. ap->link = _cmAg.list;
  170. _cmAg.list = ap;
  171. return rc;
  172. }
  173. cmApAgg_t* _cmApAggDevIdxToPtr( unsigned aggDevIdx )
  174. {
  175. cmApAgg_t* ap = _cmAg.list;
  176. unsigned i = 0;
  177. for(; ap!=NULL; ap=ap->link,++i)
  178. if( ap->aggDevIdx == aggDevIdx )
  179. return ap;
  180. return NULL;
  181. }
  182. cmAgRC_t _cmApAggGetAgg( unsigned aggDevIdx, cmApAgg_t** retPtrPtr )
  183. {
  184. if((*retPtrPtr = _cmApAggDevIdxToPtr(aggDevIdx)) == NULL )
  185. return cmErrMsg(&_cmAg.err,kInvalidDevIdxAgRC,"The aggregate system device index '%i' is invalid.");
  186. return kOkAgRC;
  187. }
  188. bool cmApAggIsDeviceAggregated( unsigned physDevIdx )
  189. {
  190. cmApAgg_t* ap = _cmAg.list;
  191. for(; ap!=NULL; ap=ap->link)
  192. {
  193. unsigned i;
  194. for(i=0; i<ap->devCnt; ++i)
  195. if( ap->devArray[i].physDevIdx == physDevIdx )
  196. return true;
  197. }
  198. return false;
  199. }
  200. cmAgRC_t cmApAggDeviceCount()
  201. {
  202. unsigned devCnt=0;
  203. cmApAgg_t* ap = _cmAg.list;
  204. for(; ap!=NULL; ap=ap->link)
  205. ++devCnt;
  206. return devCnt;
  207. }
  208. const char* cmApAggDeviceLabel( unsigned aggDevIdx )
  209. {
  210. cmApAgg_t* ap;
  211. cmAgRC_t rc;
  212. if((rc = _cmApAggGetAgg(aggDevIdx, &ap )) == kOkAgRC )
  213. return ap->label;
  214. return NULL;
  215. }
  216. unsigned cmApAggDeviceChannelCount( unsigned aggDevIdx, bool inputFl )
  217. {
  218. cmApAgg_t* ap;
  219. cmAgRC_t rc;
  220. if((rc = _cmApAggGetAgg(aggDevIdx, &ap )) == kOkAgRC )
  221. return inputFl ? ap->iChCnt : ap->oChCnt;
  222. return 0;
  223. }
  224. double cmApAggDeviceSampleRate( unsigned aggDevIdx )
  225. {
  226. cmApAgg_t* ap;
  227. cmAgRC_t rc;
  228. if((rc = _cmApAggGetAgg(aggDevIdx, &ap )) == kOkAgRC )
  229. return ap->srate;
  230. return 0;
  231. }
  232. unsigned cmApAggDeviceFramesPerCycle( unsigned aggDevIdx, bool inputFl )
  233. {
  234. cmApAgg_t* ap;
  235. cmAgRC_t rc;
  236. if((rc = _cmApAggGetAgg(aggDevIdx, &ap )) == kOkAgRC )
  237. return ap->framesPerCycle;
  238. return 0;
  239. }
  240. cmAgRC_t cmApAggDeviceSetup(
  241. unsigned aggDevIdx,
  242. double srate,
  243. unsigned framesPerCycle,
  244. cmApCallbackPtr_t callbackPtr,
  245. void* userCbPtr )
  246. {
  247. cmApAgg_t* ap;
  248. cmAgRC_t rc;
  249. unsigned i;
  250. if((rc = _cmApAggGetAgg(aggDevIdx, &ap )) != kOkAgRC )
  251. return rc;
  252. if((rc = cmApAggDeviceStop(aggDevIdx)) != kOkAgRC )
  253. return rc;
  254. for(i=0; i<ap->devCnt; ++i)
  255. {
  256. unsigned physDevIdx = ap->devArray[i].physDevIdx;
  257. cmApAggDev_t* devPtr = ap->devArray + i;
  258. if( cmApDeviceSetup( physDevIdx, srate, framesPerCycle, _cmApAggCb, devPtr ) != kOkApRC )
  259. rc = cmErrMsg(&_cmAg.err,kPhysDevSetupFailAgRC,"The physical device (index:%i '%s') setup failed for sample rate:%f frames-per-cycle:%i.",physDevIdx,cmStringNullGuard(cmApDeviceLabel(physDevIdx)),srate,framesPerCycle);
  260. }
  261. if( rc == kOkAgRC )
  262. {
  263. ap->cbFunc = callbackPtr;
  264. ap->cbArg = userCbPtr;
  265. }
  266. return rc;
  267. }
  268. cmAgRC_t cmApAggDeviceStart( unsigned aggDevIdx )
  269. {
  270. cmAgRC_t rc = kOkAgRC;
  271. cmApAgg_t* ap;
  272. unsigned i;
  273. if((rc = _cmApAggGetAgg(aggDevIdx, &ap )) != kOkAgRC )
  274. return rc;
  275. for(i=0; i<ap->devCnt; ++i)
  276. {
  277. unsigned physDevIdx = ap->devArray[i].physDevIdx;
  278. if( cmApDeviceStart( physDevIdx ) != kOkApRC )
  279. return cmErrMsg(&_cmAg.err,kPhysDevStartFailAgRC,"The physical device (index:%i '%s') start failed.",physDevIdx,cmStringNullGuard(cmApDeviceLabel(physDevIdx)));
  280. }
  281. ap->startedFl = true;
  282. return rc;
  283. }
  284. cmAgRC_t cmApAggDeviceStop( unsigned aggDevIdx )
  285. {
  286. cmAgRC_t rc = kOkAgRC;
  287. cmApAgg_t* ap;
  288. unsigned i;
  289. if((rc = _cmApAggGetAgg(aggDevIdx, &ap )) != kOkAgRC )
  290. return rc;
  291. for(i=0; i<ap->devCnt; ++i)
  292. {
  293. unsigned physDevIdx = ap->devArray[i].physDevIdx;
  294. if( cmApDeviceStop( physDevIdx ) != kOkApRC )
  295. return cmErrMsg(&_cmAg.err,kPhysDevStartFailAgRC,"The physical device (index:%i '%s') start failed.",physDevIdx,cmStringNullGuard(cmApDeviceLabel(physDevIdx)));
  296. }
  297. ap->startedFl = false;
  298. return rc;
  299. }
  300. bool cmApAggDeviceIsStarted( unsigned aggDevIdx )
  301. {
  302. cmApAgg_t* ap;
  303. if(_cmApAggGetAgg(aggDevIdx, &ap ) != kOkAgRC )
  304. return false;
  305. return ap->startedFl;
  306. }
  307. typedef struct
  308. {
  309. unsigned bufCnt; // 2=double buffering 3=triple buffering
  310. unsigned chIdx; // first test channel
  311. unsigned chCnt; // count of channels to test
  312. unsigned framesPerCycle; // DSP frames per cycle
  313. unsigned bufFrmCnt; // count of DSP frames used by the audio buffer (bufCnt * framesPerCycle)
  314. unsigned bufSmpCnt; // count of samples used by the audio buffer (chCnt * bufFrmCnt)
  315. unsigned inDevIdx; // input device index
  316. unsigned outDevIdx; // output device index
  317. double srate; // audio sample rate
  318. unsigned meterMs; // audio meter buffer length
  319. // param's and state for cmApSynthSine()
  320. bool synthFl;
  321. unsigned phase; // sine synth phase
  322. double frqHz; // sine synth frequency in Hz
  323. // buffer and state for cmApCopyIn/Out()
  324. cmApSample_t* buf; // buf[bufSmpCnt] - circular interleaved audio buffer
  325. unsigned bufInIdx; // next input buffer index
  326. unsigned bufOutIdx; // next output buffer index
  327. unsigned bufFullCnt; // count of full samples
  328. unsigned cbCnt; // count the callback
  329. unsigned underunCnt; //
  330. unsigned overunCnt;
  331. double* iMeter; // iMeter[ chCnt ]
  332. FILE* ifp;
  333. FILE* ofp;
  334. } cmApAggPortTestRecd;
  335. // The application can request any block of channels from the device. The packets are provided with the starting
  336. // device channel and channel count. This function converts device channels and channel counts to buffer
  337. // channel indexes and counts.
  338. //
  339. // Example:
  340. // input output
  341. // i,n i n
  342. // App: 0,4 0 1 2 3 -> 2 2
  343. // Pkt 2,8 2 3 4 5 6 7 8 -> 0 2
  344. //
  345. // The return value is the count of application requested channels located in this packet.
  346. //
  347. // input: *appChIdxPtr and appChCnt describe a block of device channels requested by the application.
  348. // *pktChIdxPtr and pktChCnt describe a block of device channels provided to the application
  349. //
  350. // output:*appChIdxPtr and <return value> describe a block of app buffer channels which will send/recv samples.
  351. // *pktChIdxPtr and <return value> describe a block of pkt buffer channels which will send/recv samples
  352. //
  353. unsigned _cmApAggDeviceToBuffer( unsigned* appChIdxPtr, unsigned appChCnt, unsigned* pktChIdxPtr, unsigned pktChCnt )
  354. {
  355. unsigned abi = *appChIdxPtr;
  356. unsigned aei = abi+appChCnt-1;
  357. unsigned pbi = *pktChIdxPtr;
  358. unsigned pei = pbi+pktChCnt-1;
  359. // if the ch's rqstd by the app do not overlap with this packet - return false.
  360. if( aei < pbi || abi > pei )
  361. return 0;
  362. // if the ch's rqstd by the app overlap with the beginning of the pkt channel block
  363. if( abi < pbi )
  364. {
  365. appChCnt -= pbi - abi;
  366. *appChIdxPtr = pbi - abi;
  367. *pktChIdxPtr = 0;
  368. }
  369. else
  370. {
  371. // the rqstd ch's begin inside the pkt channel block
  372. pktChCnt -= abi - pbi;
  373. *pktChIdxPtr = abi - pbi;
  374. *appChIdxPtr = 0;
  375. }
  376. // if the pkt channels extend beyond the rqstd ch block
  377. if( aei < pei )
  378. pktChCnt -= pei - aei;
  379. else
  380. appChCnt -= aei - pei; // the rqstd ch's extend beyond or coincide with the pkt block
  381. // the returned channel count must always be the same for both the rqstd and pkt
  382. return cmMin(appChCnt,pktChCnt);
  383. }
  384. // synthesize a sine signal into an interleaved audio buffer
  385. unsigned _cmApAggSynthSine( cmApAggPortTestRecd* r, float* p, unsigned chIdx, unsigned chCnt, unsigned frmCnt, unsigned phs, double hz )
  386. {
  387. long ph = 0;
  388. unsigned i;
  389. unsigned bufIdx = r->chIdx;
  390. unsigned bufChCnt;
  391. if( (bufChCnt = _cmApAggDeviceToBuffer( &bufIdx, r->chCnt, &chIdx, chCnt )) == 0)
  392. return phs;
  393. //if( r->cbCnt < 50 )
  394. // printf("ch:%i cnt:%i ch:%i cnt:%i bi:%i bcn:%i\n",r->chIdx,r->chCnt,chIdx,chCnt,bufIdx,bufChCnt);
  395. for(i=bufIdx; i<bufIdx+bufChCnt; ++i)
  396. {
  397. unsigned j;
  398. float* op = p + i;
  399. ph = phs;
  400. for(j=0; j<frmCnt; j++, op+=chCnt, ph++)
  401. {
  402. *op = (float)(0.9 * sin( 2.0 * M_PI * hz * ph / r->srate ));
  403. }
  404. }
  405. return ph;
  406. }
  407. // Copy the audio samples in the interleaved audio buffer sp[srcChCnt*srcFrameCnt]
  408. // to the internal record buffer.
  409. void _cmApAggCopyIn( cmApAggPortTestRecd* r, const cmApSample_t* sp, unsigned srcChIdx, unsigned srcChCnt, unsigned srcFrameCnt )
  410. {
  411. unsigned i,j;
  412. unsigned chCnt = cmMin(r->chCnt,srcChCnt);
  413. // write the incoming sample to an output file for debugging
  414. if( r->ifp != NULL )
  415. if( fwrite(sp,sizeof(cmApSample_t),srcChCnt*srcFrameCnt,r->ifp) != srcChCnt*srcFrameCnt )
  416. printf("file write fail.\n");
  417. // zero the input meter array
  418. for(i=0; i<r->chCnt; ++i)
  419. r->iMeter[i] = 0;
  420. for(i=0; i<srcFrameCnt; ++i)
  421. {
  422. // copy samples from the src to the buffer - both src and buffer are interleaved
  423. for(j=0; j<chCnt; ++j)
  424. {
  425. r->buf[ r->bufInIdx + j ] = sp[ (i*srcChCnt) + srcChIdx + j ];
  426. // record the max value in the input meter array
  427. if( r->buf[ r->bufInIdx + j ] > r->iMeter[j] )
  428. r->iMeter[j] = r->buf[ r->bufInIdx + j ];
  429. }
  430. // zero channels that are not used in the buffer
  431. for(; j<r->chCnt; ++j)
  432. r->buf[ r->bufInIdx + j ] = 0;
  433. // advance to the next frame
  434. r->bufInIdx = (r->bufInIdx+r->chCnt) % r->bufFrmCnt;
  435. }
  436. //r->bufFullCnt = (r->bufFullCnt + srcFrameCnt) % r->bufFrmCnt;
  437. cmThUIntIncr(&r->bufFullCnt,srcFrameCnt);
  438. if( r->bufFullCnt > r->bufFrmCnt )
  439. {
  440. //printf("Input buffer overrun.\n");
  441. ++r->overunCnt;
  442. r->bufFullCnt = 0;
  443. }
  444. }
  445. // Copy audio samples out of the internal record buffer into dp[dstChCnt*dstFrameCnt].
  446. void _cmApAggCopyOut( cmApAggPortTestRecd* r, cmApSample_t* dp, unsigned dstChIdx, unsigned dstChCnt, unsigned dstFrameCnt )
  447. {
  448. // if there are not enough samples available to fill the destination
  449. // buffer then zero the dst buf.
  450. if( r->bufFullCnt < dstFrameCnt )
  451. {
  452. //printf("Empty Output Buffer %i < %i\n",r->bufFullCnt,dstFrameCnt);
  453. memset( dp, 0, dstFrameCnt*dstChCnt*sizeof(cmApSample_t) );
  454. ++r->underunCnt;
  455. }
  456. else
  457. {
  458. unsigned i,j;
  459. unsigned chCnt = cmMin(dstChCnt,r->chCnt);
  460. for(i=0; i<dstFrameCnt; ++i)
  461. {
  462. // copy the stored buffer samples to the dst buffer
  463. for(j=0; j<chCnt; ++j)
  464. dp[ (i*dstChCnt) + dstChIdx + j ] = r->buf[ r->bufOutIdx + j ];
  465. // zero unset channels in the dst buffer
  466. for(; j<dstChCnt; ++j)
  467. dp[ (i*dstChCnt) + dstChIdx + j ] = 0;
  468. r->bufOutIdx = (r->bufOutIdx + r->chCnt) % r->bufFrmCnt;
  469. }
  470. cmThUIntDecr(&r->bufFullCnt,dstFrameCnt);
  471. }
  472. if( r->ofp != NULL )
  473. fwrite(dp,sizeof(cmApSample_t),dstChCnt*dstFrameCnt,r->ofp);
  474. }
  475. // Audio port callback function called from the audio device thread.
  476. void _cmApAggPortCb( cmApAudioPacket_t* inPktArray, unsigned inPktCnt, cmApAudioPacket_t* outPktArray, unsigned outPktCnt )
  477. {
  478. unsigned i;
  479. // for each incoming audio packet
  480. for(i=0; i<inPktCnt; ++i)
  481. {
  482. cmApAggPortTestRecd* r = (cmApAggPortTestRecd*)inPktArray[i].userCbPtr;
  483. if( r->synthFl==false && inPktArray[i].devIdx == r->inDevIdx )
  484. {
  485. // copy the incoming audio into an internal buffer where it can be picked up by _cpApCopyOut().
  486. _cmApAggCopyIn( r, (cmApSample_t*)inPktArray[i].audioBytesPtr, inPktArray[i].begChIdx, inPktArray[i].chCnt, inPktArray[i].audioFramesCnt );
  487. }
  488. ++r->cbCnt;
  489. //printf("i %4i in:%4i out:%4i\n",r->bufFullCnt,r->bufInIdx,r->bufOutIdx);
  490. }
  491. unsigned hold_phase = 0;
  492. // for each outgoing audio packet
  493. for(i=0; i<outPktCnt; ++i)
  494. {
  495. cmApAggPortTestRecd* r = (cmApAggPortTestRecd*)outPktArray[i].userCbPtr;
  496. if( outPktArray[i].devIdx == r->outDevIdx )
  497. {
  498. // zero the output buffer
  499. memset(outPktArray[i].audioBytesPtr,0,outPktArray[i].chCnt * outPktArray[i].audioFramesCnt * sizeof(cmApSample_t) );
  500. // if the synth is enabled
  501. if( r->synthFl )
  502. {
  503. unsigned tmp_phase = _cmApAggSynthSine( r, outPktArray[i].audioBytesPtr, outPktArray[i].begChIdx, outPktArray[i].chCnt, outPktArray[i].audioFramesCnt, r->phase, r->frqHz );
  504. // the phase will only change on packets that are actually used
  505. if( tmp_phase != r->phase )
  506. hold_phase = tmp_phase;
  507. }
  508. else
  509. {
  510. // copy the any audio in the internal record buffer to the playback device
  511. _cmApAggCopyOut( r, (cmApSample_t*)outPktArray[i].audioBytesPtr, outPktArray[i].begChIdx, outPktArray[i].chCnt, outPktArray[i].audioFramesCnt );
  512. }
  513. }
  514. r->phase = hold_phase;
  515. //printf("o %4i in:%4i out:%4i\n",r->bufFullCnt,r->bufInIdx,r->bufOutIdx);
  516. // count callbacks
  517. ++r->cbCnt;
  518. }
  519. }
  520. // print the usage message for cmAudioPortTest.c
  521. void _cmApAggPrintUsage( cmRpt_t* rpt )
  522. {
  523. char msg[] =
  524. "cmApAggPortTest() command switches\n"
  525. "-r <srate> -c <chcnt> -b <bufcnt> -f <frmcnt> -i <idevidx> -o <odevidx> -t -p -h \n"
  526. "\n"
  527. "-r <srate> = sample rate\n"
  528. "-a <chidx> = first channel\n"
  529. "-c <chcnt> = audio channels\n"
  530. "-b <bufcnt> = count of buffers\n"
  531. "-f <frmcnt> = count of samples per buffer\n"
  532. "-i <idevidx> = input device index\n"
  533. "-o <odevidx> = output device index\n"
  534. "-p = print report but do not start audio devices\n"
  535. "-h = print this usage message\n";
  536. cmRptPrintf(rpt,msg);
  537. }
  538. // Get a command line option.
  539. int _cmApAggGetOpt( int argc, const char* argv[], const char* label, int defaultVal, bool boolFl )
  540. {
  541. int i = 0;
  542. for(; i<argc; ++i)
  543. if( strcmp(label,argv[i]) == 0 )
  544. {
  545. if(boolFl)
  546. return 1;
  547. if( i == (argc-1) )
  548. return defaultVal;
  549. return atoi(argv[i+1]);
  550. }
  551. return defaultVal;
  552. }
  553. void _cmApBufShowMeter( cmRpt_t* rpt, unsigned devIdx )
  554. {
  555. unsigned faultCnt = 0;
  556. unsigned meterCnt = cmApBufChannelCount(devIdx,kInApFl);
  557. double meterArray[ meterCnt ];
  558. unsigned n = cmApBufGetStatus(devIdx, kInApFl, meterArray, meterCnt, &faultCnt );
  559. unsigned i;
  560. cmRptPrintf(rpt,"In: actual:%i fault: %i : ",n,faultCnt);
  561. for(i=0; i<meterCnt; ++i)
  562. cmRptPrintf(rpt,"%i:%f ",i,meterArray[i]);
  563. cmRptPrintf(rpt,"\n");
  564. }
  565. unsigned _cmAggGlobalInDevIdx = 0;
  566. unsigned _cmAggGlobalOutDevIdx = 0;
  567. void _cmApAggPortCb2( cmApAudioPacket_t* inPktArray, unsigned inPktCnt, cmApAudioPacket_t* outPktArray, unsigned outPktCnt )
  568. {
  569. if( inPktCnt )
  570. {
  571. cmApAggPortTestRecd* r = (cmApAggPortTestRecd*)inPktArray[0].userCbPtr;
  572. r->cbCnt += 1;
  573. }
  574. if( outPktCnt )
  575. {
  576. cmApAggPortTestRecd* r = (cmApAggPortTestRecd*)outPktArray[0].userCbPtr;
  577. r->cbCnt += 1;
  578. }
  579. cmApBufInputToOutput( _cmAggGlobalInDevIdx, _cmAggGlobalOutDevIdx );
  580. cmApBufUpdate( inPktArray, inPktCnt, outPktArray, outPktCnt );
  581. }
  582. //void recdPrint();
  583. // Audio Port testing function
  584. int cmApAggTest( bool runFl, cmCtx_t* ctx, int argc, const char* argv[] )
  585. {
  586. cmApAggPortTestRecd r;
  587. unsigned i;
  588. cmRpt_t* rpt = &ctx->rpt;
  589. int srateMult = 1;
  590. if( _cmApAggGetOpt(argc,argv,"-h",0,true) )
  591. _cmApAggPrintUsage(rpt);
  592. runFl = _cmApAggGetOpt(argc,argv,"-p",!runFl,true)?false:true;
  593. r.chIdx = _cmApAggGetOpt(argc,argv,"-a",0,false);
  594. r.chCnt = _cmApAggGetOpt(argc,argv,"-c",2,false);
  595. r.bufCnt = _cmApAggGetOpt(argc,argv,"-b",3,false);
  596. r.framesPerCycle = _cmApAggGetOpt(argc,argv,"-f",512,false);
  597. r.bufFrmCnt = (r.bufCnt*r.framesPerCycle);
  598. r.bufSmpCnt = (r.chCnt * r.bufFrmCnt);
  599. r.synthFl = false;
  600. r.meterMs = 50;
  601. cmApSample_t buf[r.bufSmpCnt];
  602. double imeter[r.chCnt];
  603. r.iMeter = imeter;
  604. r.inDevIdx = _cmAggGlobalInDevIdx = _cmApAggGetOpt(argc,argv,"-i",0,false);
  605. r.outDevIdx = _cmAggGlobalOutDevIdx = _cmApAggGetOpt(argc,argv,"-o",2,false);
  606. r.phase = 0;
  607. r.frqHz = 2000;
  608. r.srate = 96000;
  609. r.bufInIdx = 0;
  610. r.bufOutIdx = 0;
  611. r.bufFullCnt = 0;
  612. r.buf = buf;
  613. r.cbCnt = 0;
  614. r.underunCnt = 0;
  615. r.overunCnt = 0;
  616. r.ifp = NULL;
  617. r.ofp = NULL;
  618. if(0)
  619. {
  620. if((r.ifp = fopen("/home/kevin/temp/itemp0.bin","wb")) == NULL )
  621. cmRptPrintf(rpt,"File open failed.\n");
  622. if((r.ofp = fopen("/home/kevin/temp/otemp0.bin","wb")) == NULL )
  623. cmRptPrintf(rpt,"File open failed.\n");
  624. }
  625. 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);
  626. // allocate the aggregate device system
  627. if( cmApAggAllocate(rpt) != kOkAgRC )
  628. {
  629. cmRptPrintf(rpt,"The aggregate device system allocation failed.\n");
  630. return 1;
  631. }
  632. // allocate the audio file device system
  633. if( cmApFileAllocate( rpt ) != kOkApRC )
  634. {
  635. cmRptPrintf(rpt,"The audio file device system allocation failed.\n");
  636. goto doneLabel;
  637. }
  638. // allocate the NRT device system
  639. if( cmApNrtAllocate(rpt) != kOkApRC )
  640. {
  641. cmRptPrintf(rpt,"The NRT audio device system allocation failed.\n");
  642. goto doneLabel;
  643. }
  644. unsigned physDevIdxArray[] = { 2, 4 };
  645. unsigned physDevCnt = sizeof(physDevIdxArray)/sizeof(physDevIdxArray[0]);
  646. if( cmApAggCreateDevice("aggdev",physDevCnt,physDevIdxArray,kInAggFl | kOutAggFl) != kOkAgRC )
  647. {
  648. cmRptPrintf(rpt,"The aggregate device creation failed.n");
  649. goto doneLabel;
  650. }
  651. // initialize the audio device interface
  652. if( cmApInitialize(rpt) != kOkApRC )
  653. {
  654. cmRptPrintf(rpt,"Initialize failed.\n");
  655. goto doneLabel;
  656. }
  657. // report the current audio device configuration
  658. for(i=0; i<cmApDeviceCount(); ++i)
  659. {
  660. 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));
  661. }
  662. // report the current audio devices using the audio port interface function
  663. cmApReport(rpt);
  664. if( runFl )
  665. {
  666. // initialize the audio bufer
  667. cmApBufInitialize( cmApDeviceCount(), r.meterMs );
  668. // setup the buffer for the output device
  669. cmApBufSetup( r.outDevIdx, r.srate, r.framesPerCycle, r.bufCnt, cmApDeviceChannelCount(r.outDevIdx,true), r.framesPerCycle, cmApDeviceChannelCount(r.outDevIdx,false), r.framesPerCycle, srateMult );
  670. // setup the buffer for the input device
  671. if( r.inDevIdx != r.outDevIdx )
  672. cmApBufSetup( r.inDevIdx, r.srate, r.framesPerCycle, r.bufCnt, cmApDeviceChannelCount(r.inDevIdx,true), r.framesPerCycle, cmApDeviceChannelCount(r.inDevIdx,false), r.framesPerCycle, srateMult );
  673. // setup an input device
  674. if( cmApDeviceSetup(r.inDevIdx,r.srate,r.framesPerCycle,_cmApAggPortCb2,&r) != kOkApRC )
  675. {
  676. cmRptPrintf(rpt,"In device setup failed.\n");
  677. goto errLabel;
  678. }
  679. // setup an output device
  680. if( r.inDevIdx != r.outDevIdx )
  681. {
  682. if(cmApDeviceSetup(r.outDevIdx,r.srate,r.framesPerCycle,_cmApAggPortCb2,&r) != kOkApRC )
  683. {
  684. cmRptPrintf(rpt,"Out device setup failed.\n");
  685. goto errLabel;
  686. }
  687. }
  688. // start the input device
  689. if( cmApDeviceStart(r.inDevIdx) != kOkApRC )
  690. {
  691. cmRptPrintf(rpt,"In device start failed.\n");
  692. goto errLabel;
  693. }
  694. if( r.inDevIdx != r.outDevIdx )
  695. {
  696. // start the output device
  697. if( cmApDeviceStart(r.outDevIdx) != kOkApRC )
  698. {
  699. cmRptPrintf(rpt,"Out Device start failed.\n");
  700. goto errLabel;
  701. }
  702. }
  703. cmApBufEnableChannel(r.inDevIdx, -1, kInApFl | kEnableApFl );
  704. cmApBufEnableChannel(r.outDevIdx, -1, kOutApFl | kEnableApFl );
  705. cmApBufEnableMeter( r.inDevIdx, -1, kInApFl | kEnableApFl );
  706. cmRptPrintf(rpt,"q=quit O/o output tone, I/i input tone P/p pass\n");
  707. char c;
  708. while((c=getchar()) != 'q')
  709. {
  710. //cmApDeviceRtReport(rpt,r.outDevIdx);
  711. switch(c)
  712. {
  713. case 'i':
  714. case 'I':
  715. cmApBufEnableTone(r.inDevIdx,-1,kInApFl | (c=='I'?kEnableApFl:0));
  716. break;
  717. case 'o':
  718. case 'O':
  719. cmApBufEnableTone(r.outDevIdx,2,kOutApFl | (c=='O'?kEnableApFl:0));
  720. break;
  721. case 'p':
  722. case 'P':
  723. cmApBufEnablePass(r.outDevIdx,-1,kOutApFl | (c=='P'?kEnableApFl:0));
  724. break;
  725. case 's':
  726. cmApBufReport(rpt);
  727. break;
  728. case 'm':
  729. _cmApBufShowMeter(rpt,_cmAggGlobalInDevIdx);
  730. /*
  731. cmRptPrintf(rpt,"iMeter: ");
  732. for(i=0; i<r.chCnt; ++i)
  733. cmRptPrintf(rpt,"%f ",r.iMeter[i]);
  734. cmRptPrintf(rpt,"\n");
  735. */
  736. break;
  737. case 'r':
  738. //recdPrint();
  739. break;
  740. default:
  741. cmRptPrintf(rpt,"cb:%i\n",r.cbCnt);
  742. }
  743. }
  744. errLabel:
  745. // stop the input device
  746. if( cmApDeviceIsStarted(r.inDevIdx) )
  747. if( cmApDeviceStop(r.inDevIdx) != kOkApRC )
  748. cmRptPrintf(rpt,"In device stop failed.\n");
  749. // stop the output device
  750. if( cmApDeviceIsStarted(r.outDevIdx) )
  751. if( cmApDeviceStop(r.outDevIdx) != kOkApRC )
  752. cmRptPrintf(rpt,"Out device stop failed.\n");
  753. }
  754. doneLabel:
  755. // report the count of audio buffer callbacks
  756. cmRptPrintf(rpt,"cb:%i under:%i over:%i\n", r.cbCnt, r.underunCnt, r.overunCnt );
  757. // release any resources held by the audio port interface
  758. if( cmApFinalize() != kOkApRC )
  759. cmRptPrintf(rpt,"Finalize failed.\n");
  760. if( cmApNrtFree() != kOkApRC )
  761. cmRptPrintf(rpt,"Audio NRT device system free failed.");
  762. if( cmApFileFree() != kOkApRC )
  763. cmRptPrintf(rpt,"Audio file device system free failed.");
  764. if( cmApAggFree() != kOkAgRC )
  765. cmRptPrintf(rpt,"Agg device system free failed.");
  766. if(r.ifp != NULL)
  767. fclose(r.ifp);
  768. if(r.ofp != NULL)
  769. fclose(r.ofp);
  770. cmApBufFinalize();
  771. return 0;
  772. }