123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521 |
- //$ 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-2014 Aleksey Vaneev
- * See the "License.txt" file for license.
- */
-
- #ifndef R8B_CDSPRESAMPLER_INCLUDED
- #define R8B_CDSPRESAMPLER_INCLUDED
-
- #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).
- *
- * @param CInterpClass Interpolator class that should be used by the
- * resampler. The desired interpolation quality can be defined via the
- * template parameters of the interpolator class. See
- * r8b::CDSPFracInterpolator and r8b::CDSPFracDelayFilterBank for description
- * of the template parameters.
- */
-
- template <class CInterpClass =
- CDSPFracInterpolator<R8B_FLTLEN, R8B_FLTFRACS>>
- 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 150 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. Note that the "power of 2" upsampling
- * (but not downsampling) requires a lot of buffer memory: e.g. upsampling
- * by a factor of 16 requires an intermediate buffer MaxInLen*(16+8)
- * samples long. So, when doing the "power of 2" upsampling it is highly
- * recommended to do it in small steps, e.g. no more than 256 samples at
- * once (also set the MaxInLen to 256).
- * @param MaxInLen 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
- * the resampler may use the input buffer itself for intermediate sample
- * data storage.
- * @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 stationary signals, this value can be decreased to 20 to 10 if the
- * signal being resampled is mostly non-stationary (e.g. impulse
- * 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 by up to 1 sample, depending on
- * the minimum-phase filter's actual fractional delay. If the output
- * stream should always start at "time zero" offset with minimum-phase
- * filters the UsePower2 should be set to "false". Linear-phase filters
- * do not have fractional delay.
- * @param UsePower2 "True" if the "power of 2" resampling optimization
- * should be used when possible. This value should be set to "false" if
- * the access to interpolator is needed in any case (also the source and
- * destination sample rates should not be equal).
- * @see CDSPFIRFilterCache::getLPFilter()
- */
-
- CDSPResampler(const double SrcSampleRate, const double DstSampleRate,
- const int MaxInLen, const double ReqTransBand = 2.0,
- const double ReqAtten = 206.91,
- const EDSPFilterPhaseResponse ReqPhase = fprLinearPhase,
- const bool UsePower2 = true)
- {
- R8BASSERT(SrcSampleRate > 0.0);
- R8BASSERT(DstSampleRate > 0.0);
- R8BASSERT(MaxInLen > 0);
-
- if (SrcSampleRate == DstSampleRate)
- {
- ConvCount = 0;
- return;
- }
-
- int SrcSRMult;
- int SrcSRDiv = 1;
- int MaxOutLen = MaxInLen;
- int ConvBufCapacities[ 2 ];
- double PrevLatencyFrac = 0.0;
-
- if (DstSampleRate * 2 > SrcSampleRate)
- {
- // Only a single convolver with 2X upsampling is required.
-
- SrcSRMult = 2;
- const double NormFreq = (DstSampleRate > SrcSampleRate ? 0.5 : 0.5 * DstSampleRate / SrcSampleRate);
-
- Convs[0] = new CDSPBlockConvolver(CDSPFIRFilterCache::getLPFilter(NormFreq, ReqTransBand, ReqAtten, ReqPhase, 2.0), 2, 1, 0.0);
-
- ConvCount = 1;
- MaxOutLen = Convs[0]->getMaxOutLen(MaxOutLen);
- ConvBufCapacities[0] = MaxOutLen;
- PrevLatencyFrac = Convs[0]->getLatencyFrac();
-
- // Find if the destination to source sample rate ratio is
- // a "power of 2" value.
-
- int UseConvCount = 1;
-
- while (true)
- {
- const double TestSR = SrcSampleRate * (1 << UseConvCount);
-
- if (TestSR > DstSampleRate)
- {
- UseConvCount = 0; // Power of 2 not found.
- break;
- }
-
- if (TestSR == DstSampleRate)
- {
- break; // Power of 2 found.
- }
-
- UseConvCount++;
- }
-
- if (UsePower2 && UseConvCount > 0)
- {
- R8BASSERT(UseConvCount <= ConvCountMax);
-
- ConvBufCapacities[1] = 0;
- ConvCount = UseConvCount;
-
- for (int i = 1; i < UseConvCount; ++i)
- {
- const double tb = (i >= 2 ? 45.0 : 34.0);
-
- Convs[i] = new CDSPBlockConvolver(CDSPFIRFilterCache::getLPFilter(0.5, tb, ReqAtten, ReqPhase, 2.0), 2, 1, PrevLatencyFrac);
-
- MaxOutLen = Convs[i]->getMaxOutLen(MaxOutLen);
- ConvBufCapacities[i & 1] = MaxOutLen;
- PrevLatencyFrac = Convs[i]->getLatencyFrac();
- }
-
- ConvBufs[0].alloc(ConvBufCapacities[0]);
-
- if (ConvBufCapacities[1] > 0) { ConvBufs[1].alloc(ConvBufCapacities[1]); }
-
- return; // No interpolator is needed.
- }
-
- ConvBufs[0].alloc(ConvBufCapacities[0]);
- }
- else
- {
- SrcSRMult = 1;
- ConvBufCapacities[0] = 0;
- ConvCount = 0;
- const double CheckSR = DstSampleRate * 4;
-
- while (CheckSR * SrcSRDiv <= SrcSampleRate)
- {
- SrcSRDiv *= 2;
-
- // If downsampling is even deeper, use a less steep filter at
- // this step.
-
- const double tb =
- (CheckSR * SrcSRDiv <= SrcSampleRate ? 45.0 : 34.0);
-
- Convs[ConvCount] = new CDSPBlockConvolver(CDSPFIRFilterCache::getLPFilter(0.5, tb, ReqAtten, ReqPhase, 1.0), 1, 2, PrevLatencyFrac);
-
- MaxOutLen = Convs[ConvCount]->getMaxOutLen(MaxOutLen);
- PrevLatencyFrac = Convs[ConvCount]->getLatencyFrac();
- ConvCount++;
-
- R8BASSERT(ConvCount < ConvCountMax);
- }
-
- const double NormFreq = DstSampleRate * SrcSRDiv / SrcSampleRate;
- const int downf = (UsePower2 && NormFreq == 0.5 ? 2 : 1);
-
- Convs[ConvCount] = new CDSPBlockConvolver(CDSPFIRFilterCache::getLPFilter(NormFreq, ReqTransBand, ReqAtten, ReqPhase, 1.0), 1, downf,
- PrevLatencyFrac);
-
- MaxOutLen = Convs[ConvCount]->getMaxOutLen(MaxOutLen);
- PrevLatencyFrac = Convs[ConvCount]->getLatencyFrac();
- ConvCount++;
-
- if (downf > 1)
- {
- return; // No interpolator is needed.
- }
- }
-
- Interp = new CInterpClass(SrcSampleRate * SrcSRMult / SrcSRDiv, DstSampleRate, PrevLatencyFrac);
-
- MaxOutLen = Interp->getMaxOutLen(MaxOutLen);
-
- if (MaxOutLen <= ConvBufCapacities[0]) { InterpBuf = ConvBufs[0]; }
- else if (MaxOutLen <= MaxInLen) { InterpBuf = nullptr; }
- else
- {
- TmpBuf.alloc(MaxOutLen);
- InterpBuf = TmpBuf;
- }
- }
-
- int getLatency() const override { return (0); }
-
- double getLatencyFrac() const override { return (0.0); }
-
- int getInLenBeforeOutStart(const int NextInLen) const override
- {
- int l = (Interp == nullptr ? 0 : Interp->getInLenBeforeOutStart(NextInLen));
-
- for (int i = ConvCount - 1; i >= 0; i--) { l = Convs[i]->getInLenBeforeOutStart(l); }
-
- return (l);
- }
-
- int getMaxOutLen(const int/* MaxInLen */) const override { return (0); }
-
- /**
- * 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.
- */
- void clear() override
- {
- for (int i = 0; i < ConvCount; ++i) { Convs[i]->clear(); }
-
- if (Interp != nullptr) { Interp->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 may be used as output buffer by
- * this function.
- * @param l The number of samples available in the input buffer. Should
- * not exceed the MaxInLen supplied to the constructor.
- * @param[out] op0 This variable receives the pointer to the resampled
- * data. On function's return, this pointer may point to the address
- * within the "ip0" input buffer, or 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 may be 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.
- */
- int process(double* ip0, int l, double*& op0) override
- {
- R8BASSERT(l >= 0);
-
- if (ConvCount == 0)
- {
- op0 = ip0;
- return (l);
- }
-
- double* ip = ip0;
- double* op = nullptr;
-
- for (int i = 0; i < ConvCount; ++i)
- {
- op = (ConvBufs[i & 1] == nullptr ? ip0 : ConvBufs[i & 1]);
- l = Convs[i]->process(ip, l, op);
- ip = op;
- }
-
- if (Interp == nullptr)
- {
- op0 = op;
- return (l);
- }
-
- op = (InterpBuf == nullptr ? ip0 : InterpBuf);
- op0 = op;
-
- return (Interp->process(ip, l, op));
- }
-
- /**
- * 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 MaxInLen The max input length value which was previously passed
- * to the constructor.
- * @param ip Input buffer pointer.
- * @param iplen Length of the input buffer in samples.
- * @param op Output buffer pointer.
- * @param oplen Length of the output buffer in samples.
- */
-
- void oneshot(const int MaxInLen, const double* ip, int iplen,
- double* op, int oplen)
- {
- const CFixedBuffer<double> ZeroBuf(MaxInLen);
- memset(&ZeroBuf[0], 0, MaxInLen * sizeof(double));
-
- while (oplen > 0)
- {
- int rc;
- double* p;
-
- if (iplen == 0)
- {
- rc = MaxInLen;
- p = static_cast<double*>(&ZeroBuf[0]);
- }
- else
- {
- rc = min(iplen, MaxInLen);
- p = const_cast<double*>(ip);
- ip += rc;
- iplen -= rc;
- }
-
- double* op0;
- int wc = process(p, rc, op0);
-
- wc = min(oplen, wc);
- memcpy(op, op0, wc * sizeof(double));
- op += wc;
- oplen -= wc;
- }
-
- clear();
- }
-
- private:
- static const int ConvCountMax = 8; ///< 8 convolvers with the
- ///< built-in 2x up- or downsampling is enough for 256x up- or
- ///< downsampling.
- ///<
- CPtrKeeper<CDSPBlockConvolver*> Convs[ ConvCountMax ]; ///< Convolvers.
- ///<
- int ConvCount = 0; ///< The number of objects defined in the Convs[] array.
- ///< Equals to 0 if sample rate conversion is not needed.
- ///<
- CPtrKeeper<CInterpClass*> Interp; ///< Fractional interpolator object.
- ///< Equals NULL if no fractional interpolation is required meaning
- ///< the "power of 2" resampling is performed or no resampling is
- ///< performed at all.
- ///<
- CFixedBuffer<double> ConvBufs[ 2 ]; ///< Intermediate convolution
- ///< buffers to use, used only when at least 2x upsampling is
- ///< performed. These buffers are used in flip-flop manner. If NULL
- ///< then the input buffer will be used instead.
- ///<
- CFixedBuffer<double> TmpBuf; ///< Additional output buffer, can be
- ///< addressed by the InterpBuf pointer.
- ///<
- double* InterpBuf = nullptr; ///< Final output interpolation buffer to use. If NULL
- ///< then the input buffer will be used instead. Otherwise this
- ///< pointer points to either ConvBufs or TmpBuf.
- ///<
- };
-
- /**
- * @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 final : public CDSPResampler<CDSPFracInterpolator<18, 137>>
- {
- 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 MaxInLen 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 MaxInLen, const double ReqTransBand = 2.0)
- : CDSPResampler<CDSPFracInterpolator<18, 137>>(SrcSampleRate, DstSampleRate, MaxInLen, ReqTransBand, 136.45, fprLinearPhase, true) { }
- };
-
- /**
- * @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
- * usually do not feature stationary signal components and thus need resampler
- * with a less SNR. See the r8b::CDSPResampler class for details.
- */
-
- class CDSPResampler16IR final : public CDSPResampler<CDSPFracInterpolator<14, 67>>
- {
- 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 MaxInLen 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 MaxInLen, const double ReqTransBand = 2.0)
- : CDSPResampler<CDSPFracInterpolator<14, 67>>(SrcSampleRate, DstSampleRate, MaxInLen, ReqTransBand, 109.56, fprLinearPhase, true) { }
- };
-
- /**
- * @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 final : public CDSPResampler<CDSPFracInterpolator<24, 673>>
- {
- 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 MaxInLen 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 MaxInLen, const double ReqTransBand = 2.0)
- : CDSPResampler<CDSPFracInterpolator<24, 673>>(SrcSampleRate, DstSampleRate, MaxInLen, ReqTransBand, 180.15, fprLinearPhase, true) { }
- };
- } // namespace r8b
-
- #endif // R8B_CDSPRESAMPLER_INCLUDED
|