aboutsummaryrefslogtreecommitdiff
path: root/audio/softsynth/mt32/srchelper/srctools/src/SincResampler.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'audio/softsynth/mt32/srchelper/srctools/src/SincResampler.cpp')
-rw-r--r--audio/softsynth/mt32/srchelper/srctools/src/SincResampler.cpp136
1 files changed, 136 insertions, 0 deletions
diff --git a/audio/softsynth/mt32/srchelper/srctools/src/SincResampler.cpp b/audio/softsynth/mt32/srchelper/srctools/src/SincResampler.cpp
new file mode 100644
index 0000000000..3ed028d261
--- /dev/null
+++ b/audio/softsynth/mt32/srchelper/srctools/src/SincResampler.cpp
@@ -0,0 +1,136 @@
+/* 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <cmath>
+
+#ifdef SINC_RESAMPLER_DEBUG_LOG
+#include <iostream>
+#endif
+
+#include "SincResampler.h"
+
+#ifndef M_PI
+static const double M_PI = 3.1415926535897932;
+#endif
+
+using namespace SRCTools;
+
+using namespace SincResampler;
+
+using namespace Utils;
+
+void Utils::computeResampleFactors(unsigned int &upsampleFactor, double &downsampleFactor, const double inputFrequency, const double outputFrequency, const unsigned int maxUpsampleFactor) {
+ static const double RATIONAL_RATIO_ACCURACY_FACTOR = 1E15;
+
+ upsampleFactor = static_cast<unsigned int>(outputFrequency);
+ unsigned int downsampleFactorInt = static_cast<unsigned int>(inputFrequency);
+ if ((upsampleFactor == outputFrequency) && (downsampleFactorInt == inputFrequency)) {
+ // Input and output frequencies are integers, try to reduce them
+ const unsigned int gcd = greatestCommonDivisor(upsampleFactor, downsampleFactorInt);
+ if (gcd > 1) {
+ upsampleFactor /= gcd;
+ downsampleFactor = downsampleFactorInt / gcd;
+ } else {
+ downsampleFactor = downsampleFactorInt;
+ }
+ if (upsampleFactor <= maxUpsampleFactor) return;
+ } else {
+ // Try to recover rational resample ratio by brute force
+ double inputToOutputRatio = inputFrequency / outputFrequency;
+ for (unsigned int i = 1; i <= maxUpsampleFactor; ++i) {
+ double testFactor = inputToOutputRatio * i;
+ if (floor(RATIONAL_RATIO_ACCURACY_FACTOR * testFactor + 0.5) == RATIONAL_RATIO_ACCURACY_FACTOR * floor(testFactor + 0.5)) {
+ // inputToOutputRatio found to be rational within the accuracy
+ upsampleFactor = i;
+ downsampleFactor = floor(testFactor + 0.5);
+ return;
+ }
+ }
+ }
+ // Use interpolation of FIR taps as the last resort
+ upsampleFactor = maxUpsampleFactor;
+ downsampleFactor = maxUpsampleFactor * inputFrequency / outputFrequency;
+}
+
+unsigned int Utils::greatestCommonDivisor(unsigned int a, unsigned int b) {
+ while (0 < b) {
+ unsigned int r = a % b;
+ a = b;
+ b = r;
+ }
+ return a;
+}
+
+double KaizerWindow::estimateBeta(double dbRipple) {
+ return 0.1102 * (dbRipple - 8.7);
+}
+
+unsigned int KaizerWindow::estimateOrder(double dbRipple, double fp, double fs) {
+ const double transBW = (fs - fp);
+ return static_cast<unsigned int>(ceil((dbRipple - 8) / (2.285 * 2 * M_PI * transBW)));
+}
+
+double KaizerWindow::bessel(const double x) {
+ static const double EPS = 1.11E-16;
+
+ double sum = 0.0;
+ double f = 1.0;
+ for (unsigned int i = 1;; ++i) {
+ f *= (0.5 * x / i);
+ double f2 = f * f;
+ if (f2 <= sum * EPS) break;
+ sum += f2;
+ }
+ return 1.0 + sum;
+}
+
+void KaizerWindow::windowedSinc(FIRCoefficient kernel[], const unsigned int order, const double fc, const double beta, const double amp) {
+ const double fc_pi = M_PI * fc;
+ const double recipOrder = 1.0 / order;
+ const double mult = 2.0 * fc * amp / bessel(beta);
+ for (int i = order, j = 0; 0 <= i; i -= 2, ++j) {
+ double xw = i * recipOrder;
+ double win = bessel(beta * sqrt(fabs(1.0 - xw * xw)));
+ double xs = i * fc_pi;
+ double sinc = (i == 0) ? 1.0 : sin(xs) / xs;
+ FIRCoefficient imp = FIRCoefficient(mult * sinc * win);
+ kernel[j] = imp;
+ kernel[order - j] = imp;
+ }
+}
+
+ResamplerStage *SincResampler::createSincResampler(const double inputFrequency, const double outputFrequency, const double passbandFrequency, const double stopbandFrequency, const double dbSNR, const unsigned int maxUpsampleFactor) {
+ unsigned int upsampleFactor;
+ double downsampleFactor;
+ computeResampleFactors(upsampleFactor, downsampleFactor, inputFrequency, outputFrequency, maxUpsampleFactor);
+ double baseSamplePeriod = 1.0 / (inputFrequency * upsampleFactor);
+ double fp = passbandFrequency * baseSamplePeriod;
+ double fs = stopbandFrequency * baseSamplePeriod;
+ double fc = 0.5 * (fp + fs);
+ double beta = KaizerWindow::estimateBeta(dbSNR);
+ unsigned int order = KaizerWindow::estimateOrder(dbSNR, fp, fs);
+ const unsigned int kernelLength = order + 1;
+
+#ifdef SINC_RESAMPLER_DEBUG_LOG
+ std::clog << "FIR: " << upsampleFactor << "/" << downsampleFactor << ", N=" << kernelLength << ", NPh=" << kernelLength / double(upsampleFactor) << ", C=" << 0.5 / fc << ", fp=" << fp << ", fs=" << fs << ", M=" << maxUpsampleFactor << std::endl;
+#endif
+
+ FIRCoefficient *windowedSincKernel = new FIRCoefficient[kernelLength];
+ KaizerWindow::windowedSinc(windowedSincKernel, order, fc, beta, upsampleFactor);
+ ResamplerStage *windowedSincStage = new FIRResampler(upsampleFactor, downsampleFactor, windowedSincKernel, kernelLength);
+ delete[] windowedSincKernel;
+ return windowedSincStage;
+}