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; | ||
|  | } |