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 26KB

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