|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495 |
- //$ nocpp
-
- /**
- * @file CDSPFracInterpolator.h
- *
- * @brief Fractional delay interpolator and filter bank classes.
- *
- * This file includes fractional delay interpolator class.
- *
- * r8brain-free-src Copyright (c) 2013-2014 Aleksey Vaneev
- * See the "License.txt" file for license.
- */
-
- #ifndef R8B_CDSPFRACINTERPOLATOR_INCLUDED
- #define R8B_CDSPFRACINTERPOLATOR_INCLUDED
-
- #include "CDSPSincFilterGen.h"
- #include "CDSPProcessor.h"
-
- namespace r8b
- {
-
- /**
- * @brief Sinc function-based fractional delay filter bank class.
- *
- * Class implements storage and initialization of a bank of sinc-based
- * fractional delay filters, expressed as 1st, 2nd or 3rd order polynomial
- * interpolation coefficients. The filters are windowed by either the "Vaneev"
- * or "Kaiser" power-raised window function. The FilterLen and FilterFracs
- * parameters can be varied freely without breaking the resampler.
- *
- * @param FilterLen Specifies the number of samples (taps) each fractional
- * delay filter should have. This must be an even value, the minimal value for
- * FilterLen is 6, the maximal value is 30. To achieve a higher resampling
- * precision, the oversampling should be used in the first place instead of
- * using a higher FilterLen value. The lower this value is the lower the
- * signal-to-noise performance of the interpolator will be. Each FilterLen
- * decrease by 2 decreases SNR by approximately 12 to 14 decibel.
- * @param FilterFracs The number of fractional delay positions to sample. For
- * a high signal-to-noise ratio this has to be a larger value. The larger the
- * FilterLen is the larger the FilterFracs should be. Approximate FilterLen to
- * FilterFracs correspondence (for 2nd order interpolation only): 6:11, 8:17,
- * 10:23, 12:41, 14:67, 16:97, 18:137, 20:211, 22:353, 24:673, 26:1051,
- * 28:1733, 30:2833. The FilterFracs can be considerably reduced with 3rd
- * order interpolation in use. In order to get consistent results when
- * resampling to/from different sample rates, it is suggested to set this
- * parameter to a suitable prime number.
- * @param ElementSize The size of each filter's tap, in "double" values. This
- * parameter corresponds to the complexity of interpolation. 4 should be set
- * for 3rd order, 3 for 2nd order, 2 for linear interpolation.
- * @param InterpPoints The number of points the interpolation is based on.
- * This value should not be confused with the ElementSize. Set to 2 for linear
- * interpolation.
- */
-
- template <int FilterLen, int FilterFracs, int ElementSize, int InterpPoints>
- class CDSPFracDelayFilterBank : public R8B_BASECLASS
- {
- public:
- CDSPFracDelayFilterBank()
- {
- R8BASSERT(FilterLen >= 6);
- R8BASSERT(FilterLen <= 30);
- R8BASSERT(( FilterLen & 1 ) == 0);
- R8BASSERT(FilterFracs > 0);
- R8BASSERT(ElementSize >= 2 && ElementSize <= 4);
- R8BASSERT(InterpPoints == 2 || InterpPoints == 8);
-
- calculate();
- }
-
- /**
- * Function calculates the filter bank.
- *
- * @param Params Window function's parameters. If NULL then the built-in
- * table values for the current FilterLen will be used.
- */
-
- void calculate(const double* const Params = nullptr)
- {
- CDSPSincFilterGen sinc;
- sinc.Len2 = FilterLen / 2;
-
- double* p = Table;
- const int pc2 = InterpPoints / 2;
- int i;
-
- if (FilterLen <= 20)
- {
- for (i = -pc2 + 1; i <= FilterFracs + pc2; ++i)
- {
- sinc.FracDelay = double(FilterFracs - i) / FilterFracs;
- sinc.initFrac(CDSPSincFilterGen::wftVaneev, Params);
- sinc.generateFrac(p, &CDSPSincFilterGen::calcWindowVaneev,
- ElementSize);
-
- normalizeFIRFilter(p, FilterLen, 1.0, ElementSize);
- p += FilterSize;
- }
- }
- else
- {
- for (i = -pc2 + 1; i <= FilterFracs + pc2; ++i)
- {
- sinc.FracDelay = double(FilterFracs - i) / FilterFracs;
- sinc.initFrac(CDSPSincFilterGen::wftKaiser, Params, true);
- sinc.generateFrac(p, &CDSPSincFilterGen::calcWindowKaiser,
- ElementSize);
-
- normalizeFIRFilter(p, FilterLen, 1.0, ElementSize);
- p += FilterSize;
- }
- }
-
- const int TablePos2 = FilterSize;
- const int TablePos3 = FilterSize * 2;
- const int TablePos4 = FilterSize * 3;
- const int TablePos5 = FilterSize * 4;
- const int TablePos6 = FilterSize * 5;
- const int TablePos7 = FilterSize * 6;
- const int TablePos8 = FilterSize * 7;
- double* const TableEnd = Table + (FilterFracs + 1) * FilterSize;
- p = Table;
-
- if (InterpPoints == 8)
- {
- if (ElementSize == 3)
- {
- // Calculate 2nd order spline (polynomial) interpolation
- // coefficients using 8 points.
-
- while (p < TableEnd)
- {
- calcSpline2p8Coeffs(p, p[0], p[TablePos2],
- p[TablePos3], p[TablePos4], p[TablePos5],
- p[TablePos6], p[TablePos7], p[TablePos8]);
-
- p += ElementSize;
- }
- }
- else if (ElementSize == 4)
- {
- // Calculate 3rd order spline (polynomial) interpolation
- // coefficients using 8 points.
-
- while (p < TableEnd)
- {
- calcSpline3p8Coeffs(p, p[0], p[TablePos2],
- p[TablePos3], p[TablePos4], p[TablePos5],
- p[TablePos6], p[TablePos7], p[TablePos8]);
-
- p += ElementSize;
- }
- }
- }
- else
- {
- // Calculate linear interpolation coefficients.
-
- while (p < TableEnd)
- {
- p[1] = p[TablePos2] - p[0];
- p += ElementSize;
- }
- }
- }
-
- /**
- * @param i Filter index, in the range 0 to FilterFracs, inclusive.
- * @return Reference to the filter.
- */
-
- const double& operator [](const int i) const
- {
- R8BASSERT(i >= 0 && i <= FilterFracs);
-
- return (Table[i * FilterSize]);
- }
-
- private:
- static const int FilterSize = FilterLen * ElementSize; ///< This constant
- ///< specifies the "size" of a single filter in "double" elements.
- ///<
- double Table[ FilterSize * (FilterFracs + InterpPoints)]; ///< The
- ///< table of fractional delay filters for all discrete fractional
- ///< x = 0..1 sample positions, and interpolation coefficients.
- ///<
- };
-
- /**
- * @brief Fractional delay filter bank-based interpolator class.
- *
- * Class implements the fractional delay interpolator. This implementation at
- * first puts the input signal into a ring buffer and then performs
- * interpolation. The interpolation is performed using sinc-based fractional
- * delay filters. These filters are contained in a bank, and for higher
- * precision they are interpolated between adjacent filters.
- *
- * To increase sample timing precision, this class uses "resettable counter"
- * approach. This gives less than "1 per 100 billion" sample timing error when
- * converting 44100 to 48000 sample rate.
- *
- * VERY IMPORTANT: the interpolation step should not exceed FilterLen / 2 + 1
- * samples or the algorithm in its current form will fail. However, this
- * condition can be easily met if the input signal is suitably downsampled
- * first before the interpolation is performed.
- *
- * @param FilterLen Specifies the number of samples (taps) each fractional
- * delay filter should have. See the r8b::CDSPFracDelayFilterBank class for
- * more details.
- * @param FilterFracs The number of fractional delay positions to sample. See
- * the r8b::CDSPFracDelayFilterBank class for more details.
- */
-
- template <int FilterLen, int FilterFracs>
- class CDSPFracInterpolator final : public CDSPProcessor
- {
- public:
- /**
- * Constructor initalizes the interpolator. It is important to call the
- * getMaxOutLen() function afterwards to obtain the optimal output buffer
- * length.
- *
- * @param aSrcSampleRate Source sample rate.
- * @param aDstSampleRate Destination sample rate.
- * @param aInitFracPos Initial fractional position, in samples, in the
- * range [0; 1). A non-zero value can be specified to remove the
- * fractional delay introduced by a minimum-phase filter. This value is
- * usually equal to the CDSPBlockConvolver.getLatencyFrac() value.
- */
-
- CDSPFracInterpolator(const double aSrcSampleRate,
- const double aDstSampleRate, const double aInitFracPos)
- : SrcSampleRate(aSrcSampleRate)
- , DstSampleRate(aDstSampleRate)
- , InitFracPos(aInitFracPos)
- {
- R8BASSERT(SrcSampleRate > 0.0);
- R8BASSERT(DstSampleRate > 0.0);
- R8BASSERT(InitFracPos >= 0.0 && InitFracPos < 1.0);
- R8BASSERT(BufLenBits >= 5);
- R8BASSERT(( 1 << BufLenBits ) >= FilterLen * 3);
-
- clear();
- }
-
- int getLatency() const override { return (0); }
-
- double getLatencyFrac() const override { return (0.0); }
-
- int getInLenBeforeOutStart(const int NextInLen) const override { return (FilterLenD2 + NextInLen); }
-
- int getMaxOutLen(const int MaxInLen) const override
- {
- R8BASSERT(MaxInLen >= 0);
-
- return ((int)ceil(MaxInLen * DstSampleRate / SrcSampleRate) + 1);
- }
-
- /**
- * Function changes the destination sample rate "on the fly". Note that
- * the getMaxOutLen() function may needed to be called after calling this
- * function as the maximal number of output samples produced by the
- * interpolator depends on the destination sample rate.
- *
- * It can be a useful approach to construct *this object passing the
- * maximal possible destination sample rate to the constructor, obtaining
- * the getMaxOutLen() value and then setting the destination sample rate
- * to whatever lower value is needed.
- *
- * It is advisable to change the sample rate in small increments, and as
- * rarely as possible: e.g. every several samples.
- *
- * @param NewDstSampleRate New destination sample rate.
- */
-
- void setDstSampleRate(const double NewDstSampleRate)
- {
- R8BASSERT(DstSampleRate > 0.0);
-
- DstSampleRate = NewDstSampleRate;
- InCounter = 0;
- InPosInt = 0;
- InPosShift = InPosFrac;
- }
-
- /**
- * 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.
- *
- * Note that the destination sample rate will remain unchanged, even if it
- * was changed since the time of *this object's construction.
- */
- void clear() override
- {
- BufLeft = 0;
- WritePos = 0;
- ReadPos = BufLen - FilterLenD2Minus1; // Set "read" position to
- // account for filter's latency at zero fractional delay which
- // equals to FilterLenD2Minus1.
-
- memset(&Buf[ReadPos], 0, FilterLenD2Minus1 * sizeof(double));
-
- InCounter = 0;
- InPosInt = 0;
- InPosFrac = InitFracPos;
- InPosShift = InitFracPos;
- }
-
- int process(double* ip, int l, double*& op0) override
- {
- R8BASSERT(l >= 0);
- R8BASSERT(ip != op0 || l == 0 || SrcSampleRate > DstSampleRate);
-
- double* op = op0;
-
- while (l > 0)
- {
- // Add new input samples to both halves of the ring buffer.
-
- const int b = min(min(l, BufLen - WritePos),
- BufLeftMax - BufLeft);
-
- double* const wp1 = Buf + WritePos;
- double* const wp2 = wp1 + BufLen;
- int i;
-
- for (i = 0; i < b; ++i)
- {
- wp1[i] = ip[i];
- wp2[i] = ip[i];
- }
-
- ip += b;
- WritePos = (WritePos + b) & BufLenMask;
- l -= b;
- BufLeft += b;
-
- // Produce as many output samples as possible.
-
- while (BufLeft >= FilterLenD2Plus1)
- {
- double x = InPosFrac * FilterFracs;
- const int fti = (int)x; // Function table index.
- x -= fti; // Coefficient for interpolation between adjacent
- // fractional delay filters.
- const double x2 = x * x;
- const double* const ftp = &FilterBank[fti];
- const double* const rp = Buf + ReadPos;
- double s = 0.0;
- int ii = 0;
-
- #if R8B_FLTTEST
- const double x3 = x2 * x;
- #endif // R8B_FLTTEST
-
- for (i = 0; i < FilterLen; ++i)
- {
- #if !R8B_FLTTEST
- s += (ftp[ii] + ftp[ii + 1] * x +
- ftp[ii + 2] * x2) * rp[i];
- #else // !R8B_FLTTEST
- s += ( ftp[ ii ] + ftp[ ii + 1 ] * x +
- ftp[ ii + 2 ] * x2 + ftp[ ii + 3 ] * x3 ) * rp[ i ];
- #endif // !R8B_FLTTEST
-
- ii += FilterElementSize;
- }
-
- *op = s;
- op++;
-
- InCounter++;
- const double NextInPos =
- InCounter * SrcSampleRate / DstSampleRate + InPosShift;
-
- const int NextInPosInt = (int)NextInPos;
- const int PosIncr = NextInPosInt - InPosInt;
- InPosInt = NextInPosInt;
- InPosFrac = NextInPos - NextInPosInt;
-
- ReadPos = (ReadPos + PosIncr) & BufLenMask;
- BufLeft -= PosIncr;
- }
- }
-
- if (InCounter > 1000)
- {
- // Reset the interpolation position counter to achieve a higher
- // sample timing precision.
-
- InCounter = 0;
- InPosInt = 0;
- InPosShift = InPosFrac;
- }
-
- return (int(op - op0));
- }
-
- private:
- #if !R8B_FLTTEST
- static const int FilterElementSize = 3; ///< The number of "doubles" a
- ///< single filter tap consists of (includes interpolation
- ///< coefficients).
- ///<
- #else // !R8B_FLTTEST
- static const int FilterElementSize = 4; ///< The number of "doubles" a
- ///< single filter tap consists of (includes interpolation
- ///< coefficients). During filter testing a higher precision
- ///< interpolation is used.
- ///<
- #endif // !R8B_FLTTEST
-
- static const int FilterLenD2 = FilterLen >> 1; ///< = FilterLen / 2.
- ///<
- static const int FilterLenD2Minus1 = FilterLenD2 - 1; ///< =
- ///< FilterLen / 2 - 1. This value also equals to filter's latency in
- ///< samples (taps).
- ///<
- static const int FilterLenD2Plus1 = FilterLenD2 + 1; ///< =
- ///< FilterLen / 2 + 1.
- ///<
- static const int BufLenBits = 8; ///< The length of the ring buffer,
- ///< expressed as Nth power of 2. This value can be reduced if it is
- ///< known that only short input buffers will be passed to the
- ///< interpolator. The minimum value of this parameter is 5, and
- ///< 1 << BufLenBits should be at least 3 times larger than the
- ///< FilterLen.
- ///<
- static const int BufLen = 1 << BufLenBits; ///< The length of the ring
- ///< buffer. The actual length is twice as long to allow "beyond max
- ///< position" positioning.
- ///<
- static const int BufLenMask = BufLen - 1; ///< Mask used for quick buffer
- ///< position wrapping.
- ///<
- static const int BufLeftMax = BufLen - FilterLenD2Minus1; ///< The number
- ///< of new samples that the ring buffer can hold at most. The
- ///< remaining FilterLenD2Minus1 samples hold "previous" input samples
- ///< for the filter.
- ///<
- double Buf[ BufLen * 2 ]; ///< The ring buffer.
- ///<
- double SrcSampleRate = 0; ///< Source sample rate.
- ///<
- double DstSampleRate = 0; ///< Destination sample rate.
- ///<
- double InitFracPos = 0; ///< Initial fractional position, in samples, in the
- ///< range [0; 1).
- ///<
- int BufLeft = 0; ///< The number of samples left in the buffer to process.
- ///< When this value is below FilterLenD2Plus1, the interpolation
- ///< cycle ends.
- ///<
- int WritePos = 0; ///< The current buffer write position. Incremented together
- ///< with the BufLeft variable.
- ///<
- int ReadPos = 0; ///< The current buffer read position.
- ///<
- int InCounter = 0; ///< Interpolation step counter.
- ///<
- int InPosInt = 0; ///< Interpolation position (integer part).
- ///<
- double InPosFrac = 0; ///< Interpolation position (fractional part).
- ///<
- double InPosShift = 0; ///< Interpolation position fractional shift.
- ///<
-
- #if !R8B_FLTTEST
- static const CDSPFracDelayFilterBank<FilterLen, FilterFracs, 3,
- 8> FilterBank; ///< Filter bank object, defined statically if no
- ///< filter test takes place.
- ///<
- #else // !R8B_FLTTEST
- public:
- CDSPFracDelayFilterBank< FilterLen, FilterFracs, 4, 8 > FilterBank; ///<
- ///< Filter bank object, defined as a member variable to allow for
- ///< recalculation.
- ///<
- #endif // !R8B_FLTTEST
- };
-
- // ---------------------------------------------------------------------------
-
- #if !R8B_FLTTEST
- template <int FilterLen, int FilterFracs>
- const CDSPFracDelayFilterBank<FilterLen, FilterFracs, 3, 8>
- CDSPFracInterpolator<FilterLen, FilterFracs>::FilterBank;
- #endif // !R8B_FLTTEST
-
- // ---------------------------------------------------------------------------
- } // namespace r8b
-
- #endif // R8B_CDSPFRACINTERPOLATOR_INCLUDED
|