michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: /// michael@0: /// Sample rate transposer. Changes sample rate by using linear interpolation michael@0: /// together with anti-alias filtering (first order interpolation with anti- michael@0: /// alias filtering should be quite adequate for this application) 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: 2014-04-06 10:57:21 -0500 (Sun, 06 Apr 2014) $ michael@0: // File revision : $Revision: 4 $ michael@0: // michael@0: // $Id: RateTransposer.cpp 195 2014-04-06 15:57:21Z 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: #include "RateTransposer.h" michael@0: #include "InterpolateLinear.h" michael@0: #include "InterpolateCubic.h" michael@0: #include "InterpolateShannon.h" michael@0: #include "AAFilter.h" michael@0: michael@0: using namespace soundtouch; michael@0: michael@0: // Define default interpolation algorithm here michael@0: TransposerBase::ALGORITHM TransposerBase::algorithm = TransposerBase::CUBIC; michael@0: michael@0: michael@0: // Constructor michael@0: RateTransposer::RateTransposer() : FIFOProcessor(&outputBuffer) michael@0: { michael@0: bUseAAFilter = true; michael@0: michael@0: // Instantiates the anti-alias filter michael@0: pAAFilter = new AAFilter(64); michael@0: pTransposer = TransposerBase::newInstance(); michael@0: } michael@0: michael@0: michael@0: michael@0: RateTransposer::~RateTransposer() michael@0: { michael@0: delete pAAFilter; michael@0: delete pTransposer; michael@0: } michael@0: michael@0: michael@0: michael@0: /// Enables/disables the anti-alias filter. Zero to disable, nonzero to enable michael@0: void RateTransposer::enableAAFilter(bool newMode) michael@0: { michael@0: bUseAAFilter = newMode; michael@0: } michael@0: michael@0: michael@0: /// Returns nonzero if anti-alias filter is enabled. michael@0: bool RateTransposer::isAAFilterEnabled() const michael@0: { michael@0: return bUseAAFilter; michael@0: } michael@0: michael@0: michael@0: AAFilter *RateTransposer::getAAFilter() michael@0: { michael@0: return pAAFilter; michael@0: } michael@0: michael@0: michael@0: michael@0: // Sets new target iRate. Normal iRate = 1.0, smaller values represent slower michael@0: // iRate, larger faster iRates. michael@0: void RateTransposer::setRate(float newRate) michael@0: { michael@0: double fCutoff; michael@0: michael@0: pTransposer->setRate(newRate); michael@0: michael@0: // design a new anti-alias filter michael@0: if (newRate > 1.0f) michael@0: { michael@0: fCutoff = 0.5f / newRate; michael@0: } michael@0: else michael@0: { michael@0: fCutoff = 0.5f * newRate; michael@0: } michael@0: pAAFilter->setCutoffFreq(fCutoff); michael@0: } michael@0: michael@0: michael@0: // Adds 'nSamples' pcs of samples from the 'samples' memory position into michael@0: // the input of the object. michael@0: void RateTransposer::putSamples(const SAMPLETYPE *samples, uint nSamples) michael@0: { michael@0: processSamples(samples, nSamples); michael@0: } michael@0: michael@0: michael@0: // Transposes sample rate by applying anti-alias filter to prevent folding. michael@0: // Returns amount of samples returned in the "dest" buffer. michael@0: // The maximum amount of samples that can be returned at a time is set by michael@0: // the 'set_returnBuffer_size' function. michael@0: void RateTransposer::processSamples(const SAMPLETYPE *src, uint nSamples) michael@0: { michael@0: uint count; michael@0: michael@0: if (nSamples == 0) return; michael@0: michael@0: // Store samples to input buffer michael@0: inputBuffer.putSamples(src, nSamples); michael@0: michael@0: // If anti-alias filter is turned off, simply transpose without applying michael@0: // the filter michael@0: if (bUseAAFilter == false) michael@0: { michael@0: count = pTransposer->transpose(outputBuffer, inputBuffer); michael@0: return; michael@0: } michael@0: michael@0: assert(pAAFilter); michael@0: michael@0: // Transpose with anti-alias filter michael@0: if (pTransposer->rate < 1.0f) michael@0: { michael@0: // If the parameter 'Rate' value is smaller than 1, first transpose michael@0: // the samples and then apply the anti-alias filter to remove aliasing. michael@0: michael@0: // Transpose the samples, store the result to end of "midBuffer" michael@0: pTransposer->transpose(midBuffer, inputBuffer); michael@0: michael@0: // Apply the anti-alias filter for transposed samples in midBuffer michael@0: pAAFilter->evaluate(outputBuffer, midBuffer); michael@0: } michael@0: else michael@0: { michael@0: // If the parameter 'Rate' value is larger than 1, first apply the michael@0: // anti-alias filter to remove high frequencies (prevent them from folding michael@0: // over the lover frequencies), then transpose. michael@0: michael@0: // Apply the anti-alias filter for samples in inputBuffer michael@0: pAAFilter->evaluate(midBuffer, inputBuffer); michael@0: michael@0: // Transpose the AA-filtered samples in "midBuffer" michael@0: pTransposer->transpose(outputBuffer, midBuffer); michael@0: } michael@0: } michael@0: michael@0: michael@0: // Sets the number of channels, 1 = mono, 2 = stereo michael@0: void RateTransposer::setChannels(int nChannels) michael@0: { michael@0: assert(nChannels > 0); michael@0: michael@0: if (pTransposer->numChannels == nChannels) return; michael@0: pTransposer->setChannels(nChannels); michael@0: michael@0: inputBuffer.setChannels(nChannels); michael@0: midBuffer.setChannels(nChannels); michael@0: outputBuffer.setChannels(nChannels); michael@0: } michael@0: michael@0: michael@0: // Clears all the samples in the object michael@0: void RateTransposer::clear() michael@0: { michael@0: outputBuffer.clear(); michael@0: midBuffer.clear(); michael@0: inputBuffer.clear(); michael@0: } michael@0: michael@0: michael@0: // Returns nonzero if there aren't any samples available for outputting. michael@0: int RateTransposer::isEmpty() const michael@0: { michael@0: int res; michael@0: michael@0: res = FIFOProcessor::isEmpty(); michael@0: if (res == 0) return 0; michael@0: return inputBuffer.isEmpty(); michael@0: } michael@0: michael@0: michael@0: ////////////////////////////////////////////////////////////////////////////// michael@0: // michael@0: // TransposerBase - Base class for interpolation michael@0: // michael@0: michael@0: // static function to set interpolation algorithm michael@0: void TransposerBase::setAlgorithm(TransposerBase::ALGORITHM a) michael@0: { michael@0: TransposerBase::algorithm = a; michael@0: } michael@0: michael@0: michael@0: // Transposes the sample rate of the given samples using linear interpolation. michael@0: // Returns the number of samples returned in the "dest" buffer michael@0: int TransposerBase::transpose(FIFOSampleBuffer &dest, FIFOSampleBuffer &src) michael@0: { michael@0: int numSrcSamples = src.numSamples(); michael@0: int sizeDemand = (int)((float)numSrcSamples / rate) + 8; michael@0: int numOutput; michael@0: SAMPLETYPE *psrc = src.ptrBegin(); michael@0: SAMPLETYPE *pdest = dest.ptrEnd(sizeDemand); michael@0: michael@0: #ifndef USE_MULTICH_ALWAYS michael@0: if (numChannels == 1) michael@0: { michael@0: numOutput = transposeMono(pdest, psrc, numSrcSamples); michael@0: } michael@0: else if (numChannels == 2) michael@0: { michael@0: numOutput = transposeStereo(pdest, psrc, numSrcSamples); michael@0: } michael@0: else michael@0: #endif // USE_MULTICH_ALWAYS michael@0: { michael@0: assert(numChannels > 0); michael@0: numOutput = transposeMulti(pdest, psrc, numSrcSamples); michael@0: } michael@0: dest.putSamples(numOutput); michael@0: src.receiveSamples(numSrcSamples); michael@0: return numOutput; michael@0: } michael@0: michael@0: michael@0: TransposerBase::TransposerBase() michael@0: { michael@0: numChannels = 0; michael@0: rate = 1.0f; michael@0: } michael@0: michael@0: michael@0: TransposerBase::~TransposerBase() michael@0: { michael@0: } michael@0: michael@0: michael@0: void TransposerBase::setChannels(int channels) michael@0: { michael@0: numChannels = channels; michael@0: resetRegisters(); michael@0: } michael@0: michael@0: michael@0: void TransposerBase::setRate(float newRate) michael@0: { michael@0: rate = newRate; michael@0: } michael@0: michael@0: michael@0: // static factory function michael@0: TransposerBase *TransposerBase::newInstance() michael@0: { michael@0: #ifdef SOUNDTOUCH_INTEGER_SAMPLES michael@0: // Notice: For integer arithmetics support only linear algorithm (due to simplest calculus) michael@0: return ::new InterpolateLinearInteger; michael@0: #else michael@0: switch (algorithm) michael@0: { michael@0: case LINEAR: michael@0: return new InterpolateLinearFloat; michael@0: michael@0: case CUBIC: michael@0: return new InterpolateCubic; michael@0: michael@0: case SHANNON: michael@0: return new InterpolateShannon; michael@0: michael@0: default: michael@0: assert(false); michael@0: return NULL; michael@0: } michael@0: #endif michael@0: }