770 lines
21 KiB
C
770 lines
21 KiB
C
|
//$ nobt
|
||
|
//$ nocpp
|
||
|
|
||
|
/**
|
||
|
* @file CDSPResampler.h
|
||
|
*
|
||
|
* @brief The master sample rate converter (resampler) class.
|
||
|
*
|
||
|
* This file includes the master sample rate converter (resampler) class that
|
||
|
* combines all elements of this library into a single front-end class.
|
||
|
*
|
||
|
* r8brain-free-src Copyright (c) 2013-2022 Aleksey Vaneev
|
||
|
* See the "LICENSE" file for license.
|
||
|
*/
|
||
|
|
||
|
#ifndef R8B_CDSPRESAMPLER_INCLUDED
|
||
|
#define R8B_CDSPRESAMPLER_INCLUDED
|
||
|
|
||
|
#include "CDSPHBDownsampler.h"
|
||
|
#include "CDSPHBUpsampler.h"
|
||
|
#include "CDSPBlockConvolver.h"
|
||
|
#include "CDSPFracInterpolator.h"
|
||
|
|
||
|
namespace r8b {
|
||
|
|
||
|
/**
|
||
|
* @brief The master sample rate converter (resampler) class.
|
||
|
*
|
||
|
* This class can be considered the "master" sample rate converter (resampler)
|
||
|
* class since it combines all functionality of this library into a single
|
||
|
* front-end class to perform sample rate conversion to/from any sample rate,
|
||
|
* including non-integer sample rates.
|
||
|
*
|
||
|
* Note that objects of this class can be constructed on the stack as it has a
|
||
|
* small member data size. The default template parameters of this class are
|
||
|
* suited for 27-bit fixed point resampling.
|
||
|
*
|
||
|
* Use the CDSPResampler16 class for 16-bit resampling.
|
||
|
*
|
||
|
* Use the CDSPResampler16IR class for 16-bit impulse response resampling.
|
||
|
*
|
||
|
* Use the CDSPResampler24 class for 24-bit resampling (including 32-bit
|
||
|
* floating point resampling).
|
||
|
*/
|
||
|
|
||
|
class CDSPResampler : public CDSPProcessor
|
||
|
{
|
||
|
public:
|
||
|
/**
|
||
|
* Constructor initalizes the resampler object.
|
||
|
*
|
||
|
* Note that increasing the transition band and decreasing attenuation
|
||
|
* reduces the filter length, this in turn reduces the "input before
|
||
|
* output" delay. However, the filter length has only a minor influence on
|
||
|
* the overall resampling speed.
|
||
|
*
|
||
|
* It should be noted that the ReqAtten specifies the minimal difference
|
||
|
* between the loudest input signal component and the produced aliasing
|
||
|
* artifacts during resampling. For example, if ReqAtten=100 was specified
|
||
|
* when performing 2x upsampling, the analysis of the resulting signal may
|
||
|
* display high-frequency components which are quieter than the loudest
|
||
|
* part of the input signal by only 100 decibel meaning the high-frequency
|
||
|
* part did not become "magically" completely silent after resampling. You
|
||
|
* have to specify a higher ReqAtten value if you need a totally clean
|
||
|
* high-frequency content. On the other hand, it may not be reasonable to
|
||
|
* have a high-frequency content cleaner than the input signal itself: if
|
||
|
* the input signal is 16-bit, setting ReqAtten to 180 will make its
|
||
|
* high-frequency content 24-bit, but the original part of the signal will
|
||
|
* remain 16-bit.
|
||
|
*
|
||
|
* @param SrcSampleRate Source signal sample rate. Both sample rates can
|
||
|
* be specified as a ratio, e.g. SrcSampleRate = 1.0, DstSampleRate = 2.0.
|
||
|
* @param DstSampleRate Destination signal sample rate. The "power of 2"
|
||
|
* ratios between the source and destination sample rates force resampler
|
||
|
* to use several fast "power of 2" resampling steps, without using
|
||
|
* fractional interpolation at all.
|
||
|
* @param aMaxInLen The maximal planned length of the input buffer (in
|
||
|
* samples) that will be passed to the resampler. The resampler relies on
|
||
|
* this value as it allocates intermediate buffers. Input buffers longer
|
||
|
* than this value should never be supplied to the resampler. Note that
|
||
|
* upsampling produces more samples than was provided on input, so at
|
||
|
* higher upsampling ratios it is advisable to use smaller MaxInLen
|
||
|
* values to reduce memory footprint. When downsampling, a larger MaxInLen
|
||
|
* is suggested in order to increase downsampling performance.
|
||
|
* @param ReqTransBand Required transition band, in percent of the
|
||
|
* spectral space of the input signal (or the output signal if
|
||
|
* downsampling is performed) between filter's -3 dB point and the Nyquist
|
||
|
* frequency. The range is from CDSPFIRFilter::getLPMinTransBand() to
|
||
|
* CDSPFIRFilter::getLPMaxTransBand(), inclusive. When upsampling 88200 or
|
||
|
* 96000 audio to a higher sample rates the ReqTransBand can be
|
||
|
* considerably increased, up to 30. The selection of ReqTransBand depends
|
||
|
* on the level of desire to preserve the high-frequency content. While
|
||
|
* values 0.5 to 2 are extremely "greedy" settings, not necessary in most
|
||
|
* cases, values 2 to 3 can be used in most cases. Values 3 to 4 are
|
||
|
* relaxed settings, but they still offer a flat frequency response up to
|
||
|
* 21kHz with 44.1k source or destination sample rate.
|
||
|
* @param ReqAtten Required stop-band attenuation in decibel, in the
|
||
|
* range CDSPFIRFilter::getLPMinAtten() to CDSPFIRFilter::getLPMaxAtten(),
|
||
|
* inclusive. The actual attenuation may be 0.40-4.46 dB higher. The
|
||
|
* general formula for selecting the ReqAtten is 6.02 * Bits + 40, where
|
||
|
* "Bits" is the bit resolution (e.g. 16, 24), "40" is an added resolution
|
||
|
* for dynamic signals; this value can be decreased to 20 to 10 if the
|
||
|
* signal being resampled is non-dynamic (e.g., an impulse response or
|
||
|
* filter, with a non-steep frequency response).
|
||
|
* @param ReqPhase Required filter's phase response. Note that this
|
||
|
* setting does not affect interpolator's phase response which is always
|
||
|
* linear-phase. Also note that if the "power of 2" resampling was engaged
|
||
|
* by the resampler together with the minimum-phase response, the audio
|
||
|
* stream may become fractionally delayed, depending on the minimum-phase
|
||
|
* filter's actual fractional delay. Linear-phase filters do not have
|
||
|
* fractional delay.
|
||
|
* @see CDSPFIRFilterCache::getLPFilter()
|
||
|
*/
|
||
|
|
||
|
CDSPResampler( const double SrcSampleRate, const double DstSampleRate,
|
||
|
const int aMaxInLen, const double ReqTransBand = 2.0,
|
||
|
const double ReqAtten = 206.91,
|
||
|
const EDSPFilterPhaseResponse ReqPhase = fprLinearPhase )
|
||
|
: StepCapacity( 0 )
|
||
|
, StepCount( 0 )
|
||
|
, MaxInLen( aMaxInLen )
|
||
|
, CurMaxOutLen( aMaxInLen )
|
||
|
, LatencyFrac( 0.0 )
|
||
|
{
|
||
|
R8BASSERT( SrcSampleRate > 0.0 );
|
||
|
R8BASSERT( DstSampleRate > 0.0 );
|
||
|
R8BASSERT( MaxInLen > 0 );
|
||
|
|
||
|
R8BCONSOLE( "* CDSPResampler: src=%.1f dst=%.1f len=%i tb=%.1f "
|
||
|
"att=%.2f ph=%i\n", SrcSampleRate, DstSampleRate, aMaxInLen,
|
||
|
ReqTransBand, ReqAtten, (int) ReqPhase );
|
||
|
|
||
|
if( SrcSampleRate == DstSampleRate )
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
TmpBufCapacities[ 0 ] = 0;
|
||
|
TmpBufCapacities[ 1 ] = 0;
|
||
|
CurTmpBuf = 0;
|
||
|
|
||
|
// Try some common efficient ratios requiring only a single step.
|
||
|
|
||
|
const int CommonRatioCount = 5;
|
||
|
const int CommonRatios[ CommonRatioCount ][ 2 ] = {
|
||
|
{ 1, 2 },
|
||
|
{ 1, 3 },
|
||
|
{ 2, 3 },
|
||
|
{ 3, 2 },
|
||
|
{ 3, 4 }
|
||
|
};
|
||
|
|
||
|
int i;
|
||
|
|
||
|
for( i = 0; i < CommonRatioCount; i++ )
|
||
|
{
|
||
|
const int num = CommonRatios[ i ][ 0 ];
|
||
|
const int den = CommonRatios[ i ][ 1 ];
|
||
|
|
||
|
if( SrcSampleRate * num == DstSampleRate * den )
|
||
|
{
|
||
|
addProcessor( new CDSPBlockConvolver(
|
||
|
CDSPFIRFilterCache :: getLPFilter(
|
||
|
1.0 / ( num > den ? num : den ), ReqTransBand,
|
||
|
ReqAtten, ReqPhase, num ), num, den, LatencyFrac ));
|
||
|
|
||
|
createTmpBuffers();
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Try whole-number power-of-2 or 3*power-of-2 upsampling.
|
||
|
|
||
|
for( i = 2; i <= 3; i++ )
|
||
|
{
|
||
|
bool WasFound = false;
|
||
|
int c = 0;
|
||
|
|
||
|
while( true )
|
||
|
{
|
||
|
const double NewSR = SrcSampleRate * ( i << c );
|
||
|
|
||
|
if( NewSR == DstSampleRate )
|
||
|
{
|
||
|
WasFound = true;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if( NewSR > DstSampleRate )
|
||
|
{
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
c++;
|
||
|
}
|
||
|
|
||
|
if( WasFound )
|
||
|
{
|
||
|
addProcessor( new CDSPBlockConvolver(
|
||
|
CDSPFIRFilterCache :: getLPFilter( 1.0 / i, ReqTransBand,
|
||
|
ReqAtten, ReqPhase, i ), i, 1, LatencyFrac ));
|
||
|
|
||
|
const bool IsThird = ( i == 3 );
|
||
|
|
||
|
for( i = 0; i < c; i++ )
|
||
|
{
|
||
|
addProcessor( new CDSPHBUpsampler( ReqAtten, i, IsThird,
|
||
|
LatencyFrac ));
|
||
|
}
|
||
|
|
||
|
createTmpBuffers();
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if( DstSampleRate * 2.0 > SrcSampleRate )
|
||
|
{
|
||
|
// Upsampling or fractional downsampling down to 2X.
|
||
|
|
||
|
const double NormFreq = ( DstSampleRate > SrcSampleRate ? 0.5 :
|
||
|
0.5 * DstSampleRate / SrcSampleRate );
|
||
|
|
||
|
addProcessor( new CDSPBlockConvolver(
|
||
|
CDSPFIRFilterCache :: getLPFilter( NormFreq, ReqTransBand,
|
||
|
ReqAtten, ReqPhase, 2.0 ), 2, 1, LatencyFrac ));
|
||
|
|
||
|
// Try intermediate interpolated resampling with subsequent 2X
|
||
|
// or 3X upsampling.
|
||
|
|
||
|
const double tbw = 0.0175; // Intermediate filter's transition
|
||
|
// band extension coefficient.
|
||
|
const double ThreshSampleRate = SrcSampleRate /
|
||
|
( 1.0 - tbw * ReqTransBand ); // Make sure intermediate
|
||
|
// filter's transition band is not steeper than ReqTransBand
|
||
|
// (this keeps the latency under control).
|
||
|
|
||
|
int c = 0;
|
||
|
int div = 1;
|
||
|
|
||
|
while( true )
|
||
|
{
|
||
|
const int ndiv = div * 2;
|
||
|
|
||
|
if( DstSampleRate < ThreshSampleRate * ndiv )
|
||
|
{
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
div = ndiv;
|
||
|
c++;
|
||
|
}
|
||
|
|
||
|
int c2 = 0;
|
||
|
int div2 = 1;
|
||
|
|
||
|
while( true )
|
||
|
{
|
||
|
const int ndiv = div * ( c2 == 0 ? 3 : 2 );
|
||
|
|
||
|
if( DstSampleRate < ThreshSampleRate * ndiv )
|
||
|
{
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
div2 = ndiv;
|
||
|
c2++;
|
||
|
}
|
||
|
|
||
|
const double SrcSampleRate2 = SrcSampleRate * 2.0;
|
||
|
int tmp1;
|
||
|
int tmp2;
|
||
|
|
||
|
if( c == 1 && getWholeStepping( SrcSampleRate2, DstSampleRate,
|
||
|
tmp1, tmp2 ))
|
||
|
{
|
||
|
// Do not use intermediate interpolation if whole stepping is
|
||
|
// available as it performs very fast.
|
||
|
|
||
|
c = 0;
|
||
|
}
|
||
|
|
||
|
if( c > 0 )
|
||
|
{
|
||
|
// Add steps using intermediate interpolation.
|
||
|
|
||
|
int num;
|
||
|
|
||
|
if( c2 > 0 && div2 > div )
|
||
|
{
|
||
|
div = div2;
|
||
|
c = c2;
|
||
|
num = 3;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
num = 2;
|
||
|
}
|
||
|
|
||
|
addProcessor( new CDSPFracInterpolator( SrcSampleRate2 * div,
|
||
|
DstSampleRate, ReqAtten, false, LatencyFrac ));
|
||
|
|
||
|
double tb = ( 1.0 - SrcSampleRate * div / DstSampleRate ) /
|
||
|
tbw; // Divide TransBand by a constant that assures a
|
||
|
// linear response in the pass-band.
|
||
|
|
||
|
if( tb > CDSPFIRFilter :: getLPMaxTransBand() )
|
||
|
{
|
||
|
tb = CDSPFIRFilter :: getLPMaxTransBand();
|
||
|
}
|
||
|
|
||
|
addProcessor( new CDSPBlockConvolver(
|
||
|
CDSPFIRFilterCache :: getLPFilter( 1.0 / num, tb,
|
||
|
ReqAtten, ReqPhase, num ), num, 1, LatencyFrac ));
|
||
|
|
||
|
const bool IsThird = ( num == 3 );
|
||
|
|
||
|
for( i = 1; i < c; i++ )
|
||
|
{
|
||
|
addProcessor( new CDSPHBUpsampler( ReqAtten, i - 1,
|
||
|
IsThird, LatencyFrac ));
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
addProcessor( new CDSPFracInterpolator( SrcSampleRate2,
|
||
|
DstSampleRate, ReqAtten, false, LatencyFrac ));
|
||
|
}
|
||
|
|
||
|
createTmpBuffers();
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// Use downsampling steps, including power-of-2 downsampling.
|
||
|
|
||
|
double CheckSR = DstSampleRate * 4.0;
|
||
|
int c = 0;
|
||
|
double FinGain = 1.0;
|
||
|
|
||
|
while( CheckSR <= SrcSampleRate )
|
||
|
{
|
||
|
c++;
|
||
|
CheckSR *= 2.0;
|
||
|
FinGain *= 0.5;
|
||
|
}
|
||
|
|
||
|
const int SrcSRDiv = ( 1 << c );
|
||
|
int downf;
|
||
|
double NormFreq = 0.5;
|
||
|
bool UseInterp = true;
|
||
|
bool IsThird = false;
|
||
|
|
||
|
for( downf = 2; downf <= 3; downf++ )
|
||
|
{
|
||
|
if( DstSampleRate * SrcSRDiv * downf == SrcSampleRate )
|
||
|
{
|
||
|
NormFreq = 1.0 / downf;
|
||
|
UseInterp = false;
|
||
|
IsThird = ( downf == 3 );
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if( UseInterp )
|
||
|
{
|
||
|
downf = 1;
|
||
|
NormFreq = DstSampleRate * SrcSRDiv / SrcSampleRate;
|
||
|
IsThird = ( NormFreq * 3.0 <= 1.0 );
|
||
|
}
|
||
|
|
||
|
for( i = 0; i < c; i++ )
|
||
|
{
|
||
|
// Use a fixed very relaxed 2X downsampling filters, that at
|
||
|
// the final stage only guarantees stop-band between 0.75 and
|
||
|
// pi. 0.5-0.75 range will be aliased to 0.25-0.5 range which
|
||
|
// will then be filtered out by the final filter.
|
||
|
|
||
|
addProcessor( new CDSPHBDownsampler( ReqAtten, c - 1 - i, IsThird,
|
||
|
LatencyFrac ));
|
||
|
}
|
||
|
|
||
|
addProcessor( new CDSPBlockConvolver(
|
||
|
CDSPFIRFilterCache :: getLPFilter( NormFreq, ReqTransBand,
|
||
|
ReqAtten, ReqPhase, FinGain ), 1, downf, LatencyFrac ));
|
||
|
|
||
|
if( UseInterp )
|
||
|
{
|
||
|
addProcessor( new CDSPFracInterpolator( SrcSampleRate,
|
||
|
DstSampleRate * SrcSRDiv, ReqAtten, IsThird, LatencyFrac ));
|
||
|
}
|
||
|
|
||
|
createTmpBuffers();
|
||
|
}
|
||
|
|
||
|
virtual ~CDSPResampler()
|
||
|
{
|
||
|
int i;
|
||
|
|
||
|
for( i = 0; i < StepCount; i++ )
|
||
|
{
|
||
|
delete Steps[ i ];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
virtual int getLatency() const
|
||
|
{
|
||
|
return( 0 );
|
||
|
}
|
||
|
|
||
|
virtual double getLatencyFrac() const
|
||
|
{
|
||
|
return( LatencyFrac );
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* This function ignores the supplied parameter and returns the maximal
|
||
|
* output buffer length that depends on the MaxInLen supplied to the
|
||
|
* constructor.
|
||
|
*/
|
||
|
|
||
|
virtual int getMaxOutLen( const int/* MaxInLen */ ) const
|
||
|
{
|
||
|
return( CurMaxOutLen );
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Function clears (resets) the state of *this object and returns it to
|
||
|
* the state after construction. All input data accumulated in the
|
||
|
* internal buffer so far will be discarded.
|
||
|
*
|
||
|
* This function makes it possible to use *this object for converting
|
||
|
* separate streams from the same source sample rate to the same
|
||
|
* destination sample rate without reconstructing the object. It is more
|
||
|
* efficient to clear the state of the resampler object than to destroy it
|
||
|
* and create a new object.
|
||
|
*/
|
||
|
|
||
|
virtual void clear()
|
||
|
{
|
||
|
int i;
|
||
|
|
||
|
for( i = 0; i < StepCount; i++ )
|
||
|
{
|
||
|
Steps[ i ] -> clear();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Function performs sample rate conversion.
|
||
|
*
|
||
|
* If the source and destination sample rates are equal, the resampler
|
||
|
* will do nothing and will simply return the input buffer unchanged.
|
||
|
*
|
||
|
* You do not need to allocate an intermediate output buffer for use with
|
||
|
* this function. If required, the resampler will allocate a suitable
|
||
|
* intermediate output buffer itself.
|
||
|
*
|
||
|
* @param ip0 Input buffer. This buffer is never used as output buffer by
|
||
|
* this function. This pointer may be returned in "op0" if no resampling
|
||
|
* is happening (source sample rate equals destination sample rate).
|
||
|
* @param l The number of samples available in the input buffer. Should
|
||
|
* not exceed the MaxInLen supplied in the constructor.
|
||
|
* @param[out] op0 This variable receives the pointer to the resampled
|
||
|
* data. On function's return, this pointer points to *this object's
|
||
|
* internal buffer. In real-time applications it is suggested to pass this
|
||
|
* pointer to the next output audio block and consume any data left from
|
||
|
* the previous output audio block first before calling the process()
|
||
|
* function again. The buffer pointed to by the "op0" on return is owned
|
||
|
* by the resampler, so it should not be freed by the caller.
|
||
|
* @return The number of samples available in the "op0" output buffer. If
|
||
|
* the data from the output buffer "op0" is going to be written to a
|
||
|
* bigger output buffer, it is suggested to check the returned number of
|
||
|
* samples so that no overflow of the bigger output buffer happens.
|
||
|
*/
|
||
|
|
||
|
virtual int process( double* ip0, int l, double*& op0 )
|
||
|
{
|
||
|
R8BASSERT( l >= 0 );
|
||
|
|
||
|
double* ip = ip0;
|
||
|
int i;
|
||
|
|
||
|
for( i = 0; i < StepCount; i++ )
|
||
|
{
|
||
|
double* op = TmpBufs[ i & 1 ];
|
||
|
l = Steps[ i ] -> process( ip, l, op );
|
||
|
ip = op;
|
||
|
}
|
||
|
|
||
|
op0 = ip;
|
||
|
return( l );
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Function performs resampling of an input sample buffer of the specified
|
||
|
* length in the "one-shot" mode. This function can be useful when impulse
|
||
|
* response resampling is required.
|
||
|
*
|
||
|
* @param ip Input buffer pointer.
|
||
|
* @param iplen Length of the input buffer in samples.
|
||
|
* @param[out] op Output buffer pointer.
|
||
|
* @param oplen Length of the output buffer in samples.
|
||
|
* @tparam Tin Input buffer's element type.
|
||
|
* @tparam Tout Output buffer's element type.
|
||
|
*/
|
||
|
|
||
|
template< typename Tin, typename Tout >
|
||
|
void oneshot( const Tin* ip, int iplen, Tout* op, int oplen )
|
||
|
{
|
||
|
CFixedBuffer< double > Buf( MaxInLen );
|
||
|
bool IsZero = false;
|
||
|
|
||
|
while( oplen > 0 )
|
||
|
{
|
||
|
int rc;
|
||
|
double* p;
|
||
|
int i;
|
||
|
|
||
|
if( iplen == 0 )
|
||
|
{
|
||
|
rc = MaxInLen;
|
||
|
p = &Buf[ 0 ];
|
||
|
|
||
|
if( !IsZero )
|
||
|
{
|
||
|
IsZero = true;
|
||
|
memset( p, 0, MaxInLen * sizeof( p[ 0 ]));
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
rc = min( iplen, MaxInLen );
|
||
|
|
||
|
if( sizeof( Tin ) == sizeof( double ))
|
||
|
{
|
||
|
p = (double*) ip;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
p = &Buf[ 0 ];
|
||
|
|
||
|
for( i = 0; i < rc; i++ )
|
||
|
{
|
||
|
p[ i ] = ip[ i ];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
ip += rc;
|
||
|
iplen -= rc;
|
||
|
}
|
||
|
|
||
|
double* op0;
|
||
|
int wc = process( p, rc, op0 );
|
||
|
wc = min( oplen, wc );
|
||
|
|
||
|
for( i = 0; i < wc; i++ )
|
||
|
{
|
||
|
op[ i ] = (Tout) op0[ i ];
|
||
|
}
|
||
|
|
||
|
op += wc;
|
||
|
oplen -= wc;
|
||
|
}
|
||
|
|
||
|
clear();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Function obtains overall input sample count required to produce first
|
||
|
* output sample. Function works by iteratively passing 1 sample at a time
|
||
|
* until output begins. This is a relatively CPU-consuming operation. This
|
||
|
* function should be called after the clear() function call or after
|
||
|
* object's construction. The function itself calls the clear() function
|
||
|
* before return.
|
||
|
*
|
||
|
* Note that it is advisable to cache the value returned by this function,
|
||
|
* for each SrcSampleRate/DstSampleRate pair, if it is called frequently.
|
||
|
*/
|
||
|
|
||
|
int getInLenBeforeOutStart()
|
||
|
{
|
||
|
int inc = 0;
|
||
|
|
||
|
while( true )
|
||
|
{
|
||
|
double ins = 0.0;
|
||
|
double* op;
|
||
|
|
||
|
if( process( &ins, 1, op ) > 0 )
|
||
|
{
|
||
|
clear();
|
||
|
return( inc );
|
||
|
}
|
||
|
|
||
|
inc++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private:
|
||
|
CFixedBuffer< CDSPProcessor* > Steps; ///< Array of processing steps.
|
||
|
///<
|
||
|
int StepCapacity; ///< The capacity of the Steps array.
|
||
|
///<
|
||
|
int StepCount; ///< The number of created processing steps.
|
||
|
///<
|
||
|
int MaxInLen; ///< Maximal input length.
|
||
|
///<
|
||
|
CFixedBuffer< double > TmpBufAll; ///< Buffer containing both temporary
|
||
|
///< buffers.
|
||
|
///<
|
||
|
double* TmpBufs[ 2 ]; ///< Temporary output buffers.
|
||
|
///<
|
||
|
int TmpBufCapacities[ 2 ]; ///< Capacities of temporary buffers, updated
|
||
|
///< during processing steps building.
|
||
|
///<
|
||
|
int CurTmpBuf; ///< Current temporary buffer.
|
||
|
///<
|
||
|
int CurMaxOutLen; ///< Current maximal output length.
|
||
|
///<
|
||
|
double LatencyFrac; ///< Current fractional latency. After object's
|
||
|
///< construction, equals to the remaining fractional latency in the
|
||
|
///< output.
|
||
|
///<
|
||
|
|
||
|
/**
|
||
|
* Function adds processor, updates MaxOutLen variable and adjusts length
|
||
|
* of temporary internal buffers.
|
||
|
*
|
||
|
* @param Proc Processor to add. This pointer is inherited and will be
|
||
|
* destroyed on *this object's destruction.
|
||
|
*/
|
||
|
|
||
|
void addProcessor( CDSPProcessor* const Proc )
|
||
|
{
|
||
|
if( StepCount == StepCapacity )
|
||
|
{
|
||
|
// Reallocate and increase Steps array's capacity.
|
||
|
|
||
|
const int NewCapacity = StepCapacity + 8;
|
||
|
Steps.realloc( StepCapacity, NewCapacity );
|
||
|
StepCapacity = NewCapacity;
|
||
|
}
|
||
|
|
||
|
LatencyFrac = Proc -> getLatencyFrac();
|
||
|
CurMaxOutLen = Proc -> getMaxOutLen( CurMaxOutLen );
|
||
|
|
||
|
if( CurMaxOutLen > TmpBufCapacities[ CurTmpBuf ])
|
||
|
{
|
||
|
TmpBufCapacities[ CurTmpBuf ] = CurMaxOutLen;
|
||
|
}
|
||
|
|
||
|
CurTmpBuf ^= 1;
|
||
|
|
||
|
Steps[ StepCount ] = Proc;
|
||
|
StepCount++;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Function creates temporary buffers.
|
||
|
*/
|
||
|
|
||
|
void createTmpBuffers()
|
||
|
{
|
||
|
const int ol = TmpBufCapacities[ 0 ] + TmpBufCapacities[ 1 ];
|
||
|
|
||
|
if( ol > 0 )
|
||
|
{
|
||
|
TmpBufAll.alloc( ol );
|
||
|
TmpBufs[ 0 ] = &TmpBufAll[ 0 ];
|
||
|
TmpBufs[ 1 ] = &TmpBufAll[ TmpBufCapacities[ 0 ]];
|
||
|
}
|
||
|
|
||
|
R8BCONSOLE( "* CDSPResampler: init done\n" );
|
||
|
}
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* @brief The resampler class for 16-bit resampling.
|
||
|
*
|
||
|
* This class defines resampling parameters suitable for 16-bit resampling,
|
||
|
* using linear-phase low-pass filter. See the r8b::CDSPResampler class for
|
||
|
* details.
|
||
|
*/
|
||
|
|
||
|
class CDSPResampler16 : public CDSPResampler
|
||
|
{
|
||
|
public:
|
||
|
/**
|
||
|
* Constructor initializes the 16-bit resampler. See the
|
||
|
* r8b::CDSPResampler class for details.
|
||
|
*
|
||
|
* @param SrcSampleRate Source signal sample rate.
|
||
|
* @param DstSampleRate Destination signal sample rate.
|
||
|
* @param aMaxInLen The maximal planned length of the input buffer (in
|
||
|
* samples) that will be passed to the resampler.
|
||
|
* @param ReqTransBand Required transition band, in percent.
|
||
|
*/
|
||
|
|
||
|
CDSPResampler16( const double SrcSampleRate, const double DstSampleRate,
|
||
|
const int aMaxInLen, const double ReqTransBand = 2.0 )
|
||
|
: CDSPResampler( SrcSampleRate, DstSampleRate, aMaxInLen, ReqTransBand,
|
||
|
136.45, fprLinearPhase )
|
||
|
{
|
||
|
}
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* @brief The resampler class for 16-bit impulse response resampling.
|
||
|
*
|
||
|
* This class defines resampling parameters suitable for 16-bit impulse
|
||
|
* response resampling, using linear-phase low-pass filter. Impulse responses
|
||
|
* are non-dynamic signals, and thus need resampler with a lesser SNR. See the
|
||
|
* r8b::CDSPResampler class for details.
|
||
|
*/
|
||
|
|
||
|
class CDSPResampler16IR : public CDSPResampler
|
||
|
{
|
||
|
public:
|
||
|
/**
|
||
|
* Constructor initializes the 16-bit impulse response resampler. See the
|
||
|
* r8b::CDSPResampler class for details.
|
||
|
*
|
||
|
* @param SrcSampleRate Source signal sample rate.
|
||
|
* @param DstSampleRate Destination signal sample rate.
|
||
|
* @param aMaxInLen The maximal planned length of the input buffer (in
|
||
|
* samples) that will be passed to the resampler.
|
||
|
* @param ReqTransBand Required transition band, in percent.
|
||
|
*/
|
||
|
|
||
|
CDSPResampler16IR( const double SrcSampleRate, const double DstSampleRate,
|
||
|
const int aMaxInLen, const double ReqTransBand = 2.0 )
|
||
|
: CDSPResampler( SrcSampleRate, DstSampleRate, aMaxInLen, ReqTransBand,
|
||
|
109.56, fprLinearPhase )
|
||
|
{
|
||
|
}
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* @brief The resampler class for 24-bit resampling.
|
||
|
*
|
||
|
* This class defines resampling parameters suitable for 24-bit resampling
|
||
|
* (including 32-bit floating point resampling), using linear-phase low-pass
|
||
|
* filter. See the r8b::CDSPResampler class for details.
|
||
|
*/
|
||
|
|
||
|
class CDSPResampler24 : public CDSPResampler
|
||
|
{
|
||
|
public:
|
||
|
/**
|
||
|
* Constructor initializes the 24-bit resampler (including 32-bit floating
|
||
|
* point). See the r8b::CDSPResampler class for details.
|
||
|
*
|
||
|
* @param SrcSampleRate Source signal sample rate.
|
||
|
* @param DstSampleRate Destination signal sample rate.
|
||
|
* @param aMaxInLen The maximal planned length of the input buffer (in
|
||
|
* samples) that will be passed to the resampler.
|
||
|
* @param ReqTransBand Required transition band, in percent.
|
||
|
*/
|
||
|
|
||
|
CDSPResampler24( const double SrcSampleRate, const double DstSampleRate,
|
||
|
const int aMaxInLen, const double ReqTransBand = 2.0 )
|
||
|
: CDSPResampler( SrcSampleRate, DstSampleRate, aMaxInLen, ReqTransBand,
|
||
|
180.15, fprLinearPhase )
|
||
|
{
|
||
|
}
|
||
|
};
|
||
|
|
||
|
} // namespace r8b
|
||
|
|
||
|
#endif // R8B_CDSPRESAMPLER_INCLUDED
|