it does’t look to me like this code will produce any output without input. (but maybe i don’t understand the question.)
also, well, it’s not a realtime resampler ehh, nevermind, i see how it works. “rate” is a divisor and it only acts as an integer. (i maintain that this is weird and unpredictable, i see several ways you could go out of bounds on the internal buffer, &c, but that’s as it may be.)
if you want to do SR reduction in realtime, you need interpolated writes. with anything but zero-order interpolation this is quite a bit trickier than interpolated reads, which is why softcut::Resampler exists. (it only deals with writes) - same for some of the hairier parts of karma~ or anything with similar duties.
basically something like this, which of course i have not tested at all:
// these headers are from softcut
// you could of course rip out the relevant bits if you don't want cpp
#include "Resampler.h"
#include "Interpolate.h"
/// Resampler handles interpolated writing.
softcut::Resampler resamp;
// assumption: Resampler::sample_t == float
// we need to do interpolated reads ourselves.
// this requires a second buffer.
// this only needs to be big enough for our interpolation window.
// (if we allowed upsampling things would get more complicated, but why bother?)
#define BUFSIZE 4;
#define BUFMASK 3;
float buf[BUFSIZE];
unsigned int idx = 0;
double phase = 0.0; // current "playback" phase in [0,1]
double inc; // phase increment per sample;
// write a new value to the buffer, update the index
void writeToBuf(const float x) {
buf[idx] = x;
idx = (idx + 1) & BUFMASK;
}
// this could use other interpolation modes if you want more dirt.
float readFromBuf() {
// assumptions:
// - we always read after write, so `idx` will be the _oldest_ location
// - idx is already wrapped
float y0 = buf[(idx + 3) & BUFMASK];
float y_1 = buf[(idx + 2) & BUFMASK];
float y_2 = buf[(idx + 1) & BUFMASK];
float y_3 = buf[idx];
float y = softcut::Interpolate::hermite(phase, y_3, y_2, y_1, y0);
phase += inc;
while (phase > 1.0) { phase -= 1.0; }
return y;
}
void setRate(double r) {
if (r > 1.f) {
rate = 1.f;
} else {
rate = val;
}
calcRate();
}
void setSampleRate(double val) {
sr = val;
calcRate();
}
void calcRate() {
inc = rate / sr;
resamp.setRate(rate);
}
void processSample(const float* in, float* out) {
// currently, this returns how many samples were written.
// if rate < 1, nframes will either be zero or one.
// if rate > 1, nframes will be >= 1.
int nframes = resamp_.processFrame(*in);
// this returns a pointer to Resampler's output buffer.
// after `processFrame()`, the output buf contains N samples.
// in softcut, we'd capture all these in a buffer, and interpolate playback separately.
// here, we just want to immediately "play back" at the same rate we "recorded" with.
// write...
const float* resampBuf = resamp_.output();
for(int i=0; i<nframes; ++i) {
writeBuf(resampBuf[i]);
}
// read out the last value with interpolation
*out = readBuf();
}