/******************************************************************************\ * Copyright (c) 2004-2009 * * Author(s): * Volker Fischer * * 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 ) { // total size -> size of one block times number of blocks iBlockSize = iNewBlockSize; iMemSize = iNewBlockSize * iNewNumBlocks; // fade in first block added to the buffer bFadeInNewPutData = true; // allocate and clear memory for actual data buffer vecdMemory.Init ( iMemSize ); // use the "get" flag to make sure the buffer is cleared 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_EXTRA; } // init variables for extrapolation (in case a fade out is needed) dExPDiff = 0.0; dExPLastV = 0.0; } bool CNetBuf::Put ( CVector& 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 ) { // 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; bPutOK = false; // return error flag // check for special case: buffer memory is not sufficient if ( iInSize > iMemSize ) { // do nothing here, just return error code return bPutOK; } } // fade in new block if required if ( bFadeInNewPutData ) { FadeInAudioDataBlock ( vecdData ); } // 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& vecdData ) { bool bGetOK = true; // init return value bool bFadeOutExtrap = false; // get size of data to be get from the buffer const int iInSize = vecdData.Size(); // Check if there is not enough data available -> correct if ( GetAvailData() < iInSize ) { // 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 // check for special case: buffer memory is not sufficient if ( iInSize > iMemSize ) { // do nothing here, just return error code return bGetOK; } } // 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 { 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]; 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 ) { int iMiddleOfBuffer; #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 / ... */ iMiddleOfBuffer = ( ( ( iMemSize - iBlockSize) / 2 ) / iBlockSize ) * iBlockSize; #else // old code // somehow the old code seems to work better than the sophisticated new one....? /* 1: 0 / 2: 1 / 3: 1 / 4: 2 / ... */ iMiddleOfBuffer = ( ( iMemSize / 2 ) / iBlockSize ) * iBlockSize; #endif // different behaviour for get and put corrections if ( eClearType == CT_GET ) { // clear buffer vecdMemory.Reset ( 0.0 ); // correct buffer so that after the current get operation the pointer // are at maximum distance iPutPos = 0; 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; } } } void CNetBuf::FadeInAudioDataBlock ( CVector& vecdData ) { // correct fading length if necessary const int iCurFadingLen = min ( vecdData.Size(), iNumSamFading ); // apply linear fading for ( int i = 0; i < iCurFadingLen; i++ ) { vecdData[i] *= ( (double) i / iCurFadingLen ); } // reset flag bFadeInNewPutData = false; } void CNetBuf::FadeOutExtrapolateAudioDataBlock ( CVector& vecdData, const double dExPDiff, const double dExPLastV ) { // correct fading length if necessary const int iCurFadingLenExtra = min ( vecdData.Size(), iNumSamFadingExtra ); // apply linear extrapolation and linear fading for ( int i = 0; i < iCurFadingLenExtra; i++ ) { // calculate extrapolated value vecdData[i] = ( ( i + 1 ) * dExPDiff + dExPLastV ); // linear fading vecdData[i] *= ( (double) ( iCurFadingLenExtra - i ) / iCurFadingLenExtra ); } } /* conversion buffer implementation *******************************************/ void CConvBuf::Init ( const int iNewMemSize ) { // set memory size iMemSize = iNewMemSize; // allocate and clear memory for actual data buffer vecsMemory.Init ( iMemSize ); iPutPos = 0; } bool CConvBuf::Put ( const CVector& vecsData ) { 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++]; } // return "buffer is ready for readout" flag return iEnd == iMemSize; } CVector CConvBuf::Get() { iPutPos = 0; return vecsMemory; }