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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942
  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. //#include <unistd.h> // usleep
  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. //usleep(1000);
  277. }
  278. ap->startedFl = true;
  279. return rc;
  280. }
  281. cmAgRC_t cmApAggDeviceStop( unsigned aggDevIdx )
  282. {
  283. cmAgRC_t rc = kOkAgRC;
  284. cmApAgg_t* ap;
  285. unsigned i;
  286. if((rc = _cmApAggGetAgg(aggDevIdx, &ap )) != kOkAgRC )
  287. return rc;
  288. for(i=0; i<ap->devCnt; ++i)
  289. {
  290. unsigned physDevIdx = ap->devArray[i].physDevIdx;
  291. if( cmApDeviceStop( physDevIdx ) != kOkApRC )
  292. return cmErrMsg(&_cmAg.err,kPhysDevStartFailAgRC,"The physical device (index:%i '%s') start failed.",physDevIdx,cmStringNullGuard(cmApDeviceLabel(physDevIdx)));
  293. }
  294. ap->startedFl = false;
  295. return rc;
  296. }
  297. bool cmApAggDeviceIsStarted( unsigned aggDevIdx )
  298. {
  299. cmApAgg_t* ap;
  300. if(_cmApAggGetAgg(aggDevIdx, &ap ) != kOkAgRC )
  301. return false;
  302. return ap->startedFl;
  303. }
  304. typedef struct
  305. {
  306. unsigned bufCnt; // 2=double buffering 3=triple buffering
  307. unsigned chIdx; // first test channel
  308. unsigned chCnt; // count of channels to test
  309. unsigned framesPerCycle; // DSP frames per cycle
  310. unsigned bufFrmCnt; // count of DSP frames used by the audio buffer (bufCnt * framesPerCycle)
  311. unsigned bufSmpCnt; // count of samples used by the audio buffer (chCnt * bufFrmCnt)
  312. unsigned inDevIdx; // input device index
  313. unsigned outDevIdx; // output device index
  314. double srate; // audio sample rate
  315. unsigned meterMs; // audio meter buffer length
  316. // param's and state for cmApSynthSine()
  317. bool synthFl;
  318. unsigned phase; // sine synth phase
  319. double frqHz; // sine synth frequency in Hz
  320. // buffer and state for cmApCopyIn/Out()
  321. cmApSample_t* buf; // buf[bufSmpCnt] - circular interleaved audio buffer
  322. unsigned bufInIdx; // next input buffer index
  323. unsigned bufOutIdx; // next output buffer index
  324. unsigned bufFullCnt; // count of full samples
  325. unsigned cbCnt; // count the callback
  326. unsigned underunCnt; //
  327. unsigned overunCnt;
  328. double* iMeter; // iMeter[ chCnt ]
  329. FILE* ifp;
  330. FILE* ofp;
  331. } cmApAggPortTestRecd;
  332. // The application can request any block of channels from the device. The packets are provided with the starting
  333. // device channel and channel count. This function converts device channels and channel counts to buffer
  334. // channel indexes and counts.
  335. //
  336. // Example:
  337. // input output
  338. // i,n i n
  339. // App: 0,4 0 1 2 3 -> 2 2
  340. // Pkt 2,8 2 3 4 5 6 7 8 -> 0 2
  341. //
  342. // The return value is the count of application requested channels located in this packet.
  343. //
  344. // input: *appChIdxPtr and appChCnt describe a block of device channels requested by the application.
  345. // *pktChIdxPtr and pktChCnt describe a block of device channels provided to the application
  346. //
  347. // output:*appChIdxPtr and <return value> describe a block of app buffer channels which will send/recv samples.
  348. // *pktChIdxPtr and <return value> describe a block of pkt buffer channels which will send/recv samples
  349. //
  350. unsigned _cmApAggDeviceToBuffer( unsigned* appChIdxPtr, unsigned appChCnt, unsigned* pktChIdxPtr, unsigned pktChCnt )
  351. {
  352. unsigned abi = *appChIdxPtr;
  353. unsigned aei = abi+appChCnt-1;
  354. unsigned pbi = *pktChIdxPtr;
  355. unsigned pei = pbi+pktChCnt-1;
  356. // if the ch's rqstd by the app do not overlap with this packet - return false.
  357. if( aei < pbi || abi > pei )
  358. return 0;
  359. // if the ch's rqstd by the app overlap with the beginning of the pkt channel block
  360. if( abi < pbi )
  361. {
  362. appChCnt -= pbi - abi;
  363. *appChIdxPtr = pbi - abi;
  364. *pktChIdxPtr = 0;
  365. }
  366. else
  367. {
  368. // the rqstd ch's begin inside the pkt channel block
  369. pktChCnt -= abi - pbi;
  370. *pktChIdxPtr = abi - pbi;
  371. *appChIdxPtr = 0;
  372. }
  373. // if the pkt channels extend beyond the rqstd ch block
  374. if( aei < pei )
  375. pktChCnt -= pei - aei;
  376. else
  377. appChCnt -= aei - pei; // the rqstd ch's extend beyond or coincide with the pkt block
  378. // the returned channel count must always be the same for both the rqstd and pkt
  379. return cmMin(appChCnt,pktChCnt);
  380. }
  381. // synthesize a sine signal into an interleaved audio buffer
  382. unsigned _cmApAggSynthSine( cmApAggPortTestRecd* r, float* p, unsigned chIdx, unsigned chCnt, unsigned frmCnt, unsigned phs, double hz )
  383. {
  384. long ph = 0;
  385. unsigned i;
  386. unsigned bufIdx = r->chIdx;
  387. unsigned bufChCnt;
  388. if( (bufChCnt = _cmApAggDeviceToBuffer( &bufIdx, r->chCnt, &chIdx, chCnt )) == 0)
  389. return phs;
  390. //if( r->cbCnt < 50 )
  391. // printf("ch:%i cnt:%i ch:%i cnt:%i bi:%i bcn:%i\n",r->chIdx,r->chCnt,chIdx,chCnt,bufIdx,bufChCnt);
  392. for(i=bufIdx; i<bufIdx+bufChCnt; ++i)
  393. {
  394. unsigned j;
  395. float* op = p + i;
  396. ph = phs;
  397. for(j=0; j<frmCnt; j++, op+=chCnt, ph++)
  398. {
  399. *op = (float)(0.9 * sin( 2.0 * M_PI * hz * ph / r->srate ));
  400. }
  401. }
  402. return ph;
  403. }
  404. // Copy the audio samples in the interleaved audio buffer sp[srcChCnt*srcFrameCnt]
  405. // to the internal record buffer.
  406. void _cmApAggCopyIn( cmApAggPortTestRecd* r, const cmApSample_t* sp, unsigned srcChIdx, unsigned srcChCnt, unsigned srcFrameCnt )
  407. {
  408. unsigned i,j;
  409. unsigned chCnt = cmMin(r->chCnt,srcChCnt);
  410. // write the incoming sample to an output file for debugging
  411. if( r->ifp != NULL )
  412. if( fwrite(sp,sizeof(cmApSample_t),srcChCnt*srcFrameCnt,r->ifp) != srcChCnt*srcFrameCnt )
  413. printf("file write fail.\n");
  414. // zero the input meter array
  415. for(i=0; i<r->chCnt; ++i)
  416. r->iMeter[i] = 0;
  417. for(i=0; i<srcFrameCnt; ++i)
  418. {
  419. // copy samples from the src to the buffer - both src and buffer are interleaved
  420. for(j=0; j<chCnt; ++j)
  421. {
  422. r->buf[ r->bufInIdx + j ] = sp[ (i*srcChCnt) + srcChIdx + j ];
  423. // record the max value in the input meter array
  424. if( r->buf[ r->bufInIdx + j ] > r->iMeter[j] )
  425. r->iMeter[j] = r->buf[ r->bufInIdx + j ];
  426. }
  427. // zero channels that are not used in the buffer
  428. for(; j<r->chCnt; ++j)
  429. r->buf[ r->bufInIdx + j ] = 0;
  430. // advance to the next frame
  431. r->bufInIdx = (r->bufInIdx+r->chCnt) % r->bufFrmCnt;
  432. }
  433. //r->bufFullCnt = (r->bufFullCnt + srcFrameCnt) % r->bufFrmCnt;
  434. cmThUIntIncr(&r->bufFullCnt,srcFrameCnt);
  435. if( r->bufFullCnt > r->bufFrmCnt )
  436. {
  437. //printf("Input buffer overrun.\n");
  438. ++r->overunCnt;
  439. r->bufFullCnt = 0;
  440. }
  441. }
  442. // Copy audio samples out of the internal record buffer into dp[dstChCnt*dstFrameCnt].
  443. void _cmApAggCopyOut( cmApAggPortTestRecd* r, cmApSample_t* dp, unsigned dstChIdx, unsigned dstChCnt, unsigned dstFrameCnt )
  444. {
  445. // if there are not enough samples available to fill the destination
  446. // buffer then zero the dst buf.
  447. if( r->bufFullCnt < dstFrameCnt )
  448. {
  449. //printf("Empty Output Buffer %i < %i\n",r->bufFullCnt,dstFrameCnt);
  450. memset( dp, 0, dstFrameCnt*dstChCnt*sizeof(cmApSample_t) );
  451. ++r->underunCnt;
  452. }
  453. else
  454. {
  455. unsigned i,j;
  456. unsigned chCnt = cmMin(dstChCnt,r->chCnt);
  457. for(i=0; i<dstFrameCnt; ++i)
  458. {
  459. // copy the stored buffer samples to the dst buffer
  460. for(j=0; j<chCnt; ++j)
  461. dp[ (i*dstChCnt) + dstChIdx + j ] = r->buf[ r->bufOutIdx + j ];
  462. // zero unset channels in the dst buffer
  463. for(; j<dstChCnt; ++j)
  464. dp[ (i*dstChCnt) + dstChIdx + j ] = 0;
  465. r->bufOutIdx = (r->bufOutIdx + r->chCnt) % r->bufFrmCnt;
  466. }
  467. cmThUIntDecr(&r->bufFullCnt,dstFrameCnt);
  468. }
  469. if( r->ofp != NULL )
  470. fwrite(dp,sizeof(cmApSample_t),dstChCnt*dstFrameCnt,r->ofp);
  471. }
  472. // Audio port callback function called from the audio device thread.
  473. void _cmApAggPortCb( cmApAudioPacket_t* inPktArray, unsigned inPktCnt, cmApAudioPacket_t* outPktArray, unsigned outPktCnt )
  474. {
  475. unsigned i;
  476. // for each incoming audio packet
  477. for(i=0; i<inPktCnt; ++i)
  478. {
  479. cmApAggPortTestRecd* r = (cmApAggPortTestRecd*)inPktArray[i].userCbPtr;
  480. if( r->synthFl==false && inPktArray[i].devIdx == r->inDevIdx )
  481. {
  482. // copy the incoming audio into an internal buffer where it can be picked up by _cpApCopyOut().
  483. _cmApAggCopyIn( r, (cmApSample_t*)inPktArray[i].audioBytesPtr, inPktArray[i].begChIdx, inPktArray[i].chCnt, inPktArray[i].audioFramesCnt );
  484. }
  485. ++r->cbCnt;
  486. //printf("i %4i in:%4i out:%4i\n",r->bufFullCnt,r->bufInIdx,r->bufOutIdx);
  487. }
  488. unsigned hold_phase = 0;
  489. // for each outgoing audio packet
  490. for(i=0; i<outPktCnt; ++i)
  491. {
  492. cmApAggPortTestRecd* r = (cmApAggPortTestRecd*)outPktArray[i].userCbPtr;
  493. if( outPktArray[i].devIdx == r->outDevIdx )
  494. {
  495. // zero the output buffer
  496. memset(outPktArray[i].audioBytesPtr,0,outPktArray[i].chCnt * outPktArray[i].audioFramesCnt * sizeof(cmApSample_t) );
  497. // if the synth is enabled
  498. if( r->synthFl )
  499. {
  500. unsigned tmp_phase = _cmApAggSynthSine( r, outPktArray[i].audioBytesPtr, outPktArray[i].begChIdx, outPktArray[i].chCnt, outPktArray[i].audioFramesCnt, r->phase, r->frqHz );
  501. // the phase will only change on packets that are actually used
  502. if( tmp_phase != r->phase )
  503. hold_phase = tmp_phase;
  504. }
  505. else
  506. {
  507. // copy the any audio in the internal record buffer to the playback device
  508. _cmApAggCopyOut( r, (cmApSample_t*)outPktArray[i].audioBytesPtr, outPktArray[i].begChIdx, outPktArray[i].chCnt, outPktArray[i].audioFramesCnt );
  509. }
  510. }
  511. r->phase = hold_phase;
  512. //printf("o %4i in:%4i out:%4i\n",r->bufFullCnt,r->bufInIdx,r->bufOutIdx);
  513. // count callbacks
  514. ++r->cbCnt;
  515. }
  516. }
  517. // print the usage message for cmAudioPortTest.c
  518. void _cmApAggPrintUsage( cmRpt_t* rpt )
  519. {
  520. char msg[] =
  521. "cmApAggPortTest() command switches\n"
  522. "-r <srate> -c <chcnt> -b <bufcnt> -f <frmcnt> -i <idevidx> -o <odevidx> -t -p -h \n"
  523. "\n"
  524. "-r <srate> = sample rate\n"
  525. "-a <chidx> = first channel\n"
  526. "-c <chcnt> = audio channels\n"
  527. "-b <bufcnt> = count of buffers\n"
  528. "-f <frmcnt> = count of samples per buffer\n"
  529. "-i <idevidx> = input device index\n"
  530. "-o <odevidx> = output device index\n"
  531. "-p = print report but do not start audio devices\n"
  532. "-h = print this usage message\n";
  533. cmRptPrintf(rpt,msg);
  534. }
  535. // Get a command line option.
  536. int _cmApAggGetOpt( int argc, const char* argv[], const char* label, int defaultVal, bool boolFl )
  537. {
  538. int i = 0;
  539. for(; i<argc; ++i)
  540. if( strcmp(label,argv[i]) == 0 )
  541. {
  542. if(boolFl)
  543. return 1;
  544. if( i == (argc-1) )
  545. return defaultVal;
  546. return atoi(argv[i+1]);
  547. }
  548. return defaultVal;
  549. }
  550. void _cmApBufShowMeter( cmRpt_t* rpt, unsigned devIdx )
  551. {
  552. unsigned faultCnt = 0;
  553. unsigned meterCnt = cmApBufChannelCount(devIdx,kInApFl);
  554. double meterArray[ meterCnt ];
  555. unsigned n = cmApBufGetStatus(devIdx, kInApFl, meterArray, meterCnt, &faultCnt );
  556. unsigned i;
  557. cmRptPrintf(rpt,"In: actual:%i fault: %i : ",n,faultCnt);
  558. for(i=0; i<meterCnt; ++i)
  559. cmRptPrintf(rpt,"%i:%f ",i,meterArray[i]);
  560. cmRptPrintf(rpt,"\n");
  561. }
  562. unsigned _cmAggGlobalInDevIdx = 0;
  563. unsigned _cmAggGlobalOutDevIdx = 0;
  564. void _cmApAggPortCb2( cmApAudioPacket_t* inPktArray, unsigned inPktCnt, cmApAudioPacket_t* outPktArray, unsigned outPktCnt )
  565. {
  566. cmApBufInputToOutput( _cmAggGlobalInDevIdx, _cmAggGlobalOutDevIdx );
  567. cmApBufUpdate( inPktArray, inPktCnt, outPktArray, outPktCnt );
  568. }
  569. void recdPrint();
  570. // Audio Port testing function
  571. int cmApAggTest( bool runFl, cmCtx_t* ctx, int argc, const char* argv[] )
  572. {
  573. cmApAggPortTestRecd r;
  574. unsigned i;
  575. cmRpt_t* rpt = &ctx->rpt;
  576. if( _cmApAggGetOpt(argc,argv,"-h",0,true) )
  577. _cmApAggPrintUsage(rpt);
  578. runFl = _cmApAggGetOpt(argc,argv,"-p",!runFl,true)?false:true;
  579. r.chIdx = _cmApAggGetOpt(argc,argv,"-a",0,false);
  580. r.chCnt = _cmApAggGetOpt(argc,argv,"-c",2,false);
  581. r.bufCnt = _cmApAggGetOpt(argc,argv,"-b",3,false);
  582. r.framesPerCycle = _cmApAggGetOpt(argc,argv,"-f",512,false);
  583. r.bufFrmCnt = (r.bufCnt*r.framesPerCycle);
  584. r.bufSmpCnt = (r.chCnt * r.bufFrmCnt);
  585. r.synthFl = false;
  586. r.meterMs = 50;
  587. cmApSample_t buf[r.bufSmpCnt];
  588. double imeter[r.chCnt];
  589. r.iMeter = imeter;
  590. r.inDevIdx = _cmAggGlobalInDevIdx = _cmApAggGetOpt(argc,argv,"-i",0,false);
  591. r.outDevIdx = _cmAggGlobalOutDevIdx = _cmApAggGetOpt(argc,argv,"-o",2,false);
  592. r.phase = 0;
  593. r.frqHz = 2000;
  594. r.srate = 44100;
  595. r.bufInIdx = 0;
  596. r.bufOutIdx = 0;
  597. r.bufFullCnt = 0;
  598. r.buf = buf;
  599. r.cbCnt = 0;
  600. r.underunCnt = 0;
  601. r.overunCnt = 0;
  602. r.ifp = NULL;
  603. r.ofp = NULL;
  604. if(0)
  605. {
  606. if((r.ifp = fopen("/home/kevin/temp/itemp0.bin","wb")) == NULL )
  607. cmRptPrintf(rpt,"File open failed.\n");
  608. if((r.ofp = fopen("/home/kevin/temp/otemp0.bin","wb")) == NULL )
  609. cmRptPrintf(rpt,"File open failed.\n");
  610. }
  611. 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);
  612. // allocate the aggregate device system
  613. if( cmApAggAllocate(rpt) != kOkAgRC )
  614. {
  615. cmRptPrintf(rpt,"The aggregate device system allocation failed.\n");
  616. return 1;
  617. }
  618. unsigned physDevIdxArray[] = { 0, 1 };
  619. unsigned physDevCnt = sizeof(physDevIdxArray)/sizeof(physDevIdxArray[0]);
  620. if( cmApAggCreateDevice("aggdev",physDevCnt,physDevIdxArray,kInAggFl | kOutAggFl) != kOkAgRC )
  621. {
  622. cmRptPrintf(rpt,"The aggregate device creation failed.n");
  623. goto doneLabel;
  624. }
  625. // initialize the audio device interface
  626. if( cmApInitialize(rpt) != kOkApRC )
  627. {
  628. cmRptPrintf(rpt,"Initialize failed.\n");
  629. goto doneLabel;
  630. }
  631. // report the current audio device configuration
  632. for(i=0; i<cmApDeviceCount(); ++i)
  633. {
  634. 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));
  635. }
  636. // report the current audio devices using the audio port interface function
  637. cmApReport(rpt);
  638. if( runFl )
  639. {
  640. // initialize the audio bufer
  641. cmApBufInitialize( cmApDeviceCount(), r.meterMs );
  642. // setup the buffer for the output device
  643. cmApBufSetup( r.outDevIdx, r.srate, r.framesPerCycle, r.bufCnt, cmApDeviceChannelCount(r.outDevIdx,true), r.framesPerCycle, cmApDeviceChannelCount(r.outDevIdx,false), r.framesPerCycle );
  644. // setup the buffer for the input device
  645. if( r.inDevIdx != r.outDevIdx )
  646. cmApBufSetup( r.inDevIdx, r.srate, r.framesPerCycle, r.bufCnt, cmApDeviceChannelCount(r.inDevIdx,true), r.framesPerCycle, cmApDeviceChannelCount(r.inDevIdx,false), r.framesPerCycle );
  647. // setup an input device
  648. if( cmApDeviceSetup(r.inDevIdx,r.srate,r.framesPerCycle,_cmApAggPortCb2,&r) != kOkApRC )
  649. {
  650. cmRptPrintf(rpt,"In device setup failed.\n");
  651. goto errLabel;
  652. }
  653. // setup an output device
  654. if( r.inDevIdx != r.outDevIdx )
  655. {
  656. if(cmApDeviceSetup(r.outDevIdx,r.srate,r.framesPerCycle,_cmApAggPortCb2,&r) != kOkApRC )
  657. {
  658. cmRptPrintf(rpt,"Out device setup failed.\n");
  659. goto errLabel;
  660. }
  661. }
  662. // start the input device
  663. if( cmApDeviceStart(r.inDevIdx) != kOkApRC )
  664. {
  665. cmRptPrintf(rpt,"In device start failed.\n");
  666. goto errLabel;
  667. }
  668. if( r.inDevIdx != r.outDevIdx )
  669. {
  670. // start the output device
  671. if( cmApDeviceStart(r.outDevIdx) != kOkApRC )
  672. {
  673. cmRptPrintf(rpt,"Out Device start failed.\n");
  674. goto errLabel;
  675. }
  676. }
  677. cmApBufEnableChannel(r.inDevIdx, -1, kInApFl | kEnableApFl );
  678. cmApBufEnableChannel(r.outDevIdx, -1, kOutApFl | kEnableApFl );
  679. cmApBufEnableMeter(r.inDevIdx, -1, kInApFl | kEnableApFl );
  680. cmRptPrintf(rpt,"q=quit O/o output tone, I/i input tone P/p pass\n");
  681. char c;
  682. while((c=getchar()) != 'q')
  683. {
  684. //cmApDeviceRtReport(rpt,r.outDevIdx);
  685. switch(c)
  686. {
  687. case 'i':
  688. case 'I':
  689. cmApBufEnableTone(r.inDevIdx,-1,kInApFl | (c=='I'?kEnableApFl:0));
  690. break;
  691. case 'o':
  692. case 'O':
  693. cmApBufEnableTone(r.outDevIdx,-1,kOutApFl | (c=='O'?kEnableApFl:0));
  694. break;
  695. case 'p':
  696. case 'P':
  697. cmApBufEnablePass(r.outDevIdx,-1,kOutApFl | (c=='P'?kEnableApFl:0));
  698. break;
  699. case 's':
  700. cmApBufReport(rpt);
  701. break;
  702. case 'm':
  703. _cmApBufShowMeter(rpt,_cmAggGlobalInDevIdx);
  704. /*
  705. cmRptPrintf(rpt,"iMeter: ");
  706. for(i=0; i<r.chCnt; ++i)
  707. cmRptPrintf(rpt,"%f ",r.iMeter[i]);
  708. cmRptPrintf(rpt,"\n");
  709. */
  710. break;
  711. case 'r':
  712. recdPrint();
  713. break;
  714. default:
  715. cmRptPrintf(rpt,"cb:%i\n",r.cbCnt);
  716. }
  717. }
  718. errLabel:
  719. // stop the input device
  720. if( cmApDeviceIsStarted(r.inDevIdx) )
  721. if( cmApDeviceStop(r.inDevIdx) != kOkApRC )
  722. cmRptPrintf(rpt,"In device stop failed.\n");
  723. // stop the output device
  724. if( cmApDeviceIsStarted(r.outDevIdx) )
  725. if( cmApDeviceStop(r.outDevIdx) != kOkApRC )
  726. cmRptPrintf(rpt,"Out device stop failed.\n");
  727. }
  728. doneLabel:
  729. // report the count of audio buffer callbacks
  730. cmRptPrintf(rpt,"cb:%i under:%i over:%i\n", r.cbCnt, r.underunCnt, r.overunCnt );
  731. // release any resources held by the audio port interface
  732. if( cmApFinalize() != kOkApRC )
  733. cmRptPrintf(rpt,"Finalize failed.\n");
  734. if( cmApAggFree() != kOkAgRC )
  735. cmRptPrintf(rpt,"Agg device system free failed.");
  736. if(r.ifp != NULL)
  737. fclose(r.ifp);
  738. if(r.ofp != NULL)
  739. fclose(r.ofp);
  740. cmApBufFinalize();
  741. return 0;
  742. }