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.
This commit is contained in:
kevin 2013-12-15 18:28:11 -05:00
parent 2080d13eb2
commit 40a649dfc1
2 changed files with 85 additions and 24 deletions

View File

@ -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"
@ -73,6 +74,8 @@ typedef struct
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;
@ -176,6 +179,9 @@ void _cmApIoInitialize( cmApIO* ioPtr, double srate, unsigned framesPerCycle, un
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; i<chCnt; ++i )
_cmApChInitialize( ioPtr->chArray + 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; j<pp->chCnt; ++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; j<pp->chCnt; ++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 );
@ -652,6 +702,9 @@ void cmApBufGetIO( unsigned iDevIdx, cmApSample_t* iBufArray[], unsigned iBufC
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;
@ -681,6 +734,8 @@ void cmApBufGetIO( unsigned iDevIdx, cmApSample_t* iBufArray[], unsigned iBufC
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 );
@ -703,12 +758,13 @@ void cmApBufAdvance( unsigned devIdx, unsigned flags )
{
cmApCh* cp = ioPtr->chArray + 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);
}
}

View File

@ -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.