jamulus/src/channel.cpp

615 lines
17 KiB
C++
Raw Normal View History

2006-01-28 12:29:22 +01:00
/******************************************************************************\
* Copyright (c) 2004-2006
*
* Author(s):
* Volker Fischer
*
******************************************************************************
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation; either version 2 of the License, or (at your option) any later
* version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
\******************************************************************************/
#include "channel.h"
/******************************************************************************\
* CChannelSet *
2006-03-04 12:11:26 +01:00
\******************************************************************************/
CChannelSet::CChannelSet()
{
// make sure we have MAX_NUM_CHANNELS connections!!!
// send message
QObject::connect(&vecChannels[0],SIGNAL(MessReadyForSending(CVector<uint8_t>)),this,SLOT(OnSendProtMessCh0(CVector<uint8_t>)));
QObject::connect(&vecChannels[1],SIGNAL(MessReadyForSending(CVector<uint8_t>)),this,SLOT(OnSendProtMessCh1(CVector<uint8_t>)));
QObject::connect(&vecChannels[2],SIGNAL(MessReadyForSending(CVector<uint8_t>)),this,SLOT(OnSendProtMessCh2(CVector<uint8_t>)));
QObject::connect(&vecChannels[3],SIGNAL(MessReadyForSending(CVector<uint8_t>)),this,SLOT(OnSendProtMessCh3(CVector<uint8_t>)));
QObject::connect(&vecChannels[4],SIGNAL(MessReadyForSending(CVector<uint8_t>)),this,SLOT(OnSendProtMessCh4(CVector<uint8_t>)));
QObject::connect(&vecChannels[5],SIGNAL(MessReadyForSending(CVector<uint8_t>)),this,SLOT(OnSendProtMessCh5(CVector<uint8_t>)));
QObject::connect(&vecChannels[6],SIGNAL(MessReadyForSending(CVector<uint8_t>)),this,SLOT(OnSendProtMessCh6(CVector<uint8_t>)));
QObject::connect(&vecChannels[7],SIGNAL(MessReadyForSending(CVector<uint8_t>)),this,SLOT(OnSendProtMessCh7(CVector<uint8_t>)));
QObject::connect(&vecChannels[8],SIGNAL(MessReadyForSending(CVector<uint8_t>)),this,SLOT(OnSendProtMessCh8(CVector<uint8_t>)));
QObject::connect(&vecChannels[9],SIGNAL(MessReadyForSending(CVector<uint8_t>)),this,SLOT(OnSendProtMessCh9(CVector<uint8_t>)));
2006-03-04 12:11:26 +01:00
// request jitter buffer size
QObject::connect(&vecChannels[0],SIGNAL(NewConnection()),this,SLOT(OnNewConnectionCh0()));
QObject::connect(&vecChannels[1],SIGNAL(NewConnection()),this,SLOT(OnNewConnectionCh1()));
QObject::connect(&vecChannels[2],SIGNAL(NewConnection()),this,SLOT(OnNewConnectionCh2()));
QObject::connect(&vecChannels[3],SIGNAL(NewConnection()),this,SLOT(OnNewConnectionCh3()));
QObject::connect(&vecChannels[4],SIGNAL(NewConnection()),this,SLOT(OnNewConnectionCh4()));
QObject::connect(&vecChannels[5],SIGNAL(NewConnection()),this,SLOT(OnNewConnectionCh5()));
QObject::connect(&vecChannels[6],SIGNAL(NewConnection()),this,SLOT(OnNewConnectionCh6()));
QObject::connect(&vecChannels[7],SIGNAL(NewConnection()),this,SLOT(OnNewConnectionCh7()));
QObject::connect(&vecChannels[8],SIGNAL(NewConnection()),this,SLOT(OnNewConnectionCh8()));
QObject::connect(&vecChannels[9],SIGNAL(NewConnection()),this,SLOT(OnNewConnectionCh9()));
2006-03-04 12:12:47 +01:00
}
2006-03-04 12:11:26 +01:00
2006-01-28 12:29:22 +01:00
int CChannelSet::GetFreeChan()
{
/* look for a free channel */
2006-02-17 22:08:05 +01:00
for ( int i = 0; i < MAX_NUM_CHANNELS; i++ )
2006-01-28 12:29:22 +01:00
{
if ( !vecChannels[i].IsConnected() )
2006-02-17 22:08:05 +01:00
{
return i;
}
2006-01-28 12:29:22 +01:00
}
/* no free channel found, return invalid ID */
return INVALID_CHANNEL_ID;
}
2006-02-17 22:08:05 +01:00
int CChannelSet::CheckAddr ( const CHostAddress& Addr )
2006-01-28 12:29:22 +01:00
{
CHostAddress InetAddr;
/* Check for all possible channels if IP is already in use */
2006-02-17 22:08:05 +01:00
for ( int i = 0; i < MAX_NUM_CHANNELS; i++ )
2006-01-28 12:29:22 +01:00
{
2006-02-17 22:08:05 +01:00
if ( vecChannels[i].GetAddress ( InetAddr ) )
2006-01-28 12:29:22 +01:00
{
/* IP found, return channel number */
2006-02-17 22:08:05 +01:00
if ( InetAddr == Addr )
{
return i;
}
2006-01-28 12:29:22 +01:00
}
}
/* IP not found, return invalid ID */
return INVALID_CHANNEL_ID;
}
2006-02-17 22:08:05 +01:00
bool CChannelSet::PutData ( const CVector<unsigned char>& vecbyRecBuf,
const int iNumBytesRead,
const CHostAddress& HostAdr )
2006-01-28 12:29:22 +01:00
{
2006-03-07 23:06:45 +01:00
bool bRet = false;
2006-03-06 18:04:07 +01:00
Mutex.lock();
2006-01-28 12:29:22 +01:00
{
2006-03-07 23:06:45 +01:00
bool bChanOK = true;
2006-03-06 18:04:07 +01:00
/* get channel ID --------------------------------------------------- */
/* check address */
int iCurChanID = CheckAddr ( HostAdr );
2006-01-28 12:29:22 +01:00
2006-03-06 18:04:07 +01:00
if ( iCurChanID == INVALID_CHANNEL_ID )
{
2006-03-06 18:04:07 +01:00
/* a new client is calling, look for free channel */
iCurChanID = GetFreeChan();
2006-03-06 18:04:07 +01:00
if ( iCurChanID != INVALID_CHANNEL_ID )
{
vecChannels[iCurChanID].SetAddress ( HostAdr );
}
else
{
/* no free channel available */
2006-03-07 23:06:45 +01:00
bChanOK = false;
2006-03-06 18:04:07 +01:00
}
}
2006-01-28 12:29:22 +01:00
2006-03-06 18:04:07 +01:00
/* put received data in jitter buffer ------------------------------- */
if ( bChanOK )
{
2006-03-06 18:04:07 +01:00
/* put packet in socket buffer */
switch ( vecChannels[iCurChanID].PutData ( vecbyRecBuf, iNumBytesRead ) )
{
case PS_AUDIO_OK:
PostWinMessage ( MS_JIT_BUF_PUT, MUL_COL_LED_GREEN, iCurChanID );
2006-03-07 23:06:45 +01:00
bRet = true; // in case we have an audio packet, return true
2006-03-06 18:04:07 +01:00
break;
case PS_AUDIO_ERR:
PostWinMessage ( MS_JIT_BUF_PUT, MUL_COL_LED_RED, iCurChanID );
2006-03-07 23:06:45 +01:00
bRet = true; // in case we have an audio packet, return true
2006-03-06 18:04:07 +01:00
break;
case PS_PROT_ERR:
PostWinMessage ( MS_JIT_BUF_PUT, MUL_COL_LED_YELLOW, iCurChanID );
break;
}
}
2006-01-28 12:29:22 +01:00
}
Mutex.unlock();
2006-01-28 12:29:22 +01:00
2006-03-07 23:06:45 +01:00
return bRet;
2006-01-28 12:29:22 +01:00
}
2006-02-17 22:08:05 +01:00
void CChannelSet::GetBlockAllConC ( CVector<int>& vecChanID,
CVector<CVector<double> >& vecvecdData )
2006-01-28 12:29:22 +01:00
{
/* init temporal data vector and clear input buffers */
2006-02-17 22:08:05 +01:00
CVector<double> vecdData ( MIN_BLOCK_SIZE_SAMPLES );
2006-01-28 12:29:22 +01:00
2006-02-17 22:08:05 +01:00
vecChanID.Init ( 0 );
vecvecdData.Init ( 0 );
2006-01-28 12:29:22 +01:00
/* make put and get calls thread safe. Do not forget to unlock mutex
afterwards! */
Mutex.lock();
2006-01-28 12:29:22 +01:00
{
2006-03-06 18:04:07 +01:00
/* Check all possible channels */
for ( int i = 0; i < MAX_NUM_CHANNELS; i++ )
2006-01-28 12:29:22 +01:00
{
2006-03-06 18:04:07 +01:00
/* read out all input buffers to decrease timeout counter on
disconnected channels */
const bool bGetOK = vecChannels[i].GetData ( vecdData );
2006-01-28 12:29:22 +01:00
if ( vecChannels[i].IsConnected() )
2006-02-17 22:08:05 +01:00
{
2006-03-06 18:04:07 +01:00
/* add ID and data */
vecChanID.Add ( i );
const int iOldSize = vecvecdData.Size();
2006-03-06 18:04:07 +01:00
vecvecdData.Enlarge ( 1 );
vecvecdData[iOldSize].Init ( vecdData.Size() );
2006-03-06 18:04:07 +01:00
vecvecdData[iOldSize] = vecdData;
/* send message for get status (for GUI) */
if ( bGetOK )
{
PostWinMessage ( MS_JIT_BUF_GET, MUL_COL_LED_GREEN, i );
}
else
{
PostWinMessage ( MS_JIT_BUF_GET, MUL_COL_LED_RED, i );
}
2006-02-17 22:08:05 +01:00
}
2006-01-28 12:29:22 +01:00
}
}
Mutex.unlock(); /* release mutex */
2006-01-28 12:29:22 +01:00
}
2006-02-17 22:08:05 +01:00
void CChannelSet::GetConCliParam ( CVector<CHostAddress>& vecHostAddresses,
CVector<int>& veciJitBufSize )
2006-01-28 12:29:22 +01:00
{
CHostAddress InetAddr;
/* init return values */
2006-02-17 22:08:05 +01:00
vecHostAddresses.Init ( MAX_NUM_CHANNELS );
veciJitBufSize.Init ( MAX_NUM_CHANNELS );
2006-01-28 12:29:22 +01:00
/* Check all possible channels */
2006-02-17 22:08:05 +01:00
for ( int i = 0; i < MAX_NUM_CHANNELS; i++ )
2006-01-28 12:29:22 +01:00
{
2006-02-17 22:08:05 +01:00
if ( vecChannels[i].GetAddress ( InetAddr ) )
2006-01-28 12:29:22 +01:00
{
/* add new address and sample rate offset to vectors */
vecHostAddresses[i] = InetAddr;
veciJitBufSize[i] = vecChannels[i].GetSockBufSize ();
2006-01-28 12:29:22 +01:00
}
}
}
/******************************************************************************\
* CChannel *
\******************************************************************************/
CChannel::CChannel()
{
// query all possible network in buffer sizes for determining if an
// audio packet was received
for ( int i = 0; i < ( NET_BLOCK_SIZE_FACTOR_MAX - 1 ); i++ )
{
// network block size factor must start from 1 -> ( i + 1 )
vecNetwInBufSizes[i] = AudioCompressionIn.Init (
( i + 1 ) * MIN_BLOCK_SIZE_SAMPLES,
CAudioCompression::CT_IMAADPCM );
}
/* init time stamp index counter */
2006-03-12 13:39:25 +01:00
byTimeStampIdxCnt = 0;
iCurNetwInBlSiFact = NET_BLOCK_SIZE_FACTOR;
/* init the socket buffer */
SetSockBufSize ( DEF_NET_BUF_SIZE_NUM_BL );
2006-01-28 12:29:22 +01:00
// set initial input and output block size factors
SetNetwBufSizeFact ( NET_BLOCK_SIZE_FACTOR );
/* init time-out for the buffer with zero -> no connection */
iConTimeOut = 0;
2006-02-26 11:50:47 +01:00
/* connections ---------------------------------------------------------- */
2006-03-01 20:46:44 +01:00
QObject::connect ( &Protocol,
SIGNAL ( MessReadyForSending ( CVector<uint8_t> ) ),
2006-03-04 11:24:40 +01:00
this, SLOT ( OnSendProtMessage ( CVector<uint8_t> ) ) );
QObject::connect ( &Protocol, SIGNAL ( ChangeJittBufSize ( int ) ),
this, SLOT ( OnJittBufSizeChange ( int ) ) );
QObject::connect ( &Protocol, SIGNAL ( ReqJittBufSize() ),
SIGNAL ( ReqJittBufSize() ) );
QObject::connect ( &Protocol, SIGNAL ( ChangeNetwBlSiFact ( int ) ),
this, SLOT ( OnNetwBlSiFactChange ( int ) ) );
2006-02-26 11:50:47 +01:00
}
2006-03-04 11:24:40 +01:00
void CChannel::SetNetwInBlSiFact ( const int iNewBlockSizeFactor )
{
// store new value
iCurNetwInBlSiFact = iNewBlockSizeFactor;
/* init audio compression unit */
iAudComprSizeIn = AudioCompressionIn.Init (
iNewBlockSizeFactor * MIN_BLOCK_SIZE_SAMPLES,
CAudioCompression::CT_IMAADPCM );
// initial value for connection time out counter
iConTimeOutStartVal = ( CON_TIME_OUT_SEC_MAX * 1000 ) /
2006-03-12 12:02:16 +01:00
( iNewBlockSizeFactor * MIN_BLOCK_DURATION_MS );
// socket buffer must be adjusted
SetSockBufSize ( GetSockBufSize() );
}
void CChannel::SetNetwOutBlSiFact ( const int iNewBlockSizeFactor )
{
/* init audio compression unit */
iAudComprSizeOut = AudioCompressionOut.Init (
iNewBlockSizeFactor * MIN_BLOCK_SIZE_SAMPLES,
CAudioCompression::CT_IMAADPCM );
/* init conversion buffer */
ConvBuf.Init ( iNewBlockSizeFactor * MIN_BLOCK_SIZE_SAMPLES );
}
2006-03-04 11:24:40 +01:00
void CChannel::OnSendProtMessage ( CVector<uint8_t> vecMessage )
{
// only send messages if we are connected, otherwise delete complete queue
if ( IsConnected() )
{
2006-03-04 11:24:40 +01:00
// emit message to actually send the data
emit MessReadyForSending ( vecMessage );
}
else
{
// delete send message queue
Protocol.DeleteSendMessQueue();
}
2006-03-04 11:24:40 +01:00
}
// socket buffer size
void CChannel::SetSockBufSize ( const int iNumBlocks )
2006-01-28 12:29:22 +01:00
{
2006-02-27 21:27:47 +01:00
/* this opperation must be done with mutex */
Mutex.lock();
{
iCurSockBufSize = iNumBlocks;
// the idea of setting the jitter buffer is as follows:
// The network block size is a multiple of the internal minimal
// block size. Therefore, the minimum jitter buffer size must be
// so that it can take one network buffer -> NET_BLOCK_SIZE_FACTOR.
// The actual jitter compensation are then the additional blocks of
// the internal block size, which is set with SetSockBufSize
SockBuf.Init ( MIN_BLOCK_SIZE_SAMPLES,
iNumBlocks + iCurNetwInBlSiFact );
2006-03-06 18:04:07 +01:00
}
Mutex.unlock();
2006-01-28 12:29:22 +01:00
}
void CChannel::OnJittBufSizeChange ( int iNewJitBufSize )
{
2006-03-01 20:46:44 +01:00
// TEST
qDebug ( "new jitter buffer size: %d", iNewJitBufSize );
SetSockBufSize ( iNewJitBufSize );
}
// network buffer size factor
void CChannel::SetNetwBufSizeFact ( const int iNetNetwBlSiFact )
{
iCurNetwBlSiFact = iNetNetwBlSiFact;
SetNetwInBlSiFact ( iNetNetwBlSiFact );
SetNetwOutBlSiFact ( iNetNetwBlSiFact );
}
void CChannel::OnNetwBlSiFactChange ( int iNewNetwBlSiFact )
{
// TEST
qDebug ( "new network block size factor: %d", iNewNetwBlSiFact );
SetNetwBufSizeFact ( iNewNetwBlSiFact );
}
2006-01-28 12:29:22 +01:00
bool CChannel::GetAddress(CHostAddress& RetAddr)
{
if (IsConnected())
{
RetAddr = InetAddr;
return true;
}
else
{
RetAddr = CHostAddress();
return false;
}
}
2006-03-06 18:04:07 +01:00
EPutDataStat CChannel::PutData ( const CVector<unsigned char>& vecbyData,
int iNumBytes )
2006-01-28 12:29:22 +01:00
{
EPutDataStat eRet = PS_GEN_ERROR;
bool bNewConnection = false;
bool bIsAudioPacket = false;
// check if this is an audio packet by checking all possible lengths
for ( int i = 0; i < ( NET_BLOCK_SIZE_FACTOR_MAX - 1 ); i++ )
{
if ( iNumBytes == vecNetwInBufSizes[i] )
{
bIsAudioPacket = true;
// check if we are correctly initialized
if ( iAudComprSizeIn != vecNetwInBufSizes[i] )
{
// re-initialize to new value
SetNetwBufSizeFact ( i + 1 );
}
}
}
2006-01-28 12:29:22 +01:00
/* only process if packet has correct size */
if ( bIsAudioPacket )
2006-01-28 12:29:22 +01:00
{
Mutex.lock();
{
/* decompress audio */
CVector<short> vecsDecomprAudio ( AudioCompressionIn.Decode ( vecbyData ) );
2006-01-28 12:29:22 +01:00
/* do resampling to compensate for sample rate offsets in the
different sound cards of the clients */
2006-01-28 12:29:22 +01:00
/*
for (int i = 0; i < BLOCK_SIZE_SAMPLES; i++)
vecdResInData[i] = (double) vecsData[i];
const int iInSize = ResampleObj.Resample(vecdResInData, vecdResOutData,
(double) SAMPLE_RATE / (SAMPLE_RATE - dSamRateOffset));
*/
vecdResOutData.Init ( iCurNetwInBlSiFact * MIN_BLOCK_SIZE_SAMPLES );
for ( int i = 0; i < iCurNetwInBlSiFact * MIN_BLOCK_SIZE_SAMPLES; i++ ) {
vecdResOutData[i] = (double) vecsDecomprAudio[i];
}
2006-01-28 12:29:22 +01:00
2006-03-06 18:04:07 +01:00
if ( SockBuf.Put ( vecdResOutData ) )
{
eRet = PS_AUDIO_OK;
}
else
{
eRet = PS_AUDIO_ERR;
}
2006-03-08 22:18:20 +01:00
// check if channel was not connected, this is a new connection
bNewConnection = !IsConnected();
2006-01-28 12:29:22 +01:00
// reset time-out counter
iConTimeOut = iConTimeOutStartVal;
2006-03-06 18:04:07 +01:00
}
Mutex.unlock();
2006-01-28 12:29:22 +01:00
}
else
{
// only use protocol data if channel is connected
// must be disabled to be able to receive network buffer size factor changes
// FIXME check, if this condition must be checked somewhere else!
// if ( IsConnected() )
2006-03-06 18:04:07 +01:00
{
// this seems not to be an audio block, parse the message
if ( Protocol.ParseMessage ( vecbyData, iNumBytes ) )
{
eRet = PS_PROT_OK;
}
else
{
eRet = PS_PROT_ERR;
}
2006-03-06 18:04:07 +01:00
}
}
// inform other objects that new connection was established
if ( bNewConnection )
{
// TEST debug output
CHostAddress address ( GetAddress() );
qDebug ( "new connection with IP %s", address.InetAddr.toString().latin1() );
emit NewConnection();
}
2006-01-28 12:29:22 +01:00
2006-03-06 18:04:07 +01:00
return eRet;
2006-01-28 12:29:22 +01:00
}
2006-02-26 11:50:47 +01:00
bool CChannel::GetData ( CVector<double>& vecdData )
2006-03-06 18:04:07 +01:00
{
bool bGetOK = false;
2006-01-28 12:29:22 +01:00
Mutex.lock(); /* get mutex lock */
2006-01-28 12:29:22 +01:00
{
2006-03-06 18:04:07 +01:00
bGetOK = SockBuf.Get ( vecdData );
if ( !bGetOK )
2006-01-28 12:29:22 +01:00
{
2006-03-06 18:04:07 +01:00
/* decrease time-out counter */
if ( iConTimeOut > 0 )
{
iConTimeOut--;
2006-03-06 18:04:07 +01:00
}
2006-01-28 12:29:22 +01:00
}
}
Mutex.unlock(); /* get mutex unlock */
2006-01-28 12:29:22 +01:00
return bGetOK;
}
CVector<unsigned char> CChannel::PrepSendPacket ( const CVector<short>& vecsNPacket )
2006-01-28 12:29:22 +01:00
{
/* if the block is not ready we have to initialize with zero length to
tell the following network send routine that nothing should be sent */
CVector<unsigned char> vecbySendBuf ( 0 );
/* use conversion buffer to convert sound card block size in network
block size */
if ( ConvBuf.Put ( vecsNPacket ) )
{
/* a packet is ready, compress audio */
vecbySendBuf.Init ( iAudComprSizeOut );
vecbySendBuf = AudioCompressionOut.Encode ( ConvBuf.Get() );
}
2006-01-28 12:29:22 +01:00
return vecbySendBuf;
}
2006-03-04 11:24:40 +01:00
2006-01-28 12:29:22 +01:00
/******************************************************************************\
* CSampleOffsetEst *
\******************************************************************************/
void CSampleOffsetEst::Init()
{
/* init sample rate estimation */
dSamRateEst = SAMPLE_RATE;
/* init vectors storing the data */
veciTimeElapsed.Init(VEC_LEN_SAM_OFFS_EST);
veciTiStIdx.Init(VEC_LEN_SAM_OFFS_EST);
/* start reference time (the counter wraps to zero 24 hours after the last
call to start() or restart, but this should not concern us since this
software will most probably not be used that long) */
RefTime.start();
/* init accumulated time stamp variable */
iAccTiStVal = 0;
/* init count (do not ship any result in init phase) */
iInitCnt = VEC_LEN_SAM_OFFS_EST + 1;
}
void CSampleOffsetEst::AddTimeStampIdx(const int iTimeStampIdx)
{
int i;
const int iLastIdx = VEC_LEN_SAM_OFFS_EST - 1;
/* take care of wrap of the time stamp index (byte wrap) */
if (iTimeStampIdx < veciTiStIdx[iLastIdx] - iAccTiStVal)
iAccTiStVal += _MAXBYTE + 1;
/* add new data pair to the FIFO */
for (i = 1; i < VEC_LEN_SAM_OFFS_EST; i++)
{
/* move old data */
veciTimeElapsed[i - 1] = veciTimeElapsed[i];
veciTiStIdx[i - 1] = veciTiStIdx[i];
}
/* add new data */
veciTimeElapsed[iLastIdx] = RefTime.elapsed();
veciTiStIdx[iLastIdx] = iAccTiStVal + iTimeStampIdx;
/*
static FILE* pFile = fopen("v.dat", "w");
for (i = 0; i < VEC_LEN_SAM_OFFS_EST; i++)
fprintf(pFile, "%d\n", veciTimeElapsed[i]);
fflush(pFile);
*/
/* calculate linear regression for sample rate estimation */
/* first, calculate averages */
double dTimeAv = 0;
double dTiStAv = 0;
for (i = 0; i < VEC_LEN_SAM_OFFS_EST; i++)
{
dTimeAv += veciTimeElapsed[i];
dTiStAv += veciTiStIdx[i];
}
dTimeAv /= VEC_LEN_SAM_OFFS_EST;
dTiStAv /= VEC_LEN_SAM_OFFS_EST;
/* calculate gradient */
double dNom = 0;
double dDenom = 0;
for (i = 0; i < VEC_LEN_SAM_OFFS_EST; i++)
{
const double dCurTimeNoAv = veciTimeElapsed[i] - dTimeAv;
dNom += dCurTimeNoAv * (veciTiStIdx[i] - dTiStAv);
dDenom += dCurTimeNoAv * dCurTimeNoAv;
}
/* final sample rate offset estimation calculation */
if (iInitCnt > 0)
iInitCnt--;
else
{
2006-03-11 21:57:09 +01:00
dSamRateEst = dNom / dDenom * NUM_BL_TIME_STAMPS * MIN_BLOCK_SIZE_SAMPLES * 1000;
2006-01-28 12:29:22 +01:00
/*
static FILE* pFile = fopen("v.dat", "w");
for (i = 0; i < VEC_LEN_SAM_OFFS_EST; i++)
fprintf(pFile, "%d %d\n", veciTimeElapsed[i], veciTiStIdx[i]);
fflush(pFile);
*/
}
/*
static FILE* pFile = fopen("v.dat", "w");
fprintf(pFile, "%e\n", dSamRateEst);
fflush(pFile);
*/
}