2006-12-18 15:39:33 +01:00
|
|
|
/******************************************************************************\
|
|
|
|
* Copyright (c) 2004-2006
|
|
|
|
*
|
|
|
|
* Author(s):
|
2006-11-25 15:46:57 +01:00
|
|
|
* Volker Fischer
|
2006-01-28 12:29:22 +01:00
|
|
|
*
|
|
|
|
* Note: we assuming here that put and get operations are secured by a mutex
|
2006-12-18 15:39:33 +01:00
|
|
|
* 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 )
|
|
|
|
{
|
|
|
|
/* total size -> size of one block times number of blocks */
|
|
|
|
iBlockSize = iNewBlockSize;
|
2006-11-25 15:46:57 +01:00
|
|
|
iMemSize = iNewBlockSize * iNewNumBlocks;
|
2006-01-28 12:29:22 +01:00
|
|
|
|
2006-11-25 15:46:57 +01:00
|
|
|
/* fade in first block added to the buffer */
|
2006-12-18 15:39:33 +01:00
|
|
|
bFadeInNewPutData = true;
|
2006-11-25 15:46:57 +01:00
|
|
|
|
2006-12-18 15:39:33 +01:00
|
|
|
/* allocate and clear memory for actual data buffer */
|
|
|
|
vecdMemory.Init(iMemSize);
|
|
|
|
|
|
|
|
/* use the "get" flag to make sure the buffer is cleared */
|
2006-11-25 15:46:57 +01:00
|
|
|
Clear(CT_GET);
|
|
|
|
|
|
|
|
/* initialize number of samples for fading effect */
|
|
|
|
if (FADE_IN_OUT_NUM_SAM < iBlockSize)
|
|
|
|
iNumSamFading = iBlockSize;
|
|
|
|
else
|
|
|
|
iNumSamFading = FADE_IN_OUT_NUM_SAM;
|
|
|
|
|
|
|
|
if (FADE_IN_OUT_NUM_SAM_EXTRA > iBlockSize)
|
|
|
|
iNumSamFadingExtra = iBlockSize;
|
|
|
|
else
|
|
|
|
iNumSamFadingExtra = FADE_IN_OUT_NUM_SAM;
|
|
|
|
|
|
|
|
/* init variables for extrapolation (in case a fade out is needed) */
|
|
|
|
dExPDiff = 0.0;
|
2006-12-18 15:39:33 +01:00
|
|
|
dExPLastV = 0.0;
|
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
/* get size of data to be added to the buffer */
|
|
|
|
const int iInSize = vecdData.Size();
|
|
|
|
|
|
|
|
/* Check if there is not enough space available -> correct */
|
|
|
|
if (GetAvailSpace() < iInSize)
|
2006-11-25 15:46:57 +01:00
|
|
|
{
|
|
|
|
/* not enough space in buffer for put operation, correct buffer to
|
|
|
|
prepare for new data */
|
|
|
|
Clear(CT_PUT);
|
|
|
|
|
|
|
|
/* set flag to fade in new block to avoid clicks */
|
|
|
|
bFadeInNewPutData = true;
|
2006-12-18 15:39:33 +01:00
|
|
|
|
|
|
|
bPutOK = false; /* return error flag */
|
2006-11-25 15:46:57 +01:00
|
|
|
}
|
2006-01-28 12:29:22 +01:00
|
|
|
|
2006-11-25 15:46:57 +01:00
|
|
|
/* fade in new block if required */
|
|
|
|
if (bFadeInNewPutData)
|
|
|
|
FadeInAudioDataBlock(vecdData);
|
2006-12-18 15:39:33 +01:00
|
|
|
|
|
|
|
/* copy new data in internal buffer */
|
|
|
|
int iCurPos = 0;
|
|
|
|
if (iPutPos + iInSize > iMemSize)
|
|
|
|
{
|
|
|
|
/* remaining space size for second block */
|
|
|
|
const int iRemSpace = iPutPos + iInSize - iMemSize;
|
|
|
|
|
|
|
|
/* data must be written in two steps because of wrap around */
|
|
|
|
while (iPutPos < iMemSize)
|
|
|
|
vecdMemory[iPutPos++] = vecdData[iCurPos++];
|
|
|
|
|
|
|
|
for (iPutPos = 0; iPutPos < iRemSpace; iPutPos++)
|
|
|
|
vecdMemory[iPutPos] = vecdData[iCurPos++];
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* data can be written in one step */
|
|
|
|
const int iEnd = iPutPos + iInSize;
|
|
|
|
while (iPutPos < iEnd)
|
|
|
|
vecdMemory[iPutPos++] = vecdData[iCurPos++];
|
|
|
|
}
|
|
|
|
|
|
|
|
/* set buffer state flag */
|
|
|
|
if (iPutPos == iGetPos)
|
|
|
|
eBufState = CNetBuf::BS_FULL;
|
|
|
|
else
|
|
|
|
eBufState = CNetBuf::BS_OK;
|
|
|
|
|
|
|
|
return bPutOK;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CNetBuf::Get(CVector<double>& vecdData)
|
|
|
|
{
|
2006-11-25 15:46:57 +01:00
|
|
|
bool bGetOK = true; /* init return value */
|
|
|
|
bool bFadeOutExtrap = false;
|
2006-01-28 12:29:22 +01:00
|
|
|
|
2006-11-25 15:46:57 +01:00
|
|
|
/* get size of data to be get from the buffer */
|
|
|
|
const int iInSize = vecdData.Size();
|
2006-12-18 15:39:33 +01:00
|
|
|
|
|
|
|
/* Check if there is not enough data available -> correct */
|
|
|
|
if (GetAvailData() < iInSize)
|
2006-11-25 15:46:57 +01:00
|
|
|
{
|
|
|
|
/* not enough data in buffer for get operation, correct buffer to
|
|
|
|
prepare for getting data */
|
|
|
|
Clear(CT_GET);
|
|
|
|
|
|
|
|
/* set flag to fade in next new block in buffer and fade out last
|
|
|
|
block by extrapolation to avoid clicks */
|
|
|
|
bFadeInNewPutData = true;
|
|
|
|
bFadeOutExtrap = true;
|
|
|
|
|
|
|
|
bGetOK = false; /* return error flag */
|
2006-12-18 15:39:33 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* copy data from internal buffer in output buffer */
|
|
|
|
int iCurPos = 0;
|
|
|
|
if (iGetPos + iInSize > iMemSize)
|
|
|
|
{
|
|
|
|
/* remaining data size for second block */
|
|
|
|
const int iRemData = iGetPos + iInSize - iMemSize;
|
|
|
|
|
|
|
|
/* data must be read in two steps because of wrap around */
|
|
|
|
while (iGetPos < iMemSize)
|
|
|
|
vecdData[iCurPos++] = vecdMemory[iGetPos++];
|
|
|
|
|
|
|
|
for (iGetPos = 0; iGetPos < iRemData; iGetPos++)
|
|
|
|
vecdData[iCurPos++] = vecdMemory[iGetPos];
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* data can be read in one step */
|
|
|
|
const int iEnd = iGetPos + iInSize;
|
|
|
|
while (iGetPos < iEnd)
|
|
|
|
vecdData[iCurPos++] = vecdMemory[iGetPos++];
|
|
|
|
}
|
|
|
|
|
|
|
|
/* set buffer state flag */
|
|
|
|
if (iPutPos == iGetPos)
|
|
|
|
eBufState = CNetBuf::BS_EMPTY;
|
|
|
|
else
|
2006-11-25 15:46:57 +01:00
|
|
|
eBufState = CNetBuf::BS_OK;
|
|
|
|
|
|
|
|
|
|
|
|
/* 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) */
|
|
|
|
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! */
|
|
|
|
dExPDiff = vecdData[iInSize - 1] - vecdData[iInSize - 2];
|
|
|
|
dExPLastV = vecdData[iInSize - 1];
|
2006-12-18 15:39:33 +01:00
|
|
|
|
|
|
|
return bGetOK;
|
|
|
|
}
|
|
|
|
|
|
|
|
int CNetBuf::GetAvailSpace() const
|
|
|
|
{
|
|
|
|
/* calculate available space in buffer */
|
|
|
|
int iAvSpace = iGetPos - iPutPos;
|
|
|
|
|
|
|
|
/* check for special case and wrap around */
|
|
|
|
if (iAvSpace < 0)
|
|
|
|
iAvSpace += iMemSize; /* wrap around */
|
|
|
|
else if ((iAvSpace == 0) && (eBufState == BS_EMPTY))
|
|
|
|
iAvSpace = iMemSize;
|
|
|
|
|
|
|
|
return iAvSpace;
|
|
|
|
}
|
|
|
|
|
|
|
|
int CNetBuf::GetAvailData() const
|
|
|
|
{
|
|
|
|
/* calculate available data in buffer */
|
|
|
|
int iAvData = iPutPos - iGetPos;
|
|
|
|
|
|
|
|
/* check for special case and wrap around */
|
|
|
|
if (iAvData < 0)
|
|
|
|
iAvData += iMemSize; /* wrap around */
|
|
|
|
else if ((iAvData == 0) && (eBufState == BS_FULL))
|
|
|
|
iAvData = iMemSize;
|
|
|
|
|
|
|
|
return iAvData;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CNetBuf::Clear(const EClearType eClearType)
|
2006-01-28 12:29:22 +01:00
|
|
|
{
|
|
|
|
|
2006-11-25 15:46:57 +01:00
|
|
|
int iMiddleOfBuffer;
|
2006-01-28 12:29:22 +01:00
|
|
|
|
|
|
|
#if 0
|
2006-11-25 15:46:57 +01:00
|
|
|
/* 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 / ... */
|
|
|
|
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....?
|
2006-11-25 15:46:57 +01:00
|
|
|
/* 1: 0 / 2: 1 / 3: 1 / 4: 2 / ... */
|
|
|
|
iMiddleOfBuffer = ((iMemSize / 2) / iBlockSize) * iBlockSize;
|
2006-01-28 12:29:22 +01:00
|
|
|
#endif
|
2006-11-25 15:46:57 +01:00
|
|
|
|
|
|
|
|
|
|
|
/* different behaviour for get and put corrections */
|
|
|
|
if (eClearType == CT_GET)
|
2006-12-18 15:39:33 +01:00
|
|
|
{
|
2006-11-25 15:46:57 +01:00
|
|
|
/* clear buffer */
|
2006-12-18 15:39:33 +01:00
|
|
|
vecdMemory.Reset(0.0);
|
|
|
|
|
2006-11-25 15:46:57 +01:00
|
|
|
/* correct buffer so that after the current get operation the pointer
|
|
|
|
are at maximum distance */
|
2006-12-18 15:39:33 +01:00
|
|
|
iPutPos = 0;
|
2006-11-25 15:46:57 +01:00
|
|
|
iGetPos = iMiddleOfBuffer;
|
|
|
|
|
|
|
|
/* check for special case */
|
|
|
|
if (iPutPos == iGetPos)
|
|
|
|
eBufState = CNetBuf::BS_FULL;
|
|
|
|
else
|
|
|
|
eBufState = CNetBuf::BS_OK;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* in case of "put" correction, do not delete old data but only shift
|
|
|
|
the pointers */
|
|
|
|
iPutPos = iMiddleOfBuffer;
|
|
|
|
|
|
|
|
/* adjust put pointer relative to current get pointer, take care of
|
|
|
|
wrap around */
|
|
|
|
iPutPos += iGetPos;
|
|
|
|
if (iPutPos > iMemSize)
|
|
|
|
iPutPos -= iMemSize;
|
|
|
|
|
|
|
|
/* fade out old data right before new put pointer */
|
|
|
|
int iCurPos = iPutPos - iNumSamFading;
|
|
|
|
int i = iNumSamFading;
|
|
|
|
|
|
|
|
if (iCurPos < 0)
|
|
|
|
{
|
|
|
|
/* wrap around */
|
|
|
|
iCurPos += iMemSize;
|
|
|
|
|
|
|
|
/* data must be processed in two steps because of wrap around */
|
|
|
|
while (iCurPos < iMemSize)
|
|
|
|
{
|
|
|
|
vecdMemory[iCurPos++] *= ((double) i / iNumSamFading);
|
|
|
|
i--;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (iCurPos = 0; iCurPos < iPutPos; iCurPos++)
|
|
|
|
{
|
|
|
|
vecdMemory[iCurPos] *= ((double) i / iNumSamFading);
|
|
|
|
i--;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* data can be processed in one step */
|
|
|
|
while (iCurPos < iPutPos)
|
|
|
|
{
|
|
|
|
vecdMemory[iCurPos++] *= ((double) i / iNumSamFading);
|
|
|
|
i--;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* check for special case */
|
|
|
|
if (iPutPos == iGetPos)
|
|
|
|
{
|
|
|
|
eBufState = CNetBuf::BS_EMPTY;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
eBufState = CNetBuf::BS_OK;
|
|
|
|
}
|
|
|
|
}
|
2006-12-18 15:39:33 +01:00
|
|
|
}
|
2006-01-28 12:29:22 +01:00
|
|
|
|
|
|
|
void CNetBuf::FadeInAudioDataBlock(CVector<double>& vecdData)
|
|
|
|
{
|
2006-11-25 15:46:57 +01:00
|
|
|
/* apply linear fading */
|
|
|
|
for (int i = 0; i < iNumSamFading; i++)
|
|
|
|
{
|
|
|
|
vecdData[i] *= ((double) i / iNumSamFading);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* reset flag */
|
|
|
|
bFadeInNewPutData = false;
|
2006-01-28 12:29:22 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void CNetBuf::FadeOutExtrapolateAudioDataBlock(CVector<double>& vecdData,
|
2006-11-25 15:46:57 +01:00
|
|
|
const double dExPDiff,
|
|
|
|
const double dExPLastV)
|
2006-01-28 12:29:22 +01:00
|
|
|
{
|
2006-11-25 15:46:57 +01:00
|
|
|
/* apply linear extrapolation and linear fading */
|
|
|
|
for (int i = 0; i < iNumSamFadingExtra; i++)
|
|
|
|
{
|
|
|
|
/* calculate extrapolated value */
|
|
|
|
vecdData[i] = ((i + 1) * dExPDiff + dExPLastV);
|
|
|
|
|
|
|
|
/* linear fading */
|
|
|
|
vecdData[i] *= ((double) (iNumSamFadingExtra - i) / iNumSamFadingExtra);
|
|
|
|
}
|
2006-01-28 12:29:22 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* conversion buffer implementation *******************************************/
|
|
|
|
void CConvBuf::Init ( const int iNewMemSize )
|
|
|
|
{
|
2006-11-25 15:46:57 +01:00
|
|
|
/* set memory size */
|
|
|
|
iMemSize = iNewMemSize;
|
2006-01-28 12:29:22 +01:00
|
|
|
|
2006-11-25 15:46:57 +01:00
|
|
|
/* allocate and clear memory for actual data buffer */
|
|
|
|
vecsMemory.Init(iMemSize);
|
2006-01-28 12:29:22 +01:00
|
|
|
|
2006-11-25 15:46:57 +01:00
|
|
|
iPutPos = 0;
|
2006-01-28 12:29:22 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
bool CConvBuf::Put ( const CVector<short>& vecsData)
|
|
|
|
{
|
2006-11-25 15:46:57 +01:00
|
|
|
const int iVecSize = vecsData.Size();
|
|
|
|
|
|
|
|
/* 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()
|
|
|
|
{
|
2006-11-25 15:46:57 +01:00
|
|
|
iPutPos = 0;
|
|
|
|
return vecsMemory;
|
2006-01-28 12:29:22 +01:00
|
|
|
}
|