diff --git a/audio/softsynth/mt32/File.cpp b/audio/softsynth/mt32/File.cpp
index 333230b61c1..07164823d30 100755
--- a/audio/softsynth/mt32/File.cpp
+++ b/audio/softsynth/mt32/File.cpp
@@ -26,11 +26,15 @@ namespace MT32Emu {
AbstractFile::AbstractFile() : sha1DigestCalculated(false) {
sha1Digest[0] = 0;
+
+ reserved = NULL;
}
AbstractFile::AbstractFile(const SHA1Digest &useSHA1Digest) : sha1DigestCalculated(true) {
memcpy(sha1Digest, useSHA1Digest, sizeof(SHA1Digest) - 1);
sha1Digest[sizeof(SHA1Digest) - 1] = 0; // Ensure terminator char.
+
+ reserved = NULL;
}
const File::SHA1Digest &AbstractFile::getSHA1() {
diff --git a/audio/softsynth/mt32/File.h b/audio/softsynth/mt32/File.h
index f29d4f3c42d..c9a7d582b4c 100755
--- a/audio/softsynth/mt32/File.h
+++ b/audio/softsynth/mt32/File.h
@@ -49,6 +49,9 @@ protected:
private:
bool sha1DigestCalculated;
SHA1Digest sha1Digest;
+
+ // Binary compatibility helper.
+ void *reserved;
};
class MT32EMU_EXPORT ArrayFile : public AbstractFile {
diff --git a/audio/softsynth/mt32/Part.cpp b/audio/softsynth/mt32/Part.cpp
index c4d5b945512..9b5119ee8bb 100755
--- a/audio/softsynth/mt32/Part.cpp
+++ b/audio/softsynth/mt32/Part.cpp
@@ -376,7 +376,8 @@ void RhythmPart::noteOn(unsigned int midiKey, unsigned int velocity) {
unsigned int key = midiKey;
unsigned int drumNum = key - 24;
int drumTimbreNum = rhythmTemp[drumNum].timbre;
- if (drumTimbreNum >= 127) { // 94 on MT-32
+ const int drumTimbreCount = 64 + synth->controlROMMap->timbreRCount; // 94 on MT-32, 128 on LAPC-I/CM32-L
+ if (drumTimbreNum == 127 || drumTimbreNum >= drumTimbreCount) { // timbre #127 is OFF, no sense to play it
synth->printDebug("%s: Attempted to play unmapped key %d (velocity %d)", name, midiKey, velocity);
return;
}
diff --git a/audio/softsynth/mt32/SampleRateConverter.cpp b/audio/softsynth/mt32/SampleRateConverter.cpp
new file mode 100644
index 00000000000..70f860f3952
--- /dev/null
+++ b/audio/softsynth/mt32/SampleRateConverter.cpp
@@ -0,0 +1,97 @@
+/* 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 "SampleRateConverter.h"
+
+#if MT32EMU_WITH_LIBSOXR_RESAMPLER
+#include "srchelper/SoxrAdapter.h"
+#elif MT32EMU_WITH_LIBSAMPLERATE_RESAMPLER
+#include "srchelper/SamplerateAdapter.h"
+#else
+#include "srchelper/InternalResampler.h"
+#endif
+
+#include "Synth.h"
+
+using namespace MT32Emu;
+
+static inline void *createDelegate(Synth &synth, double targetSampleRate, SampleRateConverter::Quality quality) {
+#if MT32EMU_WITH_LIBSOXR_RESAMPLER
+ return new SoxrAdapter(synth, targetSampleRate, quality);
+#elif MT32EMU_WITH_LIBSAMPLERATE_RESAMPLER
+ return new SamplerateAdapter(synth, targetSampleRate, quality);
+#else
+ return new InternalResampler(synth, targetSampleRate, quality);
+#endif
+}
+
+AnalogOutputMode SampleRateConverter::getBestAnalogOutputMode(double targetSampleRate) {
+ if (Synth::getStereoOutputSampleRate(AnalogOutputMode_ACCURATE) < targetSampleRate) {
+ return AnalogOutputMode_OVERSAMPLED;
+ } else if (Synth::getStereoOutputSampleRate(AnalogOutputMode_COARSE) < targetSampleRate) {
+ return AnalogOutputMode_ACCURATE;
+ }
+ return AnalogOutputMode_COARSE;
+}
+
+SampleRateConverter::SampleRateConverter(Synth &useSynth, double targetSampleRate, Quality useQuality) :
+ synthInternalToTargetSampleRateRatio(SAMPLE_RATE / targetSampleRate),
+ srcDelegate(createDelegate(useSynth, targetSampleRate, useQuality))
+{}
+
+SampleRateConverter::~SampleRateConverter() {
+#if MT32EMU_WITH_LIBSOXR_RESAMPLER
+ delete static_cast(srcDelegate);
+#elif MT32EMU_WITH_LIBSAMPLERATE_RESAMPLER
+ delete static_cast(srcDelegate);
+#else
+ delete static_cast(srcDelegate);
+#endif
+}
+
+void SampleRateConverter::getOutputSamples(float *buffer, unsigned int length) {
+#if MT32EMU_WITH_LIBSOXR_RESAMPLER
+ static_cast(srcDelegate)->getOutputSamples(buffer, length);
+#elif MT32EMU_WITH_LIBSAMPLERATE_RESAMPLER
+ static_cast(srcDelegate)->getOutputSamples(buffer, length);
+#else
+ static_cast(srcDelegate)->getOutputSamples(buffer, length);
+#endif
+}
+
+void SampleRateConverter::getOutputSamples(Bit16s *outBuffer, unsigned int length) {
+ static const unsigned int CHANNEL_COUNT = 2;
+
+ float floatBuffer[CHANNEL_COUNT * MAX_SAMPLES_PER_RUN];
+ while (length > 0) {
+ const unsigned int size = MAX_SAMPLES_PER_RUN < length ? MAX_SAMPLES_PER_RUN : length;
+ getOutputSamples(floatBuffer, size);
+ float *outs = floatBuffer;
+ float *ends = floatBuffer + CHANNEL_COUNT * size;
+ while (outs < ends) {
+ *(outBuffer++) = Synth::convertSample(*(outs++));
+ }
+ length -= size;
+ }
+}
+
+double SampleRateConverter::convertOutputToSynthTimestamp(double outputTimestamp) const {
+ return outputTimestamp * synthInternalToTargetSampleRateRatio;
+}
+
+double SampleRateConverter::convertSynthToOutputTimestamp(double synthTimestamp) const {
+ return synthTimestamp / synthInternalToTargetSampleRateRatio;
+}
diff --git a/audio/softsynth/mt32/SampleRateConverter.h b/audio/softsynth/mt32/SampleRateConverter.h
new file mode 100644
index 00000000000..24bce0891b9
--- /dev/null
+++ b/audio/softsynth/mt32/SampleRateConverter.h
@@ -0,0 +1,78 @@
+/* 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 .
+ */
+
+#ifndef SAMPLE_RATE_CONVERTER_H
+#define SAMPLE_RATE_CONVERTER_H
+
+#include "globals.h"
+#include "Types.h"
+#include "Enumerations.h"
+
+namespace MT32Emu {
+
+class Synth;
+
+/* SampleRateConverter class allows to convert the synthesiser output to any desired sample rate.
+ * It processes the completely mixed stereo output signal as it passes the analogue circuit emulation,
+ * so emulating the synthesiser output signal passing further through an ADC.
+ * Several conversion quality options are provided which allow to trade-off the conversion speed vs. the passband width.
+ * All the options except FASTEST guarantee full suppression of the aliasing noise in terms of the 16-bit integer samples.
+ */
+class MT32EMU_EXPORT SampleRateConverter {
+public:
+ enum Quality {
+ // Use this only when the speed is more important than the audio quality.
+ FASTEST,
+ FAST,
+ GOOD,
+ BEST
+ };
+
+ // Returns the value of AnalogOutputMode for which the output signal may retain its full frequency spectrum
+ // at the sample rate specified by the targetSampleRate argument.
+ static AnalogOutputMode getBestAnalogOutputMode(double targetSampleRate);
+
+ // Creates a SampleRateConverter instance that converts output signal from the synth to the given sample rate
+ // with the specified conversion quality.
+ SampleRateConverter(Synth &synth, double targetSampleRate, Quality quality);
+ ~SampleRateConverter();
+
+ // Fills the provided output buffer with the results of the sample rate conversion.
+ // The input samples are automatically retrieved from the synth as necessary.
+ void getOutputSamples(MT32Emu::Bit16s *buffer, unsigned int length);
+
+ // Fills the provided output buffer with the results of the sample rate conversion.
+ // The input samples are automatically retrieved from the synth as necessary.
+ void getOutputSamples(float *buffer, unsigned int length);
+
+ // Returns the number of samples produced at the internal synth sample rate (32000 Hz)
+ // that correspond to the number of samples at the target sample rate.
+ // Intended to facilitate audio time synchronisation.
+ double convertOutputToSynthTimestamp(double outputTimestamp) const;
+
+ // Returns the number of samples produced at the target sample rate
+ // that correspond to the number of samples at the internal synth sample rate (32000 Hz).
+ // Intended to facilitate audio time synchronisation.
+ double convertSynthToOutputTimestamp(double synthTimestamp) const;
+
+private:
+ const double synthInternalToTargetSampleRateRatio;
+ void * const srcDelegate;
+}; // class SampleRateConverter
+
+} // namespace MT32Emu
+
+#endif // SAMPLE_RATE_CONVERTER_H
diff --git a/audio/softsynth/mt32/Synth.cpp b/audio/softsynth/mt32/Synth.cpp
index 162c5d8ceee..00832c5d503 100755
--- a/audio/softsynth/mt32/Synth.cpp
+++ b/audio/softsynth/mt32/Synth.cpp
@@ -63,16 +63,6 @@ static inline PartialState getPartialState(PartialManager *partialManager, unsig
return partial->isActive() ? PARTIAL_PHASE_TO_STATE[partial->getTVA()->getPhase()] : PartialState_INACTIVE;
}
-#if MT32EMU_USE_FLOAT_SAMPLES
-static inline Bit16s convertSample(float sample) {
- return Synth::clipSampleEx(Bit32s(sample * 16384.0f)); // This multiplier takes into account the DAC bit shift
-}
-#else
-static inline float convertSample(Bit16s sample) {
- return float(sample) / 16384.0f; // This multiplier takes into account the DAC bit shift
-}
-#endif
-
class SampleFormatConverter {
protected:
#if MT32EMU_USE_FLOAT_SAMPLES
@@ -98,7 +88,7 @@ public:
}
Sample *inBuffer = sampleBuffer;
while (len--) {
- *(outBuffer++) = convertSample(*(inBuffer++));
+ *(outBuffer++) = Synth::convertSample(*(inBuffer++));
}
}
@@ -133,6 +123,12 @@ public:
class Renderer {
Synth &synth;
+ // These buffers are used for building the output streams as they are found at the DAC entrance.
+ // The output is mixed down to stereo interleaved further in the analog circuitry emulation.
+ Sample tmpNonReverbLeft[MAX_SAMPLES_PER_RUN], tmpNonReverbRight[MAX_SAMPLES_PER_RUN];
+ Sample tmpReverbDryLeft[MAX_SAMPLES_PER_RUN], tmpReverbDryRight[MAX_SAMPLES_PER_RUN];
+ Sample tmpReverbWetLeft[MAX_SAMPLES_PER_RUN], tmpReverbWetRight[MAX_SAMPLES_PER_RUN];
+
public:
Renderer(Synth &useSynth) : synth(useSynth) {}
@@ -140,7 +136,7 @@ public:
void renderStreams(SampleFormatConverter &nonReverbLeft, SampleFormatConverter &nonReverbRight, SampleFormatConverter &reverbDryLeft, SampleFormatConverter &reverbDryRight, SampleFormatConverter &reverbWetLeft, SampleFormatConverter &reverbWetRight, Bit32u len);
void produceLA32Output(Sample *buffer, Bit32u len);
void convertSamplesToOutput(Sample *buffer, Bit32u len);
- void doRenderStreams(Sample *nonReverbLeft, Sample *nonReverbRight, Sample *reverbDryLeft, Sample *reverbDryRight, Sample *reverbWetLeft, Sample *reverbWetRight, Bit32u len);
+ void doRenderStreams(DACOutputStreams &streams, Bit32u len);
};
Bit32u Synth::getLibraryVersionInt() {
@@ -527,7 +523,7 @@ bool Synth::open(const ROMImage &controlROMImage, const ROMImage &pcmROMImage, A
return open(controlROMImage, pcmROMImage, DEFAULT_MAX_PARTIALS, analogOutputMode);
}
-bool Synth::open(const ROMImage &controlROMImage, const ROMImage &pcmROMImage, unsigned int usePartialCount, AnalogOutputMode analogOutputMode) {
+bool Synth::open(const ROMImage &controlROMImage, const ROMImage &pcmROMImage, Bit32u usePartialCount, AnalogOutputMode analogOutputMode) {
if (opened) {
return false;
}
@@ -1698,12 +1694,8 @@ void Renderer::render(SampleFormatConverter &converter, Bit32u len) {
return;
}
- // As in AnalogOutputMode_ACCURATE mode output is upsampled, buffer size MAX_SAMPLES_PER_RUN is more than enough.
- Sample tmpNonReverbLeft[MAX_SAMPLES_PER_RUN], tmpNonReverbRight[MAX_SAMPLES_PER_RUN];
- Sample tmpReverbDryLeft[MAX_SAMPLES_PER_RUN], tmpReverbDryRight[MAX_SAMPLES_PER_RUN];
- Sample tmpReverbWetLeft[MAX_SAMPLES_PER_RUN], tmpReverbWetRight[MAX_SAMPLES_PER_RUN];
-
while (len > 0) {
+ // As in AnalogOutputMode_ACCURATE mode output is upsampled, MAX_SAMPLES_PER_RUN is more than enough for the temp buffers.
Bit32u thisPassLen = len > MAX_SAMPLES_PER_RUN ? MAX_SAMPLES_PER_RUN : len;
synth.renderStreams(tmpNonReverbLeft, tmpNonReverbRight, tmpReverbDryLeft, tmpReverbDryRight, tmpReverbWetLeft, tmpReverbWetRight, synth.analog->getDACStreamsLength(thisPassLen));
synth.analog->process(converter.sampleBuffer, tmpNonReverbLeft, tmpNonReverbRight, tmpReverbDryLeft, tmpReverbDryRight, tmpReverbWetLeft, tmpReverbWetRight, thisPassLen);
@@ -1771,11 +1763,12 @@ void Renderer::renderStreams(
}
}
}
- doRenderStreams(
+ DACOutputStreams streams = {
nonReverbLeft.sampleBuffer, nonReverbRight.sampleBuffer,
reverbDryLeft.sampleBuffer, reverbDryRight.sampleBuffer,
- reverbWetLeft.sampleBuffer, reverbWetRight.sampleBuffer,
- thisLen);
+ reverbWetLeft.sampleBuffer, reverbWetRight.sampleBuffer
+ };
+ doRenderStreams(streams, thisLen);
nonReverbLeft.convert(thisLen);
nonReverbRight.convert(thisLen);
reverbDryLeft.convert(thisLen);
@@ -1870,17 +1863,14 @@ void Renderer::convertSamplesToOutput(Sample *buffer, Bit32u len) {
#endif
}
-void Renderer::doRenderStreams(Sample *nonReverbLeft, Sample *nonReverbRight, Sample *reverbDryLeft, Sample *reverbDryRight, Sample *reverbWetLeft, Sample *reverbWetRight, Bit32u len) {
- // Even if LA32 output isn't desired, we proceed anyway with temp buffers
- Sample tmpBufNonReverbLeft[MAX_SAMPLES_PER_RUN], tmpBufNonReverbRight[MAX_SAMPLES_PER_RUN];
- if (nonReverbLeft == NULL) nonReverbLeft = tmpBufNonReverbLeft;
- if (nonReverbRight == NULL) nonReverbRight = tmpBufNonReverbRight;
-
- Sample tmpBufReverbDryLeft[MAX_SAMPLES_PER_RUN], tmpBufReverbDryRight[MAX_SAMPLES_PER_RUN];
- if (reverbDryLeft == NULL) reverbDryLeft = tmpBufReverbDryLeft;
- if (reverbDryRight == NULL) reverbDryRight = tmpBufReverbDryRight;
-
+void Renderer::doRenderStreams(DACOutputStreams &streams, Bit32u len) {
if (synth.activated) {
+ // Even if LA32 output isn't desired, we proceed anyway with temp buffers
+ Sample *nonReverbLeft = streams.nonReverbLeft == NULL ? tmpNonReverbLeft : streams.nonReverbLeft;
+ Sample *nonReverbRight = streams.nonReverbRight == NULL ? tmpNonReverbRight : streams.nonReverbRight;
+ Sample *reverbDryLeft = streams.reverbDryLeft == NULL ? tmpReverbDryLeft : streams.reverbDryLeft;
+ Sample *reverbDryRight = streams.reverbDryRight == NULL ? tmpReverbDryRight : streams.reverbDryRight;
+
Synth::muteSampleBuffer(nonReverbLeft, len);
Synth::muteSampleBuffer(nonReverbRight, len);
Synth::muteSampleBuffer(reverbDryLeft, len);
@@ -1898,33 +1888,32 @@ void Renderer::doRenderStreams(Sample *nonReverbLeft, Sample *nonReverbRight, Sa
produceLA32Output(reverbDryRight, len);
if (synth.isReverbEnabled()) {
- synth.reverbModel->process(reverbDryLeft, reverbDryRight, reverbWetLeft, reverbWetRight, len);
- if (reverbWetLeft != NULL) convertSamplesToOutput(reverbWetLeft, len);
- if (reverbWetRight != NULL) convertSamplesToOutput(reverbWetRight, len);
+ synth.reverbModel->process(reverbDryLeft, reverbDryRight, streams.reverbWetLeft, streams.reverbWetRight, len);
+ if (streams.reverbWetLeft != NULL) convertSamplesToOutput(streams.reverbWetLeft, len);
+ if (streams.reverbWetRight != NULL) convertSamplesToOutput(streams.reverbWetRight, len);
} else {
- Synth::muteSampleBuffer(reverbWetLeft, len);
- Synth::muteSampleBuffer(reverbWetRight, len);
+ Synth::muteSampleBuffer(streams.reverbWetLeft, len);
+ Synth::muteSampleBuffer(streams.reverbWetRight, len);
}
// Don't bother with conversion if the output is going to be unused
- if (nonReverbLeft != tmpBufNonReverbLeft) {
+ if (streams.nonReverbLeft != NULL) {
produceLA32Output(nonReverbLeft, len);
convertSamplesToOutput(nonReverbLeft, len);
}
- if (nonReverbRight != tmpBufNonReverbRight) {
+ if (streams.nonReverbRight != NULL) {
produceLA32Output(nonReverbRight, len);
convertSamplesToOutput(nonReverbRight, len);
}
- if (reverbDryLeft != tmpBufReverbDryLeft) convertSamplesToOutput(reverbDryLeft, len);
- if (reverbDryRight != tmpBufReverbDryRight) convertSamplesToOutput(reverbDryRight, len);
+ if (streams.reverbDryLeft != NULL) convertSamplesToOutput(reverbDryLeft, len);
+ if (streams.reverbDryRight != NULL) convertSamplesToOutput(reverbDryRight, len);
} else {
- // Avoid muting buffers that wasn't requested
- if (nonReverbLeft != tmpBufNonReverbLeft) Synth::muteSampleBuffer(nonReverbLeft, len);
- if (nonReverbRight != tmpBufNonReverbRight) Synth::muteSampleBuffer(nonReverbRight, len);
- if (reverbDryLeft != tmpBufReverbDryLeft) Synth::muteSampleBuffer(reverbDryLeft, len);
- if (reverbDryRight != tmpBufReverbDryRight) Synth::muteSampleBuffer(reverbDryRight, len);
- Synth::muteSampleBuffer(reverbWetLeft, len);
- Synth::muteSampleBuffer(reverbWetRight, len);
+ Synth::muteSampleBuffer(streams.nonReverbLeft, len);
+ Synth::muteSampleBuffer(streams.nonReverbRight, len);
+ Synth::muteSampleBuffer(streams.reverbDryLeft, len);
+ Synth::muteSampleBuffer(streams.reverbDryRight, len);
+ Synth::muteSampleBuffer(streams.reverbWetLeft, len);
+ Synth::muteSampleBuffer(streams.reverbWetRight, len);
}
synth.partialManager->clearAlreadyOutputed();
diff --git a/audio/softsynth/mt32/Synth.h b/audio/softsynth/mt32/Synth.h
index 5561d8d5db5..fe31d99f022 100755
--- a/audio/softsynth/mt32/Synth.h
+++ b/audio/softsynth/mt32/Synth.h
@@ -120,6 +120,8 @@ friend class PartialManager;
friend class Poly;
friend class Renderer;
friend class RhythmPart;
+friend class SamplerateAdapter;
+friend class SoxrAdapter;
friend class TVA;
friend class TVP;
@@ -254,6 +256,14 @@ public:
}
}
+ static inline Bit16s convertSample(float sample) {
+ return Synth::clipSampleEx(Bit32s(sample * 16384.0f)); // This multiplier takes into account the DAC bit shift
+ }
+
+ static inline float convertSample(Bit16s sample) {
+ return float(sample) / 16384.0f; // This multiplier takes into account the DAC bit shift
+ }
+
// Returns library version as an integer in format: 0x00MMmmpp, where:
// MM - major version number
// mm - minor version number
diff --git a/audio/softsynth/mt32/config.h b/audio/softsynth/mt32/config.h
index 779cc4d4b3b..af59f055a0b 100644
--- a/audio/softsynth/mt32/config.h
+++ b/audio/softsynth/mt32/config.h
@@ -18,10 +18,10 @@
#ifndef MT32EMU_CONFIG_H
#define MT32EMU_CONFIG_H
-#define MT32EMU_VERSION "2.0.0"
+#define MT32EMU_VERSION "2.0.3"
#define MT32EMU_VERSION_MAJOR 2
#define MT32EMU_VERSION_MINOR 0
-#define MT32EMU_VERSION_PATCH 0
+#define MT32EMU_VERSION_PATCH 3
#define MT32EMU_EXPORTS_TYPE 3
diff --git a/audio/softsynth/mt32/module.mk b/audio/softsynth/mt32/module.mk
index 1a27492c2a3..7657f5b55f6 100644
--- a/audio/softsynth/mt32/module.mk
+++ b/audio/softsynth/mt32/module.mk
@@ -21,5 +21,20 @@ MODULE_OBJS := \
sha1/sha1.o \
c_interface/c_interface.o
+# SampleRateConverter.o \
+# srchelper/InternalResampler.o \
+# srchelper/SamplerateAdapter.o \
+# srchelper/SoxrAdapter.o \
+# srchelper/srctools/src/FIRResampler.o \
+# srchelper/srctools/src/IIR2xResampler.o \
+# srchelper/srctools/src/LinearResampler.o \
+# srchelper/srctools/src/ResamplerModel.o \
+# srchelper/srctools/src/SincResampler.o
+# TODO: The Munt SampleRateConverter requires these additional -I options.
+# This is not a very nice way of doing that, though, as it adds them globally.
+# INCLUDES += -I $(srcdir)/$(MODULE)/srchelper/srctools/include
+# INCLUDES += -I $(srcdir)/$(MODULE)/
+
+
# Include common rules
include $(srcdir)/rules.mk
diff --git a/audio/softsynth/mt32/mt32emu.h b/audio/softsynth/mt32/mt32emu.h
index 9f2b0582503..3f3b6af3449 100755
--- a/audio/softsynth/mt32/mt32emu.h
+++ b/audio/softsynth/mt32/mt32emu.h
@@ -77,6 +77,7 @@
#include "ROMInfo.h"
#include "Synth.h"
#include "MidiStreamParser.h"
+#include "SampleRateConverter.h"
#endif /* #if !defined(__cplusplus) || MT32EMU_API_TYPE == 1 */
diff --git a/audio/softsynth/mt32/srchelper/InternalResampler.cpp b/audio/softsynth/mt32/srchelper/InternalResampler.cpp
new file mode 100644
index 00000000000..f76c7d117e9
--- /dev/null
+++ b/audio/softsynth/mt32/srchelper/InternalResampler.cpp
@@ -0,0 +1,74 @@
+/* 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 "InternalResampler.h"
+
+#include
+#include
+
+#include "Synth.h"
+
+using namespace SRCTools;
+
+namespace MT32Emu {
+
+class SynthWrapper : public FloatSampleProvider {
+ Synth &synth;
+
+public:
+ SynthWrapper(Synth &useSynth) : synth(useSynth)
+ {}
+
+ void getOutputSamples(FloatSample *outBuffer, unsigned int size) {
+ synth.render(outBuffer, size);
+ }
+};
+
+static FloatSampleProvider &createModel(Synth &synth, SRCTools::FloatSampleProvider &synthSource, double targetSampleRate, SampleRateConverter::Quality quality) {
+ static const double MAX_AUDIBLE_FREQUENCY = 20000.0;
+
+ const double sourceSampleRate = synth.getStereoOutputSampleRate();
+ if (quality != SampleRateConverter::FASTEST) {
+ const bool oversampledMode = synth.getStereoOutputSampleRate() == Synth::getStereoOutputSampleRate(AnalogOutputMode_OVERSAMPLED);
+ // Oversampled input allows to bypass IIR interpolation stage and, in some cases, IIR decimation stage
+ if (oversampledMode && (0.5 * sourceSampleRate) <= targetSampleRate) {
+ // NOTE: In the oversampled mode, the transition band starts at 20kHz and ends at 28kHz
+ double passband = MAX_AUDIBLE_FREQUENCY;
+ double stopband = 0.5 * sourceSampleRate + MAX_AUDIBLE_FREQUENCY;
+ ResamplerStage &resamplerStage = *SincResampler::createSincResampler(sourceSampleRate, targetSampleRate, passband, stopband, ResamplerModel::DEFAULT_DB_SNR, ResamplerModel::DEFAULT_WINDOWED_SINC_MAX_UPSAMPLE_FACTOR);
+ return ResamplerModel::createResamplerModel(synthSource, resamplerStage);
+ }
+ }
+ return ResamplerModel::createResamplerModel(synthSource, sourceSampleRate, targetSampleRate, static_cast(quality));
+}
+
+} // namespace MT32Emu
+
+using namespace MT32Emu;
+
+InternalResampler::InternalResampler(Synth &synth, double targetSampleRate, SampleRateConverter::Quality quality) :
+ synthSource(*new SynthWrapper(synth)),
+ model(createModel(synth, synthSource, targetSampleRate, quality))
+{}
+
+InternalResampler::~InternalResampler() {
+ ResamplerModel::freeResamplerModel(model, synthSource);
+ delete &synthSource;
+}
+
+void InternalResampler::getOutputSamples(float *buffer, unsigned int length) {
+ model.getOutputSamples(buffer, length);
+}
diff --git a/audio/softsynth/mt32/srchelper/InternalResampler.h b/audio/softsynth/mt32/srchelper/InternalResampler.h
new file mode 100644
index 00000000000..0a5c3233c81
--- /dev/null
+++ b/audio/softsynth/mt32/srchelper/InternalResampler.h
@@ -0,0 +1,42 @@
+/* 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 .
+ */
+
+#ifndef INTERNAL_RESAMPLER_H
+#define INTERNAL_RESAMPLER_H
+
+#include "../SampleRateConverter.h"
+
+#include "FloatSampleProvider.h"
+
+namespace MT32Emu {
+
+class Synth;
+
+class InternalResampler {
+public:
+ InternalResampler(Synth &synth, double targetSampleRate, SampleRateConverter::Quality quality);
+ ~InternalResampler();
+
+ void getOutputSamples(float *buffer, unsigned int length);
+
+private:
+ SRCTools::FloatSampleProvider &synthSource;
+ SRCTools::FloatSampleProvider &model;
+};
+
+} // namespace MT32Emu
+
+#endif // INTERNAL_RESAMPLER_H
diff --git a/audio/softsynth/mt32/srchelper/SamplerateAdapter.cpp b/audio/softsynth/mt32/srchelper/SamplerateAdapter.cpp
new file mode 100644
index 00000000000..33fcfa81dd3
--- /dev/null
+++ b/audio/softsynth/mt32/srchelper/SamplerateAdapter.cpp
@@ -0,0 +1,99 @@
+/* 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 "SamplerateAdapter.h"
+
+#include "Synth.h"
+
+using namespace MT32Emu;
+
+static const unsigned int CHANNEL_COUNT = 2;
+
+long SamplerateAdapter::getInputSamples(void *cb_data, float **data) {
+ SamplerateAdapter *instance = static_cast(cb_data);
+ unsigned int length = instance->inBufferSize < 1 ? 1 : (MAX_SAMPLES_PER_RUN < instance->inBufferSize ? MAX_SAMPLES_PER_RUN : instance->inBufferSize);
+ instance->synth.render(instance->inBuffer, length);
+ *data = instance->inBuffer;
+ instance->inBufferSize -= length;
+ return length;
+}
+
+SamplerateAdapter::SamplerateAdapter(Synth &useSynth, double targetSampleRate, SampleRateConverter::Quality quality) :
+ synth(useSynth),
+ inBuffer(new float[CHANNEL_COUNT * MAX_SAMPLES_PER_RUN]),
+ inBufferSize(MAX_SAMPLES_PER_RUN),
+ inputToOutputRatio(useSynth.getStereoOutputSampleRate() / targetSampleRate),
+ outputToInputRatio(targetSampleRate / useSynth.getStereoOutputSampleRate())
+{
+ int error;
+ int conversionType;
+ switch (quality) {
+ case SampleRateConverter::FASTEST:
+ conversionType = SRC_LINEAR;
+ break;
+ case SampleRateConverter::FAST:
+ conversionType = SRC_SINC_FASTEST;
+ break;
+ case SampleRateConverter::BEST:
+ conversionType = SRC_SINC_BEST_QUALITY;
+ break;
+ case SampleRateConverter::GOOD:
+ default:
+ conversionType = SRC_SINC_MEDIUM_QUALITY;
+ break;
+ };
+ resampler = src_callback_new(getInputSamples, conversionType, CHANNEL_COUNT, &error, this);
+ if (error != 0) {
+ synth.printDebug("SamplerateAdapter: Creation of Samplerate instance failed: %s\n", src_strerror(error));
+ src_delete(resampler);
+ resampler = NULL;
+ }
+}
+
+SamplerateAdapter::~SamplerateAdapter() {
+ delete[] inBuffer;
+ src_delete(resampler);
+}
+
+void SamplerateAdapter::getOutputSamples(float *buffer, unsigned int length) {
+ if (resampler == NULL) {
+ Synth::muteSampleBuffer(buffer, CHANNEL_COUNT * length);
+ return;
+ }
+ while (length > 0) {
+ inBufferSize = static_cast(length * inputToOutputRatio + 0.5);
+ long gotFrames = src_callback_read(resampler, outputToInputRatio, long(length), buffer);
+ int error = src_error(resampler);
+ if (error != 0) {
+ synth.printDebug("SamplerateAdapter: Samplerate error during processing: %s > resetting\n", src_strerror(error));
+ error = src_reset(resampler);
+ if (error != 0) {
+ synth.printDebug("SamplerateAdapter: Samplerate failed to reset: %s\n", src_strerror(error));
+ src_delete(resampler);
+ resampler = NULL;
+ Synth::muteSampleBuffer(buffer, CHANNEL_COUNT * length);
+ synth.printDebug("SamplerateAdapter: Samplerate disabled\n");
+ return;
+ }
+ continue;
+ }
+ if (gotFrames <= 0) {
+ synth.printDebug("SamplerateAdapter: got %li frames from Samplerate, weird\n", gotFrames);
+ }
+ buffer += CHANNEL_COUNT * gotFrames;
+ length -= gotFrames;
+ }
+}
diff --git a/audio/softsynth/mt32/srchelper/SamplerateAdapter.h b/audio/softsynth/mt32/srchelper/SamplerateAdapter.h
new file mode 100644
index 00000000000..aac259b50a6
--- /dev/null
+++ b/audio/softsynth/mt32/srchelper/SamplerateAdapter.h
@@ -0,0 +1,46 @@
+/* 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 .
+ */
+
+#ifndef SAMPLERATE_ADAPTER_H
+#define SAMPLERATE_ADAPTER_H
+
+#include
+
+#include "../SampleRateConverter.h"
+
+namespace MT32Emu {
+
+class SamplerateAdapter {
+public:
+ SamplerateAdapter(Synth &synth, double targetSampleRate, SampleRateConverter::Quality quality);
+ ~SamplerateAdapter();
+
+ void getOutputSamples(float *outBuffer, unsigned int length);
+
+private:
+ Synth &synth;
+ float * const inBuffer;
+ unsigned int inBufferSize;
+ const double inputToOutputRatio;
+ const double outputToInputRatio;
+ SRC_STATE *resampler;
+
+ static long getInputSamples(void *cb_data, float **data);
+};
+
+} // namespace MT32Emu
+
+#endif // SAMPLERATE_ADAPTER_H
diff --git a/audio/softsynth/mt32/srchelper/SoxrAdapter.cpp b/audio/softsynth/mt32/srchelper/SoxrAdapter.cpp
new file mode 100644
index 00000000000..b13192be92c
--- /dev/null
+++ b/audio/softsynth/mt32/srchelper/SoxrAdapter.cpp
@@ -0,0 +1,106 @@
+/* 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 "SoxrAdapter.h"
+
+#include "Synth.h"
+
+using namespace MT32Emu;
+
+static const unsigned int CHANNEL_COUNT = 2;
+
+size_t SoxrAdapter::getInputSamples(void *input_fn_state, soxr_in_t *data, size_t requested_len) {
+ unsigned int length = requested_len < 1 ? 1 : (MAX_SAMPLES_PER_RUN < requested_len ? MAX_SAMPLES_PER_RUN : requested_len);
+ SoxrAdapter *instance = static_cast(input_fn_state);
+ instance->synth.render(instance->inBuffer, length);
+ *data = instance->inBuffer;
+ return length;
+}
+
+SoxrAdapter::SoxrAdapter(Synth &useSynth, double targetSampleRate, SampleRateConverter::Quality quality) :
+ synth(useSynth),
+ inBuffer(new float[CHANNEL_COUNT * MAX_SAMPLES_PER_RUN])
+{
+ soxr_io_spec_t ioSpec = soxr_io_spec(SOXR_FLOAT32_I, SOXR_FLOAT32_I);
+ unsigned long qualityRecipe;
+ switch (quality) {
+ case SampleRateConverter::FASTEST:
+ qualityRecipe = SOXR_QQ;
+ break;
+ case SampleRateConverter::FAST:
+ qualityRecipe = SOXR_LQ;
+ break;
+ case SampleRateConverter::GOOD:
+ qualityRecipe = SOXR_MQ;
+ break;
+ case SampleRateConverter::BEST:
+ default:
+ qualityRecipe = SOXR_16_BITQ;
+ break;
+ };
+ soxr_quality_spec_t qSpec = soxr_quality_spec(qualityRecipe, 0);
+ soxr_runtime_spec_t rtSpec = soxr_runtime_spec(1);
+ soxr_error_t error;
+ resampler = soxr_create(synth.getStereoOutputSampleRate(), targetSampleRate, CHANNEL_COUNT, &error, &ioSpec, &qSpec, &rtSpec);
+ if (error != NULL) {
+ synth.printDebug("SoxrAdapter: Creation of SOXR instance failed: %s\n", soxr_strerror(error));
+ soxr_delete(resampler);
+ resampler = NULL;
+ return;
+ }
+ error = soxr_set_input_fn(resampler, getInputSamples, this, MAX_SAMPLES_PER_RUN);
+ if (error != NULL) {
+ synth.printDebug("SoxrAdapter: Installing sample feed for SOXR failed: %s\n", soxr_strerror(error));
+ soxr_delete(resampler);
+ resampler = NULL;
+ }
+}
+
+SoxrAdapter::~SoxrAdapter() {
+ delete[] inBuffer;
+ if (resampler != NULL) {
+ soxr_delete(resampler);
+ }
+}
+
+void SoxrAdapter::getOutputSamples(float *buffer, unsigned int length) {
+ if (resampler == NULL) {
+ Synth::muteSampleBuffer(buffer, CHANNEL_COUNT * length);
+ return;
+ }
+ while (length > 0) {
+ size_t gotFrames = soxr_output(resampler, buffer, size_t(length));
+ soxr_error_t error = soxr_error(resampler);
+ if (error != NULL) {
+ synth.printDebug("SoxrAdapter: SOXR error during processing: %s > resetting\n", soxr_strerror(error));
+ error = soxr_clear(resampler);
+ if (error != NULL) {
+ synth.printDebug("SoxrAdapter: SOXR failed to reset: %s\n", soxr_strerror(error));
+ soxr_delete(resampler);
+ resampler = NULL;
+ Synth::muteSampleBuffer(buffer, CHANNEL_COUNT * length);
+ synth.printDebug("SoxrAdapter: SOXR disabled\n");
+ return;
+ }
+ continue;
+ }
+ if (gotFrames == 0) {
+ synth.printDebug("SoxrAdapter: got 0 frames from SOXR, weird\n");
+ }
+ buffer += CHANNEL_COUNT * gotFrames;
+ length -= static_cast(gotFrames);
+ }
+}
diff --git a/audio/softsynth/mt32/srchelper/SoxrAdapter.h b/audio/softsynth/mt32/srchelper/SoxrAdapter.h
new file mode 100644
index 00000000000..c764d9acfd6
--- /dev/null
+++ b/audio/softsynth/mt32/srchelper/SoxrAdapter.h
@@ -0,0 +1,43 @@
+/* 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 .
+ */
+
+#ifndef SOXR_ADAPTER_H
+#define SOXR_ADAPTER_H
+
+#include
+
+#include "../SampleRateConverter.h"
+
+namespace MT32Emu {
+
+class SoxrAdapter {
+public:
+ SoxrAdapter(Synth &synth, double targetSampleRate, SampleRateConverter::Quality quality);
+ ~SoxrAdapter();
+
+ void getOutputSamples(float *buffer, unsigned int length);
+
+private:
+ Synth &synth;
+ float * const inBuffer;
+ soxr_t resampler;
+
+ static size_t getInputSamples(void *input_fn_state, soxr_in_t *data, size_t requested_len);
+};
+
+} // namespace MT32Emu
+
+#endif // SOXR_ADAPTER_H
diff --git a/audio/softsynth/mt32/srchelper/srctools/include/FIRResampler.h b/audio/softsynth/mt32/srchelper/srctools/include/FIRResampler.h
new file mode 100644
index 00000000000..2c5d69052a2
--- /dev/null
+++ b/audio/softsynth/mt32/srchelper/srctools/include/FIRResampler.h
@@ -0,0 +1,67 @@
+/* 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 .
+ */
+
+#ifndef FIR_RESAMPLER_H
+#define FIR_RESAMPLER_H
+
+#include "ResamplerStage.h"
+
+namespace SRCTools {
+
+typedef FloatSample FIRCoefficient;
+
+static const unsigned int FIR_INTERPOLATOR_CHANNEL_COUNT = 2;
+
+class FIRResampler : public ResamplerStage {
+public:
+ FIRResampler(const unsigned int upsampleFactor, const double downsampleFactor, const FIRCoefficient kernel[], const unsigned int kernelLength);
+ ~FIRResampler();
+
+ void process(const FloatSample *&inSamples, unsigned int &inLength, FloatSample *&outSamples, unsigned int &outLength);
+ unsigned int estimateInLength(const unsigned int outLength) const;
+
+private:
+ const struct Constants {
+ // Filter coefficients
+ const FIRCoefficient *taps;
+ // Indicates whether to interpolate filter taps
+ bool usePhaseInterpolation;
+ // Size of array of filter coefficients
+ unsigned int numberOfTaps;
+ // Upsampling factor
+ unsigned int numberOfPhases;
+ // Downsampling factor
+ double phaseIncrement;
+ // Index of last delay line element, generally greater than numberOfTaps to form a proper binary mask
+ unsigned int delayLineMask;
+ // Delay line
+ FloatSample(*ringBuffer)[FIR_INTERPOLATOR_CHANNEL_COUNT];
+
+ Constants(const unsigned int upsampleFactor, const double downsampleFactor, const FIRCoefficient kernel[], const unsigned int kernelLength);
+ } constants;
+ // Index of current sample in delay line
+ unsigned int ringBufferPosition;
+ // Current phase
+ double phase;
+
+ bool needNextInSample() const;
+ void addInSamples(const FloatSample *&inSamples);
+ void getOutSamplesStereo(FloatSample *&outSamples);
+}; // class FIRResampler
+
+} // namespace SRCTools
+
+#endif // FIR_RESAMPLER_H
diff --git a/audio/softsynth/mt32/srchelper/srctools/include/FloatSampleProvider.h b/audio/softsynth/mt32/srchelper/srctools/include/FloatSampleProvider.h
new file mode 100644
index 00000000000..03038d03ec1
--- /dev/null
+++ b/audio/softsynth/mt32/srchelper/srctools/include/FloatSampleProvider.h
@@ -0,0 +1,34 @@
+/* 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 .
+ */
+
+#ifndef FLOAT_SAMPLE_PROVIDER_H
+#define FLOAT_SAMPLE_PROVIDER_H
+
+namespace SRCTools {
+
+typedef float FloatSample;
+
+/** Interface defines an abstract source of samples. It can either define a single channel stream or a stream with interleaved channels. */
+class FloatSampleProvider {
+public:
+ virtual ~FloatSampleProvider() {};
+
+ virtual void getOutputSamples(FloatSample *outBuffer, unsigned int size) = 0;
+};
+
+} // namespace SRCTools
+
+#endif // FLOAT_SAMPLE_PROVIDER_H
diff --git a/audio/softsynth/mt32/srchelper/srctools/include/IIR2xResampler.h b/audio/softsynth/mt32/srchelper/srctools/include/IIR2xResampler.h
new file mode 100644
index 00000000000..23733e40496
--- /dev/null
+++ b/audio/softsynth/mt32/srchelper/srctools/include/IIR2xResampler.h
@@ -0,0 +1,100 @@
+/* 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 .
+ */
+
+#ifndef IIR_2X_RESAMPLER_H
+#define IIR_2X_RESAMPLER_H
+
+#include "ResamplerStage.h"
+
+namespace SRCTools {
+
+static const unsigned int IIR_RESAMPER_CHANNEL_COUNT = 2;
+static const unsigned int IIR_SECTION_ORDER = 2;
+
+typedef FloatSample IIRCoefficient;
+typedef FloatSample BufferedSample;
+
+typedef BufferedSample SectionBuffer[IIR_SECTION_ORDER];
+
+// Non-trivial coefficients of a 2nd-order section of a parallel bank
+// (zero-order numerator coefficient is always zero, zero-order denominator coefficient is always unity)
+struct IIRSection {
+ IIRCoefficient num1;
+ IIRCoefficient num2;
+ IIRCoefficient den1;
+ IIRCoefficient den2;
+};
+
+class IIRResampler : public ResamplerStage {
+public:
+ enum Quality {
+ // Used when providing custom IIR filter coefficients.
+ CUSTOM,
+ // Use fast elliptic filter with symmetric ripple: N=8, Ap=As=-99 dB, fp=0.125, fs = 0.25 (in terms of sample rate)
+ FAST,
+ // Use average elliptic filter with symmetric ripple: N=12, Ap=As=-106 dB, fp=0.193, fs = 0.25 (in terms of sample rate)
+ GOOD,
+ // Use sharp elliptic filter with symmetric ripple: N=18, Ap=As=-106 dB, fp=0.238, fs = 0.25 (in terms of sample rate)
+ BEST
+ };
+
+ // Returns the retained fraction of the passband for the given standard quality value
+ static double getPassbandFractionForQuality(Quality quality);
+
+protected:
+ explicit IIRResampler(const Quality quality);
+ explicit IIRResampler(const unsigned int useSectionsCount, const IIRCoefficient useFIR, const IIRSection useSections[]);
+ ~IIRResampler();
+
+ const struct Constants {
+ // Coefficient of the 0-order FIR part
+ IIRCoefficient fir;
+ // 2nd-order sections that comprise a parallel bank
+ const IIRSection *sections;
+ // Number of 2nd-order sections
+ unsigned int sectionsCount;
+ // Delay line per channel per section
+ SectionBuffer *buffer;
+
+ Constants(const unsigned int useSectionsCount, const IIRCoefficient useFIR, const IIRSection useSections[], const Quality quality);
+ } constants;
+}; // class IIRResampler
+
+class IIR2xInterpolator : public IIRResampler {
+public:
+ explicit IIR2xInterpolator(const Quality quality);
+ explicit IIR2xInterpolator(const unsigned int useSectionsCount, const IIRCoefficient useFIR, const IIRSection useSections[]);
+
+ void process(const FloatSample *&inSamples, unsigned int &inLength, FloatSample *&outSamples, unsigned int &outLength);
+ unsigned int estimateInLength(const unsigned int outLength) const;
+
+private:
+ FloatSample lastInputSamples[IIR_RESAMPER_CHANNEL_COUNT];
+ unsigned int phase;
+};
+
+class IIR2xDecimator : public IIRResampler {
+public:
+ explicit IIR2xDecimator(const Quality quality);
+ explicit IIR2xDecimator(const unsigned int useSectionsCount, const IIRCoefficient useFIR, const IIRSection useSections[]);
+
+ void process(const FloatSample *&inSamples, unsigned int &inLength, FloatSample *&outSamples, unsigned int &outLength);
+ unsigned int estimateInLength(const unsigned int outLength) const;
+};
+
+} // namespace SRCTools
+
+#endif // IIR_2X_RESAMPLER_H
diff --git a/audio/softsynth/mt32/srchelper/srctools/include/LinearResampler.h b/audio/softsynth/mt32/srchelper/srctools/include/LinearResampler.h
new file mode 100644
index 00000000000..1f4dd2fcbd3
--- /dev/null
+++ b/audio/softsynth/mt32/srchelper/srctools/include/LinearResampler.h
@@ -0,0 +1,42 @@
+/* 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 .
+ */
+
+#ifndef LINEAR_RESAMPLER_H
+#define LINEAR_RESAMPLER_H
+
+#include "ResamplerStage.h"
+
+namespace SRCTools {
+
+static const unsigned int LINEAR_RESAMPER_CHANNEL_COUNT = 2;
+
+class LinearResampler : public ResamplerStage {
+public:
+ LinearResampler(double sourceSampleRate, double targetSampleRate);
+ ~LinearResampler() {}
+
+ unsigned int estimateInLength(const unsigned int outLength) const;
+ void process(const FloatSample *&inSamples, unsigned int &inLength, FloatSample *&outSamples, unsigned int &outLength);
+
+private:
+ const double inputToOutputRatio;
+ double position;
+ FloatSample lastInputSamples[LINEAR_RESAMPER_CHANNEL_COUNT];
+};
+
+} // namespace SRCTools
+
+#endif // LINEAR_RESAMPLER_H
diff --git a/audio/softsynth/mt32/srchelper/srctools/include/ResamplerModel.h b/audio/softsynth/mt32/srchelper/srctools/include/ResamplerModel.h
new file mode 100644
index 00000000000..0372605e876
--- /dev/null
+++ b/audio/softsynth/mt32/srchelper/srctools/include/ResamplerModel.h
@@ -0,0 +1,63 @@
+/* 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 .
+ */
+
+#ifndef RESAMPLER_MODEL_H
+#define RESAMPLER_MODEL_H
+
+#include "FloatSampleProvider.h"
+
+namespace SRCTools {
+
+class ResamplerStage;
+
+/** Model consists of one or more ResampleStage instances connected in a cascade. */
+namespace ResamplerModel {
+
+// Seems to be a good choice for 16-bit integer samples.
+static const double DEFAULT_DB_SNR = 106;
+
+// When using linear interpolation, oversampling factor necessary to achieve the DEFAULT_DB_SNR is about 256.
+// This figure is the upper estimation, and it can be found by analysing the frequency response of the linear interpolator.
+// When less SNR is desired, this value should also decrease in accordance.
+static const unsigned int DEFAULT_WINDOWED_SINC_MAX_DOWNSAMPLE_FACTOR = 256;
+
+// In the default resampler model, the input to the windowed sinc filter is always at least 2x oversampled during upsampling,
+// so oversampling factor of 128 should be sufficient to achieve the DEFAULT_DB_SNR with linear interpolation.
+static const unsigned int DEFAULT_WINDOWED_SINC_MAX_UPSAMPLE_FACTOR = DEFAULT_WINDOWED_SINC_MAX_DOWNSAMPLE_FACTOR / 2;
+
+
+enum Quality {
+ // Use when the speed is more important than the audio quality.
+ FASTEST,
+ // Use FAST quality setting of the IIR stage (50% of passband retained).
+ FAST,
+ // Use GOOD quality setting of the IIR stage (77% of passband retained).
+ GOOD,
+ // Use BEST quality setting of the IIR stage (95% of passband retained).
+ BEST
+};
+
+FloatSampleProvider &createResamplerModel(FloatSampleProvider &source, double sourceSampleRate, double targetSampleRate, Quality quality);
+FloatSampleProvider &createResamplerModel(FloatSampleProvider &source, ResamplerStage **stages, unsigned int stageCount);
+FloatSampleProvider &createResamplerModel(FloatSampleProvider &source, ResamplerStage &stage);
+
+void freeResamplerModel(FloatSampleProvider &model, FloatSampleProvider &source);
+
+} // namespace ResamplerModel
+
+} // namespace SRCTools
+
+#endif // RESAMPLER_MODEL_H
diff --git a/audio/softsynth/mt32/srchelper/srctools/include/ResamplerStage.h b/audio/softsynth/mt32/srchelper/srctools/include/ResamplerStage.h
new file mode 100644
index 00000000000..c0f0a0a50ab
--- /dev/null
+++ b/audio/softsynth/mt32/srchelper/srctools/include/ResamplerStage.h
@@ -0,0 +1,38 @@
+/* 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 .
+ */
+
+#ifndef RESAMPLER_STAGE_H
+#define RESAMPLER_STAGE_H
+
+#include "FloatSampleProvider.h"
+
+namespace SRCTools {
+
+/** Interface defines an abstract source of samples. It can either define a single channel stream or a stream with interleaved channels. */
+class ResamplerStage {
+public:
+ virtual ~ResamplerStage() {};
+
+ /** Returns a lower estimation of required number of input samples to produce the specified number of output samples. */
+ virtual unsigned int estimateInLength(const unsigned int outLength) const = 0;
+
+ /** Generates output samples. The arguments are adjusted in accordance with the number of samples processed. */
+ virtual void process(const FloatSample *&inSamples, unsigned int &inLength, FloatSample *&outSamples, unsigned int &outLength) = 0;
+};
+
+} // namespace SRCTools
+
+#endif // RESAMPLER_STAGE_H
diff --git a/audio/softsynth/mt32/srchelper/srctools/include/SincResampler.h b/audio/softsynth/mt32/srchelper/srctools/include/SincResampler.h
new file mode 100644
index 00000000000..ea3f03b1122
--- /dev/null
+++ b/audio/softsynth/mt32/srchelper/srctools/include/SincResampler.h
@@ -0,0 +1,46 @@
+/* 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 .
+ */
+
+#ifndef SINC_RESAMPLER_H
+#define SINC_RESAMPLER_H
+
+#include "FIRResampler.h"
+
+namespace SRCTools {
+
+class ResamplerStage;
+
+namespace SincResampler {
+
+ ResamplerStage *createSincResampler(const double inputFrequency, const double outputFrequency, const double passbandFrequency, const double stopbandFrequency, const double dbSNR, const unsigned int maxUpsampleFactor);
+
+ namespace Utils {
+ void computeResampleFactors(unsigned int &upsampleFactor, double &downsampleFactor, const double inputFrequency, const double outputFrequency, const unsigned int maxUpsampleFactor);
+ unsigned int greatestCommonDivisor(unsigned int a, unsigned int b);
+ }
+
+ namespace KaizerWindow {
+ double estimateBeta(double dbRipple);
+ unsigned int estimateOrder(double dbRipple, double fp, double fs);
+ double bessel(const double x);
+ void windowedSinc(FIRCoefficient kernel[], const unsigned int order, const double fc, const double beta, const double amp);
+ }
+
+} // namespace SincResampler
+
+} // namespace SRCTools
+
+#endif // SINC_RESAMPLER_H
diff --git a/audio/softsynth/mt32/srchelper/srctools/src/FIRResampler.cpp b/audio/softsynth/mt32/srchelper/srctools/src/FIRResampler.cpp
new file mode 100644
index 00000000000..2cded0c3d7c
--- /dev/null
+++ b/audio/softsynth/mt32/srchelper/srctools/src/FIRResampler.cpp
@@ -0,0 +1,107 @@
+/* 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;
+}
diff --git a/audio/softsynth/mt32/srchelper/srctools/src/IIR2xResampler.cpp b/audio/softsynth/mt32/srchelper/srctools/src/IIR2xResampler.cpp
new file mode 100644
index 00000000000..061006a1e73
--- /dev/null
+++ b/audio/softsynth/mt32/srchelper/srctools/src/IIR2xResampler.cpp
@@ -0,0 +1,229 @@
+/* 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 "IIR2xResampler.h"
+
+namespace SRCTools {
+
+ // Avoid denormals degrading performance, using biased input
+ static const BufferedSample BIAS = 1e-35f;
+
+ // Sharp elliptic filter with symmetric ripple: N=18, Ap=As=-106 dB, fp=0.238, fs = 0.25 (in terms of sample rate)
+ static const IIRCoefficient FIR_BEST = 0.0014313792470984f;
+ static const IIRSection SECTIONS_BEST[] = {
+ { 2.85800356692148000f,-0.2607342682253230f,-0.602478421807085f, 0.109823442522145f },
+ { -4.39519408383016000f, 1.4651975326003500f,-0.533817668127954f, 0.226045921792036f },
+ { 0.86638550740991800f,-2.1053851417898500f,-0.429134968401065f, 0.403512574222174f },
+ { 1.67161485530774000f, 0.7963595880494520f,-0.324989203363446f, 0.580756666711889f },
+ { -1.19962759276471000f, 0.5873595178851540f,-0.241486447489019f, 0.724264899930934f },
+ { 0.01631779946479250f,-0.6282334739461620f,-0.182766025706656f, 0.827774001858882f },
+ { 0.28404415859352400f, 0.1038619997715160f,-0.145276649558926f, 0.898510501923554f },
+ { -0.08105788424234910f, 0.0781551578108934f,-0.123965846623366f, 0.947105257601873f },
+ { -0.00872608905948005f,-0.0222098231712466f,-0.115056854360748f, 0.983542001125711f }
+ };
+
+ // Average elliptic filter with symmetric ripple: N=12, Ap=As=-106 dB, fp=0.193, fs = 0.25 (in terms of sample rate)
+ static const IIRCoefficient FIR_GOOD = 0.000891054570268146f;
+ static const IIRSection SECTIONS_GOOD[] = {
+ { 2.2650157226725700f,-0.4034180565140230f,-0.750061486095301f, 0.157801404511953f },
+ { -3.2788261989161700f, 1.3952152147542600f,-0.705854270206788f, 0.265564985645774f },
+ { 0.4397975114813240f,-1.3957634748753100f,-0.639718853965265f, 0.435324134360315f },
+ { 0.9827040216680520f, 0.1837182774040940f,-0.578569965618418f, 0.615205557837542f },
+ { -0.3759752818621670f, 0.3266073609399490f,-0.540913588637109f, 0.778264420176574f },
+ { -0.0253548089519618f,-0.0925779221603846f,-0.537704370375240f, 0.925800083252964f }
+ };
+
+ // Fast elliptic filter with symmetric ripple: N=8, Ap=As=-99 dB, fp=0.125, fs = 0.25 (in terms of sample rate)
+ static const IIRCoefficient FIR_FAST = 0.000882837778745889f;
+ static const IIRSection SECTIONS_FAST[] = {
+ { 1.215377077431620f,-0.35864455030878000f,-0.972220718789242f, 0.252934735930620f },
+ { -1.525654419254140f, 0.86784918631245500f,-0.977713689358124f, 0.376580616703668f },
+ { 0.136094441564220f,-0.50414116798010400f,-1.007004471865290f, 0.584048854845331f },
+ { 0.180604082285806f,-0.00467624342403851f,-1.093486919012100f, 0.844904524843996f }
+ };
+
+ static inline BufferedSample calcNumerator(const IIRSection §ion, const BufferedSample buffer1, const BufferedSample buffer2) {
+ return section.num1 * buffer1 + section.num2 * buffer2;
+ }
+
+ static inline BufferedSample calcDenominator(const IIRSection §ion, const BufferedSample input, const BufferedSample buffer1, const BufferedSample buffer2) {
+ return input - section.den1 * buffer1 - section.den2 * buffer2;
+ }
+
+} // namespace SRCTools
+
+using namespace SRCTools;
+
+double IIRResampler::getPassbandFractionForQuality(Quality quality) {
+ switch (quality) {
+ case FAST:
+ return 0.5;
+ case GOOD:
+ return 0.7708;
+ case BEST:
+ return 0.9524;
+ default:
+ return 0;
+ }
+}
+
+IIRResampler::Constants::Constants(const unsigned int useSectionsCount, const IIRCoefficient useFIR, const IIRSection useSections[], const Quality quality) {
+ if (quality == CUSTOM) {
+ sectionsCount = useSectionsCount;
+ fir = useFIR;
+ sections = useSections;
+ } else {
+ unsigned int sectionsSize;
+ switch (quality) {
+ case FAST:
+ fir = FIR_FAST;
+ sections = SECTIONS_FAST;
+ sectionsSize = sizeof(SECTIONS_FAST);
+ break;
+ case GOOD:
+ fir = FIR_GOOD;
+ sections = SECTIONS_GOOD;
+ sectionsSize = sizeof(SECTIONS_GOOD);
+ break;
+ case BEST:
+ fir = FIR_BEST;
+ sections = SECTIONS_BEST;
+ sectionsSize = sizeof(SECTIONS_BEST);
+ break;
+ default:
+ sectionsSize = 0;
+ break;
+ }
+ sectionsCount = (sectionsSize / sizeof(IIRSection));
+ }
+ const unsigned int delayLineSize = IIR_RESAMPER_CHANNEL_COUNT * sectionsCount;
+ buffer = new SectionBuffer[delayLineSize];
+ BufferedSample *s = buffer[0];
+ BufferedSample *e = buffer[delayLineSize];
+ while (s < e) *(s++) = 0;
+}
+
+IIRResampler::IIRResampler(const Quality quality) :
+ constants(0, 0.0f, NULL, quality)
+{}
+
+IIRResampler::IIRResampler(const unsigned int useSectionsCount, const IIRCoefficient useFIR, const IIRSection useSections[]) :
+ constants(useSectionsCount, useFIR, useSections, IIRResampler::CUSTOM)
+{}
+
+IIRResampler::~IIRResampler() {
+ delete[] constants.buffer;
+}
+
+IIR2xInterpolator::IIR2xInterpolator(const Quality quality) :
+ IIRResampler(quality),
+ phase()
+{
+ for (unsigned int chIx = 0; chIx < IIR_RESAMPER_CHANNEL_COUNT; ++chIx) {
+ lastInputSamples[chIx] = 0;
+ }
+}
+
+IIR2xInterpolator::IIR2xInterpolator(const unsigned int useSectionsCount, const IIRCoefficient useFIR, const IIRSection useSections[]) :
+ IIRResampler(useSectionsCount, useFIR, useSections),
+ phase()
+{
+ for (unsigned int chIx = 0; chIx < IIR_RESAMPER_CHANNEL_COUNT; ++chIx) {
+ lastInputSamples[chIx] = 0;
+ }
+}
+
+void IIR2xInterpolator::process(const FloatSample *&inSamples, unsigned int &inLength, FloatSample *&outSamples, unsigned int &outLength) {
+ static const IIRCoefficient INTERPOLATOR_AMP = 2.0;
+
+ while (outLength > 0 && inLength > 0) {
+ SectionBuffer *bufferp = constants.buffer;
+ for (unsigned int chIx = 0; chIx < IIR_RESAMPER_CHANNEL_COUNT; ++chIx) {
+ const FloatSample lastInputSample = lastInputSamples[chIx];
+ const FloatSample inSample = inSamples[chIx];
+ BufferedSample tmpOut = phase == 0 ? 0 : inSample * constants.fir;
+ for (unsigned int i = 0; i < constants.sectionsCount; ++i) {
+ const IIRSection §ion = constants.sections[i];
+ SectionBuffer &buffer = *bufferp;
+ // For 2x interpolation, calculation of the numerator reduces to a single multiplication depending on the phase.
+ if (phase == 0) {
+ const BufferedSample numOutSample = section.num1 * lastInputSample;
+ const BufferedSample denOutSample = calcDenominator(section, BIAS + numOutSample, buffer[0], buffer[1]);
+ buffer[1] = denOutSample;
+ tmpOut += denOutSample;
+ } else {
+ const BufferedSample numOutSample = section.num2 * lastInputSample;
+ const BufferedSample denOutSample = calcDenominator(section, BIAS + numOutSample, buffer[1], buffer[0]);
+ buffer[0] = denOutSample;
+ tmpOut += denOutSample;
+ }
+ bufferp++;
+ }
+ *(outSamples++) = FloatSample(INTERPOLATOR_AMP * tmpOut);
+ if (phase > 0) {
+ lastInputSamples[chIx] = inSample;
+ }
+ }
+ outLength--;
+ if (phase > 0) {
+ inSamples += IIR_RESAMPER_CHANNEL_COUNT;
+ inLength--;
+ phase = 0;
+ } else {
+ phase = 1;
+ }
+ }
+}
+
+unsigned int IIR2xInterpolator::estimateInLength(const unsigned int outLength) const {
+ return outLength >> 1;
+}
+
+IIR2xDecimator::IIR2xDecimator(const Quality quality) :
+ IIRResampler(quality)
+{}
+
+IIR2xDecimator::IIR2xDecimator(const unsigned int useSectionsCount, const IIRCoefficient useFIR, const IIRSection useSections[]) :
+ IIRResampler(useSectionsCount, useFIR, useSections)
+{}
+
+void IIR2xDecimator::process(const FloatSample *&inSamples, unsigned int &inLength, FloatSample *&outSamples, unsigned int &outLength) {
+ while (outLength > 0 && inLength > 1) {
+ SectionBuffer *bufferp = constants.buffer;
+ for (unsigned int chIx = 0; chIx < IIR_RESAMPER_CHANNEL_COUNT; ++chIx) {
+ BufferedSample tmpOut = inSamples[chIx] * constants.fir;
+ for (unsigned int i = 0; i < constants.sectionsCount; ++i) {
+ const IIRSection §ion = constants.sections[i];
+ SectionBuffer &buffer = *bufferp;
+ // For 2x decimation, calculation of the numerator is not performed for odd output samples which are to be omitted.
+ tmpOut += calcNumerator(section, buffer[0], buffer[1]);
+ buffer[1] = calcDenominator(section, BIAS + inSamples[chIx], buffer[0], buffer[1]);
+ buffer[0] = calcDenominator(section, BIAS + inSamples[chIx + IIR_RESAMPER_CHANNEL_COUNT], buffer[1], buffer[0]);
+ bufferp++;
+ }
+ *(outSamples++) = FloatSample(tmpOut);
+ }
+ outLength--;
+ inLength -= 2;
+ inSamples += 2 * IIR_RESAMPER_CHANNEL_COUNT;
+ }
+}
+
+unsigned int IIR2xDecimator::estimateInLength(const unsigned int outLength) const {
+ return outLength << 1;
+}
diff --git a/audio/softsynth/mt32/srchelper/srctools/src/LinearResampler.cpp b/audio/softsynth/mt32/srchelper/srctools/src/LinearResampler.cpp
new file mode 100644
index 00000000000..98b9c77c73d
--- /dev/null
+++ b/audio/softsynth/mt32/srchelper/srctools/src/LinearResampler.cpp
@@ -0,0 +1,47 @@
+/* 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 "LinearResampler.h"
+
+using namespace SRCTools;
+
+LinearResampler::LinearResampler(double sourceSampleRate, double targetSampleRate) :
+ inputToOutputRatio(sourceSampleRate / targetSampleRate),
+ position(1.0) // Preload delay line which effectively makes resampler zero phase
+{}
+
+void LinearResampler::process(const FloatSample *&inSamples, unsigned int &inLength, FloatSample *&outSamples, unsigned int &outLength) {
+ if (inLength == 0) return;
+ while (outLength > 0) {
+ while (1.0 <= position) {
+ position--;
+ inLength--;
+ for (unsigned int chIx = 0; chIx < LINEAR_RESAMPER_CHANNEL_COUNT; ++chIx) {
+ lastInputSamples[chIx] = *(inSamples++);
+ }
+ if (inLength == 0) return;
+ }
+ for (unsigned int chIx = 0; chIx < LINEAR_RESAMPER_CHANNEL_COUNT; chIx++) {
+ *(outSamples++) = FloatSample(lastInputSamples[chIx] + position * (inSamples[chIx] - lastInputSamples[chIx]));
+ }
+ outLength--;
+ position += inputToOutputRatio;
+ }
+}
+
+unsigned int LinearResampler::estimateInLength(const unsigned int outLength) const {
+ return static_cast(outLength * inputToOutputRatio);
+}
diff --git a/audio/softsynth/mt32/srchelper/srctools/src/ResamplerModel.cpp b/audio/softsynth/mt32/srchelper/srctools/src/ResamplerModel.cpp
new file mode 100644
index 00000000000..4d2d9308372
--- /dev/null
+++ b/audio/softsynth/mt32/srchelper/srctools/src/ResamplerModel.cpp
@@ -0,0 +1,153 @@
+/* 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 "ResamplerModel.h"
+
+#include "ResamplerStage.h"
+#include "SincResampler.h"
+#include "IIR2xResampler.h"
+#include "LinearResampler.h"
+
+namespace SRCTools {
+
+namespace ResamplerModel {
+
+static const unsigned int CHANNEL_COUNT = 2;
+static const unsigned int MAX_SAMPLES_PER_RUN = 4096;
+
+class CascadeStage : public FloatSampleProvider {
+friend void freeResamplerModel(FloatSampleProvider &model, FloatSampleProvider &source);
+public:
+ CascadeStage(FloatSampleProvider &source, ResamplerStage &resamplerStage);
+
+ void getOutputSamples(FloatSample *outBuffer, unsigned int size);
+
+protected:
+ ResamplerStage &resamplerStage;
+
+private:
+ FloatSampleProvider &source;
+ FloatSample buffer[CHANNEL_COUNT * MAX_SAMPLES_PER_RUN];
+ const FloatSample *bufferPtr;
+ unsigned int size;
+};
+
+class InternalResamplerCascadeStage : public CascadeStage {
+public:
+ InternalResamplerCascadeStage(FloatSampleProvider &useSource, ResamplerStage &useResamplerStage) :
+ CascadeStage(useSource, useResamplerStage)
+ {}
+
+ ~InternalResamplerCascadeStage() {
+ delete &resamplerStage;
+ }
+};
+
+} // namespace ResamplerModel
+
+} // namespace SRCTools
+
+using namespace SRCTools;
+
+FloatSampleProvider &ResamplerModel::createResamplerModel(FloatSampleProvider &source, double sourceSampleRate, double targetSampleRate, Quality quality) {
+ if (sourceSampleRate == targetSampleRate) {
+ return source;
+ }
+ if (quality == FASTEST) {
+ return *new InternalResamplerCascadeStage(source, *new LinearResampler(sourceSampleRate, targetSampleRate));
+ }
+ const IIRResampler::Quality iirQuality = static_cast(quality);
+ const double iirPassbandFraction = IIRResampler::getPassbandFractionForQuality(iirQuality);
+ if (sourceSampleRate < targetSampleRate) {
+ ResamplerStage *iir2xInterpolator = new IIR2xInterpolator(iirQuality);
+ FloatSampleProvider &iir2xInterpolatorStage = *new InternalResamplerCascadeStage(source, *iir2xInterpolator);
+
+ if (2.0 * sourceSampleRate == targetSampleRate) {
+ return iir2xInterpolatorStage;
+ }
+
+ double passband = 0.5 * sourceSampleRate * iirPassbandFraction;
+ double stopband = 1.5 * sourceSampleRate;
+ ResamplerStage *sincResampler = SincResampler::createSincResampler(2.0 * sourceSampleRate, targetSampleRate, passband, stopband, DEFAULT_DB_SNR, DEFAULT_WINDOWED_SINC_MAX_UPSAMPLE_FACTOR);
+ return *new InternalResamplerCascadeStage(iir2xInterpolatorStage, *sincResampler);
+ }
+
+ if (sourceSampleRate == 2.0 * targetSampleRate) {
+ ResamplerStage *iir2xDecimator = new IIR2xDecimator(iirQuality);
+ return *new InternalResamplerCascadeStage(source, *iir2xDecimator);
+ }
+
+ double passband = 0.5 * targetSampleRate * iirPassbandFraction;
+ double stopband = 1.5 * targetSampleRate;
+ double sincOutSampleRate = 2.0 * targetSampleRate;
+ const unsigned int maxUpsampleFactor = static_cast(ceil(DEFAULT_WINDOWED_SINC_MAX_DOWNSAMPLE_FACTOR * sincOutSampleRate / sourceSampleRate));
+ ResamplerStage *sincResampler = SincResampler::createSincResampler(sourceSampleRate, sincOutSampleRate, passband, stopband, DEFAULT_DB_SNR, maxUpsampleFactor);
+ FloatSampleProvider &sincResamplerStage = *new InternalResamplerCascadeStage(source, *sincResampler);
+
+ ResamplerStage *iir2xDecimator = new IIR2xDecimator(iirQuality);
+ return *new InternalResamplerCascadeStage(sincResamplerStage, *iir2xDecimator);
+}
+
+FloatSampleProvider &ResamplerModel::createResamplerModel(FloatSampleProvider &source, ResamplerStage **resamplerStages, unsigned int stageCount) {
+ FloatSampleProvider *prevStage = &source;
+ for (unsigned int i = 0; i < stageCount; i++) {
+ prevStage = new CascadeStage(*prevStage, *(resamplerStages[i]));
+ }
+ return *prevStage;
+}
+
+FloatSampleProvider &ResamplerModel::createResamplerModel(FloatSampleProvider &source, ResamplerStage &stage) {
+ return *new CascadeStage(source, stage);
+}
+
+void ResamplerModel::freeResamplerModel(FloatSampleProvider &model, FloatSampleProvider &source) {
+ FloatSampleProvider *currentStage = &model;
+ while (currentStage != &source) {
+ CascadeStage *cascadeStage = dynamic_cast(currentStage);
+ if (cascadeStage == NULL) return;
+ FloatSampleProvider &prevStage = cascadeStage->source;
+ delete currentStage;
+ currentStage = &prevStage;
+ }
+}
+
+using namespace ResamplerModel;
+
+CascadeStage::CascadeStage(FloatSampleProvider &useSource, ResamplerStage &useResamplerStage) :
+ resamplerStage(useResamplerStage),
+ source(useSource),
+ bufferPtr(buffer),
+ size()
+{}
+
+void CascadeStage::getOutputSamples(FloatSample *outBuffer, unsigned int length) {
+ while (length > 0) {
+ if (size == 0) {
+ size = resamplerStage.estimateInLength(length);
+ if (size < 1) {
+ size = 1;
+ } else if (MAX_SAMPLES_PER_RUN < size) {
+ size = MAX_SAMPLES_PER_RUN;
+ }
+ source.getOutputSamples(buffer, size);
+ bufferPtr = buffer;
+ }
+ resamplerStage.process(bufferPtr, size, outBuffer, length);
+ }
+}
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 00000000000..3ed028d2612
--- /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 .
+ */
+
+#include
+
+#ifdef SINC_RESAMPLER_DEBUG_LOG
+#include
+#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(outputFrequency);
+ unsigned int downsampleFactorInt = static_cast(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(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;
+}