Reading and Writing to a sped up or slowed down buffer with as minimal aliasing and distortion as possible

Howdy!

I am working on a looper pedal that allows you to control the speed that the loop plays back at.
The looper allows for overdubbing, and this is where the problem arises.
For Overdubbing, I am just adding the input to the already existing buffer.

buffer[position] = buffer[position] + in[i];

For the variable speed playback I am using a float for the buffer position and incrementing it by speed every cycle, and rounding it to the nearest int.

I overdub onto the buffer at the same speed as playback.

When I overdub at speed 0.5 or 2 it sounds pretty good (which makes sense)

But, when I overdub at say, speed 1.6, and then slow the speed down, it unsurprisingly sounds pretty terrible, very aliased/distorted.

I hope I’m managing to explain the problem in any way that makes sense, but I also hope that at this point you might know exactly what I’m talking about if you’ve ever tried to so something like this.

This seems like some sort of inherent limitation to digital recording and resampling?
Is there an actual way to do this?

I’ve been looking all around the internet at interpolation methods and aliasing filters, and most of it is going above my head, and I don’t really want to dive to deep into something super complicated and math heavy if what I am trying to do here is just inherently impossible to make sound good. I have noticed that no other looper seems to allow you to speed up or slow down the loop to a speed other that 0.5 or 2, which makes me think that it might just be sort of a digital limitation?

Has anyone done something like this? Is there a well known method to do something like this?

Some of the things I have tried:

I have tried several types of interpolation from this site: https://paulbourke.net/miscellaneous/interpolation/

I think that the basic linear interpolation sounds the best (which doesn’t really make that much sense), but I could also be implementing the other ones incorrectly.

Are these the correct buffer position to feed into the cubic interpolation?

y0 = bufferPositionInt -2
y1 = bufferPositionInt -1
y2 = bufferPositionInt
y3 = bufferPositionInt +1
mu = bufferposition - bufferpositionInt

When I overdub to the buffer I do this:

if (speed > 1){
    buf[posInt] = (buf[posInt] * fadeOut) + in[i];
    buf[posInt-1] += in[i] * (speed - 1);
    
}
if (speed < 1){
    if (posfrac != 0){
        buf[posInt] = (buf[posInt] * fadeOut) + in[i];
        
    } 
    buf[posInt+1] += in[i] * (0.5 - speed);
}
if (speed == 1){
    buf[posInt] = (buf[posInt] * fadeOut) + in[i];
}

I’m not sure if this is some primitive implementation of an already known technique, or just a kind if hacky workaround, but out of anything I have tried it definitely helps the most.

Soooo yeah, I don’t really know where to go from here.

Should I look into sinc interpolation? I haven’t been able to find a c++ implementation that I can wrap my head around yet, but if you think it would do the job, then I’m sure I can figure it out eventually.

Should I low pass filter the input when recording the overdub? Something about
Nyquist frequency?

Anyways,

If you’ve made it this far, then thank you! I hope that it has made even a little bit of sense, and I hope that this is way easier than I think it is, that would be amazing!

Sounds like a pretty complicated issue. I cant claim to have a perfect solution for you but here are some thoughts.
It is true that speeding up the playback speed will shift the frequencies in your audio up the spectrum which could lead to aliasing if you go beyond the nyquist frequency, given that things sound bad at 1.6 playback speed and then good at 2, Im inclined to believe aliasing might not be the issue at hand.

I think it might be more likely that the issue could be arising due to the fact you are rounding your read index to the nearest integer. This would lead to an inconsistent sampling period in your output, subject to rounding error. Perhaps a better solution can be found in the read method for the delayline class in daisySP. Rather than rounding to a integer index, this class instead can linearly interpolate between two samples in order to approximate any intermediate value. Perhaps this would be a better way to playback audio at variable speeds.

Then in order to write at variable speeds. I don’t believe it is possible to use the above method of linear interpolation. Your write index would need to be an integer value. An alternate solution I can think of is using a second buffer, in which you can write the sped up value from the first buffer, and then your new input sample. Then by switching between the two bbuffers the desired functionality may be achieved.

I may be well off here, but perhaps these ideas you might find helpful