| @@ -1,39 +1,2 @@ | |||
| #pragma once | |||
| #include "string.h" | |||
| #include "dsp/ringbuffer.hpp" | |||
| #include "dsp/fir.hpp" | |||
| namespace rack { | |||
| template<int OVERSAMPLE, int QUALITY> | |||
| struct Decimator { | |||
| DoubleRingBuffer<float, OVERSAMPLE*QUALITY> inBuffer; | |||
| float kernel[OVERSAMPLE*QUALITY]; | |||
| Decimator(float cutoff = 0.9) { | |||
| boxcarFIR(kernel, OVERSAMPLE*QUALITY, cutoff * 0.5 / OVERSAMPLE); | |||
| blackmanHarrisWindow(kernel, OVERSAMPLE*QUALITY); | |||
| // The sum of the kernel should be 1 | |||
| float sum = 0.0; | |||
| for (int i = 0; i < OVERSAMPLE*QUALITY; i++) { | |||
| sum += kernel[i]; | |||
| } | |||
| for (int i = 0; i < OVERSAMPLE*QUALITY; i++) { | |||
| kernel[i] /= sum; | |||
| } | |||
| // Zero input buffer | |||
| memset(inBuffer.data, 0, sizeof(inBuffer.data)); | |||
| } | |||
| /** `in` must be OVERSAMPLE floats long */ | |||
| float process(float *in) { | |||
| memcpy(inBuffer.endData(), in, OVERSAMPLE*sizeof(float)); | |||
| inBuffer.endIncr(OVERSAMPLE); | |||
| float out = convolve(inBuffer.endData() + OVERSAMPLE*QUALITY, kernel, OVERSAMPLE*QUALITY); | |||
| // Ignore the ring buffer's start position | |||
| return out; | |||
| } | |||
| }; | |||
| } // namespace rack | |||
| #include "resampler.hpp" | |||
| @@ -5,34 +5,36 @@ | |||
| namespace rack { | |||
| /** Perform a direct convolution | |||
| x[-len + 1] to x[0] must be defined | |||
| */ | |||
| inline float convolve(const float *x, const float *kernel, int len) { | |||
| float y = 0.0; | |||
| /** Performs a direct sum convolution */ | |||
| inline float convolveNaive(const float *in, const float *kernel, int len) { | |||
| float y = 0.f; | |||
| for (int i = 0; i < len; i++) { | |||
| y += x[-i] * kernel[i]; | |||
| y += in[len - 1 - i] * kernel[i]; | |||
| } | |||
| return y; | |||
| } | |||
| inline void blackmanHarrisWindow(float *x, int n) { | |||
| const float a0 = 0.35875; | |||
| const float a1 = 0.48829; | |||
| const float a2 = 0.14128; | |||
| const float a3 = 0.01168; | |||
| for (int i = 0; i < n; i++) { | |||
| x[i] *= a0 | |||
| - a1 * cosf(2 * M_PI * i / (n - 1)) | |||
| + a2 * cosf(4 * M_PI * i / (n - 1)) | |||
| - a3 * cosf(6 * M_PI * i / (n - 1)); | |||
| /** Computes the impulse response of a boxcar lowpass filter */ | |||
| inline void boxcarLowpassIR(float *out, int len, float cutoff = 0.5f) { | |||
| for (int i = 0; i < len; i++) { | |||
| float t = i - (len - 1) / 2.f; | |||
| out[i] = 2 * cutoff * sinc(2 * cutoff * t); | |||
| } | |||
| } | |||
| inline void boxcarFIR(float *x, int n, float cutoff) { | |||
| for (int i = 0; i < n; i++) { | |||
| float t = (float)i / (n - 1) * 2.0 - 1.0; | |||
| x[i] = sinc(t * n * cutoff); | |||
| inline void blackmanHarrisWindow(float *x, int len) { | |||
| // Constants from https://en.wikipedia.org/wiki/Window_function#Blackman%E2%80%93Harris_window | |||
| const float a0 = 0.35875f; | |||
| const float a1 = 0.48829f; | |||
| const float a2 = 0.14128f; | |||
| const float a3 = 0.01168f; | |||
| float factor = 2*M_PI / (len - 1); | |||
| for (int i = 0; i < len; i++) { | |||
| x[i] *= | |||
| + a0 | |||
| - a1 * cosf(1*factor * i) | |||
| + a2 * cosf(2*factor * i) | |||
| - a3 * cosf(3*factor * i); | |||
| } | |||
| } | |||
| @@ -6,6 +6,7 @@ | |||
| namespace rack { | |||
| /** The normalized sinc function. https://en.wikipedia.org/wiki/Sinc_function */ | |||
| inline float sinc(float x) { | |||
| if (x == 0.f) | |||
| return 1.f; | |||
| @@ -0,0 +1,145 @@ | |||
| #pragma once | |||
| #include <assert.h> | |||
| #include <string.h> | |||
| #include <speex/speex_resampler.h> | |||
| #include "frame.hpp" | |||
| #include "ringbuffer.hpp" | |||
| #include "fir.hpp" | |||
| namespace rack { | |||
| template<int CHANNELS> | |||
| struct SampleRateConverter { | |||
| SpeexResamplerState *st = NULL; | |||
| int channels = CHANNELS; | |||
| int quality = SPEEX_RESAMPLER_QUALITY_DEFAULT; | |||
| int inRate = 44100; | |||
| int outRate = 44100; | |||
| SampleRateConverter() { | |||
| refreshState(); | |||
| } | |||
| ~SampleRateConverter() { | |||
| if (st) { | |||
| speex_resampler_destroy(st); | |||
| } | |||
| } | |||
| /** Sets the number of channels to actually process. This can be at most CHANNELS. */ | |||
| void setChannels(int channels) { | |||
| assert(channels <= CHANNELS); | |||
| if (channels == this->channels) | |||
| return; | |||
| this->channels = channels; | |||
| refreshState(); | |||
| } | |||
| /** From 0 (worst, fastest) to 10 (best, slowest) */ | |||
| void setQuality(int quality) { | |||
| if (quality == this->quality) | |||
| return; | |||
| this->quality = quality; | |||
| refreshState(); | |||
| } | |||
| void setRates(int inRate, int outRate) { | |||
| if (inRate == this->inRate && outRate == this->outRate) | |||
| return; | |||
| this->inRate = inRate; | |||
| this->outRate = outRate; | |||
| refreshState(); | |||
| } | |||
| void refreshState() { | |||
| if (st) { | |||
| speex_resampler_destroy(st); | |||
| st = NULL; | |||
| } | |||
| if (channels > 0 && inRate != outRate) { | |||
| int err; | |||
| st = speex_resampler_init(channels, inRate, outRate, quality, &err); | |||
| assert(st); | |||
| assert(err == RESAMPLER_ERR_SUCCESS); | |||
| speex_resampler_set_input_stride(st, CHANNELS); | |||
| speex_resampler_set_output_stride(st, CHANNELS); | |||
| } | |||
| } | |||
| /** `in` and `out` are interlaced with the number of channels */ | |||
| void process(const Frame<CHANNELS> *in, int *inFrames, Frame<CHANNELS> *out, int *outFrames) { | |||
| assert(in); | |||
| assert(inFrames); | |||
| assert(out); | |||
| assert(outFrames); | |||
| if (st) { | |||
| // Resample each channel at a time | |||
| spx_uint32_t inLen; | |||
| spx_uint32_t outLen; | |||
| for (int i = 0; i < channels; i++) { | |||
| inLen = *inFrames; | |||
| outLen = *outFrames; | |||
| int err = speex_resampler_process_float(st, i, ((const float*) in) + i, &inLen, ((float*) out) + i, &outLen); | |||
| assert(err == RESAMPLER_ERR_SUCCESS); | |||
| } | |||
| *inFrames = inLen; | |||
| *outFrames = outLen; | |||
| } | |||
| else { | |||
| // Simply copy the buffer without conversion | |||
| int frames = min(*inFrames, *outFrames); | |||
| memcpy(out, in, frames * sizeof(Frame<CHANNELS>)); | |||
| *inFrames = frames; | |||
| *outFrames = frames; | |||
| } | |||
| } | |||
| }; | |||
| template<int OVERSAMPLE, int QUALITY> | |||
| struct Decimator { | |||
| float inBuffer[OVERSAMPLE*QUALITY]; | |||
| float kernel[OVERSAMPLE*QUALITY]; | |||
| int inIndex = 0; | |||
| Decimator(float cutoff = 0.9f) { | |||
| boxcarLowpassIR(kernel, OVERSAMPLE*QUALITY, cutoff * 0.5f / OVERSAMPLE); | |||
| blackmanHarrisWindow(kernel, OVERSAMPLE*QUALITY); | |||
| reset(); | |||
| } | |||
| void reset() { | |||
| // Zero input buffer | |||
| memset(inBuffer, 0, sizeof(inBuffer)); | |||
| } | |||
| /** `in` must be length OVERSAMPLE */ | |||
| float process(float *in) { | |||
| // Copy input to buffer | |||
| memcpy(&inBuffer[inIndex], in, OVERSAMPLE*sizeof(float)); | |||
| inIndex += OVERSAMPLE; | |||
| inIndex %= OVERSAMPLE*QUALITY; | |||
| // Perform naive convolution | |||
| float out = 0.f; | |||
| for (int i = 0; i < OVERSAMPLE*QUALITY; i++) { | |||
| int index = inIndex - 1 - i; | |||
| index = (index + OVERSAMPLE*QUALITY) % (OVERSAMPLE*QUALITY); | |||
| out += kernel[i] * inBuffer[index]; | |||
| } | |||
| return out; | |||
| } | |||
| }; | |||
| template<int OVERSAMPLE, int QUALITY> | |||
| struct Upsampler { | |||
| /** `out` must be length OVERSAMPLE */ | |||
| void process(float in, float *out) { | |||
| // TODO | |||
| } | |||
| }; | |||
| } // namespace rack | |||
| @@ -1,99 +1,2 @@ | |||
| #pragma once | |||
| #include <assert.h> | |||
| #include <string.h> | |||
| #include <speex/speex_resampler.h> | |||
| #include "frame.hpp" | |||
| namespace rack { | |||
| template<int CHANNELS> | |||
| struct SampleRateConverter { | |||
| SpeexResamplerState *st = NULL; | |||
| int channels = CHANNELS; | |||
| int quality = SPEEX_RESAMPLER_QUALITY_DEFAULT; | |||
| int inRate = 44100; | |||
| int outRate = 44100; | |||
| SampleRateConverter() { | |||
| refreshState(); | |||
| } | |||
| ~SampleRateConverter() { | |||
| if (st) { | |||
| speex_resampler_destroy(st); | |||
| } | |||
| } | |||
| /** Sets the number of channels to actually process. This can be at most CHANNELS. */ | |||
| void setChannels(int channels) { | |||
| assert(channels <= CHANNELS); | |||
| if (channels == this->channels) | |||
| return; | |||
| this->channels = channels; | |||
| refreshState(); | |||
| } | |||
| /** From 0 (worst, fastest) to 10 (best, slowest) */ | |||
| void setQuality(int quality) { | |||
| if (quality == this->quality) | |||
| return; | |||
| this->quality = quality; | |||
| refreshState(); | |||
| } | |||
| void setRates(int inRate, int outRate) { | |||
| if (inRate == this->inRate && outRate == this->outRate) | |||
| return; | |||
| this->inRate = inRate; | |||
| this->outRate = outRate; | |||
| refreshState(); | |||
| } | |||
| void refreshState() { | |||
| if (st) { | |||
| speex_resampler_destroy(st); | |||
| st = NULL; | |||
| } | |||
| if (channels > 0 && inRate != outRate) { | |||
| int err; | |||
| st = speex_resampler_init(channels, inRate, outRate, quality, &err); | |||
| assert(st); | |||
| assert(err == RESAMPLER_ERR_SUCCESS); | |||
| speex_resampler_set_input_stride(st, CHANNELS); | |||
| speex_resampler_set_output_stride(st, CHANNELS); | |||
| } | |||
| } | |||
| /** `in` and `out` are interlaced with the number of channels */ | |||
| void process(const Frame<CHANNELS> *in, int *inFrames, Frame<CHANNELS> *out, int *outFrames) { | |||
| assert(in); | |||
| assert(inFrames); | |||
| assert(out); | |||
| assert(outFrames); | |||
| if (st) { | |||
| // Resample each channel at a time | |||
| spx_uint32_t inLen; | |||
| spx_uint32_t outLen; | |||
| for (int i = 0; i < channels; i++) { | |||
| inLen = *inFrames; | |||
| outLen = *outFrames; | |||
| int err = speex_resampler_process_float(st, i, ((const float*) in) + i, &inLen, ((float*) out) + i, &outLen); | |||
| assert(err == RESAMPLER_ERR_SUCCESS); | |||
| } | |||
| *inFrames = inLen; | |||
| *outFrames = outLen; | |||
| } | |||
| else { | |||
| // Simply copy the buffer without conversion | |||
| int frames = min(*inFrames, *outFrames); | |||
| memcpy(out, in, frames * sizeof(Frame<CHANNELS>)); | |||
| *inFrames = frames; | |||
| *outFrames = frames; | |||
| } | |||
| } | |||
| }; | |||
| } // namespace rack | |||
| #include "resampler.hpp" | |||
| @@ -6,7 +6,7 @@ | |||
| #include <condition_variable> | |||
| #include "Core.hpp" | |||
| #include "audio.hpp" | |||
| #include "dsp/samplerate.hpp" | |||
| #include "dsp/resampler.hpp" | |||
| #include "dsp/ringbuffer.hpp" | |||