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:
parent
2080d13eb2
commit
40a649dfc1
99
cmApBuf.c
99
cmApBuf.c
@ -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 );
|
||||||
@ -651,6 +701,9 @@ void cmApBufGetIO( unsigned iDevIdx, cmApSample_t* iBufArray[], unsigned iBufC
|
|||||||
unsigned minChCnt = cmMin(iBufChCnt,oBufChCnt);
|
unsigned minChCnt = cmMin(iBufChCnt,oBufChCnt);
|
||||||
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)
|
||||||
{
|
{
|
||||||
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
10
cmApBuf.h
10
cmApBuf.h
@ -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.
|
||||||
|
Loading…
Reference in New Issue
Block a user