dolphin/Externals/soundtouch/RateTransposer.cpp

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

314 lines
8.5 KiB
C++
Raw Permalink Normal View History

2013-06-22 20:19:27 +02:00
////////////////////////////////////////////////////////////////////////////////
///
/// 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
///
////////////////////////////////////////////////////////////////////////////////
//
// 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 <memory.h>
#include <assert.h>
#include <stdlib.h>
#include <stdio.h>
#include "RateTransposer.h"
#include "InterpolateLinear.h"
#include "InterpolateCubic.h"
#include "InterpolateShannon.h"
2013-06-22 20:19:27 +02:00
#include "AAFilter.h"
using namespace soundtouch;
// Define default interpolation algorithm here
TransposerBase::ALGORITHM TransposerBase::algorithm = TransposerBase::CUBIC;
2013-06-22 20:19:27 +02:00
// Constructor
RateTransposer::RateTransposer() : FIFOProcessor(&outputBuffer)
{
bUseAAFilter =
#ifndef SOUNDTOUCH_PREVENT_CLICK_AT_RATE_CROSSOVER
true;
#else
// Disable Anti-alias filter if desirable to avoid click at rate change zero value crossover
false;
#endif
2013-06-22 20:19:27 +02:00
// Instantiates the anti-alias filter
pAAFilter = new AAFilter(64);
pTransposer = TransposerBase::newInstance();
clear();
2013-06-22 20:19:27 +02:00
}
RateTransposer::~RateTransposer()
{
delete pAAFilter;
delete pTransposer;
2013-06-22 20:19:27 +02:00
}
/// Enables/disables the anti-alias filter. Zero to disable, nonzero to enable
void RateTransposer::enableAAFilter(bool newMode)
2013-06-22 20:19:27 +02:00
{
#ifndef SOUNDTOUCH_PREVENT_CLICK_AT_RATE_CROSSOVER
// Disable Anti-alias filter if desirable to avoid click at rate change zero value crossover
2013-06-22 20:19:27 +02:00
bUseAAFilter = newMode;
clear();
#endif
2013-06-22 20:19:27 +02:00
}
/// Returns nonzero if anti-alias filter is enabled.
bool RateTransposer::isAAFilterEnabled() const
2013-06-22 20:19:27 +02:00
{
return bUseAAFilter;
}
AAFilter *RateTransposer::getAAFilter()
{
return pAAFilter;
}
// Sets new target iRate. Normal iRate = 1.0, smaller values represent slower
// iRate, larger faster iRates.
2015-12-28 13:07:53 +01:00
void RateTransposer::setRate(double newRate)
2013-06-22 20:19:27 +02:00
{
double fCutoff;
pTransposer->setRate(newRate);
2013-06-22 20:19:27 +02:00
// design a new anti-alias filter
2015-12-28 13:07:53 +01:00
if (newRate > 1.0)
2013-06-22 20:19:27 +02:00
{
2015-12-28 13:07:53 +01:00
fCutoff = 0.5 / newRate;
2013-06-22 20:19:27 +02:00
}
else
{
2015-12-28 13:07:53 +01:00
fCutoff = 0.5 * newRate;
2013-06-22 20:19:27 +02:00
}
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, uint nSamples)
{
processSamples(samples, nSamples);
}
// 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)
{
if (nSamples == 0) return;
// Store samples to input buffer
inputBuffer.putSamples(src, nSamples);
2013-06-22 20:19:27 +02:00
// If anti-alias filter is turned off, simply transpose without applying
// the filter
if (bUseAAFilter == false)
2013-06-22 20:19:27 +02:00
{
(void)pTransposer->transpose(outputBuffer, inputBuffer);
2013-06-22 20:19:27 +02:00
return;
}
assert(pAAFilter);
2013-06-22 20:19:27 +02:00
// Transpose with anti-alias filter
if (pTransposer->rate < 1.0f)
2013-06-22 20:19:27 +02:00
{
// If the parameter 'Rate' value is smaller than 1, first transpose
// the samples and then apply the anti-alias filter to remove aliasing.
// Transpose the samples, store the result to end of "midBuffer"
pTransposer->transpose(midBuffer, inputBuffer);
// Apply the anti-alias filter for transposed samples in midBuffer
pAAFilter->evaluate(outputBuffer, midBuffer);
2013-06-22 20:19:27 +02:00
}
else
{
// If the parameter 'Rate' value is larger than 1, first apply the
// anti-alias filter to remove high frequencies (prevent them from folding
// over the lover frequencies), then transpose.
2013-06-22 20:19:27 +02:00
// Apply the anti-alias filter for samples in inputBuffer
pAAFilter->evaluate(midBuffer, inputBuffer);
2013-06-22 20:19:27 +02:00
// Transpose the AA-filtered samples in "midBuffer"
pTransposer->transpose(outputBuffer, midBuffer);
2013-06-22 20:19:27 +02:00
}
}
// Sets the number of channels, 1 = mono, 2 = stereo
void RateTransposer::setChannels(int nChannels)
{
if (!verifyNumberOfChannels(nChannels) ||
(pTransposer->numChannels == nChannels)) return;
2013-06-22 20:19:27 +02:00
pTransposer->setChannels(nChannels);
inputBuffer.setChannels(nChannels);
midBuffer.setChannels(nChannels);
outputBuffer.setChannels(nChannels);
2013-06-22 20:19:27 +02:00
}
// Clears all the samples in the object
void RateTransposer::clear()
{
outputBuffer.clear();
midBuffer.clear();
inputBuffer.clear();
pTransposer->resetRegisters();
// prefill buffer to avoid losing first samples at beginning of stream
int prefill = getLatency();
inputBuffer.addSilent(prefill);
2013-06-22 20:19:27 +02:00
}
// 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 inputBuffer.isEmpty();
2013-06-22 20:19:27 +02:00
}
/// Return approximate initial input-output latency
int RateTransposer::getLatency() const
{
return pTransposer->getLatency() +
((bUseAAFilter) ? (pAAFilter->getLength() / 2) : 0);
}
2013-06-22 20:19:27 +02:00
//////////////////////////////////////////////////////////////////////////////
//
// TransposerBase - Base class for interpolation
2013-06-22 20:19:27 +02:00
//
// static function to set interpolation algorithm
void TransposerBase::setAlgorithm(TransposerBase::ALGORITHM a)
2013-06-22 20:19:27 +02:00
{
TransposerBase::algorithm = a;
2013-06-22 20:19:27 +02:00
}
// Transposes the sample rate of the given samples using linear interpolation.
// Returns the number of samples returned in the "dest" buffer
int TransposerBase::transpose(FIFOSampleBuffer &dest, FIFOSampleBuffer &src)
2013-06-22 20:19:27 +02:00
{
int numSrcSamples = src.numSamples();
2015-12-28 13:07:53 +01:00
int sizeDemand = (int)((double)numSrcSamples / rate) + 8;
int numOutput;
SAMPLETYPE *psrc = src.ptrBegin();
SAMPLETYPE *pdest = dest.ptrEnd(sizeDemand);
2013-06-22 20:19:27 +02:00
#ifndef USE_MULTICH_ALWAYS
if (numChannels == 1)
2013-06-22 20:19:27 +02:00
{
numOutput = transposeMono(pdest, psrc, numSrcSamples);
2013-06-22 20:19:27 +02:00
}
else if (numChannels == 2)
2013-06-22 20:19:27 +02:00
{
numOutput = transposeStereo(pdest, psrc, numSrcSamples);
}
else
#endif // USE_MULTICH_ALWAYS
2013-06-22 20:19:27 +02:00
{
assert(numChannels > 0);
numOutput = transposeMulti(pdest, psrc, numSrcSamples);
2013-06-22 20:19:27 +02:00
}
dest.putSamples(numOutput);
src.receiveSamples(numSrcSamples);
return numOutput;
2013-06-22 20:19:27 +02:00
}
TransposerBase::TransposerBase()
2013-06-22 20:19:27 +02:00
{
numChannels = 0;
rate = 1.0f;
2013-06-22 20:19:27 +02:00
}
TransposerBase::~TransposerBase()
2013-06-22 20:19:27 +02:00
{
}
void TransposerBase::setChannels(int channels)
2013-06-22 20:19:27 +02:00
{
numChannels = channels;
resetRegisters();
2013-06-22 20:19:27 +02:00
}
2015-12-28 13:07:53 +01:00
void TransposerBase::setRate(double newRate)
2013-06-22 20:19:27 +02:00
{
rate = newRate;
2013-06-22 20:19:27 +02:00
}
// static factory function
TransposerBase *TransposerBase::newInstance()
2013-06-22 20:19:27 +02:00
{
#ifdef SOUNDTOUCH_INTEGER_SAMPLES
// Notice: For integer arithmetic support only linear algorithm (due to simplest calculus)
return ::new InterpolateLinearInteger;
#else
switch (algorithm)
2013-06-22 20:19:27 +02:00
{
case LINEAR:
return new InterpolateLinearFloat;
2013-06-22 20:19:27 +02:00
case CUBIC:
return new InterpolateCubic;
2013-06-22 20:19:27 +02:00
case SHANNON:
return new InterpolateShannon;
2013-06-22 20:19:27 +02:00
default:
assert(false);
return nullptr;
2013-06-22 20:19:27 +02:00
}
#endif
2013-06-22 20:19:27 +02:00
}