//////////////////////////////////////////////////////////////////////////////// /// /// Sample rate transposer. Changes sample rate by using linear interpolation /// together with anti-alias filtering (first order interpolation with anti- /// alias filtering should be quite adequate for this application) /// /// Author : Copyright (c) Olli Parviainen /// Author e-mail : oparviai 'at' iki.fi /// SoundTouch WWW: http://www.surina.net/soundtouch /// //////////////////////////////////////////////////////////////////////////////// // // Last changed : $Date: 2011-09-02 15:56:11 -0300 (sex, 02 set 2011) $ // File revision : $Revision: 4 $ // // $Id: RateTransposer.cpp 131 2011-09-02 18:56:11Z oparviai $ // //////////////////////////////////////////////////////////////////////////////// // // 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 #include "RateTransposer.h" #include "AAFilter.h" using namespace soundtouch; /// A linear samplerate transposer class that uses integer arithmetics. /// for the transposing. class RateTransposerInteger : public RateTransposer { protected: int iSlopeCount; int iRate; SAMPLETYPE sPrevSampleL, sPrevSampleR; virtual void resetRegisters(); virtual uint32_t transposeStereo(SAMPLETYPE *dest, const SAMPLETYPE *src, uint32_t numSamples); virtual uint32_t transposeMono(SAMPLETYPE *dest, const SAMPLETYPE *src, uint32_t numSamples); public: RateTransposerInteger(); virtual ~RateTransposerInteger(); /// Sets new target rate. Normal rate = 1.0, smaller values represent slower /// rate, larger faster rates. virtual void setRate(float newRate); }; /// A linear samplerate transposer class that uses floating point arithmetics /// for the transposing. class RateTransposerFloat : public RateTransposer { protected: float fSlopeCount; SAMPLETYPE sPrevSampleL, sPrevSampleR; virtual void resetRegisters(); virtual uint32_t transposeStereo(SAMPLETYPE *dest, const SAMPLETYPE *src, uint32_t numSamples); virtual uint32_t transposeMono(SAMPLETYPE *dest, const SAMPLETYPE *src, uint32_t numSamples); public: RateTransposerFloat(); virtual ~RateTransposerFloat(); }; // Operator 'new' is overloaded so that it automatically creates a suitable instance // depending on if we've a MMX/SSE/etc-capable CPU available or not. void *RateTransposer::operator new(size_t) { // Notice! don't use "new TDStretch" directly, use "newInstance" to create a new instance instead! //assert(false); //return NULL; throw std::runtime_error("Don't use 'new RateTransposer', use 'newInstance' member instead!"); } RateTransposer *RateTransposer::newInstance() { #ifdef SOUNDTOUCH_INTEGER_SAMPLES return ::new RateTransposerInteger; #else return ::new RateTransposerFloat; #endif } // Constructor RateTransposer::RateTransposer() : FIFOProcessor(&outputBuffer) { this->numChannels = 2; this->bUseAAFilter = true; this->fRate = 0; // Instantiates the anti-alias filter with default tap length // of 32 this->pAAFilter.reset(new AAFilter(32)); } RateTransposer::~RateTransposer() { } /// Enables/disables the anti-alias filter. Zero to disable, nonzero to enable void RateTransposer::enableAAFilter(bool newMode) { this->bUseAAFilter = newMode; } /// Returns nonzero if anti-alias filter is enabled. bool RateTransposer::isAAFilterEnabled() const { return this->bUseAAFilter; } AAFilter *RateTransposer::getAAFilter() { return this->pAAFilter.get(); } // Sets new target iRate. Normal iRate = 1.0, smaller values represent slower // iRate, larger faster iRates. void RateTransposer::setRate(float newRate) { double fCutoff; this->fRate = newRate; // design a new anti-alias filter if (newRate > 1.0f) fCutoff = 0.5f / newRate; else fCutoff = 0.5f * newRate; this->pAAFilter->setCutoffFreq(fCutoff); } // Adds 'nSamples' pcs of samples from the 'samples' memory position into // the input of the object. void RateTransposer::putSamples(const SAMPLETYPE *samples, uint32_t nSamples) { this->processSamples(samples, nSamples); } // Transposes up the sample rate, causing the observed playback 'rate' of the // sound to decrease void RateTransposer::upsample(const SAMPLETYPE *src, uint32_t nSamples) { // If the parameter 'uRate' value is smaller than 'SCALE', first transpose // the samples and then apply the anti-alias filter to remove aliasing. // First check that there's enough room in 'storeBuffer' // (+16 is to reserve some slack in the destination buffer) uint32_t sizeTemp = static_cast(nSamples / this->fRate + 16.0f); // Transpose the samples, store the result into the end of "storeBuffer" uint32_t count = this->transpose(this->storeBuffer.ptrEnd(sizeTemp), src, nSamples); this->storeBuffer.putSamples(count); // Apply the anti-alias filter to samples in "store output", output the // result to "dest" uint32_t num = this->storeBuffer.numSamples(); count = this->pAAFilter->evaluate(this->outputBuffer.ptrEnd(num), this->storeBuffer.ptrBegin(), num, this->numChannels); this->outputBuffer.putSamples(count); // Remove the processed samples from "storeBuffer" this->storeBuffer.receiveSamples(count); } // Transposes down the sample rate, causing the observed playback 'rate' of the // sound to increase void RateTransposer::downsample(const SAMPLETYPE *src, uint32_t nSamples) { // If the parameter 'uRate' value is larger than 'SCALE', first apply the // anti-alias filter to remove high frequencies (prevent them from folding // over the lover frequencies), then transpose. */ // Add the new samples to the end of the storeBuffer */ this->storeBuffer.putSamples(src, nSamples); // Anti-alias filter the samples to prevent folding and output the filtered // data to tempBuffer. Note : because of the FIR filter length, the // filtering routine takes in 'filter_length' more samples than it outputs. assert(this->tempBuffer.isEmpty()); uint32_t sizeTemp = this->storeBuffer.numSamples(); uint32_t count = this->pAAFilter->evaluate(this->tempBuffer.ptrEnd(sizeTemp), this->storeBuffer.ptrBegin(), sizeTemp, this->numChannels); // Remove the filtered samples from 'storeBuffer' this->storeBuffer.receiveSamples(count); // Transpose the samples (+16 is to reserve some slack in the destination buffer) sizeTemp = static_cast(nSamples / this->fRate + 16.0f); count = this->transpose(this->outputBuffer.ptrEnd(sizeTemp), this->tempBuffer.ptrBegin(), count); this->outputBuffer.putSamples(count); } // Transposes sample rate by applying anti-alias filter to prevent folding. // Returns amount of samples returned in the "dest" buffer. // The maximum amount of samples that can be returned at a time is set by // the 'set_returnBuffer_size' function. void RateTransposer::processSamples(const SAMPLETYPE *src, uint32_t nSamples) { if (!nSamples) return; assert(this->pAAFilter.get()); // If anti-alias filter is turned off, simply transpose without applying // the filter if (!bUseAAFilter) { uint32_t sizeReq = static_cast(nSamples / this->fRate + 1.0f); uint32_t count = this->transpose(this->outputBuffer.ptrEnd(sizeReq), src, nSamples); this->outputBuffer.putSamples(count); return; } // Transpose with anti-alias filter if (this->fRate < 1.0f) this->upsample(src, nSamples); else this->downsample(src, nSamples); } // Transposes the sample rate of the given samples using linear interpolation. // Returns the number of samples returned in the "dest" buffer uint32_t RateTransposer::transpose(SAMPLETYPE *dest, const SAMPLETYPE *src, uint32_t nSamples) { if (this->numChannels == 2) return this->transposeStereo(dest, src, nSamples); else return this->transposeMono(dest, src, nSamples); } // Sets the number of channels, 1 = mono, 2 = stereo void RateTransposer::setChannels(int32_t nChannels) { assert(nChannels > 0); if (this->numChannels == nChannels) return; assert(nChannels == 1 || nChannels == 2); this->numChannels = nChannels; this->storeBuffer.setChannels(this->numChannels); this->tempBuffer.setChannels(this->numChannels); this->outputBuffer.setChannels(this->numChannels); // Inits the linear interpolation registers this->resetRegisters(); } // Clears all the samples in the object void RateTransposer::clear() { this->outputBuffer.clear(); this->storeBuffer.clear(); } // Returns nonzero if there aren't any samples available for outputting. bool RateTransposer::isEmpty() const { bool res = FIFOProcessor::isEmpty(); if (!res) return false; return this->storeBuffer.isEmpty(); } ////////////////////////////////////////////////////////////////////////////// // // RateTransposerInteger - integer arithmetic implementation // /// fixed-point interpolation routine precision static const int SCALE = 65536; // Constructor RateTransposerInteger::RateTransposerInteger() : RateTransposer() { // Notice: use local function calling syntax for sake of clarity, // to indicate the fact that C++ constructor can't call virtual functions. RateTransposerInteger::resetRegisters(); RateTransposerInteger::setRate(1.0f); } RateTransposerInteger::~RateTransposerInteger() { } void RateTransposerInteger::resetRegisters() { this->iSlopeCount = 0; this->sPrevSampleL = this->sPrevSampleR = 0; } // Transposes the sample rate of the given samples using linear interpolation. // 'Mono' version of the routine. Returns the number of samples returned in // the "dest" buffer uint32_t RateTransposerInteger::transposeMono(SAMPLETYPE *dest, const SAMPLETYPE *src, uint32_t nSamples) { if (!nSamples) return 0; // no samples, no work unsigned used = 0, i = 0; // Process the last sample saved from the previous call first... LONG_SAMPLETYPE temp, vol1; while (this->iSlopeCount <= SCALE) { vol1 = SCALE - this->iSlopeCount; temp = vol1 * this->sPrevSampleL + this->iSlopeCount * src[0]; dest[i] = static_cast(temp / SCALE); ++i; this->iSlopeCount += this->iRate; } // now always (iSlopeCount > SCALE) this->iSlopeCount -= SCALE; while (1) { while (this->iSlopeCount > SCALE) { this->iSlopeCount -= SCALE; ++used; if (used >= nSamples - 1) goto end; } vol1 = SCALE - this->iSlopeCount; temp = src[used] * vol1 + this->iSlopeCount * src[used + 1]; dest[i] = static_cast(temp / SCALE); ++i; this->iSlopeCount += this->iRate; } end: // Store the last sample for the next round this->sPrevSampleL = src[nSamples - 1]; return i; } // Transposes the sample rate of the given samples using linear interpolation. // 'Stereo' version of the routine. Returns the number of samples returned in // the "dest" buffer uint32_t RateTransposerInteger::transposeStereo(SAMPLETYPE *dest, const SAMPLETYPE *src, uint32_t nSamples) { if (!nSamples) return 0; // no samples, no work unsigned used = 0, i = 0; // Process the last sample saved from the sPrevSampleLious call first... LONG_SAMPLETYPE temp, vol1; while (this->iSlopeCount <= SCALE) { vol1 = SCALE - this->iSlopeCount; temp = vol1 * this->sPrevSampleL + this->iSlopeCount * src[0]; dest[2 * i] = static_cast(temp / SCALE); temp = vol1 * this->sPrevSampleR + this->iSlopeCount * src[1]; dest[2 * i + 1] = static_cast(temp / SCALE); ++i; this->iSlopeCount += this->iRate; } // now always (iSlopeCount > SCALE) this->iSlopeCount -= SCALE; while (1) { while (this->iSlopeCount > SCALE) { this->iSlopeCount -= SCALE; ++used; if (used >= nSamples - 1) goto end; } unsigned srcPos = 2 * used; vol1 = SCALE - this->iSlopeCount; temp = src[srcPos] * vol1 + this->iSlopeCount * src[srcPos + 2]; dest[2 * i] = static_cast(temp / SCALE); temp = src[srcPos + 1] * vol1 + this->iSlopeCount * src[srcPos + 3]; dest[2 * i + 1] = static_cast(temp / SCALE); ++i; this->iSlopeCount += this->iRate; } end: // Store the last sample for the next round this->sPrevSampleL = src[2 * nSamples - 2]; this->sPrevSampleR = src[2 * nSamples - 1]; return i; } // Sets new target iRate. Normal iRate = 1.0, smaller values represent slower // iRate, larger faster iRates. void RateTransposerInteger::setRate(float newRate) { this->iRate = static_cast(newRate * SCALE + 0.5f); RateTransposer::setRate(newRate); } ////////////////////////////////////////////////////////////////////////////// // // RateTransposerFloat - floating point arithmetic implementation // ////////////////////////////////////////////////////////////////////////////// // Constructor RateTransposerFloat::RateTransposerFloat() : RateTransposer() { // Notice: use local function calling syntax for sake of clarity, // to indicate the fact that C++ constructor can't call virtual functions. RateTransposerFloat::resetRegisters(); RateTransposerFloat::setRate(1.0f); } RateTransposerFloat::~RateTransposerFloat() { } void RateTransposerFloat::resetRegisters() { this->fSlopeCount = 0; this->sPrevSampleL = this->sPrevSampleR = 0; } // Transposes the sample rate of the given samples using linear interpolation. // 'Mono' version of the routine. Returns the number of samples returned in // the "dest" buffer uint32_t RateTransposerFloat::transposeMono(SAMPLETYPE *dest, const SAMPLETYPE *src, uint32_t nSamples) { unsigned used = 0, i = 0; // Process the last sample saved from the previous call first... while (this->fSlopeCount <= 1.0f) { dest[i] = static_cast((1.0f - this->fSlopeCount) * this->sPrevSampleL + this->fSlopeCount * src[0]); ++i; this->fSlopeCount += this->fRate; } this->fSlopeCount -= 1.0f; if (nSamples > 1) { while (1) { while (this->fSlopeCount > 1.0f) { this->fSlopeCount -= 1.0f; ++used; if (used >= nSamples - 1) goto end; } dest[i] = static_cast((1.0f - this->fSlopeCount) * src[used] + this->fSlopeCount * src[used + 1]); ++i; this->fSlopeCount += this->fRate; } } end: // Store the last sample for the next round this->sPrevSampleL = src[nSamples - 1]; return i; } // Transposes the sample rate of the given samples using linear interpolation. // 'Mono' version of the routine. Returns the number of samples returned in // the "dest" buffer uint32_t RateTransposerFloat::transposeStereo(SAMPLETYPE *dest, const SAMPLETYPE *src, uint32_t nSamples) { if (!nSamples) return 0; // no samples, no work unsigned used = 0, i = 0; // Process the last sample saved from the sPrevSampleLious call first... while (this->fSlopeCount <= 1.0f) { dest[2 * i] = static_cast((1.0f - this->fSlopeCount) * this->sPrevSampleL + this->fSlopeCount * src[0]); dest[2 * i + 1] = static_cast((1.0f - this->fSlopeCount) * this->sPrevSampleR + this->fSlopeCount * src[1]); ++i; this->fSlopeCount += this->fRate; } // now always (iSlopeCount > 1.0f) this->fSlopeCount -= 1.0f; if (nSamples > 1) { while (1) { while (this->fSlopeCount > 1.0f) { this->fSlopeCount -= 1.0f; ++used; if (used >= nSamples - 1) goto end; } unsigned srcPos = 2 * used; dest[2 * i] = static_cast((1.0f - this->fSlopeCount) * src[srcPos] + this->fSlopeCount * src[srcPos + 2]); dest[2 * i + 1] = static_cast((1.0f - this->fSlopeCount) * src[srcPos + 1] + this->fSlopeCount * src[srcPos + 3]); ++i; this->fSlopeCount += this->fRate; } } end: // Store the last sample for the next round this->sPrevSampleL = src[2 * nSamples - 2]; this->sPrevSampleR = src[2 * nSamples - 1]; return i; }