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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185
  1. //| Copyright: (C) 2009-2020 Kevin Larke <contact AT larke DOT org>
  2. //| License: GNU GPL version 3.0 or above. See the accompanying LICENSE file.
  3. #include "cmPrefix.h"
  4. #include "cmGlobal.h"
  5. #include "cmRpt.h"
  6. #include "cmErr.h"
  7. #include "cmMem.h"
  8. #include "cmMallocDebug.h"
  9. #include "cmTime.h"
  10. #include "cmAudioPort.h"
  11. #include "cmApBuf.h"
  12. #include "cmThread.h"
  13. /*
  14. This API is in general called by two types of threads:
  15. audio devices threads and the client thread. There
  16. may be multiple devie threads however there is only
  17. one client thread.
  18. The audio device threads only call cmApBufUpdate().
  19. cmApBufUpdate() is never called by any other threads.
  20. A call from the audio update threads targets specific channels
  21. (cmApCh records). The variables within each channels that
  22. it modifies are confined to:
  23. on input channels: increments ii and increments fn (data is entering the ch. buffers)
  24. on output channels: increments oi and decrements fn (data is leaving the ch. buffers)
  25. The client picks up incoming audio and provides outgoing audio via
  26. cmApBufGet(). It then informs the cmApBuf() that it has completed
  27. the audio data transfer by calling cmApBufAdvance().
  28. cmApBufAdvance() modifies the following internal variables:
  29. on input channels: increments oi and decrements fn (data has left the ch buffer)
  30. on output channels: increments ii and increments fn (data has enterned the ch. buffer)
  31. Based on the above scenario the channel ii and oi variables are always thread-safe
  32. because they are only changed by a single thread.
  33. ii oi fn
  34. ------ ----- ----
  35. input ch: audio client both
  36. output ch: client audio both
  37. The fn variable however is not thread-safe and therefore care must be taken as
  38. to how it is read and updated.
  39. */
  40. enum { kInApIdx=0, kOutApIdx=1, kIoApCnt=2 };
  41. typedef struct
  42. {
  43. unsigned fl; // kChApFl|kToneApFl|kMeterApFl ...
  44. cmApSample_t* b; // b[n]
  45. unsigned ii; // next in
  46. unsigned oi; // next out
  47. unsigned fn; // full cnt - count of samples currently in the buffer - incr'd by incoming, decr'd by outgoing
  48. unsigned phs; // tone phase
  49. double hz; // tone frequency
  50. double gain; // channel gain
  51. cmApSample_t* m; // m[mn] meter sample sum
  52. unsigned mn; // length of m[]
  53. unsigned mi; // next ele of m[] to rcv sum
  54. cmApSample_t s0; // buffered sample used for srate conversion
  55. } cmApCh;
  56. typedef struct
  57. {
  58. unsigned chCnt; // Count of channels
  59. cmApCh* chArray; // chArray[chCnt] channel record array
  60. unsigned n; // Length of b[] (multiple of dspFrameCnt) bufCnt*framesPerCycle
  61. double srate; // Device sample rate;
  62. int srateMult; // Internal sample rate multiplier (negative values for dividing). This srateMult*srate is the sample rate of
  63. // signals held in the chApCh[] input and output buffers. Sample rate conversion to/from this rate
  64. // occurs in cmApBufUpdate() as signals go from/to the audio device.
  65. unsigned faultCnt; // error count since start
  66. unsigned framesPerCycle; // expected count of frames per channel to/from the audio device on each call to cmApBufUpdate()
  67. unsigned dspFrameCnt; // number of frames per channel in buffers returned by cmApBufGet().
  68. cmTimeSpec_t timeStamp; // base (starting) time stamp for this device
  69. unsigned ioFrameCnt; // count of frames input or output for this device
  70. } cmApIO;
  71. typedef struct
  72. {
  73. // ioArray[] always contains 2 elements - one for input the other for output.
  74. cmApIO ioArray[kIoApCnt];
  75. } cmApDev;
  76. typedef struct
  77. {
  78. cmApDev* devArray;
  79. unsigned devCnt;
  80. unsigned meterMs;
  81. cmApSample_t* zeroBuf; // buffer of zeros
  82. unsigned zeroBufCnt; // max of all dspFrameCnt for all devices.
  83. unsigned abufIdx;
  84. cmApSample_t abuf[ 16384 ];
  85. } cmApBuf;
  86. cmApBuf _cmApBuf;
  87. // Copy the source channel (srcChIdx) to the destination buffer and apply up-sampling.
  88. // 'src' is an interleaved buffer with 'srcN' samples per channel and 'srcChN' channels (total size in samples = srcN*srcChN)
  89. // 'dst' is a non-interleaved (single channel) circular buffer of length 'dstN' where 'dstIdx' is the index of the first dst slot to receive a sample..
  90. // Return the index into dst[] of the next location to receive an incoming sample.
  91. unsigned _cmApCopyInUpSample( const cmApSample_t* src, unsigned srcN, unsigned srcChN, unsigned srcChIdx, cmApSample_t* dst, unsigned dstN, unsigned dstIdx, unsigned mult, double gain, cmApSample_t* s0_Ref )
  92. {
  93. cmApSample_t s0 = *s0_Ref;
  94. unsigned si,k,di=dstIdx;
  95. for(si=0; si<srcN; ++si)
  96. {
  97. cmApSample_t s1 = src[si*srcChN+srcChIdx];
  98. for(k=1; k<mult; ++k)
  99. {
  100. dst[di] = gain * (s0 + (s1 - s0) * k / mult );
  101. di = (di+1) % dstN;
  102. }
  103. dst[di] = gain * s1;
  104. di = (di+1) % dstN;
  105. s0 = s1;
  106. }
  107. *s0_Ref = s0;
  108. return di;
  109. }
  110. // Copy the source channel (srcChIdx) to the destination buffer and apply down-sampling.
  111. // 'src' is an interleaved linear buffer with 'srcN' samples per channel and 'srcChN' channels,
  112. // 'dst' is a non-interleaved circular buffer of length 'dstN' where 'dstIdx' is the index of the first dst slot to receive a sample..
  113. // Return the index into dst[] of the next location to receive an incoming sample.
  114. unsigned _cmApCopyInDnSample( const cmApSample_t* src, unsigned srcN, unsigned srcChN, unsigned srcChIdx, cmApSample_t* dst, unsigned dstN, unsigned dstIdx, unsigned div, double gain )
  115. {
  116. unsigned si,di=dstIdx;
  117. for(si=0; si<srcN; si+=div)
  118. {
  119. dst[di] = gain * src[si*srcChN+srcChIdx];
  120. di = (di+1) % dstN;
  121. }
  122. return di;
  123. }
  124. // Copy samples from an interleaved src buffer to a non-interleaved dst buffer with sample rate conversion
  125. unsigned _cmApCopyInSamples( const cmApSample_t* src, unsigned srcN, unsigned srcChN, unsigned srcChIdx, cmApSample_t* dst, unsigned dstN, unsigned dstIdx, int srateMult, double gain, cmApSample_t* s0_Ref )
  126. {
  127. if( srateMult < 1 )
  128. return _cmApCopyInDnSample( src, srcN, srcChN, srcChIdx, dst, dstN, dstIdx, -srateMult, gain );
  129. return _cmApCopyInUpSample( src, srcN, srcChN, srcChIdx, dst, dstN, dstIdx, srateMult, gain, s0_Ref );
  130. }
  131. void printBuf( const cmApSample_t* src, unsigned srcN, unsigned bi, unsigned n )
  132. {
  133. unsigned i,j;
  134. for(i=bi,j=0; j<n; ++i,++j)
  135. {
  136. i = i % srcN;
  137. printf("(%i,%i,%f),\n",j,i,src[i]);
  138. }
  139. printf("-----\n");
  140. }
  141. // Copy
  142. // 'src' is a non-interleaved circular buf of total length 'srcN', with the first sample coming at 'srcIdx'.
  143. // 'dst' is an interleaved buffer of length 'dstN' with 'dstChN' channels
  144. // Return the index of the next src sample.
  145. unsigned _cmApCopyOutUpSample( const cmApSample_t* src, unsigned srcN, unsigned srcIdx, cmApSample_t* dst, unsigned dstN, unsigned dstChN, unsigned dstChIdx, int mult, double gain, cmApSample_t* s0_Ref )
  146. {
  147. unsigned di,si=srcIdx;
  148. cmApSample_t s0 = *s0_Ref;
  149. for(di=0; di<dstN; ++di)
  150. {
  151. cmApSample_t s1 = src[si];
  152. unsigned k;
  153. for(k=1; k<mult && di<dstN; ++di,++k)
  154. {
  155. dst[di*dstChN+dstChIdx] = gain * (s0 + (s1 - s0) * k / mult );
  156. }
  157. if( di < dstN )
  158. {
  159. dst[di*dstChN+dstChIdx] = gain * s1;
  160. ++di;
  161. }
  162. si = (si + 1) % srcN;
  163. s0 = s1;
  164. }
  165. *s0_Ref = s0;
  166. return si;
  167. }
  168. // 'src' is a non-interleaved (single channel) circular buf of total length 'srcN', with the first sample coming at 'srcIdx'.
  169. // 'dst' is an interleaved buffer of length 'dstN' with 'dstChN' channels
  170. // Return the index of the next src sample.
  171. unsigned _cmApCopyOutDnSample( const cmApSample_t* src, unsigned srcN, unsigned srcIdx, cmApSample_t* dst, unsigned dstN, unsigned dstChN, unsigned dstChIdx, int div, double gain )
  172. {
  173. unsigned di,si;
  174. // The total count of output samples is determined by 'dstN'
  175. // Downsampling is acheived by advancing the src index by 'div' samples.
  176. for(di=0,si=srcIdx; di<dstN; ++di)
  177. {
  178. dst[di*dstChN+dstChIdx] = gain * src[si];
  179. si = (si + div) % srcN;
  180. }
  181. return si;
  182. }
  183. // Copy samples from a non-interleaved src buffer to an interleaved dst buffer with sample rate conversion
  184. unsigned _cmApCopyOutSamples( const cmApSample_t* src, unsigned srcN, unsigned srcIdx, cmApSample_t* dst, unsigned dstN, unsigned dstChN, unsigned dstChIdx, int srateMult, double gain, cmApSample_t* s0_Ref )
  185. {
  186. if( srateMult > 0 )
  187. return _cmApCopyOutDnSample(src, srcN, srcIdx, dst, dstN, dstChN, dstChIdx, srateMult, gain );
  188. return _cmApCopyOutUpSample( src, srcN, srcIdx, dst, dstN, dstChN, dstChIdx, -srateMult, gain, s0_Ref );
  189. }
  190. cmApSample_t _cmApMeterValue( const cmApCh* cp )
  191. {
  192. double sum = 0;
  193. unsigned i;
  194. for(i=0; i<cp->mn; ++i)
  195. sum += cp->m[i];
  196. return cp->mn==0 ? 0 : (cmApSample_t)sqrt(sum/cp->mn);
  197. }
  198. void _cmApSine0( cmApCh* cp, cmApSample_t* b0, unsigned n0, cmApSample_t* b1, unsigned n1, unsigned stride, float srate )
  199. {
  200. unsigned i;
  201. for(i=0; i<n0; ++i,++cp->phs)
  202. b0[i*stride] = (float)(cp->gain * sin( 2.0 * M_PI * cp->hz * cp->phs / srate ));
  203. for(i=0; i<n1; ++i,++cp->phs)
  204. b1[i*stride] = (float)(cp->gain * sin( 2.0 * M_PI * cp->hz * cp->phs / srate ));
  205. }
  206. // Synthesize a sine signal of length sigN*srateMult starting at dst[dstidx]
  207. // Assume dst[dstN] is a circular buffer
  208. unsigned _cmApSine( cmApCh* cp, cmApSample_t* dst, unsigned dstN, unsigned dstIdx, unsigned dstChCnt, unsigned sigN, unsigned srateMult, float srate, double gain )
  209. {
  210. unsigned i,di;
  211. sigN = srateMult < 0 ? sigN/abs(srateMult) : sigN * srateMult;
  212. for(i=0, di=dstIdx; i<sigN; ++i,++cp->phs)
  213. {
  214. dst[di] = (cmApSample_t)(gain * sin( 2.0 * M_PI * cp->hz * cp->phs / srate ));
  215. di = (di+dstChCnt) % dstN;
  216. }
  217. return sigN;
  218. }
  219. cmApSample_t _cmApMeter( const cmApSample_t* b, unsigned bn, unsigned stride )
  220. {
  221. const cmApSample_t* ep = b + bn;
  222. cmApSample_t sum = 0;
  223. for(; b<ep; b+=stride)
  224. sum += *b * *b;
  225. return sum / bn;
  226. }
  227. void _cmApChFinalize( cmApCh* chPtr )
  228. {
  229. cmMemPtrFree( &chPtr->b );
  230. cmMemPtrFree( &chPtr->m );
  231. }
  232. // n=buf sample cnt mn=meter buf smp cnt
  233. void _cmApChInitialize( cmApCh* chPtr, unsigned n, unsigned mn )
  234. {
  235. _cmApChFinalize(chPtr);
  236. chPtr->b = n==0 ? NULL : cmMemAllocZ( cmApSample_t, n );
  237. chPtr->ii = 0;
  238. chPtr->oi = 0;
  239. chPtr->fn = 0;
  240. chPtr->fl = (n!=0 ? kChApFl : 0);
  241. chPtr->hz = 440;
  242. chPtr->gain = 0.8; // use reduced gain to prevent clipping
  243. chPtr->mn = mn;
  244. chPtr->m = cmMemAllocZ(cmApSample_t,mn);
  245. chPtr->mi = 0;
  246. }
  247. void _cmApIoFinalize( cmApIO* ioPtr )
  248. {
  249. unsigned i;
  250. for(i=0; i<ioPtr->chCnt; ++i)
  251. _cmApChFinalize( ioPtr->chArray + i );
  252. cmMemPtrFree(&ioPtr->chArray);
  253. ioPtr->chCnt = 0;
  254. ioPtr->n = 0;
  255. }
  256. void _cmApIoInitialize( cmApIO* ioPtr, double srate, unsigned framesPerCycle, unsigned chCnt, unsigned n, unsigned meterBufN, unsigned dspFrameCnt, int srateMult )
  257. {
  258. unsigned i;
  259. if( srateMult == 0 )
  260. srateMult = 1;
  261. _cmApIoFinalize(ioPtr);
  262. n += (n % dspFrameCnt); // force buffer size to be a multiple of dspFrameCnt
  263. ioPtr->chArray = chCnt==0 ? NULL : cmMemAllocZ( cmApCh, chCnt );
  264. ioPtr->chCnt = chCnt;
  265. ioPtr->n = srateMult<0 ? n : n*srateMult;
  266. ioPtr->faultCnt = 0;
  267. ioPtr->framesPerCycle = framesPerCycle;
  268. ioPtr->srate = srate;
  269. ioPtr->srateMult = srateMult;
  270. ioPtr->dspFrameCnt = dspFrameCnt;
  271. ioPtr->timeStamp.tv_sec = 0;
  272. ioPtr->timeStamp.tv_nsec = 0;
  273. ioPtr->ioFrameCnt = 0;
  274. for(i=0; i<chCnt; ++i )
  275. _cmApChInitialize( ioPtr->chArray + i, ioPtr->n, meterBufN );
  276. }
  277. void _cmApDevFinalize( cmApDev* dp )
  278. {
  279. unsigned i;
  280. for(i=0; i<kIoApCnt; ++i)
  281. _cmApIoFinalize( dp->ioArray+i);
  282. }
  283. void _cmApDevInitialize( cmApDev* dp, double srate, unsigned iFpC, unsigned iChCnt, unsigned iBufN, unsigned oFpC, unsigned oChCnt, unsigned oBufN, unsigned meterBufN, unsigned dspFrameCnt, int srateMult )
  284. {
  285. unsigned i;
  286. _cmApDevFinalize(dp);
  287. for(i=0; i<kIoApCnt; ++i)
  288. {
  289. unsigned chCnt = i==kInApIdx ? iChCnt : oChCnt;
  290. unsigned bufN = i==kInApIdx ? iBufN : oBufN;
  291. unsigned fpc = i==kInApIdx ? iFpC : oFpC;
  292. _cmApIoInitialize( dp->ioArray+i, srate, fpc, chCnt, bufN, meterBufN, dspFrameCnt, srateMult );
  293. }
  294. }
  295. cmAbRC_t cmApBufInitialize( unsigned devCnt, unsigned meterMs )
  296. {
  297. cmAbRC_t rc;
  298. if((rc = cmApBufFinalize()) != kOkAbRC )
  299. return rc;
  300. _cmApBuf.devArray = cmMemAllocZ( cmApDev, devCnt );
  301. _cmApBuf.devCnt = devCnt;
  302. cmApBufSetMeterMs(meterMs);
  303. return kOkAbRC;
  304. }
  305. cmAbRC_t cmApBufFinalize()
  306. {
  307. unsigned i;
  308. for(i=0; i<_cmApBuf.devCnt; ++i)
  309. _cmApDevFinalize(_cmApBuf.devArray + i);
  310. cmMemPtrFree( &_cmApBuf.devArray );
  311. cmMemPtrFree( &_cmApBuf.zeroBuf );
  312. _cmApBuf.devCnt = 0;
  313. FILE* fp;
  314. if(_cmApBuf.abufIdx>0 )
  315. {
  316. if((fp = fopen("/home/kevin/temp/temp.csv","wt")) != NULL )
  317. {
  318. int i;
  319. for(i=0; i<_cmApBuf.abufIdx; ++i)
  320. {
  321. fprintf(fp,"%f,\n",_cmApBuf.abuf[i]);
  322. }
  323. fclose(fp);
  324. }
  325. }
  326. return kOkAbRC;
  327. }
  328. cmAbRC_t cmApBufSetup(
  329. unsigned devIdx,
  330. double srate,
  331. unsigned dspFrameCnt,
  332. unsigned bufCnt,
  333. unsigned inChCnt,
  334. unsigned inFramesPerCycle,
  335. unsigned outChCnt,
  336. unsigned outFramesPerCycle,
  337. int srateMult)
  338. {
  339. cmApDev* devPtr = _cmApBuf.devArray + devIdx;
  340. unsigned iBufN = bufCnt * inFramesPerCycle;
  341. unsigned oBufN = bufCnt * outFramesPerCycle;
  342. unsigned meterBufN = cmMax(1,floor(srate * _cmApBuf.meterMs / (1000.0 * outFramesPerCycle)));
  343. _cmApDevInitialize( devPtr, srate, inFramesPerCycle, inChCnt, iBufN, outFramesPerCycle, outChCnt, oBufN, meterBufN, dspFrameCnt, srateMult );
  344. if( inFramesPerCycle > _cmApBuf.zeroBufCnt || outFramesPerCycle > _cmApBuf.zeroBufCnt )
  345. {
  346. _cmApBuf.zeroBufCnt = cmMax(inFramesPerCycle,outFramesPerCycle);
  347. _cmApBuf.zeroBuf = cmMemResizeZ(cmApSample_t,_cmApBuf.zeroBuf,_cmApBuf.zeroBufCnt);
  348. }
  349. return kOkAbRC;
  350. }
  351. cmAbRC_t cmApBufPrimeOutput( unsigned devIdx, unsigned audioCycleCnt )
  352. {
  353. cmApIO* iop = _cmApBuf.devArray[devIdx].ioArray + kOutApIdx;
  354. unsigned i;
  355. for(i=0; i<iop->chCnt; ++i)
  356. {
  357. cmApCh* cp = iop->chArray + i;
  358. unsigned bn = iop->n * sizeof(cmApSample_t);
  359. memset(cp->b,0,bn);
  360. cp->oi = 0;
  361. cp->ii = iop->framesPerCycle * audioCycleCnt;
  362. cp->fn = iop->framesPerCycle * audioCycleCnt;
  363. }
  364. return kOkAbRC;
  365. }
  366. void cmApBufOnPortEnable( unsigned devIdx, bool enableFl )
  367. {
  368. if( devIdx == cmInvalidIdx || enableFl==false)
  369. return;
  370. cmApIO* iop = _cmApBuf.devArray[devIdx].ioArray + kOutApIdx;
  371. iop->timeStamp.tv_sec = 0;
  372. iop->timeStamp.tv_nsec = 0;
  373. iop->ioFrameCnt = 0;
  374. iop = _cmApBuf.devArray[devIdx].ioArray + kInApIdx;
  375. iop->timeStamp.tv_sec = 0;
  376. iop->timeStamp.tv_nsec = 0;
  377. iop->ioFrameCnt = 0;
  378. }
  379. cmAbRC_t cmApBufUpdate(
  380. cmApAudioPacket_t* inPktArray,
  381. unsigned inPktCnt,
  382. cmApAudioPacket_t* outPktArray,
  383. unsigned outPktCnt )
  384. {
  385. unsigned i,j;
  386. // copy samples from the packet to the buffer
  387. if( inPktArray != NULL )
  388. {
  389. for(i=0; i<inPktCnt; ++i)
  390. {
  391. cmApAudioPacket_t* pp = inPktArray + i;
  392. cmApIO* ip = _cmApBuf.devArray[pp->devIdx].ioArray + kInApIdx; // dest io recd
  393. // if the base time stamp has not yet been set - then set it
  394. if( ip->timeStamp.tv_sec==0 && ip->timeStamp.tv_nsec==0 )
  395. ip->timeStamp = pp->timeStamp;
  396. // for each source packet channel and enabled dest channel
  397. for(j=0; j<pp->chCnt; ++j)
  398. {
  399. cmApCh* cp = ip->chArray + pp->begChIdx +j; // dest ch
  400. assert(pp->begChIdx + j < ip->chCnt );
  401. // if the incoming samples would overflow the buffer then ignore them
  402. if( cp->fn + pp->audioFramesCnt > ip->n )
  403. {
  404. ++ip->faultCnt; // record input overflow
  405. continue;
  406. }
  407. // if the incoming samples would go off the end of the buffer then
  408. // copy in the samples in two segments (one at the end and another at begin of dest channel)
  409. bool enaFl = cmIsFlag(cp->fl,kChApFl) && cmIsFlag(cp->fl,kMuteApFl)==false;
  410. const cmApSample_t* sp = enaFl ? ((cmApSample_t*)pp->audioBytesPtr) + j : _cmApBuf.zeroBuf;
  411. //unsigned ssn = enaFl ? pp->chCnt : 1; // stride (packet samples are interleaved)
  412. //cmApSample_t* dp = cp->b + cp->ii;
  413. //const cmApSample_t* ep = dp + n0;
  414. // update the meter
  415. if( cmIsFlag(cp->fl,kMeterApFl) )
  416. {
  417. cp->m[cp->mi] = _cmApMeter(sp,pp->audioFramesCnt,pp->chCnt);
  418. cp->mi = (cp->mi + 1) % cp->mn;
  419. }
  420. unsigned incrSmpN = 0;
  421. // if the test tone is enabled on this input channel
  422. if( enaFl && cmIsFlag(cp->fl,kToneApFl) )
  423. {
  424. //_cmApSine(cp, dp, n0, cp->b, n1, 1, ip->srate );
  425. incrSmpN = _cmApSine( cp, cp->b, ip->n, cp->ii, 1, pp->audioFramesCnt, ip->srateMult, ip->srate, cp->gain );
  426. }
  427. else // otherwise copy samples from the packet to the buffer
  428. {
  429. unsigned pi = cp->ii;
  430. cp->ii = _cmApCopyInSamples( (cmApSample_t*)pp->audioBytesPtr, pp->audioFramesCnt, pp->chCnt, j, cp->b, ip->n, cp->ii, ip->srateMult, cp->gain, &cp->s0 );
  431. if( false )
  432. if( j == 2 && _cmApBuf.abufIdx < 16384 )
  433. {
  434. int ii;
  435. for(ii=pi; ii!=cp->ii && _cmApBuf.abufIdx<16384; _cmApBuf.abufIdx++)
  436. {
  437. _cmApBuf.abuf[ _cmApBuf.abufIdx ] = cp->b[ii];
  438. ii = (ii + 1) % ip->n;
  439. }
  440. }
  441. incrSmpN = cp->ii > pi ? cp->ii-pi : (ip->n-pi) + cp->ii;
  442. }
  443. cmThUIntIncr(&cp->fn,incrSmpN);
  444. }
  445. }
  446. }
  447. // copy samples from the buffer to the packet
  448. if( outPktArray != NULL )
  449. {
  450. for(i=0; i<outPktCnt; ++i)
  451. {
  452. cmApAudioPacket_t* pp = outPktArray + i;
  453. cmApIO* op = _cmApBuf.devArray[pp->devIdx].ioArray + kOutApIdx; // dest io recd
  454. // if the base timestamp has not yet been set then set it.
  455. if( op->timeStamp.tv_sec==0 && op->timeStamp.tv_nsec==0 )
  456. op->timeStamp = pp->timeStamp;
  457. // for each dest packet channel and enabled source channel
  458. for(j=0; j<pp->chCnt; ++j)
  459. {
  460. cmApCh* cp = op->chArray + pp->begChIdx + j; // dest ch
  461. //unsigned n0 = op->n - cp->oi; // first src segment
  462. //unsigned n1 = 0; // second src segment
  463. volatile unsigned fn = cp->fn; // store fn because it may be changed by the client thread
  464. // if the outgoing samples will underflow the buffer
  465. if( pp->audioFramesCnt > fn )
  466. {
  467. ++op->faultCnt; // record an output underflow
  468. // if the buffer is empty - zero the packet and return
  469. if( fn == 0 )
  470. {
  471. memset( pp->audioBytesPtr, 0, pp->audioFramesCnt*sizeof(cmApSample_t));
  472. continue;
  473. }
  474. // ... otherwise decrease the count of returned samples
  475. pp->audioFramesCnt = fn;
  476. }
  477. // if the outgong segments would go off the end of the buffer then
  478. //// arrange to wrap to the begining of the buffer
  479. //if( n0 < pp->audioFramesCnt )
  480. // n1 = pp->audioFramesCnt-n0;
  481. //else
  482. // n0 = pp->audioFramesCnt;
  483. //cmApSample_t* bpp = ((cmApSample_t*)pp->audioBytesPtr) + j;
  484. //cmApSample_t* dp = bpp;
  485. bool enaFl = cmIsFlag(cp->fl,kChApFl) && cmIsFlag(cp->fl,kMuteApFl)==false;
  486. unsigned decrSmpN = 0;
  487. // if the tone is enabled on this channel
  488. if( enaFl && cmIsFlag(cp->fl,kToneApFl) )
  489. {
  490. //_cmApSine(cp, dp, n0, dp + n0*pp->chCnt, n1, pp->chCnt, op->srate );
  491. decrSmpN = _cmApSine( cp, (cmApSample_t*)pp->audioBytesPtr, pp->audioFramesCnt * pp->chCnt, j, pp->chCnt, pp->audioFramesCnt, op->srateMult, op->srate, cp->gain );
  492. }
  493. else // otherwise copy samples from the output buffer to the packet
  494. {
  495. //const cmApSample_t* sp = enaFl ? cp->b + cp->oi : _cmApBuf.zeroBuf;
  496. //const cmApSample_t* ep = sp + n0;
  497. unsigned pi = cp->oi;
  498. cp->oi = _cmApCopyOutSamples( enaFl ? cp->b : _cmApBuf.zeroBuf, op->n, cp->oi, (cmApSample_t*)pp->audioBytesPtr, pp->audioFramesCnt, pp->chCnt, j, op->srateMult, cp->gain, &cp->s0 );
  499. decrSmpN = cp->oi>pi ? cp->oi-pi : (op->n-pi) + cp->oi;
  500. /*
  501. if( false )
  502. if( j == 2 && _cmApBuf.abufIdx < 16384 )
  503. {
  504. int ii;
  505. for(ii=pi; ii!=cp->oi && _cmApBuf.abufIdx<16384; _cmApBuf.abufIdx++)
  506. {
  507. _cmApBuf.abuf[ _cmApBuf.abufIdx ] = cp->b[ii];
  508. ii = (ii + 1) % op->n;
  509. }
  510. }
  511. */
  512. /*
  513. // copy the first segment
  514. for(; sp < ep; dp += pp->chCnt )
  515. *dp = cp->gain * *sp++;
  516. // if there is a second segment
  517. if( n1 > 0 )
  518. {
  519. // copy the second segment
  520. sp = enaFl ? cp->b : _cmApBuf.zeroBuf;
  521. ep = sp + n1;
  522. for(; sp<ep; dp += pp->chCnt )
  523. *dp = cp->gain * *sp++;
  524. }
  525. */
  526. }
  527. // update the meter
  528. if( cmIsFlag(cp->fl,kMeterApFl) )
  529. {
  530. cp->m[cp->mi] = _cmApMeter(((cmApSample_t*)pp->audioBytesPtr)+j,pp->audioFramesCnt,pp->chCnt);
  531. cp->mi = (cp->mi + 1) % cp->mn;
  532. }
  533. // advance the output channel buffer
  534. /*
  535. cp->oi = n1>0 ? n1 : cp->oi + n0;
  536. cmThUIntDecr(&cp->fn,pp->audioFramesCnt);
  537. */
  538. //cp->fn -= pp->audioFramesCnt;
  539. cmThUIntDecr(&cp->fn,decrSmpN);
  540. }
  541. }
  542. }
  543. return kOkAbRC;
  544. }
  545. unsigned cmApBufMeterMs()
  546. { return _cmApBuf.meterMs; }
  547. void cmApBufSetMeterMs( unsigned meterMs )
  548. {
  549. _cmApBuf.meterMs = cmMin(1000,cmMax(10,meterMs));
  550. }
  551. unsigned cmApBufChannelCount( unsigned devIdx, unsigned flags )
  552. {
  553. if( devIdx == cmInvalidIdx )
  554. return 0;
  555. unsigned idx = flags & kInApFl ? kInApIdx : kOutApIdx;
  556. return _cmApBuf.devArray[devIdx].ioArray[ idx ].chCnt;
  557. }
  558. void cmApBufSetFlag( unsigned devIdx, unsigned chIdx, unsigned flags )
  559. {
  560. if( devIdx == cmInvalidIdx )
  561. return;
  562. unsigned idx = flags & kInApFl ? kInApIdx : kOutApIdx;
  563. bool enableFl = flags & kEnableApFl ? true : false;
  564. unsigned i = chIdx != -1 ? chIdx : 0;
  565. unsigned n = chIdx != -1 ? chIdx+1 : _cmApBuf.devArray[devIdx].ioArray[idx].chCnt;
  566. for(; i<n; ++i)
  567. {
  568. cmApCh* cp = _cmApBuf.devArray[devIdx].ioArray[idx].chArray + i;
  569. cp->fl = cmEnaFlag(cp->fl, flags & (kChApFl|kToneApFl|kMeterApFl|kMuteApFl|kPassApFl), enableFl );
  570. }
  571. }
  572. bool cmApBufIsFlag( unsigned devIdx, unsigned chIdx, unsigned flags )
  573. {
  574. if( devIdx == cmInvalidIdx )
  575. return false;
  576. unsigned idx = flags & kInApFl ? kInApIdx : kOutApIdx;
  577. return cmIsFlag(_cmApBuf.devArray[devIdx].ioArray[idx].chArray[chIdx].fl,flags);
  578. }
  579. void cmApBufEnableChannel( unsigned devIdx, unsigned chIdx, unsigned flags )
  580. { cmApBufSetFlag(devIdx,chIdx,flags | kChApFl); }
  581. bool cmApBufIsChannelEnabled( unsigned devIdx, unsigned chIdx, unsigned flags )
  582. { return cmApBufIsFlag(devIdx, chIdx, flags | kChApFl); }
  583. void cmApBufEnableTone( unsigned devIdx, unsigned chIdx, unsigned flags )
  584. { cmApBufSetFlag(devIdx,chIdx,flags | kToneApFl); }
  585. bool cmApBufIsToneEnabled( unsigned devIdx, unsigned chIdx, unsigned flags )
  586. { return cmApBufIsFlag(devIdx,chIdx,flags | kToneApFl); }
  587. void cmApBufEnableMute( unsigned devIdx, unsigned chIdx, unsigned flags )
  588. { cmApBufSetFlag(devIdx,chIdx,flags | kMuteApFl); }
  589. bool cmApBufIsMuteEnabled( unsigned devIdx, unsigned chIdx, unsigned flags )
  590. { return cmApBufIsFlag(devIdx,chIdx,flags | kMuteApFl); }
  591. void cmApBufEnablePass( unsigned devIdx, unsigned chIdx, unsigned flags )
  592. { cmApBufSetFlag(devIdx,chIdx,flags | kPassApFl); }
  593. bool cmApBufIsPassEnabled( unsigned devIdx, unsigned chIdx, unsigned flags )
  594. { return cmApBufIsFlag(devIdx,chIdx,flags | kPassApFl); }
  595. void cmApBufEnableMeter( unsigned devIdx, unsigned chIdx, unsigned flags )
  596. { cmApBufSetFlag(devIdx,chIdx,flags | kMeterApFl); }
  597. bool cmApBufIsMeterEnabled(unsigned devIdx, unsigned chIdx, unsigned flags )
  598. { return cmApBufIsFlag(devIdx,chIdx,flags | kMeterApFl); }
  599. cmApSample_t cmApBufMeter(unsigned devIdx, unsigned chIdx, unsigned flags )
  600. {
  601. if( devIdx == cmInvalidIdx )
  602. return 0;
  603. unsigned idx = flags & kInApFl ? kInApIdx : kOutApIdx;
  604. const cmApCh* cp = _cmApBuf.devArray[devIdx].ioArray[idx].chArray + chIdx;
  605. return _cmApMeterValue(cp);
  606. }
  607. void cmApBufSetGain( unsigned devIdx, unsigned chIdx, unsigned flags, double gain )
  608. {
  609. if( devIdx == cmInvalidIdx )
  610. return;
  611. unsigned idx = flags & kInApFl ? kInApIdx : kOutApIdx;
  612. unsigned i = chIdx != -1 ? chIdx : 0;
  613. unsigned n = i + (chIdx != -1 ? 1 : _cmApBuf.devArray[devIdx].ioArray[idx].chCnt);
  614. for(; i<n; ++i)
  615. _cmApBuf.devArray[devIdx].ioArray[idx].chArray[i].gain = gain;
  616. }
  617. double cmApBufGain( unsigned devIdx, unsigned chIdx, unsigned flags )
  618. {
  619. if( devIdx == cmInvalidIdx )
  620. return 0;
  621. unsigned idx = flags & kInApFl ? kInApIdx : kOutApIdx;
  622. return _cmApBuf.devArray[devIdx].ioArray[idx].chArray[chIdx].gain;
  623. }
  624. unsigned cmApBufGetStatus( unsigned devIdx, unsigned flags, double* meterArray, unsigned meterCnt, unsigned* faultCntPtr )
  625. {
  626. if( devIdx == cmInvalidIdx )
  627. return 0;
  628. unsigned ioIdx = cmIsFlag(flags,kInApFl) ? kInApIdx : kOutApIdx;
  629. cmApIO* iop = _cmApBuf.devArray[devIdx].ioArray + ioIdx;
  630. unsigned chCnt = cmMin(iop->chCnt, meterCnt );
  631. unsigned i;
  632. if( faultCntPtr != NULL )
  633. *faultCntPtr = iop->faultCnt;
  634. for(i=0; i<chCnt; ++i)
  635. meterArray[i] = _cmApMeterValue(iop->chArray + i);
  636. return chCnt;
  637. }
  638. bool cmApBufIsDeviceReady( unsigned devIdx, unsigned flags )
  639. {
  640. //bool iFl = true;
  641. //bool oFl = true;
  642. unsigned i = 0;
  643. if( devIdx == cmInvalidIdx )
  644. return false;
  645. if( flags & kInApFl )
  646. {
  647. const cmApIO* ioPtr = _cmApBuf.devArray[devIdx].ioArray + kInApIdx;
  648. for(i=0; i<ioPtr->chCnt; ++i)
  649. if( ioPtr->chArray[i].fn < ioPtr->dspFrameCnt )
  650. return false;
  651. //iFl = ioPtr->fn > ioPtr->dspFrameCnt;
  652. }
  653. if( flags & kOutApFl )
  654. {
  655. const cmApIO* ioPtr = _cmApBuf.devArray[devIdx].ioArray + kOutApIdx;
  656. for(i=0; i<ioPtr->chCnt; ++i)
  657. if( (ioPtr->n - ioPtr->chArray[i].fn) < ioPtr->dspFrameCnt )
  658. return false;
  659. //oFl = (ioPtr->n - ioPtr->fn) > ioPtr->dspFrameCnt;
  660. }
  661. return true;
  662. //return iFl & oFl;
  663. }
  664. // Note that his function returns audio samples but does NOT
  665. // change any internal states.
  666. void cmApBufGet( unsigned devIdx, unsigned flags, cmApSample_t* bufArray[], unsigned bufChCnt )
  667. {
  668. unsigned i;
  669. if( devIdx == cmInvalidIdx )
  670. {
  671. for(i=0; i<bufChCnt; ++i)
  672. bufArray[i] = NULL;
  673. return;
  674. }
  675. unsigned idx = flags & kInApFl ? kInApIdx : kOutApIdx;
  676. const cmApIO* ioPtr = _cmApBuf.devArray[devIdx].ioArray + idx;
  677. unsigned n = bufChCnt < ioPtr->chCnt ? bufChCnt : ioPtr->chCnt;
  678. cmApCh* cp = ioPtr->chArray;
  679. for(i=0; i<n; ++i,++cp)
  680. {
  681. unsigned offs = flags & kInApFl ? cp->oi : cp->ii;
  682. bufArray[i] = cmIsFlag(cp->fl,kChApFl) ? cp->b + offs : NULL;
  683. }
  684. }
  685. void _cmApBufCalcTimeStamp( double srate, const cmTimeSpec_t* baseTimeStamp, unsigned frmCnt, cmTimeSpec_t* retTimeStamp )
  686. {
  687. if( retTimeStamp==NULL )
  688. return;
  689. double secs = frmCnt / srate;
  690. unsigned int_secs = floor(secs);
  691. double frac_secs = secs - int_secs;
  692. retTimeStamp->tv_nsec = floor(baseTimeStamp->tv_nsec + frac_secs * 1000000000);
  693. retTimeStamp->tv_sec = baseTimeStamp->tv_sec + int_secs;
  694. if( retTimeStamp->tv_nsec > 1000000000 )
  695. {
  696. retTimeStamp->tv_nsec -= 1000000000;
  697. retTimeStamp->tv_sec += 1;
  698. }
  699. }
  700. void cmApBufGetIO( unsigned iDevIdx, cmApSample_t* iBufArray[], unsigned iBufChCnt, cmTimeSpec_t* iTimeStampPtr, unsigned oDevIdx, cmApSample_t* oBufArray[], unsigned oBufChCnt, cmTimeSpec_t* oTimeStampPtr )
  701. {
  702. cmApBufGet( iDevIdx, kInApFl, iBufArray, iBufChCnt );
  703. cmApBufGet( oDevIdx, kOutApFl,oBufArray, oBufChCnt );
  704. unsigned i = 0;
  705. if( iDevIdx != cmInvalidIdx && oDevIdx != cmInvalidIdx )
  706. {
  707. const cmApIO* ip = _cmApBuf.devArray[iDevIdx].ioArray + kInApIdx;
  708. const cmApIO* op = _cmApBuf.devArray[oDevIdx].ioArray + kOutApIdx;
  709. unsigned minChCnt = cmMin(iBufChCnt,oBufChCnt);
  710. unsigned frmCnt = cmMin(ip->dspFrameCnt,op->dspFrameCnt);
  711. unsigned byteCnt = frmCnt * sizeof(cmApSample_t);
  712. _cmApBufCalcTimeStamp(ip->srate, &ip->timeStamp, ip->ioFrameCnt, iTimeStampPtr );
  713. _cmApBufCalcTimeStamp(op->srate, &op->timeStamp, op->ioFrameCnt, oTimeStampPtr );
  714. for(i=0; i<minChCnt; ++i)
  715. {
  716. cmApCh* ocp = op->chArray + i;
  717. cmApCh* icp = ip->chArray + i;
  718. if( oBufArray[i] != NULL )
  719. {
  720. // if either the input or output channel is marked for pass-through
  721. if( cmAllFlags(ocp->fl,kPassApFl) || cmAllFlags(icp->fl,kPassApFl) )
  722. {
  723. memcpy( oBufArray[i], iBufArray[i], byteCnt );
  724. // set the output buffer to NULL to prevent it being over written by the client
  725. oBufArray[i] = NULL;
  726. }
  727. else
  728. {
  729. // zero the output buffer
  730. memset(oBufArray[i],0,byteCnt);
  731. }
  732. }
  733. }
  734. }
  735. if( oDevIdx != cmInvalidIdx )
  736. {
  737. const cmApIO* op = _cmApBuf.devArray[oDevIdx].ioArray + kOutApIdx;
  738. unsigned byteCnt = op->dspFrameCnt * sizeof(cmApSample_t);
  739. _cmApBufCalcTimeStamp(op->srate, &op->timeStamp, op->ioFrameCnt, oTimeStampPtr );
  740. for(; i<oBufChCnt; ++i)
  741. if( oBufArray[i] != NULL )
  742. memset( oBufArray[i], 0, byteCnt );
  743. }
  744. }
  745. void cmApBufAdvance( unsigned devIdx, unsigned flags )
  746. {
  747. unsigned i;
  748. if( devIdx == cmInvalidIdx )
  749. return;
  750. if( flags & kInApFl )
  751. {
  752. cmApIO* ioPtr = _cmApBuf.devArray[devIdx].ioArray + kInApIdx;
  753. for(i=0; i<ioPtr->chCnt; ++i)
  754. {
  755. cmApCh* cp = ioPtr->chArray + i;
  756. cp->oi = (cp->oi + ioPtr->dspFrameCnt) % ioPtr->n;
  757. cmThUIntDecr(&cp->fn,ioPtr->dspFrameCnt);
  758. }
  759. // count the number of samples input from this device
  760. if( ioPtr->timeStamp.tv_sec!=0 && ioPtr->timeStamp.tv_nsec!=0 )
  761. cmThUIntIncr(&ioPtr->ioFrameCnt,ioPtr->dspFrameCnt);
  762. }
  763. if( flags & kOutApFl )
  764. {
  765. cmApIO* ioPtr = _cmApBuf.devArray[devIdx].ioArray + kOutApIdx;
  766. for(i=0; i<ioPtr->chCnt; ++i)
  767. {
  768. cmApCh* cp = ioPtr->chArray + i;
  769. cp->ii = (cp->ii + ioPtr->dspFrameCnt) % ioPtr->n;
  770. cmThUIntIncr(&cp->fn,ioPtr->dspFrameCnt);
  771. }
  772. // count the number of samples output from this device
  773. if( ioPtr->timeStamp.tv_sec!=0 && ioPtr->timeStamp.tv_nsec!=0 )
  774. cmThUIntIncr(&ioPtr->ioFrameCnt,ioPtr->dspFrameCnt);
  775. }
  776. }
  777. void cmApBufInputToOutput( unsigned iDevIdx, unsigned oDevIdx )
  778. {
  779. if( iDevIdx == cmInvalidIdx || oDevIdx == cmInvalidIdx )
  780. return;
  781. unsigned iChCnt = cmApBufChannelCount( iDevIdx, kInApFl );
  782. unsigned oChCnt = cmApBufChannelCount( oDevIdx, kOutApFl );
  783. unsigned chCnt = iChCnt < oChCnt ? iChCnt : oChCnt;
  784. unsigned i;
  785. cmApSample_t* iBufPtrArray[ iChCnt ];
  786. cmApSample_t* oBufPtrArray[ oChCnt ];
  787. while( cmApBufIsDeviceReady( iDevIdx, kInApFl ) && cmApBufIsDeviceReady( oDevIdx, kOutApFl ) )
  788. {
  789. cmApBufGet( iDevIdx, kInApFl, iBufPtrArray, iChCnt );
  790. cmApBufGet( oDevIdx, kOutApFl, oBufPtrArray, oChCnt );
  791. // Warning: buffer pointers to disabled channels will be set to NULL
  792. for(i=0; i<chCnt; ++i)
  793. {
  794. cmApIO* ip = _cmApBuf.devArray[iDevIdx ].ioArray + kInApIdx;
  795. cmApIO* op = _cmApBuf.devArray[oDevIdx].ioArray + kOutApIdx;
  796. assert( ip->dspFrameCnt == op->dspFrameCnt );
  797. unsigned byteCnt = ip->dspFrameCnt * sizeof(cmApSample_t);
  798. if( oBufPtrArray[i] != NULL )
  799. {
  800. // the input channel is not disabled
  801. if( iBufPtrArray[i]!=NULL )
  802. memcpy(oBufPtrArray[i],iBufPtrArray[i],byteCnt);
  803. else
  804. // the input channel is disabled but the output is not - so fill the output with zeros
  805. memset(oBufPtrArray[i],0,byteCnt);
  806. }
  807. }
  808. cmApBufAdvance( iDevIdx, kInApFl );
  809. cmApBufAdvance( oDevIdx, kOutApFl );
  810. }
  811. }
  812. void cmApBufReport( cmRpt_t* rpt )
  813. {
  814. unsigned i,j,k;
  815. for(i=0; i<_cmApBuf.devCnt; ++i)
  816. {
  817. cmRptPrintf(rpt,"%i ",i);
  818. for(j=0; j<kIoApCnt; ++j)
  819. {
  820. cmApIO* ip = _cmApBuf.devArray[i].ioArray + j;
  821. unsigned ii = 0;
  822. unsigned oi = 0;
  823. unsigned fn = 0;
  824. cmApSample_t mtr = 0;
  825. for(k=0; k<ip->chCnt; ++k)
  826. {
  827. cmApCh* cp = ip->chArray + i;
  828. ii += cp->ii;
  829. oi += cp->oi;
  830. fn += cp->fn;
  831. mtr += _cmApMeterValue(cp);
  832. }
  833. cmRptPrintf(rpt,"%s - i:%7i o:%7i f:%7i n:%7i err %s:%7i mtr:%5.4f ",
  834. j==0?"IN ":"OUT",
  835. ii,oi,fn,ip->n, (j==0?"over ":"under"), ip->faultCnt, mtr);
  836. }
  837. cmRptPrintf(rpt,"\n");
  838. }
  839. }
  840. //{ { label:cmApBufExample }
  841. //(
  842. // cmApBufTest() demonstrates the audio buffer usage.
  843. //)
  844. //(
  845. void cmApBufTest( cmRpt_t* rpt )
  846. {
  847. unsigned devIdx = 0;
  848. unsigned devCnt = 1 ;
  849. unsigned dspFrameCnt = 10;
  850. unsigned cycleCnt = 3;
  851. unsigned framesPerCycle = 25;
  852. unsigned inChCnt = 2;
  853. unsigned outChCnt = inChCnt;
  854. unsigned sigN = cycleCnt*framesPerCycle*inChCnt;
  855. double srate = 44100.0;
  856. unsigned meterMs = 50;
  857. int srateMult = 1;
  858. unsigned bufChCnt= inChCnt;
  859. cmApSample_t* inBufArray[ bufChCnt ];
  860. cmApSample_t* outBufArray[ bufChCnt ];
  861. cmApSample_t iSig[ sigN ];
  862. cmApSample_t oSig[ sigN ];
  863. cmApSample_t* os = oSig;
  864. cmApAudioPacket_t pkt;
  865. int i,j;
  866. // create a simulated signal
  867. for(i=0; i<sigN; ++i)
  868. {
  869. iSig[i] = i;
  870. oSig[i] = 0;
  871. }
  872. pkt.devIdx = 0;
  873. pkt.begChIdx = 0;
  874. pkt.chCnt = inChCnt;
  875. pkt.audioFramesCnt = framesPerCycle;
  876. pkt.bitsPerSample = 32;
  877. pkt.flags = 0;
  878. // initialize a the audio buffer
  879. cmApBufInitialize(devCnt,meterMs);
  880. // setup the buffer with the specific parameters to by used by the host audio ports
  881. cmApBufSetup(devIdx,srate,dspFrameCnt,cycleCnt,inChCnt,framesPerCycle,outChCnt,framesPerCycle, srateMult);
  882. // simulate cylcing through sigN buffer transactions
  883. for(i=0; i<sigN; i+=framesPerCycle*inChCnt)
  884. {
  885. // setup an incoming audio packet
  886. pkt.audioFramesCnt = framesPerCycle;
  887. pkt.audioBytesPtr = iSig+i;
  888. // simulate a call from the audio port with incoming samples
  889. // (fill the audio buffers internal input buffers)
  890. cmApBufUpdate(&pkt,1,NULL,0);
  891. // if all devices need to be serviced
  892. while( cmApBufIsDeviceReady( devIdx, kInApFl | kOutApFl ))
  893. {
  894. // get pointers to full internal input buffers
  895. cmApBufGet(devIdx, kInApFl, inBufArray, bufChCnt );
  896. // get pointers to empty internal output buffers
  897. cmApBufGet(devIdx, kOutApFl, outBufArray, bufChCnt );
  898. // Warning: pointers to disabled channels will be set to NULL.
  899. // simulate a play through by copying the incoming samples to the outgoing buffers.
  900. for(j=0; j<bufChCnt; ++j)
  901. if( outBufArray[j] != NULL )
  902. {
  903. // if the input is disabled - but the output is not then zero the output buffer
  904. if( inBufArray[j] == NULL )
  905. memset( outBufArray[j], 0, dspFrameCnt*sizeof(cmApSample_t));
  906. else
  907. // copy the input to the output
  908. memcpy( outBufArray[j], inBufArray[j], dspFrameCnt*sizeof(cmApSample_t));
  909. }
  910. // signal the buffer that this cycle is complete.
  911. // (marks current internal input/output buffer empty/full)
  912. cmApBufAdvance( devIdx, kInApFl | kOutApFl );
  913. }
  914. pkt.audioBytesPtr = os;
  915. // simulate a call from the audio port picking up outgoing samples
  916. // (empties the audio buffers internal output buffers)
  917. cmApBufUpdate(NULL,0,&pkt,1);
  918. os += pkt.audioFramesCnt * pkt.chCnt;
  919. }
  920. for(i=0; i<sigN; ++i)
  921. cmRptPrintf(rpt,"%f ",oSig[i]);
  922. cmRptPrintf(rpt,"\n");
  923. cmApBufFinalize();
  924. }
  925. //)
  926. //}