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

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