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.

cmAudioAggDev.c 27KB

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