michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: /// michael@0: /// A buffer class for temporarily storaging sound samples, operates as a michael@0: /// first-in-first-out pipe. michael@0: /// michael@0: /// Samples are added to the end of the sample buffer with the 'putSamples' michael@0: /// function, and are received from the beginning of the buffer by calling michael@0: /// the 'receiveSamples' function. The class automatically removes the michael@0: /// outputted samples from the buffer, as well as grows the buffer size michael@0: /// whenever necessary. michael@0: /// michael@0: /// Author : Copyright (c) Olli Parviainen michael@0: /// Author e-mail : oparviai 'at' iki.fi michael@0: /// SoundTouch WWW: http://www.surina.net/soundtouch michael@0: /// michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: // michael@0: // Last changed : $Date: 2012-11-08 12:53:01 -0600 (Thu, 08 Nov 2012) $ michael@0: // File revision : $Revision: 4 $ michael@0: // michael@0: // $Id: FIFOSampleBuffer.cpp 160 2012-11-08 18:53:01Z oparviai $ michael@0: // michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: // michael@0: // License : michael@0: // michael@0: // SoundTouch audio processing library michael@0: // Copyright (c) Olli Parviainen michael@0: // michael@0: // This library is free software; you can redistribute it and/or michael@0: // modify it under the terms of the GNU Lesser General Public michael@0: // License as published by the Free Software Foundation; either michael@0: // version 2.1 of the License, or (at your option) any later version. michael@0: // michael@0: // This library is distributed in the hope that it will be useful, michael@0: // but WITHOUT ANY WARRANTY; without even the implied warranty of michael@0: // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU michael@0: // Lesser General Public License for more details. michael@0: // michael@0: // You should have received a copy of the GNU Lesser General Public michael@0: // License along with this library; if not, write to the Free Software michael@0: // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA michael@0: // michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: michael@0: #include "FIFOSampleBuffer.h" michael@0: michael@0: using namespace soundtouch; michael@0: michael@0: // Constructor michael@0: FIFOSampleBuffer::FIFOSampleBuffer(int numChannels) michael@0: { michael@0: assert(numChannels > 0); michael@0: sizeInBytes = 0; // reasonable initial value michael@0: buffer = NULL; michael@0: bufferUnaligned = NULL; michael@0: samplesInBuffer = 0; michael@0: bufferPos = 0; michael@0: channels = (uint)numChannels; michael@0: ensureCapacity(32); // allocate initial capacity michael@0: } michael@0: michael@0: michael@0: // destructor michael@0: FIFOSampleBuffer::~FIFOSampleBuffer() michael@0: { michael@0: delete[] bufferUnaligned; michael@0: bufferUnaligned = NULL; michael@0: buffer = NULL; michael@0: } michael@0: michael@0: michael@0: // Sets number of channels, 1 = mono, 2 = stereo michael@0: void FIFOSampleBuffer::setChannels(int numChannels) michael@0: { michael@0: uint usedBytes; michael@0: michael@0: assert(numChannels > 0); michael@0: usedBytes = channels * samplesInBuffer; michael@0: channels = (uint)numChannels; michael@0: samplesInBuffer = usedBytes / channels; michael@0: } michael@0: michael@0: michael@0: // if output location pointer 'bufferPos' isn't zero, 'rewinds' the buffer and michael@0: // zeroes this pointer by copying samples from the 'bufferPos' pointer michael@0: // location on to the beginning of the buffer. michael@0: void FIFOSampleBuffer::rewind() michael@0: { michael@0: if (buffer && bufferPos) michael@0: { michael@0: memmove(buffer, ptrBegin(), sizeof(SAMPLETYPE) * channels * samplesInBuffer); michael@0: bufferPos = 0; michael@0: } michael@0: } michael@0: michael@0: michael@0: // Adds 'numSamples' pcs of samples from the 'samples' memory position to michael@0: // the sample buffer. michael@0: void FIFOSampleBuffer::putSamples(const SAMPLETYPE *samples, uint nSamples) michael@0: { michael@0: memcpy(ptrEnd(nSamples), samples, sizeof(SAMPLETYPE) * nSamples * channels); michael@0: samplesInBuffer += nSamples; michael@0: } michael@0: michael@0: michael@0: // Increases the number of samples in the buffer without copying any actual michael@0: // samples. michael@0: // michael@0: // This function is used to update the number of samples in the sample buffer michael@0: // when accessing the buffer directly with 'ptrEnd' function. Please be michael@0: // careful though! michael@0: void FIFOSampleBuffer::putSamples(uint nSamples) michael@0: { michael@0: uint req; michael@0: michael@0: req = samplesInBuffer + nSamples; michael@0: ensureCapacity(req); michael@0: samplesInBuffer += nSamples; michael@0: } michael@0: michael@0: michael@0: // Returns a pointer to the end of the used part of the sample buffer (i.e. michael@0: // where the new samples are to be inserted). This function may be used for michael@0: // inserting new samples into the sample buffer directly. Please be careful! michael@0: // michael@0: // Parameter 'slackCapacity' tells the function how much free capacity (in michael@0: // terms of samples) there _at least_ should be, in order to the caller to michael@0: // succesfully insert all the required samples to the buffer. When necessary, michael@0: // the function grows the buffer size to comply with this requirement. michael@0: // michael@0: // When using this function as means for inserting new samples, also remember michael@0: // to increase the sample count afterwards, by calling the michael@0: // 'putSamples(numSamples)' function. michael@0: SAMPLETYPE *FIFOSampleBuffer::ptrEnd(uint slackCapacity) michael@0: { michael@0: ensureCapacity(samplesInBuffer + slackCapacity); michael@0: return buffer + samplesInBuffer * channels; michael@0: } michael@0: michael@0: michael@0: // Returns a pointer to the beginning of the currently non-outputted samples. michael@0: // This function is provided for accessing the output samples directly. michael@0: // Please be careful! michael@0: // michael@0: // When using this function to output samples, also remember to 'remove' the michael@0: // outputted samples from the buffer by calling the michael@0: // 'receiveSamples(numSamples)' function michael@0: SAMPLETYPE *FIFOSampleBuffer::ptrBegin() michael@0: { michael@0: assert(buffer); michael@0: return buffer + bufferPos * channels; michael@0: } michael@0: michael@0: michael@0: // Ensures that the buffer has enought capacity, i.e. space for _at least_ michael@0: // 'capacityRequirement' number of samples. The buffer is grown in steps of michael@0: // 4 kilobytes to eliminate the need for frequently growing up the buffer, michael@0: // as well as to round the buffer size up to the virtual memory page size. michael@0: void FIFOSampleBuffer::ensureCapacity(uint capacityRequirement) michael@0: { michael@0: SAMPLETYPE *tempUnaligned, *temp; michael@0: michael@0: if (capacityRequirement > getCapacity()) michael@0: { michael@0: // enlarge the buffer in 4kbyte steps (round up to next 4k boundary) michael@0: sizeInBytes = (capacityRequirement * channels * sizeof(SAMPLETYPE) + 4095) & (uint)-4096; michael@0: assert(sizeInBytes % 2 == 0); michael@0: tempUnaligned = new SAMPLETYPE[sizeInBytes / sizeof(SAMPLETYPE) + 16 / sizeof(SAMPLETYPE)]; michael@0: if (tempUnaligned == NULL) michael@0: { michael@0: ST_THROW_RT_ERROR("Couldn't allocate memory!\n"); michael@0: } michael@0: // Align the buffer to begin at 16byte cache line boundary for optimal performance michael@0: temp = (SAMPLETYPE *)SOUNDTOUCH_ALIGN_POINTER_16(tempUnaligned); michael@0: if (samplesInBuffer) michael@0: { michael@0: memcpy(temp, ptrBegin(), samplesInBuffer * channels * sizeof(SAMPLETYPE)); michael@0: } michael@0: delete[] bufferUnaligned; michael@0: buffer = temp; michael@0: bufferUnaligned = tempUnaligned; michael@0: bufferPos = 0; michael@0: } michael@0: else michael@0: { michael@0: // simply rewind the buffer (if necessary) michael@0: rewind(); michael@0: } michael@0: } michael@0: michael@0: michael@0: // Returns the current buffer capacity in terms of samples michael@0: uint FIFOSampleBuffer::getCapacity() const michael@0: { michael@0: return sizeInBytes / (channels * sizeof(SAMPLETYPE)); michael@0: } michael@0: michael@0: michael@0: // Returns the number of samples currently in the buffer michael@0: uint FIFOSampleBuffer::numSamples() const michael@0: { michael@0: return samplesInBuffer; michael@0: } michael@0: michael@0: michael@0: // Output samples from beginning of the sample buffer. Copies demanded number michael@0: // of samples to output and removes them from the sample buffer. If there michael@0: // are less than 'numsample' samples in the buffer, returns all available. michael@0: // michael@0: // Returns number of samples copied. michael@0: uint FIFOSampleBuffer::receiveSamples(SAMPLETYPE *output, uint maxSamples) michael@0: { michael@0: uint num; michael@0: michael@0: num = (maxSamples > samplesInBuffer) ? samplesInBuffer : maxSamples; michael@0: michael@0: memcpy(output, ptrBegin(), channels * sizeof(SAMPLETYPE) * num); michael@0: return receiveSamples(num); michael@0: } michael@0: michael@0: michael@0: // Removes samples from the beginning of the sample buffer without copying them michael@0: // anywhere. Used to reduce the number of samples in the buffer, when accessing michael@0: // the sample buffer with the 'ptrBegin' function. michael@0: uint FIFOSampleBuffer::receiveSamples(uint maxSamples) michael@0: { michael@0: if (maxSamples >= samplesInBuffer) michael@0: { michael@0: uint temp; michael@0: michael@0: temp = samplesInBuffer; michael@0: samplesInBuffer = 0; michael@0: return temp; michael@0: } michael@0: michael@0: samplesInBuffer -= maxSamples; michael@0: bufferPos += maxSamples; michael@0: michael@0: return maxSamples; michael@0: } michael@0: michael@0: michael@0: // Returns nonzero if the sample buffer is empty michael@0: int FIFOSampleBuffer::isEmpty() const michael@0: { michael@0: return (samplesInBuffer == 0) ? 1 : 0; michael@0: } michael@0: michael@0: michael@0: // Clears the sample buffer michael@0: void FIFOSampleBuffer::clear() michael@0: { michael@0: samplesInBuffer = 0; michael@0: bufferPos = 0; michael@0: } michael@0: michael@0: michael@0: /// allow trimming (downwards) amount of samples in pipeline. michael@0: /// Returns adjusted amount of samples michael@0: uint FIFOSampleBuffer::adjustAmountOfSamples(uint numSamples) michael@0: { michael@0: if (numSamples < samplesInBuffer) michael@0: { michael@0: samplesInBuffer = numSamples; michael@0: } michael@0: return samplesInBuffer; michael@0: } michael@0: