mirror of
https://github.com/libretro/scummvm.git
synced 2024-12-12 03:56:20 +00:00
MT32: Update Munt to 2.0.3
This update uses upstream commit 777c51cdb4dbb4e02a53c23edea9086f0b600e26. The new SampleRateConverter is added, but not built as we don't use it. Also, building it without source changes will need additional include directories. This update of Munt reduces the stack size, and thus fixes bug #9630.
This commit is contained in:
parent
e2a72393d2
commit
867511d2d0
@ -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() {
|
||||
|
@ -49,6 +49,9 @@ protected:
|
||||
private:
|
||||
bool sha1DigestCalculated;
|
||||
SHA1Digest sha1Digest;
|
||||
|
||||
// Binary compatibility helper.
|
||||
void *reserved;
|
||||
};
|
||||
|
||||
class MT32EMU_EXPORT ArrayFile : public AbstractFile {
|
||||
|
@ -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;
|
||||
}
|
||||
|
97
audio/softsynth/mt32/SampleRateConverter.cpp
Normal file
97
audio/softsynth/mt32/SampleRateConverter.cpp
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#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<SoxrAdapter *>(srcDelegate);
|
||||
#elif MT32EMU_WITH_LIBSAMPLERATE_RESAMPLER
|
||||
delete static_cast<SamplerateAdapter *>(srcDelegate);
|
||||
#else
|
||||
delete static_cast<InternalResampler *>(srcDelegate);
|
||||
#endif
|
||||
}
|
||||
|
||||
void SampleRateConverter::getOutputSamples(float *buffer, unsigned int length) {
|
||||
#if MT32EMU_WITH_LIBSOXR_RESAMPLER
|
||||
static_cast<SoxrAdapter *>(srcDelegate)->getOutputSamples(buffer, length);
|
||||
#elif MT32EMU_WITH_LIBSAMPLERATE_RESAMPLER
|
||||
static_cast<SamplerateAdapter *>(srcDelegate)->getOutputSamples(buffer, length);
|
||||
#else
|
||||
static_cast<InternalResampler *>(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;
|
||||
}
|
78
audio/softsynth/mt32/SampleRateConverter.h
Normal file
78
audio/softsynth/mt32/SampleRateConverter.h
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#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
|
@ -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<Sample> &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<Sample> 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<Sample> &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();
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -77,6 +77,7 @@
|
||||
#include "ROMInfo.h"
|
||||
#include "Synth.h"
|
||||
#include "MidiStreamParser.h"
|
||||
#include "SampleRateConverter.h"
|
||||
|
||||
#endif /* #if !defined(__cplusplus) || MT32EMU_API_TYPE == 1 */
|
||||
|
||||
|
74
audio/softsynth/mt32/srchelper/InternalResampler.cpp
Normal file
74
audio/softsynth/mt32/srchelper/InternalResampler.cpp
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "InternalResampler.h"
|
||||
|
||||
#include <SincResampler.h>
|
||||
#include <ResamplerModel.h>
|
||||
|
||||
#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<ResamplerModel::Quality>(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);
|
||||
}
|
42
audio/softsynth/mt32/srchelper/InternalResampler.h
Normal file
42
audio/softsynth/mt32/srchelper/InternalResampler.h
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#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
|
99
audio/softsynth/mt32/srchelper/SamplerateAdapter.cpp
Normal file
99
audio/softsynth/mt32/srchelper/SamplerateAdapter.cpp
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#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<SamplerateAdapter *>(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<unsigned int>(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;
|
||||
}
|
||||
}
|
46
audio/softsynth/mt32/srchelper/SamplerateAdapter.h
Normal file
46
audio/softsynth/mt32/srchelper/SamplerateAdapter.h
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef SAMPLERATE_ADAPTER_H
|
||||
#define SAMPLERATE_ADAPTER_H
|
||||
|
||||
#include <samplerate.h>
|
||||
|
||||
#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
|
106
audio/softsynth/mt32/srchelper/SoxrAdapter.cpp
Normal file
106
audio/softsynth/mt32/srchelper/SoxrAdapter.cpp
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#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<SoxrAdapter *>(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<unsigned int>(gotFrames);
|
||||
}
|
||||
}
|
43
audio/softsynth/mt32/srchelper/SoxrAdapter.h
Normal file
43
audio/softsynth/mt32/srchelper/SoxrAdapter.h
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef SOXR_ADAPTER_H
|
||||
#define SOXR_ADAPTER_H
|
||||
|
||||
#include <soxr.h>
|
||||
|
||||
#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
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#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
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#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
|
100
audio/softsynth/mt32/srchelper/srctools/include/IIR2xResampler.h
Normal file
100
audio/softsynth/mt32/srchelper/srctools/include/IIR2xResampler.h
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#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
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#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
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#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
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#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
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#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
|
107
audio/softsynth/mt32/srchelper/srctools/src/FIRResampler.cpp
Normal file
107
audio/softsynth/mt32/srchelper/srctools/src/FIRResampler.cpp
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <cmath>
|
||||
#include <cstring>
|
||||
|
||||
#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<unsigned int>(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<unsigned int>((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<unsigned int>(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<unsigned int>(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;
|
||||
}
|
229
audio/softsynth/mt32/srchelper/srctools/src/IIR2xResampler.cpp
Normal file
229
audio/softsynth/mt32/srchelper/srctools/src/IIR2xResampler.cpp
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
#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;
|
||||
}
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#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<unsigned int>(outLength * inputToOutputRatio);
|
||||
}
|
153
audio/softsynth/mt32/srchelper/srctools/src/ResamplerModel.cpp
Normal file
153
audio/softsynth/mt32/srchelper/srctools/src/ResamplerModel.cpp
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <cmath>
|
||||
#include <cstddef>
|
||||
|
||||
#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<IIRResampler::Quality>(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<unsigned int>(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<CascadeStage *>(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);
|
||||
}
|
||||
}
|
136
audio/softsynth/mt32/srchelper/srctools/src/SincResampler.cpp
Normal file
136
audio/softsynth/mt32/srchelper/srctools/src/SincResampler.cpp
Normal file
@ -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;
|
||||
}
|
Loading…
Reference in New Issue
Block a user