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