mirror of
				https://github.com/RetroDECK/Duckstation.git
				synced 2025-04-10 19:15:14 +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;
 | 
						|
}
 |