#pragma once #include #include #include "../processor.h" #include "../filter/decimating_fir.h" #include "../taps/from_array.h" #include "polyphase_resampler.h" #include "power_decimator.h" #include "../taps/low_pass.h" #include "../window/nuttall.h" namespace dsp::multirate { template class RationalResampler : public Processor { using base_type = Processor; public: RationalResampler() {} RationalResampler(stream* in, double inSamplerate, double outSamplerate) { init(in, inSamplerate, outSamplerate); } ~RationalResampler() { if (!base_type::_block_init) { return; } base_type::stop(); taps::free(rtaps); } void init(stream* in, double inSamplerate, double outSamplerate) { _inSamplerate = inSamplerate; _outSamplerate = outSamplerate; // Dummy initialization since only used for processing rtaps = taps::lowPass(0.25, 0.1, 1.0); decim.init(NULL, 2); resamp.init(NULL, 1, 1, rtaps); decim.out.free(); resamp.out.free(); // Proper configuration reconfigure(); base_type::init(in); } void reset() { assert(base_type::_block_init); std::lock_guard lck(base_type::ctrlMtx); base_type::tempStop(); decim.reset(); resamp.reset(); base_type::tempStart(); } void setInSamplerate(double inSamplerate) { assert(base_type::_block_init); std::lock_guard lck(base_type::ctrlMtx); base_type::tempStop(); _inSamplerate = inSamplerate; reconfigure(); base_type::tempStart(); } void setOutSamplerate(double outSamplerate) { assert(base_type::_block_init); std::lock_guard lck(base_type::ctrlMtx); base_type::tempStop(); _outSamplerate = outSamplerate; reconfigure(); base_type::tempStart(); } void setRates(double inSamplerate, double outSamplerate) { assert(base_type::_block_init); std::lock_guard lck(base_type::ctrlMtx); base_type::tempStop(); _inSamplerate = inSamplerate; _outSamplerate = outSamplerate; reconfigure(); base_type::tempStart(); } inline int process(int count, const T* in, T* out) { switch(mode) { case Mode::BOTH: count = decim.process(count, in, out); return resamp.process(count, out, out); case Mode::DECIM_ONLY: return decim.process(count, in, out); case Mode::RESAMP_ONLY: return resamp.process(count, in, out); case Mode::NONE: memcpy(out, in, count * sizeof(T)); return count; } return count; } int run() { int count = base_type::_in->read(); if (count < 0) { return -1; } int outCount = process(count, base_type::_in->readBuf, base_type::out.writeBuf); // Swap if some data was generated base_type::_in->flush(); if (outCount) { if (!base_type::out.swap(outCount)) { return -1; } } return outCount; } protected: enum Mode { BOTH, DECIM_ONLY, RESAMP_ONLY, NONE }; void reconfigure() { // Calculate highest power-of-two decimation for the power decimator int predecPower = std::min(floor(log2(_inSamplerate / _outSamplerate)), PowerDecimator::getMaxRatio()); int predecRatio = std::min(1 << predecPower, PowerDecimator::getMaxRatio()); double intSamplerate = _inSamplerate; // Configure the DDC bool useDecim = (_inSamplerate > _outSamplerate && predecPower > 0); if (useDecim) { intSamplerate = _inSamplerate / (double)predecRatio; decim.setRatio(predecRatio); } // Calculate interpolation and decimation for polyphase resampler int IntSR = round(intSamplerate); int OutSR = round(_outSamplerate); int gcd = std::gcd(IntSR, OutSR); int interp = OutSR / gcd; int decim = IntSR / gcd; // Check for excessive error double actualOutSR = (double)IntSR * (double)interp / (double)decim; double error = abs((actualOutSR - _outSamplerate) / _outSamplerate) * 100.0; if (error > 0.01) { fprintf(stderr, "Warning: resampling error is over 0.01%%: %lf\n", error); } // If the power decimator already did all the work, don't use the resampler if (interp == decim) { mode = useDecim ? Mode::DECIM_ONLY : Mode::NONE; return; } // Configure the polyphase resampler double tapSamplerate = intSamplerate * (double)interp; double tapBandwidth = std::min(_inSamplerate, _outSamplerate) / 2.0; double tapTransWidth = tapBandwidth * 0.1; taps::free(rtaps); rtaps = taps::lowPass(tapBandwidth, tapTransWidth, tapSamplerate); for (int i = 0; i < rtaps.size; i++) { rtaps.taps[i] *= (float)interp; } resamp.setRatio(interp, decim, rtaps); printf("[Resamp] predec: %d, interp: %d, decim: %d, inacc: %lf%%, taps: %d\n", predecRatio, interp, decim, error, rtaps.size); mode = useDecim ? Mode::BOTH : Mode::RESAMP_ONLY; } PowerDecimator decim; PolyphaseResampler resamp; tap rtaps; double _inSamplerate; double _outSamplerate; Mode mode; }; }