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

cmApBuf.c 26KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910
  1. #include "cmPrefix.h"
  2. #include "cmGlobal.h"
  3. #include "cmRpt.h"
  4. #include "cmErr.h"
  5. #include "cmMem.h"
  6. #include "cmMallocDebug.h"
  7. #include "cmAudioPort.h"
  8. #include "cmApBuf.h"
  9. #include "cmThread.h"
  10. /*
  11. This API is in general called by two types of threads:
  12. audio devices threads and the client thread. There
  13. may be multiple devie threads however there is only
  14. one client thread.
  15. The audio device threads only call cmApBufUpdate().
  16. cmApBufUpdate() is never called by any other threads.
  17. A call from the audio update threads targets specific channels
  18. (cmApCh records). The variables within each channels that
  19. it modifies are confined to:
  20. on input channels: increments ii and increments fn (data is entering the ch. buffers)
  21. on output channels: increments oi and decrements fn (data is leaving the ch. buffers)
  22. The client picks up incoming audio and provides outgoing audio via
  23. cmApBufGet(). It then informs the cmApBuf() that it has completed
  24. the audio data transfer by calling cmApBufAdvance().
  25. cmApBufAdvance() modifies the following internal variables:
  26. on input channels: increments oi and decrements fn (data has left the ch buffer)
  27. on output channels: increments ii and increments fn (data has enterned the ch. buffer)
  28. Based on the above scenario the channel ii and oi variables are always thread-safe
  29. because they are only changed by a single thread.
  30. ii oi fn
  31. ------ ----- ----
  32. input ch: audio client both
  33. output ch: client audio both
  34. The fn variable however is not thread-safe and therefore care must be taken as
  35. to how it is read and updated.
  36. */
  37. enum { kInApIdx=0, kOutApIdx=1, kIoApCnt=2 };
  38. typedef struct
  39. {
  40. unsigned fl; // kChApFl|kToneApFl|kMeterApFl ...
  41. cmApSample_t* b; // b[n]
  42. unsigned ii; // next in
  43. unsigned oi; // next out
  44. unsigned fn; // full cnt - count of samples currently in the buffer - incr'd by incoming, decr'd by outgoing
  45. unsigned phs; // tone phase
  46. double hz; // tone frequency
  47. double gain; // channel gain
  48. cmApSample_t* m; // m[mn] meter sample sum
  49. unsigned mn; // length of m[]
  50. unsigned mi; // next ele of m[] to rcv sum
  51. } cmApCh;
  52. typedef struct
  53. {
  54. unsigned chCnt;
  55. cmApCh* chArray;
  56. unsigned n; // length of b[] (multiple of dspFrameCnt) bufCnt*framesPerCycle
  57. double srate; // device sample rate;
  58. unsigned faultCnt;
  59. unsigned framesPerCycle;
  60. unsigned dspFrameCnt;
  61. } cmApIO;
  62. typedef struct
  63. {
  64. // ioArray[] always contains 2 elements - one for input the other for output.
  65. cmApIO ioArray[kIoApCnt];
  66. } cmApDev;
  67. typedef struct
  68. {
  69. cmApDev* devArray;
  70. unsigned devCnt;
  71. unsigned meterMs;
  72. cmApSample_t* zeroBuf; // buffer of zeros
  73. unsigned zeroBufCnt; // max of all dspFrameCnt for all devices.
  74. } cmApBuf;
  75. cmApBuf _cmApBuf;
  76. cmApSample_t _cmApMeterValue( const cmApCh* cp )
  77. {
  78. double sum = 0;
  79. unsigned i;
  80. for(i=0; i<cp->mn; ++i)
  81. sum += cp->m[i];
  82. return (cmApSample_t)sqrt(sum);
  83. }
  84. void _cmApSine( cmApCh* cp, cmApSample_t* b0, unsigned n0, cmApSample_t* b1, unsigned n1, unsigned stride, float srate )
  85. {
  86. unsigned i;
  87. for(i=0; i<n0; ++i,++cp->phs)
  88. b0[i*stride] = (float)(cp->gain * sin( 2.0 * M_PI * cp->hz * cp->phs / srate ));
  89. for(i=0; i<n1; ++i,++cp->phs)
  90. b1[i*stride] = (float)(cp->gain * sin( 2.0 * M_PI * cp->hz * cp->phs / srate ));
  91. }
  92. cmApSample_t _cmApMeter( const cmApSample_t* b, unsigned bn, unsigned stride )
  93. {
  94. const cmApSample_t* ep = b + bn;
  95. cmApSample_t sum = 0;
  96. for(; b<ep; b+=stride)
  97. sum += *b * *b;
  98. return sum / bn;
  99. }
  100. void _cmApChFinalize( cmApCh* chPtr )
  101. {
  102. cmMemPtrFree( &chPtr->b );
  103. cmMemPtrFree( &chPtr->m );
  104. }
  105. // n=buf sample cnt mn=meter buf smp cnt
  106. void _cmApChInitialize( cmApCh* chPtr, unsigned n, unsigned mn )
  107. {
  108. _cmApChFinalize(chPtr);
  109. chPtr->b = n==0 ? NULL : cmMemAllocZ( cmApSample_t, n );
  110. chPtr->ii = 0;
  111. chPtr->oi = 0;
  112. chPtr->fn = 0;
  113. chPtr->fl = (n!=0 ? kChApFl : 0);
  114. chPtr->hz = 1000;
  115. chPtr->gain = 1.0;
  116. chPtr->mn = mn;
  117. chPtr->m = cmMemAllocZ(cmApSample_t,mn);
  118. chPtr->mi = 0;
  119. }
  120. void _cmApIoFinalize( cmApIO* ioPtr )
  121. {
  122. unsigned i;
  123. for(i=0; i<ioPtr->chCnt; ++i)
  124. _cmApChFinalize( ioPtr->chArray + i );
  125. cmMemPtrFree(&ioPtr->chArray);
  126. ioPtr->chCnt = 0;
  127. ioPtr->n = 0;
  128. }
  129. void _cmApIoInitialize( cmApIO* ioPtr, double srate, unsigned framesPerCycle, unsigned chCnt, unsigned n, unsigned meterBufN, unsigned dspFrameCnt )
  130. {
  131. unsigned i;
  132. _cmApIoFinalize(ioPtr);
  133. n += (n % dspFrameCnt); // force buffer size to be a multiple of dspFrameCnt
  134. ioPtr->chArray = chCnt==0 ? NULL : cmMemAllocZ( cmApCh, chCnt );
  135. ioPtr->chCnt = chCnt;
  136. ioPtr->n = n;
  137. ioPtr->faultCnt = 0;
  138. ioPtr->framesPerCycle = framesPerCycle;
  139. ioPtr->srate = srate;
  140. ioPtr->dspFrameCnt = dspFrameCnt;
  141. for(i=0; i<chCnt; ++i )
  142. _cmApChInitialize( ioPtr->chArray + i, n, meterBufN );
  143. }
  144. void _cmApDevFinalize( cmApDev* dp )
  145. {
  146. unsigned i;
  147. for(i=0; i<kIoApCnt; ++i)
  148. _cmApIoFinalize( dp->ioArray+i);
  149. }
  150. void _cmApDevInitialize( cmApDev* dp, double srate, unsigned iFpC, unsigned iChCnt, unsigned iBufN, unsigned oFpC, unsigned oChCnt, unsigned oBufN, unsigned meterBufN, unsigned dspFrameCnt )
  151. {
  152. unsigned i;
  153. _cmApDevFinalize(dp);
  154. for(i=0; i<kIoApCnt; ++i)
  155. {
  156. unsigned chCnt = i==kInApIdx ? iChCnt : oChCnt;
  157. unsigned bufN = i==kInApIdx ? iBufN : oBufN;
  158. unsigned fpc = i==kInApIdx ? iFpC : oFpC;
  159. _cmApIoInitialize( dp->ioArray+i, srate, fpc, chCnt, bufN, meterBufN, dspFrameCnt );
  160. }
  161. }
  162. cmAbRC_t cmApBufInitialize( unsigned devCnt, unsigned meterMs )
  163. {
  164. cmAbRC_t rc;
  165. if((rc = cmApBufFinalize()) != kOkAbRC )
  166. return rc;
  167. _cmApBuf.devArray = cmMemAllocZ( cmApDev, devCnt );
  168. _cmApBuf.devCnt = devCnt;
  169. _cmApBuf.meterMs = meterMs;
  170. return kOkAbRC;
  171. }
  172. cmAbRC_t cmApBufFinalize()
  173. {
  174. unsigned i;
  175. for(i=0; i<_cmApBuf.devCnt; ++i)
  176. _cmApDevFinalize(_cmApBuf.devArray + i);
  177. cmMemPtrFree( &_cmApBuf.devArray );
  178. cmMemPtrFree( &_cmApBuf.zeroBuf );
  179. _cmApBuf.devCnt = 0;
  180. return kOkAbRC;
  181. }
  182. cmAbRC_t cmApBufSetup(
  183. unsigned devIdx,
  184. double srate,
  185. unsigned dspFrameCnt,
  186. unsigned bufCnt,
  187. unsigned inChCnt,
  188. unsigned inFramesPerCycle,
  189. unsigned outChCnt,
  190. unsigned outFramesPerCycle)
  191. {
  192. cmApDev* devPtr = _cmApBuf.devArray + devIdx;
  193. unsigned iBufN = bufCnt * inFramesPerCycle;
  194. unsigned oBufN = bufCnt * outFramesPerCycle;
  195. unsigned meterBufN = cmMax(1,floor(srate * _cmApBuf.meterMs / (1000.0 * outFramesPerCycle)));
  196. _cmApDevInitialize( devPtr, srate, inFramesPerCycle, inChCnt, iBufN, outFramesPerCycle, outChCnt, oBufN, meterBufN, dspFrameCnt );
  197. if( inFramesPerCycle > _cmApBuf.zeroBufCnt || outFramesPerCycle > _cmApBuf.zeroBufCnt )
  198. {
  199. _cmApBuf.zeroBufCnt = cmMax(inFramesPerCycle,outFramesPerCycle);
  200. _cmApBuf.zeroBuf = cmMemResizeZ(cmApSample_t,_cmApBuf.zeroBuf,_cmApBuf.zeroBufCnt);
  201. }
  202. return kOkAbRC;
  203. }
  204. cmAbRC_t cmApBufPrimeOutput( unsigned devIdx, unsigned audioCycleCnt )
  205. {
  206. cmApIO* iop = _cmApBuf.devArray[devIdx].ioArray + kOutApIdx;
  207. unsigned i;
  208. for(i=0; i<iop->chCnt; ++i)
  209. {
  210. cmApCh* cp = iop->chArray + i;
  211. unsigned bn = iop->n * sizeof(cmApSample_t);
  212. memset(cp->b,0,bn);
  213. cp->oi = 0;
  214. cp->ii = iop->framesPerCycle * audioCycleCnt;
  215. cp->fn = iop->framesPerCycle * audioCycleCnt;
  216. }
  217. return kOkAbRC;
  218. }
  219. cmAbRC_t cmApBufUpdate(
  220. cmApAudioPacket_t* inPktArray,
  221. unsigned inPktCnt,
  222. cmApAudioPacket_t* outPktArray,
  223. unsigned outPktCnt )
  224. {
  225. unsigned i,j;
  226. // copy samples from the packet to the buffer
  227. if( inPktArray != NULL )
  228. {
  229. for(i=0; i<inPktCnt; ++i)
  230. {
  231. cmApAudioPacket_t* pp = inPktArray + i;
  232. cmApIO* ip = _cmApBuf.devArray[pp->devIdx].ioArray + kInApIdx; // dest io recd
  233. // for each source packet channel and enabled dest channel
  234. for(j=0; j<pp->chCnt; ++j)
  235. {
  236. cmApCh* cp = ip->chArray + pp->begChIdx +j; // dest ch
  237. unsigned n0 = ip->n - cp->ii; // first dest segment
  238. unsigned n1 = 0; // second dest segment
  239. assert(pp->begChIdx + j < ip->chCnt );
  240. // if the incoming samples would overflow the buffer then ignore them
  241. if( cp->fn + pp->audioFramesCnt > ip->n )
  242. {
  243. ++ip->faultCnt; // record input overflow
  244. continue;
  245. }
  246. // if the incoming samples would go off the end of the buffer then
  247. // copy in the samples in two segments (one at the end and another at begin of dest channel)
  248. if( n0 < pp->audioFramesCnt )
  249. n1 = pp->audioFramesCnt-n0;
  250. else
  251. n0 = pp->audioFramesCnt;
  252. bool enaFl = cmIsFlag(cp->fl,kChApFl) && cmIsFlag(cp->fl,kMuteApFl)==false;
  253. const cmApSample_t* sp = enaFl ? ((cmApSample_t*)pp->audioBytesPtr) + j : _cmApBuf.zeroBuf;
  254. unsigned ssn = enaFl ? pp->chCnt : 1; // stride (packet samples are interleaved)
  255. cmApSample_t* dp = cp->b + cp->ii;
  256. const cmApSample_t* ep = dp + n0;
  257. // update the meter
  258. if( cmIsFlag(cp->fl,kMeterApFl) )
  259. {
  260. cp->m[cp->mi] = _cmApMeter(sp,pp->audioFramesCnt,pp->chCnt);
  261. cp->mi = (cp->mi + 1) % cp->mn;
  262. }
  263. // if the test tone is enabled on this input channel
  264. if( enaFl && cmIsFlag(cp->fl,kToneApFl) )
  265. {
  266. _cmApSine(cp, dp, n0, cp->b, n1, 1, ip->srate );
  267. }
  268. else // otherwise copy samples from the packet to the buffer
  269. {
  270. // copy the first segment
  271. for(; dp < ep; sp += ssn )
  272. *dp++ = cp->gain * *sp;
  273. // if there is a second segment
  274. if( n1 > 0 )
  275. {
  276. // copy the second segment
  277. dp = cp->b;
  278. ep = dp + n1;
  279. for(; dp<ep; sp += ssn )
  280. *dp++ = cp->gain * *sp;
  281. }
  282. }
  283. // advance the input channel buffer
  284. cp->ii = n1>0 ? n1 : cp->ii + n0;
  285. //cp->fn += pp->audioFramesCnt;
  286. cmThUIntIncr(&cp->fn,pp->audioFramesCnt);
  287. }
  288. }
  289. }
  290. // copy samples from the buffer to the packet
  291. if( outPktArray != NULL )
  292. {
  293. for(i=0; i<outPktCnt; ++i)
  294. {
  295. cmApAudioPacket_t* pp = outPktArray + i;
  296. cmApIO* op = _cmApBuf.devArray[pp->devIdx].ioArray + kOutApIdx; // dest io recd
  297. // for each dest packet channel and enabled source channel
  298. for(j=0; j<pp->chCnt; ++j)
  299. {
  300. cmApCh* cp = op->chArray + pp->begChIdx + j; // dest ch
  301. unsigned n0 = op->n - cp->oi; // first src segment
  302. unsigned n1 = 0; // second src segment
  303. volatile unsigned fn = cp->fn; // store fn because it may be changed by the client thread
  304. // if the outgoing samples will underflow the buffer
  305. if( pp->audioFramesCnt > fn )
  306. {
  307. ++op->faultCnt; // record an output underflow
  308. // if the buffer is empty - zero the packet and return
  309. if( fn == 0 )
  310. {
  311. memset( pp->audioBytesPtr, 0, pp->audioFramesCnt*sizeof(cmApSample_t));
  312. continue;
  313. }
  314. // ... otherwise decrease the count of returned samples
  315. pp->audioFramesCnt = fn;
  316. }
  317. // if the outgong segments would go off the end of the buffer then
  318. // arrange to wrap to the begining of the buffer
  319. if( n0 < pp->audioFramesCnt )
  320. n1 = pp->audioFramesCnt-n0;
  321. else
  322. n0 = pp->audioFramesCnt;
  323. cmApSample_t* dp = ((cmApSample_t*)pp->audioBytesPtr) + j;
  324. bool enaFl = cmIsFlag(cp->fl,kChApFl) && cmIsFlag(cp->fl,kMuteApFl)==false;
  325. // if the tone is enabled on this channel
  326. if( enaFl && cmIsFlag(cp->fl,kToneApFl) )
  327. {
  328. _cmApSine(cp, dp, n0, dp + n0*pp->chCnt, n1, pp->chCnt, op->srate );
  329. }
  330. else // otherwise copy samples from the output buffer to the packet
  331. {
  332. const cmApSample_t* sp = enaFl ? cp->b + cp->oi : _cmApBuf.zeroBuf;
  333. const cmApSample_t* ep = sp + n0;
  334. // copy the first segment
  335. for(; sp < ep; dp += pp->chCnt )
  336. *dp = cp->gain * *sp++;
  337. // if there is a second segment
  338. if( n1 > 0 )
  339. {
  340. // copy the second segment
  341. sp = enaFl ? cp->b : _cmApBuf.zeroBuf;
  342. ep = sp + n1;
  343. for(; sp<ep; dp += pp->chCnt )
  344. *dp = cp->gain * *sp++;
  345. }
  346. }
  347. // update the meter
  348. if( cmIsFlag(cp->fl,kMeterApFl) )
  349. {
  350. cp->m[cp->mi] = _cmApMeter(((cmApSample_t*)pp->audioBytesPtr)+j,pp->audioFramesCnt,pp->chCnt);
  351. cp->mi = (cp->mi + 1) % cp->mn;
  352. }
  353. // advance the output channel buffer
  354. cp->oi = n1>0 ? n1 : cp->oi + n0;
  355. //cp->fn -= pp->audioFramesCnt;
  356. cmThUIntDecr(&cp->fn,pp->audioFramesCnt);
  357. }
  358. }
  359. }
  360. return kOkAbRC;
  361. }
  362. unsigned cmApBufMeterMs()
  363. { return _cmApBuf.meterMs; }
  364. unsigned cmApBufChannelCount( unsigned devIdx, unsigned flags )
  365. {
  366. if( devIdx == cmInvalidIdx )
  367. return 0;
  368. unsigned idx = flags & kInApFl ? kInApIdx : kOutApIdx;
  369. return _cmApBuf.devArray[devIdx].ioArray[ idx ].chCnt;
  370. }
  371. void cmApBufSetFlag( unsigned devIdx, unsigned chIdx, unsigned flags )
  372. {
  373. if( devIdx == cmInvalidIdx )
  374. return;
  375. unsigned idx = flags & kInApFl ? kInApIdx : kOutApIdx;
  376. bool enableFl = flags & kEnableApFl ? true : false;
  377. unsigned i = chIdx != -1 ? chIdx : 0;
  378. unsigned n = chIdx != -1 ? chIdx+1 : _cmApBuf.devArray[devIdx].ioArray[idx].chCnt;
  379. for(; i<n; ++i)
  380. {
  381. cmApCh* cp = _cmApBuf.devArray[devIdx].ioArray[idx].chArray + i;
  382. cp->fl = cmEnaFlag(cp->fl, flags & (kChApFl|kToneApFl|kMeterApFl|kMuteApFl|kPassApFl), enableFl );
  383. }
  384. }
  385. bool cmApBufIsFlag( unsigned devIdx, unsigned chIdx, unsigned flags )
  386. {
  387. if( devIdx == cmInvalidIdx )
  388. return false;
  389. unsigned idx = flags & kInApFl ? kInApIdx : kOutApIdx;
  390. return cmIsFlag(_cmApBuf.devArray[devIdx].ioArray[idx].chArray[chIdx].fl,flags);
  391. }
  392. void cmApBufEnableChannel( unsigned devIdx, unsigned chIdx, unsigned flags )
  393. { cmApBufSetFlag(devIdx,chIdx,flags | kChApFl); }
  394. bool cmApBufIsChannelEnabled( unsigned devIdx, unsigned chIdx, unsigned flags )
  395. { return cmApBufIsFlag(devIdx, chIdx, flags | kChApFl); }
  396. void cmApBufEnableTone( unsigned devIdx, unsigned chIdx, unsigned flags )
  397. { cmApBufSetFlag(devIdx,chIdx,flags | kToneApFl); }
  398. bool cmApBufIsToneEnabled( unsigned devIdx, unsigned chIdx, unsigned flags )
  399. { return cmApBufIsFlag(devIdx,chIdx,flags | kToneApFl); }
  400. void cmApBufEnableMute( unsigned devIdx, unsigned chIdx, unsigned flags )
  401. { cmApBufSetFlag(devIdx,chIdx,flags | kMuteApFl); }
  402. bool cmApBufIsMuteEnabled( unsigned devIdx, unsigned chIdx, unsigned flags )
  403. { return cmApBufIsFlag(devIdx,chIdx,flags | kMuteApFl); }
  404. void cmApBufEnablePass( unsigned devIdx, unsigned chIdx, unsigned flags )
  405. { cmApBufSetFlag(devIdx,chIdx,flags | kPassApFl); }
  406. bool cmApBufIsPassEnabled( unsigned devIdx, unsigned chIdx, unsigned flags )
  407. { return cmApBufIsFlag(devIdx,chIdx,flags | kPassApFl); }
  408. void cmApBufEnableMeter( unsigned devIdx, unsigned chIdx, unsigned flags )
  409. { cmApBufSetFlag(devIdx,chIdx,flags | kMeterApFl); }
  410. bool cmApBufIsMeterEnabled(unsigned devIdx, unsigned chIdx, unsigned flags )
  411. { return cmApBufIsFlag(devIdx,chIdx,flags | kMeterApFl); }
  412. cmApSample_t cmApBufMeter(unsigned devIdx, unsigned chIdx, unsigned flags )
  413. {
  414. if( devIdx == cmInvalidIdx )
  415. return 0;
  416. unsigned idx = flags & kInApFl ? kInApIdx : kOutApIdx;
  417. const cmApCh* cp = _cmApBuf.devArray[devIdx].ioArray[idx].chArray + chIdx;
  418. return _cmApMeterValue(cp);
  419. }
  420. void cmApBufSetGain( unsigned devIdx, unsigned chIdx, unsigned flags, double gain )
  421. {
  422. if( devIdx == cmInvalidIdx )
  423. return;
  424. unsigned idx = flags & kInApFl ? kInApIdx : kOutApIdx;
  425. unsigned i = chIdx != -1 ? chIdx : 0;
  426. unsigned n = i + (chIdx != -1 ? 1 : _cmApBuf.devArray[devIdx].ioArray[idx].chCnt);
  427. for(; i<n; ++i)
  428. _cmApBuf.devArray[devIdx].ioArray[idx].chArray[i].gain = gain;
  429. }
  430. double cmApBufGain( unsigned devIdx, unsigned chIdx, unsigned flags )
  431. {
  432. if( devIdx == cmInvalidIdx )
  433. return 0;
  434. unsigned idx = flags & kInApFl ? kInApIdx : kOutApIdx;
  435. return _cmApBuf.devArray[devIdx].ioArray[idx].chArray[chIdx].gain;
  436. }
  437. unsigned cmApBufGetStatus( unsigned devIdx, unsigned flags, double* meterArray, unsigned meterCnt, unsigned* faultCntPtr )
  438. {
  439. if( devIdx == cmInvalidIdx )
  440. return 0;
  441. unsigned ioIdx = cmIsFlag(flags,kInApFl) ? kInApIdx : kOutApIdx;
  442. cmApIO* iop = _cmApBuf.devArray[devIdx].ioArray + ioIdx;
  443. unsigned chCnt = cmMin(iop->chCnt, meterCnt );
  444. unsigned i;
  445. if( faultCntPtr != NULL )
  446. *faultCntPtr = iop->faultCnt;
  447. for(i=0; i<chCnt; ++i)
  448. meterArray[i] = _cmApMeterValue(iop->chArray + i);
  449. return chCnt;
  450. }
  451. bool cmApBufIsDeviceReady( unsigned devIdx, unsigned flags )
  452. {
  453. //bool iFl = true;
  454. //bool oFl = true;
  455. unsigned i = 0;
  456. if( devIdx == cmInvalidIdx )
  457. return false;
  458. if( flags & kInApFl )
  459. {
  460. const cmApIO* ioPtr = _cmApBuf.devArray[devIdx].ioArray + kInApIdx;
  461. for(i=0; i<ioPtr->chCnt; ++i)
  462. if( ioPtr->chArray[i].fn < ioPtr->dspFrameCnt )
  463. return false;
  464. //iFl = ioPtr->fn > ioPtr->dspFrameCnt;
  465. }
  466. if( flags & kOutApFl )
  467. {
  468. const cmApIO* ioPtr = _cmApBuf.devArray[devIdx].ioArray + kOutApIdx;
  469. for(i=0; i<ioPtr->chCnt; ++i)
  470. if( (ioPtr->n - ioPtr->chArray[i].fn) < ioPtr->dspFrameCnt )
  471. return false;
  472. //oFl = (ioPtr->n - ioPtr->fn) > ioPtr->dspFrameCnt;
  473. }
  474. return true;
  475. //return iFl & oFl;
  476. }
  477. // Note that his function returns audio samples but does NOT
  478. // change any internal states.
  479. void cmApBufGet( unsigned devIdx, unsigned flags, cmApSample_t* bufArray[], unsigned bufChCnt )
  480. {
  481. unsigned i;
  482. if( devIdx == cmInvalidIdx )
  483. {
  484. for(i=0; i<bufChCnt; ++i)
  485. bufArray[i] = NULL;
  486. return;
  487. }
  488. unsigned idx = flags & kInApFl ? kInApIdx : kOutApIdx;
  489. const cmApIO* ioPtr = _cmApBuf.devArray[devIdx].ioArray + idx;
  490. unsigned n = bufChCnt < ioPtr->chCnt ? bufChCnt : ioPtr->chCnt;
  491. //unsigned offs = flags & kInApFl ? ioPtr->oi : ioPtr->ii;
  492. cmApCh* cp = ioPtr->chArray;
  493. for(i=0; i<n; ++i,++cp)
  494. {
  495. unsigned offs = flags & kInApFl ? cp->oi : cp->ii;
  496. bufArray[i] = cmIsFlag(cp->fl,kChApFl) ? cp->b + offs : NULL;
  497. }
  498. }
  499. void cmApBufGetIO( unsigned iDevIdx, cmApSample_t* iBufArray[], unsigned iBufChCnt, unsigned oDevIdx, cmApSample_t* oBufArray[], unsigned oBufChCnt )
  500. {
  501. cmApBufGet( iDevIdx, kInApFl, iBufArray, iBufChCnt );
  502. cmApBufGet( oDevIdx, kOutApFl,oBufArray, oBufChCnt );
  503. unsigned i = 0;
  504. if( iDevIdx != cmInvalidIdx && oDevIdx != cmInvalidIdx )
  505. {
  506. const cmApIO* ip = _cmApBuf.devArray[iDevIdx].ioArray + kInApIdx;
  507. const cmApIO* op = _cmApBuf.devArray[oDevIdx].ioArray + kOutApIdx;
  508. unsigned minChCnt = cmMin(iBufChCnt,oBufChCnt);
  509. unsigned frmCnt = cmMin(ip->dspFrameCnt,op->dspFrameCnt);
  510. unsigned byteCnt = frmCnt * sizeof(cmApSample_t);
  511. for(i=0; i<minChCnt; ++i)
  512. {
  513. cmApCh* ocp = op->chArray + i;
  514. cmApCh* icp = ip->chArray + i;
  515. if( oBufArray[i] != NULL )
  516. {
  517. // if either the input or output channel is marked for pass-through
  518. if( cmAllFlags(ocp->fl,kPassApFl) || cmAllFlags(icp->fl,kPassApFl) )
  519. {
  520. memcpy( oBufArray[i], iBufArray[i], byteCnt );
  521. // set the output buffer to NULL to prevent it being over written by the client
  522. oBufArray[i] = NULL;
  523. }
  524. else
  525. {
  526. // zero the output buffer
  527. memset(oBufArray[i],0,byteCnt);
  528. }
  529. }
  530. }
  531. }
  532. if( oDevIdx != cmInvalidIdx )
  533. {
  534. const cmApIO* op = _cmApBuf.devArray[oDevIdx].ioArray + kOutApIdx;
  535. unsigned byteCnt = op->dspFrameCnt * sizeof(cmApSample_t);
  536. for(; i<oBufChCnt; ++i)
  537. if( oBufArray[i] != NULL )
  538. memset( oBufArray[i], 0, byteCnt );
  539. }
  540. }
  541. void cmApBufAdvance( unsigned devIdx, unsigned flags )
  542. {
  543. unsigned i;
  544. if( devIdx == cmInvalidIdx )
  545. return;
  546. if( flags & kInApFl )
  547. {
  548. cmApIO* ioPtr = _cmApBuf.devArray[devIdx].ioArray + kInApIdx;
  549. for(i=0; i<ioPtr->chCnt; ++i)
  550. {
  551. cmApCh* cp = ioPtr->chArray + i;
  552. cp->oi = (cp->oi + ioPtr->dspFrameCnt) % ioPtr->n;
  553. //cp->fn -= ioPtr->dspFrameCnt;
  554. cmThUIntDecr(&cp->fn,ioPtr->dspFrameCnt);
  555. }
  556. //ioPtr->oi = (ioPtr->oi + ioPtr->dspFrameCnt) % ioPtr->n;
  557. //ioPtr->fn -= ioPtr->dspFrameCnt;
  558. }
  559. if( flags & kOutApFl )
  560. {
  561. cmApIO* ioPtr = _cmApBuf.devArray[devIdx].ioArray + kOutApIdx;
  562. for(i=0; i<ioPtr->chCnt; ++i)
  563. {
  564. cmApCh* cp = ioPtr->chArray + i;
  565. cp->ii = (cp->ii + ioPtr->dspFrameCnt) % ioPtr->n;
  566. //cp->fn += ioPtr->dspFrameCnt;
  567. cmThUIntIncr(&cp->fn,ioPtr->dspFrameCnt);
  568. }
  569. //ioPtr->ii = (ioPtr->ii + ioPtr->dspFrameCnt) % ioPtr->n;
  570. //ioPtr->fn += ioPtr->dspFrameCnt;
  571. }
  572. }
  573. void cmApBufInputToOutput( unsigned iDevIdx, unsigned oDevIdx )
  574. {
  575. if( iDevIdx == cmInvalidIdx || oDevIdx == cmInvalidIdx )
  576. return;
  577. unsigned iChCnt = cmApBufChannelCount( iDevIdx, kInApFl );
  578. unsigned oChCnt = cmApBufChannelCount( oDevIdx, kOutApFl );
  579. unsigned chCnt = iChCnt < oChCnt ? iChCnt : oChCnt;
  580. unsigned i;
  581. cmApSample_t* iBufPtrArray[ iChCnt ];
  582. cmApSample_t* oBufPtrArray[ oChCnt ];
  583. while( cmApBufIsDeviceReady( iDevIdx, kInApFl ) && cmApBufIsDeviceReady( oDevIdx, kOutApFl ) )
  584. {
  585. cmApBufGet( iDevIdx, kInApFl, iBufPtrArray, iChCnt );
  586. cmApBufGet( oDevIdx, kOutApFl, oBufPtrArray, oChCnt );
  587. // Warning: buffer pointers to disabled channels will be set to NULL
  588. for(i=0; i<chCnt; ++i)
  589. {
  590. cmApIO* ip = _cmApBuf.devArray[iDevIdx ].ioArray + kInApIdx;
  591. cmApIO* op = _cmApBuf.devArray[oDevIdx].ioArray + kOutApIdx;
  592. assert( ip->dspFrameCnt == op->dspFrameCnt );
  593. unsigned byteCnt = ip->dspFrameCnt * sizeof(cmApSample_t);
  594. if( oBufPtrArray[i] != NULL )
  595. {
  596. // the input channel is not disabled
  597. if( iBufPtrArray[i]!=NULL )
  598. memcpy(oBufPtrArray[i],iBufPtrArray[i],byteCnt);
  599. else
  600. // the input channel is disabled but the output is not - so fill the output with zeros
  601. memset(oBufPtrArray[i],0,byteCnt);
  602. }
  603. }
  604. cmApBufAdvance( iDevIdx, kInApFl );
  605. cmApBufAdvance( oDevIdx, kOutApFl );
  606. }
  607. }
  608. void cmApBufReport( cmRpt_t* rpt )
  609. {
  610. unsigned i,j,k;
  611. for(i=0; i<_cmApBuf.devCnt; ++i)
  612. {
  613. cmRptPrintf(rpt,"%i ",i);
  614. for(j=0; j<kIoApCnt; ++j)
  615. {
  616. cmApIO* ip = _cmApBuf.devArray[i].ioArray + j;
  617. unsigned ii = 0;
  618. unsigned oi = 0;
  619. unsigned fn = 0;
  620. for(k=0; k<ip->chCnt; ++k)
  621. {
  622. cmApCh* cp = ip->chArray + i;
  623. ii += cp->ii;
  624. oi += cp->oi;
  625. fn += cp->fn;
  626. }
  627. cmRptPrintf(rpt,"%s - i:%7i o:%7i f:%7i n:%7i err %s:%7i ",
  628. j==0?"IN":"OUT",
  629. ii,oi,fn,ip->n, (j==0?"over":"under"), ip->faultCnt);
  630. }
  631. cmRptPrintf(rpt,"\n");
  632. }
  633. }
  634. /// [cmApBufExample]
  635. void cmApBufTest( cmRpt_t* rpt )
  636. {
  637. unsigned devIdx = 0;
  638. unsigned devCnt = 1 ;
  639. unsigned dspFrameCnt = 10;
  640. unsigned cycleCnt = 3;
  641. unsigned framesPerCycle = 25;
  642. unsigned inChCnt = 2;
  643. unsigned outChCnt = inChCnt;
  644. unsigned sigN = cycleCnt*framesPerCycle*inChCnt;
  645. double srate = 44100.0;
  646. unsigned meterMs = 50;
  647. unsigned bufChCnt= inChCnt;
  648. cmApSample_t* inBufArray[ bufChCnt ];
  649. cmApSample_t* outBufArray[ bufChCnt ];
  650. cmApSample_t iSig[ sigN ];
  651. cmApSample_t oSig[ sigN ];
  652. cmApSample_t* os = oSig;
  653. cmApAudioPacket_t pkt;
  654. int i,j;
  655. // create a simulated signal
  656. for(i=0; i<sigN; ++i)
  657. {
  658. iSig[i] = i;
  659. oSig[i] = 0;
  660. }
  661. pkt.devIdx = 0;
  662. pkt.begChIdx = 0;
  663. pkt.chCnt = inChCnt;
  664. pkt.audioFramesCnt = framesPerCycle;
  665. pkt.bitsPerSample = 32;
  666. pkt.flags = 0;
  667. // initialize a the audio buffer
  668. cmApBufInitialize(devCnt,meterMs);
  669. // setup the buffer with the specific parameters to by used by the host audio ports
  670. cmApBufSetup(devIdx,srate,dspFrameCnt,cycleCnt,inChCnt,framesPerCycle,outChCnt,framesPerCycle);
  671. // simulate cylcing through sigN buffer transactions
  672. for(i=0; i<sigN; i+=framesPerCycle*inChCnt)
  673. {
  674. // setup an incoming audio packet
  675. pkt.audioFramesCnt = framesPerCycle;
  676. pkt.audioBytesPtr = iSig+i;
  677. // simulate a call from the audio port with incoming samples
  678. // (fill the audio buffers internal input buffers)
  679. cmApBufUpdate(&pkt,1,NULL,0);
  680. // if all devices need to be serviced
  681. while( cmApBufIsDeviceReady( devIdx, kInApFl | kOutApFl ))
  682. {
  683. // get pointers to full internal input buffers
  684. cmApBufGet(devIdx, kInApFl, inBufArray, bufChCnt );
  685. // get pointers to empty internal output buffers
  686. cmApBufGet(devIdx, kOutApFl, outBufArray, bufChCnt );
  687. // Warning: pointers to disabled channels will be set to NULL.
  688. // simulate a play through by copying the incoming samples to the outgoing buffers.
  689. for(j=0; j<bufChCnt; ++j)
  690. if( outBufArray[j] != NULL )
  691. {
  692. // if the input is disabled - but the output is not then zero the output buffer
  693. if( inBufArray[j] == NULL )
  694. memset( outBufArray[j], 0, dspFrameCnt*sizeof(cmApSample_t));
  695. else
  696. // copy the input to the output
  697. memcpy( outBufArray[j], inBufArray[j], dspFrameCnt*sizeof(cmApSample_t));
  698. }
  699. // signal the buffer that this cycle is complete.
  700. // (marks current internal input/output buffer empty/full)
  701. cmApBufAdvance( devIdx, kInApFl | kOutApFl );
  702. }
  703. pkt.audioBytesPtr = os;
  704. // simulate a call from the audio port picking up outgoing samples
  705. // (empties the audio buffers internal output buffers)
  706. cmApBufUpdate(NULL,0,&pkt,1);
  707. os += pkt.audioFramesCnt * pkt.chCnt;
  708. }
  709. for(i=0; i<sigN; ++i)
  710. cmRptPrintf(rpt,"%f ",oSig[i]);
  711. cmRptPrintf(rpt,"\n");
  712. cmApBufFinalize();
  713. }
  714. /// [cmApBufExample]