//////////////////////////////////////////////////////////////////////////////// /// /// 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$ // File revision : $Revision: 4 $ // // $Id$ // //////////////////////////////////////////////////////////////////////////////// // // 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 #include #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 uint transposeStereo(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSamples); virtual uint transposeMono(SAMPLETYPE *dest, const SAMPLETYPE *src, uint 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 uint transposeStereo(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSamples); virtual uint transposeMono(SAMPLETYPE *dest, const SAMPLETYPE *src, uint 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 s) { ST_THROW_RT_ERROR("Error in RateTransoser::new: don't use \"new TDStretch\" directly, use \"newInstance\" to create a new instance instead!"); return newInstance(); } RateTransposer *RateTransposer::newInstance() { #ifdef SOUNDTOUCH_INTEGER_SAMPLES return ::new RateTransposerInteger; #else return ::new RateTransposerFloat; #endif } // Constructor RateTransposer::RateTransposer() : FIFOProcessor(&outputBuffer) { numChannels = 2; bUseAAFilter = TRUE; fRate = 0; // Instantiates the anti-alias filter with default tap length // of 32 pAAFilter = new AAFilter(32); } RateTransposer::~RateTransposer() { delete pAAFilter; } /// Enables/disables the anti-alias filter. Zero to disable, nonzero to enable void RateTransposer::enableAAFilter(BOOL newMode) { bUseAAFilter = newMode; } /// Returns nonzero if anti-alias filter is enabled. BOOL RateTransposer::isAAFilterEnabled() const { return bUseAAFilter; } AAFilter *RateTransposer::getAAFilter() { return pAAFilter; } // Sets new target iRate. Normal iRate = 1.0, smaller values represent slower // iRate, larger faster iRates. void RateTransposer::setRate(float newRate) { double fCutoff; fRate = newRate; // design a new anti-alias filter if (newRate > 1.0f) { fCutoff = 0.5f / newRate; } else { fCutoff = 0.5f * newRate; } pAAFilter->setCutoffFreq(fCutoff); } // Outputs as many samples of the 'outputBuffer' as possible, and if there's // any room left, outputs also as many of the incoming samples as possible. // The goal is to drive the outputBuffer empty. // // It's allowed for 'output' and 'input' parameters to point to the same // memory position. /* void RateTransposer::flushStoreBuffer() { if (storeBuffer.isEmpty()) return; outputBuffer.moveSamples(storeBuffer); } */ // Adds 'nSamples' pcs of samples from the 'samples' memory position into // the input of the object. void RateTransposer::putSamples(const SAMPLETYPE *samples, uint nSamples) { processSamples(samples, nSamples); } // Transposes up the sample rate, causing the observed playback 'rate' of the // sound to decrease void RateTransposer::upsample(const SAMPLETYPE *src, uint nSamples) { uint count, sizeTemp, num; // 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) sizeTemp = (uint)((float)nSamples / fRate + 16.0f); // Transpose the samples, store the result into the end of "storeBuffer" count = transpose(storeBuffer.ptrEnd(sizeTemp), src, nSamples); storeBuffer.putSamples(count); // Apply the anti-alias filter to samples in "store output", output the // result to "dest" num = storeBuffer.numSamples(); count = pAAFilter->evaluate(outputBuffer.ptrEnd(num), storeBuffer.ptrBegin(), num, (uint)numChannels); outputBuffer.putSamples(count); // Remove the processed samples from "storeBuffer" storeBuffer.receiveSamples(count); } // Transposes down the sample rate, causing the observed playback 'rate' of the // sound to increase void RateTransposer::downsample(const SAMPLETYPE *src, uint nSamples) { uint count, sizeTemp; // 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 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(tempBuffer.isEmpty()); sizeTemp = storeBuffer.numSamples(); count = pAAFilter->evaluate(tempBuffer.ptrEnd(sizeTemp), storeBuffer.ptrBegin(), sizeTemp, (uint)numChannels); if (count == 0) return; // Remove the filtered samples from 'storeBuffer' storeBuffer.receiveSamples(count); // Transpose the samples (+16 is to reserve some slack in the destination buffer) sizeTemp = (uint)((float)nSamples / fRate + 16.0f); count = transpose(outputBuffer.ptrEnd(sizeTemp), tempBuffer.ptrBegin(), count); 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, uint nSamples) { uint count; uint sizeReq; if (nSamples == 0) return; assert(pAAFilter); // If anti-alias filter is turned off, simply transpose without applying // the filter if (bUseAAFilter == FALSE) { sizeReq = (uint)((float)nSamples / fRate + 1.0f); count = transpose(outputBuffer.ptrEnd(sizeReq), src, nSamples); outputBuffer.putSamples(count); return; } // Transpose with anti-alias filter if (fRate < 1.0f) { upsample(src, nSamples); } else { downsample(src, nSamples); } } // Transposes the sample rate of the given samples using linear interpolation. // Returns the number of samples returned in the "dest" buffer inline uint RateTransposer::transpose(SAMPLETYPE *dest, const SAMPLETYPE *src, uint nSamples) { if (numChannels == 2) { return transposeStereo(dest, src, nSamples); } else { return transposeMono(dest, src, nSamples); } } // Sets the number of channels, 1 = mono, 2 = stereo void RateTransposer::setChannels(int nChannels) { assert(nChannels > 0); if (numChannels == nChannels) return; assert(nChannels == 1 || nChannels == 2); numChannels = nChannels; storeBuffer.setChannels(numChannels); tempBuffer.setChannels(numChannels); outputBuffer.setChannels(numChannels); // Inits the linear interpolation registers resetRegisters(); } // Clears all the samples in the object void RateTransposer::clear() { outputBuffer.clear(); storeBuffer.clear(); } // Returns nonzero if there aren't any samples available for outputting. int RateTransposer::isEmpty() const { int res; res = FIFOProcessor::isEmpty(); if (res == 0) return 0; return storeBuffer.isEmpty(); } ////////////////////////////////////////////////////////////////////////////// // // RateTransposerInteger - integer arithmetic implementation // /// fixed-point interpolation routine precision #define 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() { iSlopeCount = 0; sPrevSampleL = 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 uint RateTransposerInteger::transposeMono(SAMPLETYPE *dest, const SAMPLETYPE *src, uint nSamples) { unsigned int i, used; LONG_SAMPLETYPE temp, vol1; if (nSamples == 0) return 0; // no samples, no work used = 0; i = 0; // Process the last sample saved from the previous call first... while (iSlopeCount <= SCALE) { vol1 = (LONG_SAMPLETYPE)(SCALE - iSlopeCount); temp = vol1 * sPrevSampleL + iSlopeCount * src[0]; dest[i] = (SAMPLETYPE)(temp / SCALE); i++; iSlopeCount += iRate; } // now always (iSlopeCount > SCALE) iSlopeCount -= SCALE; while (1) { while (iSlopeCount > SCALE) { iSlopeCount -= SCALE; used ++; if (used >= nSamples - 1) goto end; } vol1 = (LONG_SAMPLETYPE)(SCALE - iSlopeCount); temp = src[used] * vol1 + iSlopeCount * src[used + 1]; dest[i] = (SAMPLETYPE)(temp / SCALE); i++; iSlopeCount += iRate; } end: // Store the last sample for the next round 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 uint RateTransposerInteger::transposeStereo(SAMPLETYPE *dest, const SAMPLETYPE *src, uint nSamples) { unsigned int srcPos, i, used; LONG_SAMPLETYPE temp, vol1; if (nSamples == 0) return 0; // no samples, no work used = 0; i = 0; // Process the last sample saved from the sPrevSampleLious call first... while (iSlopeCount <= SCALE) { vol1 = (LONG_SAMPLETYPE)(SCALE - iSlopeCount); temp = vol1 * sPrevSampleL + iSlopeCount * src[0]; dest[2 * i] = (SAMPLETYPE)(temp / SCALE); temp = vol1 * sPrevSampleR + iSlopeCount * src[1]; dest[2 * i + 1] = (SAMPLETYPE)(temp / SCALE); i++; iSlopeCount += iRate; } // now always (iSlopeCount > SCALE) iSlopeCount -= SCALE; while (1) { while (iSlopeCount > SCALE) { iSlopeCount -= SCALE; used ++; if (used >= nSamples - 1) goto end; } srcPos = 2 * used; vol1 = (LONG_SAMPLETYPE)(SCALE - iSlopeCount); temp = src[srcPos] * vol1 + iSlopeCount * src[srcPos + 2]; dest[2 * i] = (SAMPLETYPE)(temp / SCALE); temp = src[srcPos + 1] * vol1 + iSlopeCount * src[srcPos + 3]; dest[2 * i + 1] = (SAMPLETYPE)(temp / SCALE); i++; iSlopeCount += iRate; } end: // Store the last sample for the next round sPrevSampleL = src[2 * nSamples - 2]; 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) { iRate = (int)(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() { fSlopeCount = 0; sPrevSampleL = 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 uint RateTransposerFloat::transposeMono(SAMPLETYPE *dest, const SAMPLETYPE *src, uint nSamples) { unsigned int i, used; used = 0; i = 0; // Process the last sample saved from the previous call first... while (fSlopeCount <= 1.0f) { dest[i] = (SAMPLETYPE)((1.0f - fSlopeCount) * sPrevSampleL + fSlopeCount * src[0]); i++; fSlopeCount += fRate; } fSlopeCount -= 1.0f; if (nSamples > 1) { while (1) { while (fSlopeCount > 1.0f) { fSlopeCount -= 1.0f; used ++; if (used >= nSamples - 1) goto end; } dest[i] = (SAMPLETYPE)((1.0f - fSlopeCount) * src[used] + fSlopeCount * src[used + 1]); i++; fSlopeCount += fRate; } } end: // Store the last sample for the next round 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 uint RateTransposerFloat::transposeStereo(SAMPLETYPE *dest, const SAMPLETYPE *src, uint nSamples) { unsigned int srcPos, i, used; if (nSamples == 0) return 0; // no samples, no work used = 0; i = 0; // Process the last sample saved from the sPrevSampleLious call first... while (fSlopeCount <= 1.0f) { dest[2 * i] = (SAMPLETYPE)((1.0f - fSlopeCount) * sPrevSampleL + fSlopeCount * src[0]); dest[2 * i + 1] = (SAMPLETYPE)((1.0f - fSlopeCount) * sPrevSampleR + fSlopeCount * src[1]); i++; fSlopeCount += fRate; } // now always (iSlopeCount > 1.0f) fSlopeCount -= 1.0f; if (nSamples > 1) { while (1) { while (fSlopeCount > 1.0f) { fSlopeCount -= 1.0f; used ++; if (used >= nSamples - 1) goto end; } srcPos = 2 * used; dest[2 * i] = (SAMPLETYPE)((1.0f - fSlopeCount) * src[srcPos] + fSlopeCount * src[srcPos + 2]); dest[2 * i + 1] = (SAMPLETYPE)((1.0f - fSlopeCount) * src[srcPos + 1] + fSlopeCount * src[srcPos + 3]); i++; fSlopeCount += fRate; } } end: // Store the last sample for the next round sPrevSampleL = src[2 * nSamples - 2]; sPrevSampleR = src[2 * nSamples - 1]; return i; }