/* Copyright (C) 2015-2017 Sergey V. Mikayev * * This program 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 program 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 program. If not, see . */ #include #include #include "FIRResampler.h" using namespace SRCTools; FIRResampler::Constants::Constants(const unsigned int upsampleFactor, const double downsampleFactor, const FIRCoefficient kernel[], const unsigned int kernelLength) { usePhaseInterpolation = downsampleFactor != floor(downsampleFactor); FIRCoefficient *kernelCopy = new FIRCoefficient[kernelLength]; memcpy(kernelCopy, kernel, kernelLength * sizeof(FIRCoefficient)); taps = kernelCopy; numberOfTaps = kernelLength; numberOfPhases = upsampleFactor; phaseIncrement = downsampleFactor; unsigned int minDelayLineLength = static_cast(ceil(double(kernelLength) / upsampleFactor)); unsigned int delayLineLength = 2; while (delayLineLength < minDelayLineLength) delayLineLength <<= 1; delayLineMask = delayLineLength - 1; ringBuffer = new FloatSample[delayLineLength][FIR_INTERPOLATOR_CHANNEL_COUNT]; FloatSample *s = *ringBuffer; FloatSample *e = ringBuffer[delayLineLength]; while (s < e) *(s++) = 0; } FIRResampler::FIRResampler(const unsigned int upsampleFactor, const double downsampleFactor, const FIRCoefficient kernel[], const unsigned int kernelLength) : constants(upsampleFactor, downsampleFactor, kernel, kernelLength), ringBufferPosition(0), phase(constants.numberOfPhases) {} FIRResampler::~FIRResampler() { delete[] constants.ringBuffer; delete[] constants.taps; } void FIRResampler::process(const FloatSample *&inSamples, unsigned int &inLength, FloatSample *&outSamples, unsigned int &outLength) { while (outLength > 0) { while (needNextInSample()) { if (inLength == 0) return; addInSamples(inSamples); --inLength; } getOutSamplesStereo(outSamples); --outLength; } } unsigned int FIRResampler::estimateInLength(const unsigned int outLength) const { return static_cast((outLength * constants.phaseIncrement + phase) / constants.numberOfPhases); } bool FIRResampler::needNextInSample() const { return constants.numberOfPhases <= phase; } void FIRResampler::addInSamples(const FloatSample *&inSamples) { ringBufferPosition = (ringBufferPosition - 1) & constants.delayLineMask; for (unsigned int i = 0; i < FIR_INTERPOLATOR_CHANNEL_COUNT; i++) { constants.ringBuffer[ringBufferPosition][i] = *(inSamples++); } phase -= constants.numberOfPhases; } // Optimised for processing stereo interleaved streams void FIRResampler::getOutSamplesStereo(FloatSample *&outSamples) { FloatSample leftSample = 0.0; FloatSample rightSample = 0.0; unsigned int delaySampleIx = ringBufferPosition; if (constants.usePhaseInterpolation) { double phaseFraction = phase - floor(phase); unsigned int maxTapIx = phaseFraction == 0 ? constants.numberOfTaps : constants.numberOfTaps - 1; for (unsigned int tapIx = static_cast(phase); tapIx < maxTapIx; tapIx += constants.numberOfPhases) { FIRCoefficient tap = FIRCoefficient(constants.taps[tapIx] + (constants.taps[tapIx + 1] - constants.taps[tapIx]) * phaseFraction); leftSample += tap * constants.ringBuffer[delaySampleIx][0]; rightSample += tap * constants.ringBuffer[delaySampleIx][1]; delaySampleIx = (delaySampleIx + 1) & constants.delayLineMask; } } else { // Optimised for rational resampling ratios when phase is always integer for (unsigned int tapIx = static_cast(phase); tapIx < constants.numberOfTaps; tapIx += constants.numberOfPhases) { FIRCoefficient tap = constants.taps[tapIx]; leftSample += tap * constants.ringBuffer[delaySampleIx][0]; rightSample += tap * constants.ringBuffer[delaySampleIx][1]; delaySampleIx = (delaySampleIx + 1) & constants.delayLineMask; } } *(outSamples++) = leftSample; *(outSamples++) = rightSample; phase += constants.phaseIncrement; }