From 40a649dfc14cf0bba1f4d32419857712b0bbdf12 Mon Sep 17 00:00:00 2001 From: kevin Date: Sun, 15 Dec 2013 18:28:11 -0500 Subject: [PATCH] cmApBuf.h/c : Added code to handle audio timestamps. cmApBufUpdate() sets an input/output timestamp on each device according to the audio packet timestamp. cmApBufGetIO() returns the timestamp associated with each returned buffer. Added cmApBufOnPortEnable() to zero the timestamp when a port is enabled. This implementation of the audio timestamping processes is a hack because only the first timestamp is stored (after a call to cmApBufOnPortEnable()) and all subsequent timestamps are generated by incrementing this value according to the incoming/outgoing sample count. --- cmApBuf.c | 99 ++++++++++++++++++++++++++++++++++++++++++------------- cmApBuf.h | 10 ++++-- 2 files changed, 85 insertions(+), 24 deletions(-) diff --git a/cmApBuf.c b/cmApBuf.c index 8262ebb..0858d78 100644 --- a/cmApBuf.c +++ b/cmApBuf.c @@ -4,6 +4,7 @@ #include "cmErr.h" #include "cmMem.h" #include "cmMallocDebug.h" +#include "cmTime.h" #include "cmAudioPort.h" #include "cmApBuf.h" #include "cmThread.h" @@ -64,15 +65,17 @@ typedef struct typedef struct { - unsigned chCnt; - cmApCh* chArray; + unsigned chCnt; + cmApCh* chArray; - unsigned n; // length of b[] (multiple of dspFrameCnt) bufCnt*framesPerCycle - double srate; // device sample rate; + unsigned n; // length of b[] (multiple of dspFrameCnt) bufCnt*framesPerCycle + double srate; // device sample rate; - unsigned faultCnt; - unsigned framesPerCycle; - unsigned dspFrameCnt; + unsigned faultCnt; + unsigned framesPerCycle; + unsigned dspFrameCnt; + cmTimeSpec_t timeStamp; // base (starting) time stamp for this device + unsigned ioFrameCnt; // count of frames input or output for this device } cmApIO; @@ -169,13 +172,16 @@ void _cmApIoInitialize( cmApIO* ioPtr, double srate, unsigned framesPerCycle, un 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 = n; - ioPtr->faultCnt = 0; - ioPtr->framesPerCycle = framesPerCycle; - ioPtr->srate = srate; - ioPtr->dspFrameCnt = dspFrameCnt; + ioPtr->chArray = chCnt==0 ? NULL : cmMemAllocZ( cmApCh, chCnt ); + ioPtr->chCnt = chCnt; + ioPtr->n = n; + ioPtr->faultCnt = 0; + ioPtr->framesPerCycle = framesPerCycle; + ioPtr->srate = srate; + ioPtr->dspFrameCnt = dspFrameCnt; + ioPtr->timeStamp.tv_sec = 0; + ioPtr->timeStamp.tv_nsec = 0; + ioPtr->ioFrameCnt = 0; for(i=0; ichArray + i, n, meterBufN ); @@ -277,6 +283,24 @@ cmAbRC_t cmApBufPrimeOutput( unsigned devIdx, unsigned 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, @@ -293,6 +317,10 @@ cmAbRC_t cmApBufUpdate( 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; jchCnt; ++j) { @@ -369,6 +397,9 @@ cmAbRC_t cmApBufUpdate( 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; jchCnt; ++j) @@ -637,7 +668,26 @@ void cmApBufGet( unsigned devIdx, unsigned flags, cmApSample_t* bufArray[], unsi } -void cmApBufGetIO( unsigned iDevIdx, cmApSample_t* iBufArray[], unsigned iBufChCnt, unsigned oDevIdx, cmApSample_t* oBufArray[], unsigned oBufChCnt ) +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 ); @@ -651,6 +701,9 @@ void cmApBufGetIO( unsigned iDevIdx, cmApSample_t* iBufArray[], unsigned iBufC 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; idspFrameCnt * sizeof(cmApSample_t); + _cmApBufCalcTimeStamp(op->srate, &op->timeStamp, op->ioFrameCnt, oTimeStampPtr ); + for(; ichArray + i; cp->oi = (cp->oi + ioPtr->dspFrameCnt) % ioPtr->n; - //cp->fn -= ioPtr->dspFrameCnt; cmThUIntDecr(&cp->fn,ioPtr->dspFrameCnt); } - //ioPtr->oi = (ioPtr->oi + ioPtr->dspFrameCnt) % ioPtr->n; - //ioPtr->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 ) @@ -718,13 +774,12 @@ void cmApBufAdvance( unsigned devIdx, unsigned flags ) { cmApCh* cp = ioPtr->chArray + i; cp->ii = (cp->ii + ioPtr->dspFrameCnt) % ioPtr->n; - //cp->fn += ioPtr->dspFrameCnt; cmThUIntIncr(&cp->fn,ioPtr->dspFrameCnt); } - - //ioPtr->ii = (ioPtr->ii + ioPtr->dspFrameCnt) % ioPtr->n; - //ioPtr->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); } } diff --git a/cmApBuf.h b/cmApBuf.h index ec016ce..3583d46 100644 --- a/cmApBuf.h +++ b/cmApBuf.h @@ -64,9 +64,12 @@ extern "C" { unsigned outFramesPerCycle ///< maximum number of outgoing sample frames in an audio port cycle ); - // Prime the buffer with 'audioCycleCnt' * outFramesPerCycle samples ready to be played + /// Prime the buffer with 'audioCycleCnt' * outFramesPerCycle samples ready to be played cmAbRC_t cmApBufPrimeOutput( unsigned devIdx, unsigned audioCycleCnt ); + /// Notify the audio buffer that a device is being enabled or disabled. + void cmApBufOnPortEnable( unsigned devIdx, bool enabelFl ); + /// This function is called asynchronously by the audio device driver to transfer incoming samples to the /// the buffer and to send outgoing samples to the DAC. This function is /// intended to be called from the audio port callback function (\see cmApCallbackPtr_t). @@ -204,7 +207,10 @@ extern "C" { /// 2) The client is required to use this function to implement pass-through internally. /// 3) This function just returns audio information it does not /// change any cmApBuf() internal states. - void cmApBufGetIO( unsigned iDevIdx, cmApSample_t* iBufArray[], unsigned iBufChCnt, unsigned oDevIdx, cmApSample_t* oBufArray[], unsigned oBufChCnt ); + /// 4) The timestamp pointers are optional. + void cmApBufGetIO( unsigned iDevIdx, cmApSample_t* iBufArray[], unsigned iBufChCnt, cmTimeSpec_t* iTimeStampPtr, + unsigned oDevIdx, cmApSample_t* oBufArray[], unsigned oBufChCnt, cmTimeSpec_t* oTimeStampPtr ); + /// The application calls this function each time it completes processing of a bufArray[] /// returned from cmApBufGet(). 'flags' can be set to either or both kInApFl and kOutApFl.