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 "cmErr.h"
#include "cmMem.h" #include "cmMem.h"
#include "cmMallocDebug.h" #include "cmMallocDebug.h"
#include "cmTime.h"
#include "cmAudioPort.h" #include "cmAudioPort.h"
#include "cmApBuf.h" #include "cmApBuf.h"
#include "cmThread.h" #include "cmThread.h"
@ -64,15 +65,17 @@ typedef struct
typedef struct typedef struct
{ {
unsigned chCnt; unsigned chCnt;
cmApCh* chArray; cmApCh* chArray;
unsigned n; // length of b[] (multiple of dspFrameCnt) bufCnt*framesPerCycle unsigned n; // length of b[] (multiple of dspFrameCnt) bufCnt*framesPerCycle
double srate; // device sample rate; double srate; // device sample rate;
unsigned faultCnt; unsigned faultCnt;
unsigned framesPerCycle; unsigned framesPerCycle;
unsigned dspFrameCnt; unsigned dspFrameCnt;
cmTimeSpec_t timeStamp; // base (starting) time stamp for this device
unsigned ioFrameCnt; // count of frames input or output for this device
} cmApIO; } 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 n += (n % dspFrameCnt); // force buffer size to be a multiple of dspFrameCnt
ioPtr->chArray = chCnt==0 ? NULL : cmMemAllocZ( cmApCh, chCnt ); ioPtr->chArray = chCnt==0 ? NULL : cmMemAllocZ( cmApCh, chCnt );
ioPtr->chCnt = chCnt; ioPtr->chCnt = chCnt;
ioPtr->n = n; ioPtr->n = n;
ioPtr->faultCnt = 0; ioPtr->faultCnt = 0;
ioPtr->framesPerCycle = framesPerCycle; ioPtr->framesPerCycle = framesPerCycle;
ioPtr->srate = srate; ioPtr->srate = srate;
ioPtr->dspFrameCnt = dspFrameCnt; ioPtr->dspFrameCnt = dspFrameCnt;
ioPtr->timeStamp.tv_sec = 0;
ioPtr->timeStamp.tv_nsec = 0;
ioPtr->ioFrameCnt = 0;
for(i=0; i<chCnt; ++i ) for(i=0; i<chCnt; ++i )
_cmApChInitialize( ioPtr->chArray + i, n, meterBufN ); _cmApChInitialize( ioPtr->chArray + i, n, meterBufN );
@ -277,6 +283,24 @@ cmAbRC_t cmApBufPrimeOutput( unsigned devIdx, unsigned audioCycleCnt )
return kOkAbRC; 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( cmAbRC_t cmApBufUpdate(
cmApAudioPacket_t* inPktArray, cmApAudioPacket_t* inPktArray,
unsigned inPktCnt, unsigned inPktCnt,
@ -293,6 +317,10 @@ cmAbRC_t cmApBufUpdate(
cmApAudioPacket_t* pp = inPktArray + i; cmApAudioPacket_t* pp = inPktArray + i;
cmApIO* ip = _cmApBuf.devArray[pp->devIdx].ioArray + kInApIdx; // dest io recd 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 each source packet channel and enabled dest channel
for(j=0; j<pp->chCnt; ++j) for(j=0; j<pp->chCnt; ++j)
{ {
@ -369,6 +397,9 @@ cmAbRC_t cmApBufUpdate(
cmApAudioPacket_t* pp = outPktArray + i; cmApAudioPacket_t* pp = outPktArray + i;
cmApIO* op = _cmApBuf.devArray[pp->devIdx].ioArray + kOutApIdx; // dest io recd 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 each dest packet channel and enabled source channel
for(j=0; j<pp->chCnt; ++j) 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( iDevIdx, kInApFl, iBufArray, iBufChCnt );
cmApBufGet( oDevIdx, kOutApFl,oBufArray, oBufChCnt ); 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 frmCnt = cmMin(ip->dspFrameCnt,op->dspFrameCnt);
unsigned byteCnt = frmCnt * sizeof(cmApSample_t); 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) for(i=0; i<minChCnt; ++i)
{ {
cmApCh* ocp = op->chArray + 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; const cmApIO* op = _cmApBuf.devArray[oDevIdx].ioArray + kOutApIdx;
unsigned byteCnt = op->dspFrameCnt * sizeof(cmApSample_t); unsigned byteCnt = op->dspFrameCnt * sizeof(cmApSample_t);
_cmApBufCalcTimeStamp(op->srate, &op->timeStamp, op->ioFrameCnt, oTimeStampPtr );
for(; i<oBufChCnt; ++i) for(; i<oBufChCnt; ++i)
if( oBufArray[i] != NULL ) if( oBufArray[i] != NULL )
memset( oBufArray[i], 0, byteCnt ); memset( oBufArray[i], 0, byteCnt );
@ -703,12 +758,13 @@ void cmApBufAdvance( unsigned devIdx, unsigned flags )
{ {
cmApCh* cp = ioPtr->chArray + i; cmApCh* cp = ioPtr->chArray + i;
cp->oi = (cp->oi + ioPtr->dspFrameCnt) % ioPtr->n; cp->oi = (cp->oi + ioPtr->dspFrameCnt) % ioPtr->n;
//cp->fn -= ioPtr->dspFrameCnt;
cmThUIntDecr(&cp->fn,ioPtr->dspFrameCnt); cmThUIntDecr(&cp->fn,ioPtr->dspFrameCnt);
} }
//ioPtr->oi = (ioPtr->oi + ioPtr->dspFrameCnt) % ioPtr->n; // count the number of samples input from this device
//ioPtr->fn -= ioPtr->dspFrameCnt; if( ioPtr->timeStamp.tv_sec!=0 && ioPtr->timeStamp.tv_nsec!=0 )
cmThUIntIncr(&ioPtr->ioFrameCnt,ioPtr->dspFrameCnt);
} }
if( flags & kOutApFl ) if( flags & kOutApFl )
@ -718,13 +774,12 @@ void cmApBufAdvance( unsigned devIdx, unsigned flags )
{ {
cmApCh* cp = ioPtr->chArray + i; cmApCh* cp = ioPtr->chArray + i;
cp->ii = (cp->ii + ioPtr->dspFrameCnt) % ioPtr->n; cp->ii = (cp->ii + ioPtr->dspFrameCnt) % ioPtr->n;
//cp->fn += ioPtr->dspFrameCnt;
cmThUIntIncr(&cp->fn,ioPtr->dspFrameCnt); cmThUIntIncr(&cp->fn,ioPtr->dspFrameCnt);
} }
// count the number of samples output from this device
//ioPtr->ii = (ioPtr->ii + ioPtr->dspFrameCnt) % ioPtr->n; if( ioPtr->timeStamp.tv_sec!=0 && ioPtr->timeStamp.tv_nsec!=0 )
//ioPtr->fn += ioPtr->dspFrameCnt; 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 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 ); 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 /// 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 /// 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). /// 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. /// 2) The client is required to use this function to implement pass-through internally.
/// 3) This function just returns audio information it does not /// 3) This function just returns audio information it does not
/// change any cmApBuf() internal states. /// 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[] /// 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. /// returned from cmApBufGet(). 'flags' can be set to either or both kInApFl and kOutApFl.