jamulus/src/buffer.cpp

434 lines
11 KiB
C++
Raw Normal View History

/******************************************************************************\
* Copyright (c) 2004-2008
*
* Author(s):
* Volker Fischer
2006-01-28 12:29:22 +01:00
*
* Note: we assuming here that put and get operations are secured by a mutex
* and do not take place at the same time
*
******************************************************************************
*
* 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 "buffer.h"
/* Implementation *************************************************************/
void CNetBuf::Init ( const int iNewBlockSize, const int iNewNumBlocks )
{
2007-12-31 14:09:12 +01:00
// total size -> size of one block times number of blocks
iBlockSize = iNewBlockSize;
iMemSize = iNewBlockSize * iNewNumBlocks;
2006-01-28 12:29:22 +01:00
2007-12-31 14:09:12 +01:00
// fade in first block added to the buffer
bFadeInNewPutData = true;
2007-12-31 14:09:12 +01:00
// allocate and clear memory for actual data buffer
2007-09-08 12:45:14 +02:00
vecdMemory.Init ( iMemSize );
2007-12-31 14:09:12 +01:00
// use the "get" flag to make sure the buffer is cleared
2007-09-08 12:45:14 +02:00
Clear ( CT_GET );
2007-12-31 14:09:12 +01:00
// initialize number of samples for fading effect
2007-09-08 12:45:14 +02:00
if ( FADE_IN_OUT_NUM_SAM < iBlockSize )
{
iNumSamFading = iBlockSize;
2007-09-08 12:45:14 +02:00
}
else
2007-09-08 12:45:14 +02:00
{
iNumSamFading = FADE_IN_OUT_NUM_SAM;
2007-09-08 12:45:14 +02:00
}
2007-09-08 12:45:14 +02:00
if ( FADE_IN_OUT_NUM_SAM_EXTRA > iBlockSize )
{
iNumSamFadingExtra = iBlockSize;
2007-09-08 12:45:14 +02:00
}
else
2007-09-08 12:45:14 +02:00
{
2008-08-11 20:30:37 +02:00
iNumSamFadingExtra = FADE_IN_OUT_NUM_SAM_EXTRA;
2007-09-08 12:45:14 +02:00
}
2007-12-31 14:09:12 +01:00
// init variables for extrapolation (in case a fade out is needed)
dExPDiff = 0.0;
dExPLastV = 0.0;
}
2007-09-08 12:45:14 +02:00
bool CNetBuf::Put ( CVector<double>& vecdData )
{
#ifdef _DEBUG_
static FILE* pFileBI = fopen("bufferin.dat", "w");
fprintf(pFileBI, "%d %d\n", GetAvailSpace() / iBlockSize, iMemSize / iBlockSize);
fflush(pFileBI);
#endif
bool bPutOK = true;
2007-12-31 14:09:12 +01:00
// get size of data to be added to the buffer
const int iInSize = vecdData.Size();
2007-12-31 14:09:12 +01:00
// Check if there is not enough space available -> correct
2007-09-08 12:45:14 +02:00
if ( GetAvailSpace() < iInSize )
{
2007-12-31 14:09:12 +01:00
// not enough space in buffer for put operation, correct buffer to
// prepare for new data
2007-09-08 12:45:14 +02:00
Clear ( CT_PUT );
2007-12-31 14:09:12 +01:00
// set flag to fade in new block to avoid clicks
bFadeInNewPutData = true;
2007-12-31 14:09:12 +01:00
bPutOK = false; // return error flag
}
2006-01-28 12:29:22 +01:00
2007-12-31 14:09:12 +01:00
// fade in new block if required
2007-09-08 12:45:14 +02:00
if ( bFadeInNewPutData )
{
FadeInAudioDataBlock ( vecdData );
}
2007-12-31 14:09:12 +01:00
// copy new data in internal buffer
int iCurPos = 0;
2007-09-08 12:45:14 +02:00
if ( iPutPos + iInSize > iMemSize )
{
2007-12-31 14:09:12 +01:00
// remaining space size for second block
const int iRemSpace = iPutPos + iInSize - iMemSize;
2007-12-31 14:09:12 +01:00
// data must be written in two steps because of wrap around
while (iPutPos < iMemSize)
2007-09-08 12:45:14 +02:00
{
vecdMemory[iPutPos++] = vecdData[iCurPos++];
2007-09-08 12:45:14 +02:00
}
2007-09-08 12:45:14 +02:00
for ( iPutPos = 0; iPutPos < iRemSpace; iPutPos++ )
{
vecdMemory[iPutPos] = vecdData[iCurPos++];
2007-09-08 12:45:14 +02:00
}
}
else
{
2007-12-31 14:09:12 +01:00
// data can be written in one step
const int iEnd = iPutPos + iInSize;
2007-09-08 12:45:14 +02:00
while ( iPutPos < iEnd )
{
vecdMemory[iPutPos++] = vecdData[iCurPos++];
2007-09-08 12:45:14 +02:00
}
}
2007-12-31 14:09:12 +01:00
// set buffer state flag
2007-09-08 12:45:14 +02:00
if ( iPutPos == iGetPos )
{
eBufState = CNetBuf::BS_FULL;
2007-09-08 12:45:14 +02:00
}
else
2007-09-08 12:45:14 +02:00
{
eBufState = CNetBuf::BS_OK;
2007-09-08 12:45:14 +02:00
}
return bPutOK;
}
2007-09-08 12:45:14 +02:00
bool CNetBuf::Get ( CVector<double>& vecdData )
{
2007-12-31 14:09:12 +01:00
bool bGetOK = true; // init return value
bool bFadeOutExtrap = false;
2006-01-28 12:29:22 +01:00
2007-12-31 14:09:12 +01:00
// get size of data to be get from the buffer
const int iInSize = vecdData.Size();
2007-12-31 14:09:12 +01:00
// Check if there is not enough data available -> correct
2007-09-08 12:45:14 +02:00
if ( GetAvailData() < iInSize )
{
2007-12-31 14:09:12 +01:00
// not enough data in buffer for get operation, correct buffer to
// prepare for getting data
2007-09-08 12:45:14 +02:00
Clear ( CT_GET );
2007-12-31 14:09:12 +01:00
// set flag to fade in next new block in buffer and fade out last
// block by extrapolation to avoid clicks
bFadeInNewPutData = true;
bFadeOutExtrap = true;
2007-12-31 14:09:12 +01:00
bGetOK = false; // return error flag
}
2007-12-31 14:09:12 +01:00
// copy data from internal buffer in output buffer
int iCurPos = 0;
2007-09-08 12:45:14 +02:00
if ( iGetPos + iInSize > iMemSize )
{
2007-12-31 14:09:12 +01:00
// remaining data size for second block
const int iRemData = iGetPos + iInSize - iMemSize;
2007-12-31 14:09:12 +01:00
// data must be read in two steps because of wrap around
2007-09-08 12:45:14 +02:00
while ( iGetPos < iMemSize )
{
vecdData[iCurPos++] = vecdMemory[iGetPos++];
2007-09-08 12:45:14 +02:00
}
2007-09-08 12:45:14 +02:00
for ( iGetPos = 0; iGetPos < iRemData; iGetPos++ )
{
vecdData[iCurPos++] = vecdMemory[iGetPos];
2007-09-08 12:45:14 +02:00
}
}
else
{
2007-12-31 14:09:12 +01:00
// data can be read in one step
const int iEnd = iGetPos + iInSize;
2007-09-08 12:45:14 +02:00
while ( iGetPos < iEnd )
{
vecdData[iCurPos++] = vecdMemory[iGetPos++];
2007-09-08 12:45:14 +02:00
}
}
2007-12-31 14:09:12 +01:00
// set buffer state flag
2007-09-08 12:45:14 +02:00
if ( iPutPos == iGetPos )
{
eBufState = CNetBuf::BS_EMPTY;
2007-09-08 12:45:14 +02:00
}
else
2007-09-08 12:45:14 +02:00
{
eBufState = CNetBuf::BS_OK;
2007-09-08 12:45:14 +02:00
}
/* extrapolate data from old block to avoid "clicks"
we have to do this method since we cannot fade out the old block
anymore since it is already gone (processed or send through the
network) */
2007-09-08 12:45:14 +02:00
if ( bFadeOutExtrap )
{
FadeOutExtrapolateAudioDataBlock ( vecdData, dExPDiff, dExPLastV );
}
/* save some paramters from last block which is needed in case we do not
have enough data for next "get" operation and need to extrapolate the
signal to avoid "clicks"
we assume here that "iBlockSize" is larger than 1! */
2007-09-08 12:45:14 +02:00
dExPDiff = vecdData[iInSize - 1] - vecdData[iInSize - 2];
dExPLastV = vecdData[iInSize - 1];
return bGetOK;
}
int CNetBuf::GetAvailSpace() const
{
2007-12-31 14:09:12 +01:00
// calculate available space in buffer
int iAvSpace = iGetPos - iPutPos;
2007-12-31 14:09:12 +01:00
// check for special case and wrap around
2007-09-08 12:45:14 +02:00
if ( iAvSpace < 0 )
{
2007-12-31 14:09:12 +01:00
iAvSpace += iMemSize; // wrap around
2007-09-08 12:45:14 +02:00
}
else
{
if ( ( iAvSpace == 0 ) && ( eBufState == BS_EMPTY ) )
{
iAvSpace = iMemSize;
}
}
return iAvSpace;
}
int CNetBuf::GetAvailData() const
{
2007-12-31 14:09:12 +01:00
// calculate available data in buffer
int iAvData = iPutPos - iGetPos;
2007-12-31 14:09:12 +01:00
// check for special case and wrap around
2007-09-08 12:45:14 +02:00
if ( iAvData < 0 )
{
2007-12-31 14:09:12 +01:00
iAvData += iMemSize; // wrap around
2007-09-08 12:45:14 +02:00
}
else
{
if ( ( iAvData == 0 ) && ( eBufState == BS_FULL ) )
{
iAvData = iMemSize;
}
}
return iAvData;
}
2007-09-08 12:45:14 +02:00
void CNetBuf::Clear ( const EClearType eClearType )
2006-01-28 12:29:22 +01:00
{
int iMiddleOfBuffer;
2006-01-28 12:29:22 +01:00
#if 0
/* with the following operation we set the new get pos to a block
boundary (one block below the middle of the buffer in case of odd
number of blocks, e.g.:
[buffer size]: [get pos]
1: 0 / 2: 0 / 3: 1 / 4: 1 / ... */
2007-09-08 12:45:14 +02:00
iMiddleOfBuffer = ( ( ( iMemSize - iBlockSize) / 2 ) / iBlockSize ) * iBlockSize;
2006-01-28 12:29:22 +01:00
#else
// old code
// somehow the old code seems to work better than the sophisticated new one....?
/* 1: 0 / 2: 1 / 3: 1 / 4: 2 / ... */
2007-09-08 12:45:14 +02:00
iMiddleOfBuffer = ( ( iMemSize / 2 ) / iBlockSize ) * iBlockSize;
2006-01-28 12:29:22 +01:00
#endif
2007-09-08 12:45:14 +02:00
2007-12-31 14:09:12 +01:00
// different behaviour for get and put corrections
2007-09-08 12:45:14 +02:00
if ( eClearType == CT_GET )
{
2007-12-31 14:09:12 +01:00
// clear buffer
2007-09-08 12:45:14 +02:00
vecdMemory.Reset ( 0.0 );
2007-12-31 14:09:12 +01:00
// correct buffer so that after the current get operation the pointer
// are at maximum distance
iPutPos = 0;
iGetPos = iMiddleOfBuffer;
/* check for special case */
2007-09-08 12:45:14 +02:00
if ( iPutPos == iGetPos )
{
eBufState = CNetBuf::BS_FULL;
2007-09-08 12:45:14 +02:00
}
else
2007-09-08 12:45:14 +02:00
{
eBufState = CNetBuf::BS_OK;
2007-09-08 12:45:14 +02:00
}
}
else
{
2007-12-31 14:09:12 +01:00
// in case of "put" correction, do not delete old data but only shift
// the pointers
iPutPos = iMiddleOfBuffer;
2007-12-31 14:09:12 +01:00
// adjust put pointer relative to current get pointer, take care of
// wrap around
iPutPos += iGetPos;
2007-09-08 12:45:14 +02:00
if ( iPutPos > iMemSize )
{
iPutPos -= iMemSize;
2007-09-08 12:45:14 +02:00
}
2007-12-31 14:09:12 +01:00
// fade out old data right before new put pointer
int iCurPos = iPutPos - iNumSamFading;
int i = iNumSamFading;
2007-09-08 12:45:14 +02:00
if ( iCurPos < 0 )
{
2007-12-31 14:09:12 +01:00
// wrap around
iCurPos += iMemSize;
2007-12-31 14:09:12 +01:00
// data must be processed in two steps because of wrap around
2007-09-08 12:45:14 +02:00
while ( iCurPos < iMemSize )
{
2007-09-08 12:45:14 +02:00
vecdMemory[iCurPos++] *= ( (double) i / iNumSamFading );
i--;
}
2007-09-08 12:45:14 +02:00
for ( iCurPos = 0; iCurPos < iPutPos; iCurPos++ )
{
2007-09-08 12:45:14 +02:00
vecdMemory[iCurPos] *= ( (double) i / iNumSamFading );
i--;
}
}
else
{
2007-12-31 14:09:12 +01:00
// data can be processed in one step
2007-09-08 12:45:14 +02:00
while ( iCurPos < iPutPos )
{
2007-09-08 12:45:14 +02:00
vecdMemory[iCurPos++] *= ( (double) i / iNumSamFading );
i--;
}
}
2007-12-31 14:09:12 +01:00
// check for special case
2007-09-08 12:45:14 +02:00
if ( iPutPos == iGetPos )
{
eBufState = CNetBuf::BS_EMPTY;
}
else
{
eBufState = CNetBuf::BS_OK;
}
}
}
2006-01-28 12:29:22 +01:00
2007-09-08 12:45:14 +02:00
void CNetBuf::FadeInAudioDataBlock ( CVector<double>& vecdData )
2006-01-28 12:29:22 +01:00
{
2007-12-31 14:09:12 +01:00
// apply linear fading
2007-09-08 12:45:14 +02:00
for ( int i = 0; i < iNumSamFading; i++ )
{
2007-09-08 12:45:14 +02:00
vecdData[i] *= ( (double) i / iNumSamFading );
}
2007-12-31 14:09:12 +01:00
// reset flag
bFadeInNewPutData = false;
2006-01-28 12:29:22 +01:00
}
2007-09-08 12:45:14 +02:00
void CNetBuf::FadeOutExtrapolateAudioDataBlock ( CVector<double>& vecdData,
const double dExPDiff,
const double dExPLastV )
2006-01-28 12:29:22 +01:00
{
2007-12-31 14:09:12 +01:00
// apply linear extrapolation and linear fading
2007-09-08 12:45:14 +02:00
for ( int i = 0; i < iNumSamFadingExtra; i++ )
{
2007-12-31 14:09:12 +01:00
// calculate extrapolated value
2007-09-08 12:45:14 +02:00
vecdData[i] = ( ( i + 1 ) * dExPDiff + dExPLastV );
2007-12-31 14:09:12 +01:00
// linear fading
2007-09-08 12:45:14 +02:00
vecdData[i] *= ( (double) ( iNumSamFadingExtra - i ) / iNumSamFadingExtra );
}
2006-01-28 12:29:22 +01:00
}
/* conversion buffer implementation *******************************************/
void CConvBuf::Init ( const int iNewMemSize )
{
2007-12-31 14:09:12 +01:00
// set memory size
iMemSize = iNewMemSize;
2006-01-28 12:29:22 +01:00
2007-12-31 14:09:12 +01:00
// allocate and clear memory for actual data buffer
2007-09-08 12:45:14 +02:00
vecsMemory.Init ( iMemSize );
2006-01-28 12:29:22 +01:00
iPutPos = 0;
2006-01-28 12:29:22 +01:00
}
2007-09-08 12:45:14 +02:00
bool CConvBuf::Put ( const CVector<short>& vecsData )
2006-01-28 12:29:22 +01:00
{
const int iVecSize = vecsData.Size();
2007-12-31 14:09:12 +01:00
// copy new data in internal buffer
int iCurPos = 0;
const int iEnd = iPutPos + iVecSize;
while ( iPutPos < iEnd )
{
vecsMemory[iPutPos++] = vecsData[iCurPos++];
}
bool bBufOk = false;
if ( iEnd == iMemSize )
{
bBufOk = true;
}
return bBufOk;
2006-01-28 12:29:22 +01:00
}
CVector<short> CConvBuf::Get()
{
iPutPos = 0;
return vecsMemory;
2006-01-28 12:29:22 +01:00
}