123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176 |
- #include "cmPrefix.h"
- #include "cmGlobal.h"
- #include "cmRpt.h"
- #include "cmErr.h"
- #include "cmMem.h"
- #include "cmMallocDebug.h"
- #include "cmTime.h"
- #include "cmAudioPort.h"
- #include "cmApBuf.h"
- #include "cmThread.h"
-
- /*
- This API is in general called by two types of threads:
- audio devices threads and the client thread. There
- may be multiple devie threads however there is only
- one client thread.
-
- The audio device threads only call cmApBufUpdate().
- cmApBufUpdate() is never called by any other threads.
- A call from the audio update threads targets specific channels
- (cmApCh records). The variables within each channels that
- it modifies are confined to:
- on input channels: increments ii and increments fn (data is entering the ch. buffers)
- on output channels: increments oi and decrements fn (data is leaving the ch. buffers)
-
- The client picks up incoming audio and provides outgoing audio via
- cmApBufGet(). It then informs the cmApBuf() that it has completed
- the audio data transfer by calling cmApBufAdvance().
-
- cmApBufAdvance() modifies the following internal variables:
- on input channels: increments oi and decrements fn (data has left the ch buffer)
- on output channels: increments ii and increments fn (data has enterned the ch. buffer)
-
- Based on the above scenario the channel ii and oi variables are always thread-safe
- because they are only changed by a single thread.
-
- ii oi fn
- ------ ----- ----
- input ch: audio client both
- output ch: client audio both
-
- The fn variable however is not thread-safe and therefore care must be taken as
- to how it is read and updated.
- */
-
- enum { kInApIdx=0, kOutApIdx=1, kIoApCnt=2 };
-
- typedef struct
- {
- unsigned fl; // kChApFl|kToneApFl|kMeterApFl ...
- cmApSample_t* b; // b[n]
- unsigned ii; // next in
- unsigned oi; // next out
- unsigned fn; // full cnt - count of samples currently in the buffer - incr'd by incoming, decr'd by outgoing
- unsigned phs; // tone phase
- double hz; // tone frequency
- double gain; // channel gain
- cmApSample_t* m; // m[mn] meter sample sum
- unsigned mn; // length of m[]
- unsigned mi; // next ele of m[] to rcv sum
- cmApSample_t s0; // buffered sample used for srate conversion
- } cmApCh;
-
- typedef struct
- {
- unsigned chCnt; // Count of channels
- cmApCh* chArray; // chArray[chCnt] channel record array
-
- unsigned n; // Length of b[] (multiple of dspFrameCnt) bufCnt*framesPerCycle
- double srate; // Device sample rate;
- int srateMult; // Internal sample rate multiplier (negative values for dividing). This srateMult*srate is the sample rate of
- // signals held in the chApCh[] input and output buffers. Sample rate conversion to/from this rate
- // occurs in cmApBufUpdate() as signals go from/to the audio device.
-
- unsigned faultCnt; // error count since start
- unsigned framesPerCycle; // expected count of frames per channel to/from the audio device on each call to cmApBufUpdate()
- unsigned dspFrameCnt; // number of frames per channel in buffers returned by cmApBufGet().
- cmTimeSpec_t timeStamp; // base (starting) time stamp for this device
- unsigned ioFrameCnt; // count of frames input or output for this device
-
- } cmApIO;
-
- typedef struct
- {
- // ioArray[] always contains 2 elements - one for input the other for output.
- cmApIO ioArray[kIoApCnt];
- } cmApDev;
-
- typedef struct
- {
- cmApDev* devArray;
- unsigned devCnt;
- unsigned meterMs;
-
- cmApSample_t* zeroBuf; // buffer of zeros
- unsigned zeroBufCnt; // max of all dspFrameCnt for all devices.
-
-
- unsigned abufIdx;
- cmApSample_t abuf[ 16384 ];
- } cmApBuf;
-
- cmApBuf _cmApBuf;
-
-
- // Copy the source channel (srcChIdx) to the destination buffer and apply up-sampling.
- // 'src' is an interleaved buffer with 'srcN' samples per channel and 'srcChN' channels (total size in samples = srcN*srcChN)
- // '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..
- // Return the index into dst[] of the next location to receive an incoming sample.
- 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 )
- {
- cmApSample_t s0 = *s0_Ref;
- unsigned si,k,di=dstIdx;
-
- for(si=0; si<srcN; ++si)
- {
- cmApSample_t s1 = src[si*srcChN+srcChIdx];
-
- for(k=1; k<mult; ++k)
- {
- dst[di] = gain * (s0 + (s1 - s0) * k / mult );
- di = (di+1) % dstN;
- }
-
- dst[di] = gain * s1;
- di = (di+1) % dstN;
- s0 = s1;
- }
-
- *s0_Ref = s0;
-
- return di;
- }
-
- // Copy the source channel (srcChIdx) to the destination buffer and apply down-sampling.
- // 'src' is an interleaved linear buffer with 'srcN' samples per channel and 'srcChN' channels,
- // 'dst' is a non-interleaved circular buffer of length 'dstN' where 'dstIdx' is the index of the first dst slot to receive a sample..
- // Return the index into dst[] of the next location to receive an incoming sample.
- unsigned _cmApCopyInDnSample( const cmApSample_t* src, unsigned srcN, unsigned srcChN, unsigned srcChIdx, cmApSample_t* dst, unsigned dstN, unsigned dstIdx, unsigned div, double gain )
- {
- unsigned si,di=dstIdx;
-
- for(si=0; si<srcN; si+=div)
- {
- dst[di] = gain * src[si*srcChN+srcChIdx];
- di = (di+1) % dstN;
- }
- return di;
- }
-
-
- // Copy samples from an interleaved src buffer to a non-interleaved dst buffer with sample rate conversion
- 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 )
- {
- if( srateMult < 1 )
- return _cmApCopyInDnSample( src, srcN, srcChN, srcChIdx, dst, dstN, dstIdx, -srateMult, gain );
-
- return _cmApCopyInUpSample( src, srcN, srcChN, srcChIdx, dst, dstN, dstIdx, srateMult, gain, s0_Ref );
- }
-
- void printBuf( const cmApSample_t* src, unsigned srcN, unsigned bi, unsigned n )
- {
- unsigned i,j;
- for(i=bi,j=0; j<n; ++i,++j)
- {
- i = i % srcN;
- printf("(%i,%i,%f),\n",j,i,src[i]);
- }
- printf("-----\n");
- }
-
-
- // Copy
- // 'src' is a non-interleaved circular buf of total length 'srcN', with the first sample coming at 'srcIdx'.
- // 'dst' is an interleaved buffer of length 'dstN' with 'dstChN' channels
- // Return the index of the next src sample.
- 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 )
- {
- unsigned di,si=srcIdx;
- cmApSample_t s0 = *s0_Ref;
-
- for(di=0; di<dstN; ++di)
- {
- cmApSample_t s1 = src[si];
- unsigned k;
- for(k=1; k<mult && di<dstN; ++di,++k)
- {
- dst[di*dstChN+dstChIdx] = gain * (s0 + (s1 - s0) * k / mult );
- }
-
- if( di < dstN )
- {
- dst[di*dstChN+dstChIdx] = gain * s1;
- ++di;
- }
-
- si = (si + 1) % srcN;
-
- s0 = s1;
- }
-
- *s0_Ref = s0;
- return si;
- }
-
- // 'src' is a non-interleaved (single channel) circular buf of total length 'srcN', with the first sample coming at 'srcIdx'.
- // 'dst' is an interleaved buffer of length 'dstN' with 'dstChN' channels
- // Return the index of the next src sample.
- unsigned _cmApCopyOutDnSample( const cmApSample_t* src, unsigned srcN, unsigned srcIdx, cmApSample_t* dst, unsigned dstN, unsigned dstChN, unsigned dstChIdx, int div, double gain )
- {
- unsigned di,si;
-
- // The total count of output samples is determined by 'dstN'
- // Downsampling is acheived by advancing the src index by 'div' samples.
-
- for(di=0,si=srcIdx; di<dstN; ++di)
- {
- dst[di*dstChN+dstChIdx] = gain * src[si];
- si = (si + div) % srcN;
- }
-
- return si;
- }
-
-
- // Copy samples from a non-interleaved src buffer to an interleaved dst buffer with sample rate conversion
- 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 )
- {
- if( srateMult > 0 )
- return _cmApCopyOutDnSample(src, srcN, srcIdx, dst, dstN, dstChN, dstChIdx, srateMult, gain );
-
- return _cmApCopyOutUpSample( src, srcN, srcIdx, dst, dstN, dstChN, dstChIdx, -srateMult, gain, s0_Ref );
- }
-
-
- cmApSample_t _cmApMeterValue( const cmApCh* cp )
- {
- double sum = 0;
- unsigned i;
- for(i=0; i<cp->mn; ++i)
- sum += cp->m[i];
-
- return cp->mn==0 ? 0 : (cmApSample_t)sqrt(sum/cp->mn);
- }
-
- void _cmApSine0( cmApCh* cp, cmApSample_t* b0, unsigned n0, cmApSample_t* b1, unsigned n1, unsigned stride, float srate )
- {
- unsigned i;
-
- for(i=0; i<n0; ++i,++cp->phs)
- b0[i*stride] = (float)(cp->gain * sin( 2.0 * M_PI * cp->hz * cp->phs / srate ));
-
- for(i=0; i<n1; ++i,++cp->phs)
- b1[i*stride] = (float)(cp->gain * sin( 2.0 * M_PI * cp->hz * cp->phs / srate ));
- }
-
- // Synthesize a sine signal of length sigN*srateMult starting at dst[dstidx]
- // Assume dst[dstN] is a circular buffer
- unsigned _cmApSine( cmApCh* cp, cmApSample_t* dst, unsigned dstN, unsigned dstIdx, unsigned dstChCnt, unsigned sigN, unsigned srateMult, float srate, double gain )
- {
- unsigned i,di;
-
- sigN = srateMult < 0 ? sigN/abs(srateMult) : sigN * srateMult;
- for(i=0, di=dstIdx; i<sigN; ++i,++cp->phs)
- {
- dst[di] = (cmApSample_t)(gain * sin( 2.0 * M_PI * cp->hz * cp->phs / srate ));
- di = (di+dstChCnt) % dstN;
- }
-
- return sigN;
- }
-
-
- cmApSample_t _cmApMeter( const cmApSample_t* b, unsigned bn, unsigned stride )
- {
- const cmApSample_t* ep = b + bn;
- cmApSample_t sum = 0;
-
- for(; b<ep; b+=stride)
- sum += *b * *b;
-
- return sum / bn;
- }
-
- void _cmApChFinalize( cmApCh* chPtr )
- {
- cmMemPtrFree( &chPtr->b );
- cmMemPtrFree( &chPtr->m );
- }
-
- // n=buf sample cnt mn=meter buf smp cnt
- void _cmApChInitialize( cmApCh* chPtr, unsigned n, unsigned mn )
- {
- _cmApChFinalize(chPtr);
-
- chPtr->b = n==0 ? NULL : cmMemAllocZ( cmApSample_t, n );
- chPtr->ii = 0;
- chPtr->oi = 0;
- chPtr->fn = 0;
- chPtr->fl = (n!=0 ? kChApFl : 0);
- chPtr->hz = 1000;
- chPtr->gain = 1.0;
- chPtr->mn = mn;
- chPtr->m = cmMemAllocZ(cmApSample_t,mn);
- chPtr->mi = 0;
-
- }
-
- void _cmApIoFinalize( cmApIO* ioPtr )
- {
- unsigned i;
- for(i=0; i<ioPtr->chCnt; ++i)
- _cmApChFinalize( ioPtr->chArray + i );
-
- cmMemPtrFree(&ioPtr->chArray);
- ioPtr->chCnt = 0;
- ioPtr->n = 0;
- }
-
- void _cmApIoInitialize( cmApIO* ioPtr, double srate, unsigned framesPerCycle, unsigned chCnt, unsigned n, unsigned meterBufN, unsigned dspFrameCnt, int srateMult )
- {
- unsigned i;
-
- if( srateMult == 0 )
- srateMult = 1;
-
- _cmApIoFinalize(ioPtr);
-
- n += (n % dspFrameCnt); // force buffer size to be a multiple of dspFrameCnt
-
- ioPtr->chArray = chCnt==0 ? NULL : cmMemAllocZ( cmApCh, chCnt );
- ioPtr->chCnt = chCnt;
- ioPtr->n = srateMult<0 ? n : n*srateMult;
- ioPtr->faultCnt = 0;
- ioPtr->framesPerCycle = framesPerCycle;
- ioPtr->srate = srate;
- ioPtr->srateMult = srateMult;
- ioPtr->dspFrameCnt = dspFrameCnt;
- ioPtr->timeStamp.tv_sec = 0;
- ioPtr->timeStamp.tv_nsec = 0;
- ioPtr->ioFrameCnt = 0;
-
- for(i=0; i<chCnt; ++i )
- _cmApChInitialize( ioPtr->chArray + i, ioPtr->n, meterBufN );
-
- }
-
- void _cmApDevFinalize( cmApDev* dp )
- {
- unsigned i;
- for(i=0; i<kIoApCnt; ++i)
- _cmApIoFinalize( dp->ioArray+i);
- }
-
- void _cmApDevInitialize( cmApDev* dp, double srate, unsigned iFpC, unsigned iChCnt, unsigned iBufN, unsigned oFpC, unsigned oChCnt, unsigned oBufN, unsigned meterBufN, unsigned dspFrameCnt, int srateMult )
- {
- unsigned i;
-
- _cmApDevFinalize(dp);
-
- for(i=0; i<kIoApCnt; ++i)
- {
- unsigned chCnt = i==kInApIdx ? iChCnt : oChCnt;
- unsigned bufN = i==kInApIdx ? iBufN : oBufN;
- unsigned fpc = i==kInApIdx ? iFpC : oFpC;
- _cmApIoInitialize( dp->ioArray+i, srate, fpc, chCnt, bufN, meterBufN, dspFrameCnt, srateMult );
-
- }
-
- }
-
- cmAbRC_t cmApBufInitialize( unsigned devCnt, unsigned meterMs )
- {
- cmAbRC_t rc;
-
- if((rc = cmApBufFinalize()) != kOkAbRC )
- return rc;
-
- _cmApBuf.devArray = cmMemAllocZ( cmApDev, devCnt );
- _cmApBuf.devCnt = devCnt;
- cmApBufSetMeterMs(meterMs);
-
- return kOkAbRC;
- }
-
- cmAbRC_t cmApBufFinalize()
- {
- unsigned i;
- for(i=0; i<_cmApBuf.devCnt; ++i)
- _cmApDevFinalize(_cmApBuf.devArray + i);
-
- cmMemPtrFree( &_cmApBuf.devArray );
- cmMemPtrFree( &_cmApBuf.zeroBuf );
-
- _cmApBuf.devCnt = 0;
-
- FILE* fp;
- if(_cmApBuf.abufIdx>0 )
- {
- if((fp = fopen("/home/kevin/temp/temp.csv","wt")) != NULL )
- {
- int i;
- for(i=0; i<_cmApBuf.abufIdx; ++i)
- {
- fprintf(fp,"%f,\n",_cmApBuf.abuf[i]);
- }
- fclose(fp);
- }
- }
-
-
-
- return kOkAbRC;
- }
-
- cmAbRC_t cmApBufSetup(
- unsigned devIdx,
- double srate,
- unsigned dspFrameCnt,
- unsigned bufCnt,
- unsigned inChCnt,
- unsigned inFramesPerCycle,
- unsigned outChCnt,
- unsigned outFramesPerCycle,
- int srateMult)
- {
- cmApDev* devPtr = _cmApBuf.devArray + devIdx;
- unsigned iBufN = bufCnt * inFramesPerCycle;
- unsigned oBufN = bufCnt * outFramesPerCycle;
- unsigned meterBufN = cmMax(1,floor(srate * _cmApBuf.meterMs / (1000.0 * outFramesPerCycle)));
-
- _cmApDevInitialize( devPtr, srate, inFramesPerCycle, inChCnt, iBufN, outFramesPerCycle, outChCnt, oBufN, meterBufN, dspFrameCnt, srateMult );
-
- if( inFramesPerCycle > _cmApBuf.zeroBufCnt || outFramesPerCycle > _cmApBuf.zeroBufCnt )
- {
- _cmApBuf.zeroBufCnt = cmMax(inFramesPerCycle,outFramesPerCycle);
- _cmApBuf.zeroBuf = cmMemResizeZ(cmApSample_t,_cmApBuf.zeroBuf,_cmApBuf.zeroBufCnt);
- }
-
- return kOkAbRC;
- }
-
- cmAbRC_t cmApBufPrimeOutput( unsigned devIdx, unsigned audioCycleCnt )
- {
- cmApIO* iop = _cmApBuf.devArray[devIdx].ioArray + kOutApIdx;
- unsigned i;
-
- for(i=0; i<iop->chCnt; ++i)
- {
- cmApCh* cp = iop->chArray + i;
- unsigned bn = iop->n * sizeof(cmApSample_t);
- memset(cp->b,0,bn);
- cp->oi = 0;
- cp->ii = iop->framesPerCycle * audioCycleCnt;
- cp->fn = iop->framesPerCycle * audioCycleCnt;
- }
-
- return kOkAbRC;
- }
-
- void cmApBufOnPortEnable( unsigned devIdx, bool enableFl )
- {
- if( devIdx == cmInvalidIdx || enableFl==false)
- return;
-
- cmApIO* iop = _cmApBuf.devArray[devIdx].ioArray + kOutApIdx;
- iop->timeStamp.tv_sec = 0;
- iop->timeStamp.tv_nsec = 0;
- iop->ioFrameCnt = 0;
-
- iop = _cmApBuf.devArray[devIdx].ioArray + kInApIdx;
- iop->timeStamp.tv_sec = 0;
- iop->timeStamp.tv_nsec = 0;
- iop->ioFrameCnt = 0;
-
-
- }
-
- cmAbRC_t cmApBufUpdate(
- cmApAudioPacket_t* inPktArray,
- unsigned inPktCnt,
- cmApAudioPacket_t* outPktArray,
- unsigned outPktCnt )
- {
- unsigned i,j;
-
- // copy samples from the packet to the buffer
- if( inPktArray != NULL )
- {
- for(i=0; i<inPktCnt; ++i)
- {
- cmApAudioPacket_t* pp = inPktArray + i;
- cmApIO* ip = _cmApBuf.devArray[pp->devIdx].ioArray + kInApIdx; // dest io recd
-
- // if the base time stamp has not yet been set - then set it
- if( ip->timeStamp.tv_sec==0 && ip->timeStamp.tv_nsec==0 )
- ip->timeStamp = pp->timeStamp;
-
- // for each source packet channel and enabled dest channel
- for(j=0; j<pp->chCnt; ++j)
- {
- cmApCh* cp = ip->chArray + pp->begChIdx +j; // dest ch
-
- assert(pp->begChIdx + j < ip->chCnt );
-
- // if the incoming samples would overflow the buffer then ignore them
- if( cp->fn + pp->audioFramesCnt > ip->n )
- {
- ++ip->faultCnt; // record input overflow
- continue;
- }
-
- // if the incoming samples would go off the end of the buffer then
- // copy in the samples in two segments (one at the end and another at begin of dest channel)
-
- bool enaFl = cmIsFlag(cp->fl,kChApFl) && cmIsFlag(cp->fl,kMuteApFl)==false;
- const cmApSample_t* sp = enaFl ? ((cmApSample_t*)pp->audioBytesPtr) + j : _cmApBuf.zeroBuf;
- //unsigned ssn = enaFl ? pp->chCnt : 1; // stride (packet samples are interleaved)
- //cmApSample_t* dp = cp->b + cp->ii;
- //const cmApSample_t* ep = dp + n0;
-
-
- // update the meter
- if( cmIsFlag(cp->fl,kMeterApFl) )
- {
- cp->m[cp->mi] = _cmApMeter(sp,pp->audioFramesCnt,pp->chCnt);
- cp->mi = (cp->mi + 1) % cp->mn;
- }
-
- unsigned incrSmpN = 0;
-
- // if the test tone is enabled on this input channel
- if( enaFl && cmIsFlag(cp->fl,kToneApFl) )
- {
- //_cmApSine(cp, dp, n0, cp->b, n1, 1, ip->srate );
- incrSmpN = _cmApSine( cp, cp->b, ip->n, cp->ii, 1, pp->audioFramesCnt, ip->srateMult, ip->srate, cp->gain );
-
-
- }
- else // otherwise copy samples from the packet to the buffer
- {
-
- unsigned pi = cp->ii;
-
- cp->ii = _cmApCopyInSamples( (cmApSample_t*)pp->audioBytesPtr, pp->audioFramesCnt, pp->chCnt, j, cp->b, ip->n, cp->ii, ip->srateMult, cp->gain, &cp->s0 );
-
- if( false )
- if( j == 2 && _cmApBuf.abufIdx < 16384 )
- {
- int ii;
- for(ii=pi; ii!=cp->ii && _cmApBuf.abufIdx<16384; _cmApBuf.abufIdx++)
- {
- _cmApBuf.abuf[ _cmApBuf.abufIdx ] = cp->b[ii];
- ii = (ii + 1) % ip->n;
- }
-
- }
-
-
- incrSmpN = cp->ii > pi ? cp->ii-pi : (ip->n-pi) + cp->ii;
-
- }
-
- cmThUIntIncr(&cp->fn,incrSmpN);
- }
- }
- }
-
- // copy samples from the buffer to the packet
- if( outPktArray != NULL )
- {
- for(i=0; i<outPktCnt; ++i)
- {
- cmApAudioPacket_t* pp = outPktArray + i;
- cmApIO* op = _cmApBuf.devArray[pp->devIdx].ioArray + kOutApIdx; // dest io recd
-
- // if the base timestamp has not yet been set then set it.
- if( op->timeStamp.tv_sec==0 && op->timeStamp.tv_nsec==0 )
- op->timeStamp = pp->timeStamp;
-
- // for each dest packet channel and enabled source channel
- for(j=0; j<pp->chCnt; ++j)
- {
- cmApCh* cp = op->chArray + pp->begChIdx + j; // dest ch
- unsigned n0 = op->n - cp->oi; // first src segment
- unsigned n1 = 0; // second src segment
- volatile unsigned fn = cp->fn; // store fn because it may be changed by the client thread
-
- // if the outgoing samples will underflow the buffer
- if( pp->audioFramesCnt > fn )
- {
- ++op->faultCnt; // record an output underflow
-
- // if the buffer is empty - zero the packet and return
- if( fn == 0 )
- {
- memset( pp->audioBytesPtr, 0, pp->audioFramesCnt*sizeof(cmApSample_t));
- continue;
- }
-
- // ... otherwise decrease the count of returned samples
- pp->audioFramesCnt = fn;
- }
-
- // if the outgong segments would go off the end of the buffer then
- // arrange to wrap to the begining of the buffer
- if( n0 < pp->audioFramesCnt )
- n1 = pp->audioFramesCnt-n0;
- else
- n0 = pp->audioFramesCnt;
-
- cmApSample_t* bpp = ((cmApSample_t*)pp->audioBytesPtr) + j;
- cmApSample_t* dp = bpp;
- bool enaFl = cmIsFlag(cp->fl,kChApFl) && cmIsFlag(cp->fl,kMuteApFl)==false;
-
- unsigned decrSmpN = 0;
-
- // if the tone is enabled on this channel
- if( enaFl && cmIsFlag(cp->fl,kToneApFl) )
- {
- //_cmApSine(cp, dp, n0, dp + n0*pp->chCnt, n1, pp->chCnt, op->srate );
- decrSmpN = _cmApSine( cp, (cmApSample_t*)pp->audioBytesPtr, pp->audioFramesCnt, 0, pp->chCnt, pp->audioFramesCnt, op->srateMult, op->srate, cp->gain );
-
- }
- else // otherwise copy samples from the output buffer to the packet
- {
- const cmApSample_t* sp = enaFl ? cp->b + cp->oi : _cmApBuf.zeroBuf;
- const cmApSample_t* ep = sp + n0;
-
- unsigned pi = cp->oi;
- 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 );
-
- decrSmpN = cp->oi>pi ? cp->oi-pi : (op->n-pi) + cp->oi;
-
- if( true )
- if( j == 2 && _cmApBuf.abufIdx < 16384 )
- {
- int ii;
- for(ii=pi; ii!=cp->oi && _cmApBuf.abufIdx<16384; _cmApBuf.abufIdx++)
- {
- _cmApBuf.abuf[ _cmApBuf.abufIdx ] = cp->b[ii];
- ii = (ii + 1) % op->n;
- }
-
- }
-
- /*
- // copy the first segment
- for(; sp < ep; dp += pp->chCnt )
- *dp = cp->gain * *sp++;
-
- // if there is a second segment
- if( n1 > 0 )
- {
- // copy the second segment
- sp = enaFl ? cp->b : _cmApBuf.zeroBuf;
- ep = sp + n1;
- for(; sp<ep; dp += pp->chCnt )
- *dp = cp->gain * *sp++;
-
- }
- */
- }
-
- // update the meter
- if( cmIsFlag(cp->fl,kMeterApFl) )
- {
- cp->m[cp->mi] = _cmApMeter(((cmApSample_t*)pp->audioBytesPtr)+j,pp->audioFramesCnt,pp->chCnt);
- cp->mi = (cp->mi + 1) % cp->mn;
- }
-
- // advance the output channel buffer
- /*
- cp->oi = n1>0 ? n1 : cp->oi + n0;
- cmThUIntDecr(&cp->fn,pp->audioFramesCnt);
- */
-
- //cp->fn -= pp->audioFramesCnt;
- cmThUIntDecr(&cp->fn,decrSmpN);
- }
- }
- }
- return kOkAbRC;
- }
-
- unsigned cmApBufMeterMs()
- { return _cmApBuf.meterMs; }
-
- void cmApBufSetMeterMs( unsigned meterMs )
- {
- _cmApBuf.meterMs = cmMin(1000,cmMax(10,meterMs));
- }
-
- unsigned cmApBufChannelCount( unsigned devIdx, unsigned flags )
- {
- if( devIdx == cmInvalidIdx )
- return 0;
-
- unsigned idx = flags & kInApFl ? kInApIdx : kOutApIdx;
- return _cmApBuf.devArray[devIdx].ioArray[ idx ].chCnt;
- }
-
- void cmApBufSetFlag( unsigned devIdx, unsigned chIdx, unsigned flags )
- {
- if( devIdx == cmInvalidIdx )
- return;
-
- unsigned idx = flags & kInApFl ? kInApIdx : kOutApIdx;
- bool enableFl = flags & kEnableApFl ? true : false;
- unsigned i = chIdx != -1 ? chIdx : 0;
- unsigned n = chIdx != -1 ? chIdx+1 : _cmApBuf.devArray[devIdx].ioArray[idx].chCnt;
-
- for(; i<n; ++i)
- {
- cmApCh* cp = _cmApBuf.devArray[devIdx].ioArray[idx].chArray + i;
- cp->fl = cmEnaFlag(cp->fl, flags & (kChApFl|kToneApFl|kMeterApFl|kMuteApFl|kPassApFl), enableFl );
- }
-
- }
-
- bool cmApBufIsFlag( unsigned devIdx, unsigned chIdx, unsigned flags )
- {
- if( devIdx == cmInvalidIdx )
- return false;
-
- unsigned idx = flags & kInApFl ? kInApIdx : kOutApIdx;
- return cmIsFlag(_cmApBuf.devArray[devIdx].ioArray[idx].chArray[chIdx].fl,flags);
- }
-
-
- void cmApBufEnableChannel( unsigned devIdx, unsigned chIdx, unsigned flags )
- { cmApBufSetFlag(devIdx,chIdx,flags | kChApFl); }
-
- bool cmApBufIsChannelEnabled( unsigned devIdx, unsigned chIdx, unsigned flags )
- { return cmApBufIsFlag(devIdx, chIdx, flags | kChApFl); }
-
- void cmApBufEnableTone( unsigned devIdx, unsigned chIdx, unsigned flags )
- { cmApBufSetFlag(devIdx,chIdx,flags | kToneApFl); }
-
- bool cmApBufIsToneEnabled( unsigned devIdx, unsigned chIdx, unsigned flags )
- { return cmApBufIsFlag(devIdx,chIdx,flags | kToneApFl); }
-
- void cmApBufEnableMute( unsigned devIdx, unsigned chIdx, unsigned flags )
- { cmApBufSetFlag(devIdx,chIdx,flags | kMuteApFl); }
-
- bool cmApBufIsMuteEnabled( unsigned devIdx, unsigned chIdx, unsigned flags )
- { return cmApBufIsFlag(devIdx,chIdx,flags | kMuteApFl); }
-
- void cmApBufEnablePass( unsigned devIdx, unsigned chIdx, unsigned flags )
- { cmApBufSetFlag(devIdx,chIdx,flags | kPassApFl); }
-
- bool cmApBufIsPassEnabled( unsigned devIdx, unsigned chIdx, unsigned flags )
- { return cmApBufIsFlag(devIdx,chIdx,flags | kPassApFl); }
-
- void cmApBufEnableMeter( unsigned devIdx, unsigned chIdx, unsigned flags )
- { cmApBufSetFlag(devIdx,chIdx,flags | kMeterApFl); }
-
- bool cmApBufIsMeterEnabled(unsigned devIdx, unsigned chIdx, unsigned flags )
- { return cmApBufIsFlag(devIdx,chIdx,flags | kMeterApFl); }
-
- cmApSample_t cmApBufMeter(unsigned devIdx, unsigned chIdx, unsigned flags )
- {
- if( devIdx == cmInvalidIdx )
- return 0;
-
- unsigned idx = flags & kInApFl ? kInApIdx : kOutApIdx;
- const cmApCh* cp = _cmApBuf.devArray[devIdx].ioArray[idx].chArray + chIdx;
- return _cmApMeterValue(cp);
- }
-
- void cmApBufSetGain( unsigned devIdx, unsigned chIdx, unsigned flags, double gain )
- {
- if( devIdx == cmInvalidIdx )
- return;
-
- unsigned idx = flags & kInApFl ? kInApIdx : kOutApIdx;
- unsigned i = chIdx != -1 ? chIdx : 0;
- unsigned n = i + (chIdx != -1 ? 1 : _cmApBuf.devArray[devIdx].ioArray[idx].chCnt);
-
- for(; i<n; ++i)
- _cmApBuf.devArray[devIdx].ioArray[idx].chArray[i].gain = gain;
- }
-
- double cmApBufGain( unsigned devIdx, unsigned chIdx, unsigned flags )
- {
- if( devIdx == cmInvalidIdx )
- return 0;
-
- unsigned idx = flags & kInApFl ? kInApIdx : kOutApIdx;
- return _cmApBuf.devArray[devIdx].ioArray[idx].chArray[chIdx].gain;
- }
-
-
- unsigned cmApBufGetStatus( unsigned devIdx, unsigned flags, double* meterArray, unsigned meterCnt, unsigned* faultCntPtr )
- {
- if( devIdx == cmInvalidIdx )
- return 0;
-
- unsigned ioIdx = cmIsFlag(flags,kInApFl) ? kInApIdx : kOutApIdx;
- cmApIO* iop = _cmApBuf.devArray[devIdx].ioArray + ioIdx;
- unsigned chCnt = cmMin(iop->chCnt, meterCnt );
- unsigned i;
-
- if( faultCntPtr != NULL )
- *faultCntPtr = iop->faultCnt;
-
- for(i=0; i<chCnt; ++i)
- meterArray[i] = _cmApMeterValue(iop->chArray + i);
- return chCnt;
- }
-
-
- bool cmApBufIsDeviceReady( unsigned devIdx, unsigned flags )
- {
- //bool iFl = true;
- //bool oFl = true;
- unsigned i = 0;
-
- if( devIdx == cmInvalidIdx )
- return false;
-
- if( flags & kInApFl )
- {
- const cmApIO* ioPtr = _cmApBuf.devArray[devIdx].ioArray + kInApIdx;
- for(i=0; i<ioPtr->chCnt; ++i)
- if( ioPtr->chArray[i].fn < ioPtr->dspFrameCnt )
- return false;
-
- //iFl = ioPtr->fn > ioPtr->dspFrameCnt;
- }
-
- if( flags & kOutApFl )
- {
- const cmApIO* ioPtr = _cmApBuf.devArray[devIdx].ioArray + kOutApIdx;
-
- for(i=0; i<ioPtr->chCnt; ++i)
- if( (ioPtr->n - ioPtr->chArray[i].fn) < ioPtr->dspFrameCnt )
- return false;
-
-
- //oFl = (ioPtr->n - ioPtr->fn) > ioPtr->dspFrameCnt;
- }
-
- return true;
- //return iFl & oFl;
- }
-
-
- // Note that his function returns audio samples but does NOT
- // change any internal states.
- void cmApBufGet( unsigned devIdx, unsigned flags, cmApSample_t* bufArray[], unsigned bufChCnt )
- {
- unsigned i;
- if( devIdx == cmInvalidIdx )
- {
- for(i=0; i<bufChCnt; ++i)
- bufArray[i] = NULL;
- return;
- }
-
- unsigned idx = flags & kInApFl ? kInApIdx : kOutApIdx;
- const cmApIO* ioPtr = _cmApBuf.devArray[devIdx].ioArray + idx;
- unsigned n = bufChCnt < ioPtr->chCnt ? bufChCnt : ioPtr->chCnt;
- cmApCh* cp = ioPtr->chArray;
-
- for(i=0; i<n; ++i,++cp)
- {
- unsigned offs = flags & kInApFl ? cp->oi : cp->ii;
- bufArray[i] = cmIsFlag(cp->fl,kChApFl) ? cp->b + offs : NULL;
- }
-
- }
-
- void _cmApBufCalcTimeStamp( double srate, const cmTimeSpec_t* baseTimeStamp, unsigned frmCnt, cmTimeSpec_t* retTimeStamp )
- {
- if( retTimeStamp==NULL )
- return;
-
- double secs = frmCnt / srate;
- unsigned int_secs = floor(secs);
- double frac_secs = secs - int_secs;
-
- retTimeStamp->tv_nsec = floor(baseTimeStamp->tv_nsec + frac_secs * 1000000000);
- retTimeStamp->tv_sec = baseTimeStamp->tv_sec + int_secs;
-
- if( retTimeStamp->tv_nsec > 1000000000 )
- {
- retTimeStamp->tv_nsec -= 1000000000;
- retTimeStamp->tv_sec += 1;
- }
- }
-
- void cmApBufGetIO( unsigned iDevIdx, cmApSample_t* iBufArray[], unsigned iBufChCnt, cmTimeSpec_t* iTimeStampPtr, unsigned oDevIdx, cmApSample_t* oBufArray[], unsigned oBufChCnt, cmTimeSpec_t* oTimeStampPtr )
- {
- cmApBufGet( iDevIdx, kInApFl, iBufArray, iBufChCnt );
- cmApBufGet( oDevIdx, kOutApFl,oBufArray, oBufChCnt );
-
- unsigned i = 0;
-
- if( iDevIdx != cmInvalidIdx && oDevIdx != cmInvalidIdx )
- {
- const cmApIO* ip = _cmApBuf.devArray[iDevIdx].ioArray + kInApIdx;
- const cmApIO* op = _cmApBuf.devArray[oDevIdx].ioArray + kOutApIdx;
- unsigned minChCnt = cmMin(iBufChCnt,oBufChCnt);
- unsigned frmCnt = cmMin(ip->dspFrameCnt,op->dspFrameCnt);
- unsigned byteCnt = frmCnt * sizeof(cmApSample_t);
-
- _cmApBufCalcTimeStamp(ip->srate, &ip->timeStamp, ip->ioFrameCnt, iTimeStampPtr );
- _cmApBufCalcTimeStamp(op->srate, &op->timeStamp, op->ioFrameCnt, oTimeStampPtr );
-
- for(i=0; i<minChCnt; ++i)
- {
- cmApCh* ocp = op->chArray + i;
- cmApCh* icp = ip->chArray + i;
-
- if( oBufArray[i] != NULL )
- {
- // if either the input or output channel is marked for pass-through
- if( cmAllFlags(ocp->fl,kPassApFl) || cmAllFlags(icp->fl,kPassApFl) )
- {
- memcpy( oBufArray[i], iBufArray[i], byteCnt );
-
- // set the output buffer to NULL to prevent it being over written by the client
- oBufArray[i] = NULL;
- }
- else
- {
- // zero the output buffer
- memset(oBufArray[i],0,byteCnt);
- }
- }
- }
- }
-
- if( oDevIdx != cmInvalidIdx )
- {
- const cmApIO* op = _cmApBuf.devArray[oDevIdx].ioArray + kOutApIdx;
- unsigned byteCnt = op->dspFrameCnt * sizeof(cmApSample_t);
-
- _cmApBufCalcTimeStamp(op->srate, &op->timeStamp, op->ioFrameCnt, oTimeStampPtr );
-
- for(; i<oBufChCnt; ++i)
- if( oBufArray[i] != NULL )
- memset( oBufArray[i], 0, byteCnt );
- }
-
- }
-
- void cmApBufAdvance( unsigned devIdx, unsigned flags )
- {
- unsigned i;
-
- if( devIdx == cmInvalidIdx )
- return;
-
- if( flags & kInApFl )
- {
- cmApIO* ioPtr = _cmApBuf.devArray[devIdx].ioArray + kInApIdx;
-
- for(i=0; i<ioPtr->chCnt; ++i)
- {
- cmApCh* cp = ioPtr->chArray + i;
- cp->oi = (cp->oi + ioPtr->dspFrameCnt) % ioPtr->n;
- cmThUIntDecr(&cp->fn,ioPtr->dspFrameCnt);
- }
-
- // count the number of samples input from this device
- if( ioPtr->timeStamp.tv_sec!=0 && ioPtr->timeStamp.tv_nsec!=0 )
- cmThUIntIncr(&ioPtr->ioFrameCnt,ioPtr->dspFrameCnt);
-
- }
-
- if( flags & kOutApFl )
- {
- cmApIO* ioPtr = _cmApBuf.devArray[devIdx].ioArray + kOutApIdx;
- for(i=0; i<ioPtr->chCnt; ++i)
- {
- cmApCh* cp = ioPtr->chArray + i;
- cp->ii = (cp->ii + ioPtr->dspFrameCnt) % ioPtr->n;
- cmThUIntIncr(&cp->fn,ioPtr->dspFrameCnt);
- }
-
- // count the number of samples output from this device
- if( ioPtr->timeStamp.tv_sec!=0 && ioPtr->timeStamp.tv_nsec!=0 )
- cmThUIntIncr(&ioPtr->ioFrameCnt,ioPtr->dspFrameCnt);
- }
- }
-
-
- void cmApBufInputToOutput( unsigned iDevIdx, unsigned oDevIdx )
- {
- if( iDevIdx == cmInvalidIdx || oDevIdx == cmInvalidIdx )
- return;
-
- unsigned iChCnt = cmApBufChannelCount( iDevIdx, kInApFl );
- unsigned oChCnt = cmApBufChannelCount( oDevIdx, kOutApFl );
- unsigned chCnt = iChCnt < oChCnt ? iChCnt : oChCnt;
-
- unsigned i;
-
- cmApSample_t* iBufPtrArray[ iChCnt ];
- cmApSample_t* oBufPtrArray[ oChCnt ];
-
-
- while( cmApBufIsDeviceReady( iDevIdx, kInApFl ) && cmApBufIsDeviceReady( oDevIdx, kOutApFl ) )
- {
- cmApBufGet( iDevIdx, kInApFl, iBufPtrArray, iChCnt );
- cmApBufGet( oDevIdx, kOutApFl, oBufPtrArray, oChCnt );
-
- // Warning: buffer pointers to disabled channels will be set to NULL
-
- for(i=0; i<chCnt; ++i)
- {
- cmApIO* ip = _cmApBuf.devArray[iDevIdx ].ioArray + kInApIdx;
- cmApIO* op = _cmApBuf.devArray[oDevIdx].ioArray + kOutApIdx;
-
- assert( ip->dspFrameCnt == op->dspFrameCnt );
-
- unsigned byteCnt = ip->dspFrameCnt * sizeof(cmApSample_t);
-
-
- if( oBufPtrArray[i] != NULL )
- {
- // the input channel is not disabled
- if( iBufPtrArray[i]!=NULL )
- memcpy(oBufPtrArray[i],iBufPtrArray[i],byteCnt);
- else
- // the input channel is disabled but the output is not - so fill the output with zeros
- memset(oBufPtrArray[i],0,byteCnt);
- }
- }
-
- cmApBufAdvance( iDevIdx, kInApFl );
- cmApBufAdvance( oDevIdx, kOutApFl );
- }
-
- }
-
- void cmApBufReport( cmRpt_t* rpt )
- {
- unsigned i,j,k;
- for(i=0; i<_cmApBuf.devCnt; ++i)
- {
- cmRptPrintf(rpt,"%i ",i);
-
- for(j=0; j<kIoApCnt; ++j)
- {
- cmApIO* ip = _cmApBuf.devArray[i].ioArray + j;
-
- unsigned ii = 0;
- unsigned oi = 0;
- unsigned fn = 0;
- cmApSample_t mtr = 0;
- for(k=0; k<ip->chCnt; ++k)
- {
- cmApCh* cp = ip->chArray + i;
- ii += cp->ii;
- oi += cp->oi;
- fn += cp->fn;
- mtr += _cmApMeterValue(cp);
- }
-
- cmRptPrintf(rpt,"%s - i:%7i o:%7i f:%7i n:%7i err %s:%7i mtr:%5.4f ",
- j==0?"IN":"OUT",
- ii,oi,fn,ip->n, (j==0?"over":"under"), ip->faultCnt, mtr);
-
- }
-
- cmRptPrintf(rpt,"\n");
- }
- }
-
- /// [cmApBufExample]
-
- void cmApBufTest( cmRpt_t* rpt )
- {
- unsigned devIdx = 0;
- unsigned devCnt = 1 ;
- unsigned dspFrameCnt = 10;
- unsigned cycleCnt = 3;
- unsigned framesPerCycle = 25;
- unsigned inChCnt = 2;
- unsigned outChCnt = inChCnt;
- unsigned sigN = cycleCnt*framesPerCycle*inChCnt;
- double srate = 44100.0;
- unsigned meterMs = 50;
- int srateMult = 1;
-
- unsigned bufChCnt= inChCnt;
- cmApSample_t* inBufArray[ bufChCnt ];
- cmApSample_t* outBufArray[ bufChCnt ];
- cmApSample_t iSig[ sigN ];
- cmApSample_t oSig[ sigN ];
- cmApSample_t* os = oSig;
- cmApAudioPacket_t pkt;
- int i,j;
-
- // create a simulated signal
- for(i=0; i<sigN; ++i)
- {
- iSig[i] = i;
- oSig[i] = 0;
- }
-
- pkt.devIdx = 0;
- pkt.begChIdx = 0;
- pkt.chCnt = inChCnt;
- pkt.audioFramesCnt = framesPerCycle;
- pkt.bitsPerSample = 32;
- pkt.flags = 0;
-
-
- // initialize a the audio buffer
- cmApBufInitialize(devCnt,meterMs);
-
- // setup the buffer with the specific parameters to by used by the host audio ports
- cmApBufSetup(devIdx,srate,dspFrameCnt,cycleCnt,inChCnt,framesPerCycle,outChCnt,framesPerCycle, srateMult);
-
- // simulate cylcing through sigN buffer transactions
- for(i=0; i<sigN; i+=framesPerCycle*inChCnt)
- {
- // setup an incoming audio packet
- pkt.audioFramesCnt = framesPerCycle;
- pkt.audioBytesPtr = iSig+i;
-
- // simulate a call from the audio port with incoming samples
- // (fill the audio buffers internal input buffers)
- cmApBufUpdate(&pkt,1,NULL,0);
-
- // if all devices need to be serviced
- while( cmApBufIsDeviceReady( devIdx, kInApFl | kOutApFl ))
- {
- // get pointers to full internal input buffers
- cmApBufGet(devIdx, kInApFl, inBufArray, bufChCnt );
-
- // get pointers to empty internal output buffers
- cmApBufGet(devIdx, kOutApFl, outBufArray, bufChCnt );
-
- // Warning: pointers to disabled channels will be set to NULL.
-
- // simulate a play through by copying the incoming samples to the outgoing buffers.
- for(j=0; j<bufChCnt; ++j)
- if( outBufArray[j] != NULL )
- {
- // if the input is disabled - but the output is not then zero the output buffer
- if( inBufArray[j] == NULL )
- memset( outBufArray[j], 0, dspFrameCnt*sizeof(cmApSample_t));
- else
- // copy the input to the output
- memcpy( outBufArray[j], inBufArray[j], dspFrameCnt*sizeof(cmApSample_t));
- }
-
- // signal the buffer that this cycle is complete.
- // (marks current internal input/output buffer empty/full)
- cmApBufAdvance( devIdx, kInApFl | kOutApFl );
- }
-
- pkt.audioBytesPtr = os;
-
- // simulate a call from the audio port picking up outgoing samples
- // (empties the audio buffers internal output buffers)
- cmApBufUpdate(NULL,0,&pkt,1);
-
- os += pkt.audioFramesCnt * pkt.chCnt;
- }
-
- for(i=0; i<sigN; ++i)
- cmRptPrintf(rpt,"%f ",oSig[i]);
- cmRptPrintf(rpt,"\n");
-
- cmApBufFinalize();
- }
-
- /// [cmApBufExample]
-
|