Hello everyone!
First off - here is a port of the JUCE oversampling class that does not work - I am wondering if it’s a filter design problem, implementation issue or both.
This is my first post in the forum: I am a self taught coder and just got a Daisy Field 3 weeks ago, but am finding it to be big fun! Right now, I’m working on porting over an old JUCE delay project that I am really excited about, but I am also creating a personal template system for the Daisy Field so I can quickly add DSP code to it without worring too much about coding for the human interface (The OLED and the knobs and the CV jacks) over and over again. That’ll be a seperate post, eventually.
Any-how, to get to the meat of the matter: below is some code basically ported from the JUCE oversampling class. I thought it would be useful for waveshaping and distortion applications, as well as taming delay-head tweaks and twists that push frequencies over Nyquist.
This runs on my Daisy Field, but for whatever reason, it does filter, but poorly - in the downsampling I am getting some rejection of aliasing, but it’s not what I’d like to see. I am using the same FIR coeffcients for the upsampling and downsampling, which could just be me having a lack of understanding essential concepts of this particular algorithm.
I am using https://fiiir.com/ to generate the table, and being very conservative about the stopband.
I’m also using a 47-coefficient FIR table currently, which is taking approximately 15% of the cpu of the Daisy Field, just running a single oscillator, FWIW:
Code below!
#ifndef OVERSAMPLING_H
#define OVERSAMPLING_H
#include <stddef.h>
template<size_t audio_block_size, size_t NUM_CHANS>
class Oversampling
{
public:
Oversampling(){};
~Oversampling(){};
auto getArray()
{
return oversample_;
}
void Upsample(const float* const* in, float** out, size_t size)
{
for (int chan = 0; chan < NUM_CHANS; chan++)
{
// 3 buffers - ring buffer for input, fir buffer and output buffer
// loop through input buffer
for (size_t i = 0; i < size; i++)
{
// Move current input sample to 2nd to last index in ring buffer
// also does 0 padding
ring_buffer_[chan][N - 1] = 2 * in[chan][i];
// Convolution
float out = 0.;
for (size_t k = 0; k < Ndiv2; k += 2) // Every other sample
out += (ring_buffer_[chan][k] + ring_buffer_[chan][N - k - 1]) * fir[k]; // Convolve 2 samples
// Outputs
oversample_[chan][i << 1] = out;
oversample_[chan][(i << 1) + 1] = ring_buffer_[chan][Ndiv2 + 1] * fir[Ndiv2];
// Shift data in ring buffer
for (size_t k = 0; k < N -2; k +=2)
ring_buffer_[chan][k] = ring_buffer_[chan][k + 2];
}
}
}
void Downsample(const float* const* in, float** out, size_t size)
{
/*
for (int chan = 0; chan < NUM_CHANS; chan++)
{
for (size_t j = 0, k = 0; j < audio_block_size; j++, k+=2)
{
out[chan][j] = oversample_[chan][k];
}
}
*/
for (int chan = 0; chan < NUM_CHANS; chan++)
{
// 3 buffers - ring buffer for input, fir buffer and output buffer
int pos = position[chan];
// loop through input buffer
for (size_t i = 0; i < size; ++i)
{
// Go through ever other sample of the oversampled block, move to ring buffer
ring_buffer2_[chan][N - 1] = oversample_[chan][i << 1];
// Convolution
float output = 0.;
for (size_t k = 0; k < Ndiv2; k += 2) // Every other sample
output += (ring_buffer2_[chan][k] + ring_buffer2_[chan][N - k - 1]) * fir[k]; // Convolve 2 samples
// Outputs
output += ring_buffer3_[chan][pos] * fir[Ndiv2];
ring_buffer3_[chan][pos] = oversample_[chan][(i << 1) + 1];
out[chan][i] = output;
// Shift data in ring buffer
for (size_t k = 0; k < N -2; k +=2)
ring_buffer2_[chan][k] = ring_buffer2_[chan][k + 2];
pos = (pos == 0? Ndiv4 : pos -1);
}
position[chan] = pos;
}
};
private:
int N = 47;
int filter_order = N - 1;
int Ndiv2 = N / 2;
int Ndiv4 = N / 4;
float ring_buffer_[2][47];
float ring_buffer2_[2][47];
float ring_buffer3_[2][12]; // NDIV4 + 1
float oversample_[2][audio_block_size * 2];
int position[2];
float fir [47] =
{0.000000000000000000,
-0.000007557788881071,
-0.000103221978462192,
0.000000000000000000,
0.000489705572593109,
0.000264597153084575,
-0.001222266266451664,
-0.001226283269580699,
0.002158218826490908,
0.003504088919578970,
-0.002722240662191572,
-0.007735246094119957,
0.001696911603050828,
0.014271225531182942,
0.002906758125478506,
-0.022851937327185167,
-0.014069845936793281,
0.032438904580599219,
0.037098373517579816,
-0.041348647561233541,
-0.088223035243884654,
0.047696118455589541,
0.311989058091958482,
0.449992643503193734,
0.311989058091958538,
0.047696118455589541,
-0.088223035243884640,
-0.041348647561233555,
0.037098373517579823,
0.032438904580599219,
-0.014069845936793281,
-0.022851937327185184,
0.002906758125478507,
0.014271225531182938,
0.001696911603050827,
-0.007735246094119960,
-0.002722240662191574,
0.003504088919578971,
0.002158218826490911,
-0.001226283269580700,
-0.001222266266451664,
0.000264597153084576,
0.000489705572593109,
0.000000000000000000,
-0.000103221978462193,
-0.000007557788881071,
0.000000000000000000};
};
#endif