Browse Source

Reorganize DSP headers, rewrite Decimator, rewrite some FIR functions

tags/v0.6.2b
Andrew Belt 6 years ago
parent
commit
925031b9b1
6 changed files with 171 additions and 157 deletions
  1. +1
    -38
      include/dsp/decimator.hpp
  2. +22
    -20
      include/dsp/fir.hpp
  3. +1
    -0
      include/dsp/functions.hpp
  4. +145
    -0
      include/dsp/resampler.hpp
  5. +1
    -98
      include/dsp/samplerate.hpp
  6. +1
    -1
      src/Core/AudioInterface.cpp

+ 1
- 38
include/dsp/decimator.hpp View File

@@ -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"

+ 22
- 20
include/dsp/fir.hpp View File

@@ -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);
}
}



+ 1
- 0
include/dsp/functions.hpp View File

@@ -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;


+ 145
- 0
include/dsp/resampler.hpp View File

@@ -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
- 98
include/dsp/samplerate.hpp View File

@@ -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"

+ 1
- 1
src/Core/AudioInterface.cpp View File

@@ -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"




Loading…
Cancel
Save