Delay Line Interpolation
-
- KVRer
- 23 posts since 3 May, 2009
I want to cure audio distortion on my delay line plug-in when the delay parameter is changed. I've done a bit of research into this problem and the general consensus seems to be for big changes use a cross-fade and for small changes use interpolation. The cross-fade solution seems fairly self explanatory (although probably tricky to implement), but at this time I am failing to understand how to apply interpolation to this problem or how it will help cure the problem.
This is what I have. The delay line is a memory buffer with a size equal to the maximum delay time. I then have two pointer: a "read head" and a "write head". The distance between the two heads is then proportional to the current value of the delay parameter. When the delay parameter is changed the read head remains in the same position, but the write head is then moved to a new location that is proportional to new parameter value. This causes the content of the audio buffer to sharply change and will generate audio distortion. This is more pronounced when the delay parameter is changed small amounts in quick succession i.e. the delay slider is slide to a new location.
I've looked at different data interpolation techniques, but as I said at the beginning I can't quite see how to apply it this problem. As the write head is moved to a new location I have two points, lets call them "current position" and "new position". Would I then interpolate between the two points to give me intermediate points and a smoother transition? Presumably I'd have to move between the intermediate points at a slower rate to give a smooth transition? How would further changes to the parameter while the "smooth transition" is happening, be managed?
Would linear interpolation be moving from the "current position" to the "new position" in a linear ramp? Do I then also have to interpolate between samples actually in the buffer? If not there will there not still be choppy changes in the audio?
Sorry to ask so many question, but any pointers would be greatly appreciated.
Darrell.
This is what I have. The delay line is a memory buffer with a size equal to the maximum delay time. I then have two pointer: a "read head" and a "write head". The distance between the two heads is then proportional to the current value of the delay parameter. When the delay parameter is changed the read head remains in the same position, but the write head is then moved to a new location that is proportional to new parameter value. This causes the content of the audio buffer to sharply change and will generate audio distortion. This is more pronounced when the delay parameter is changed small amounts in quick succession i.e. the delay slider is slide to a new location.
I've looked at different data interpolation techniques, but as I said at the beginning I can't quite see how to apply it this problem. As the write head is moved to a new location I have two points, lets call them "current position" and "new position". Would I then interpolate between the two points to give me intermediate points and a smoother transition? Presumably I'd have to move between the intermediate points at a slower rate to give a smooth transition? How would further changes to the parameter while the "smooth transition" is happening, be managed?
Would linear interpolation be moving from the "current position" to the "new position" in a linear ramp? Do I then also have to interpolate between samples actually in the buffer? If not there will there not still be choppy changes in the audio?
Sorry to ask so many question, but any pointers would be greatly appreciated.
Darrell.
- KVRAF
- 9600 posts since 17 Sep, 2002 from Gothenburg Sweden
Isn't it the other way around? The write head stays where it is and you move the read head. That's what i would do.
-
- KVRer
- Topic Starter
- 23 posts since 3 May, 2009
I don't see it will really make much difference. If you do it my way (move the move the write head) when the read head reached that point there will be an audio jump (mind you I guess the audio after the jump, assuming an increase, will be old of empty). If you move the read head there will also be an audio jump. Or am I missing something? I don't think this changes the audio distortion problem.jupiter8 wrote:Isn't it the other way around? The write head stays where it is and you move the read head. That's what i would do.
Darrell.
- KVRAF
- 9600 posts since 17 Sep, 2002 from Gothenburg Sweden
You cannot move the write head forward since there's no audio there. It's in the future.It has'nt arrived yet (not entirely true since we're working with buffers but you get the idea).gibsond0120 wrote:I don't see it will really make much difference. If you do it my way (move the move the write head) when the read head reached that point there will be an audio jump (mind you I guess the audio after the jump, assuming an increase, will be old of empty). If you move the read head there will also be an audio jump. Or am I missing something? I don't think this changes the audio distortion problem.jupiter8 wrote:Isn't it the other way around? The write head stays where it is and you move the read head. That's what i would do.
Darrell.
Anyways that isn't the main problem. The problem is that you move the head to quickly. If you implement a read speed parameter and increase/decrease that until you hit the desired delay time things would improve.
That's when you need the interpolation.
EDIT: Or you could just make a lopass filter on the read head index so it does'nt jump to the new read location but slowly gets there. You'd still need interpolation for the fractional part of the index.
-
- KVRer
- Topic Starter
- 23 posts since 3 May, 2009
Agreed. Of course I was hearing the old data in the buffer. On a music piece this was not immediatly obvious. I've now changed my plug-in to reflect this and it has made an improvement to the audio quality.jupiter8 wrote:You cannot move the write head forward since there's no audio there. It's in the future.It has'nt arrived yet (not entirely true since we're working with buffers but you get the idea).
jupiter8 wrote:Anyways that isn't the main problem. The problem is that you move the head to quickly. If you implement a read speed parameter and increase/decrease that until you hit the desired delay time things would improve.
Agreed this is the problem I'm try to eliminate, but I'm not clear on next bit. A new "plug-in parameter" for read speed? Then use this to time the transition from the "current position" to the "new position"? That is not really what I'm looking to do. Most delay plug-ins I've seen don't offer such a parameter. I presume when you talk about interpolation here you are talking about interpolating between values in the actual audio data?
This sounds like what I'm after, but is also the point of confusion. The index is discrete. How can it have a fractional part? It has to move from one discrete position (current) to a new discrete position in discrete steps. Where is the fraction? Sorry if this sound like a silly question, but at the moment I'm just not seeing it.jupiter8 wrote: EDIT: Or you could just make a lopass filter on the read head index so it does'nt jump to the new read location but slowly gets there. You'd still need interpolation for the fractional part of the index.
Thanks for you help. It is much appreciated.
Darrell.
-
Music Engineer Music Engineer https://www.kvraudio.com/forum/memberlist.php?mode=viewprofile&u=15959
- KVRAF
- 4390 posts since 8 Mar, 2004 from Berlin, Germany
O.K. so you now have a delayline that allows only for integer delays. that's the first step. but depending on your application, you may need/want to allow for non-integer delay times. in that case the delay (in units of samples) will be represented by a float/double variable. the floor of that variable is referred to as the 'integer part' and the remainder is the 'fractional part'. you will need these two parts then for your interpolator.gibsond0120 wrote:This sounds like what I'm after, but is also the point of confusion. The index is discrete. How can it have a fractional part? It has to move from one discrete position (current) to a new discrete position in discrete steps. Where is the fraction? Sorry if this sound like a silly question, but at the moment I'm just not seeing it.
-
Ben [Camel Audio] Ben [Camel Audio] https://www.kvraudio.com/forum/memberlist.php?mode=viewprofile&u=1122
- KVRian
- 757 posts since 18 Sep, 2001 from Edinburgh, Scotland
When the delay time changes from an old value to a new value, and you want to avoid clicks you have two options.
Option 1 - you crossfade between a tap at the old delay time, and a tap at the new delay time. This crossfade would last (say) 0.5s, and during this time you have to compute both delay values.
Option 2 - you increase or decrease the rate of movement of the read head, so that it goes from the old delay time to the new delay time. This results in pitch up/down effects when changing delay time, unlike option 1. (I think this is the option jupiter 8 is describing.)
Writing this makes me realise how much time and effort it takes to explain stuff. Respect to all the people on the devforum who give up so much time to answer other peoples questions
Apologies for being brief here and not contributing much - its hard to find the time!
Option 1 - you crossfade between a tap at the old delay time, and a tap at the new delay time. This crossfade would last (say) 0.5s, and during this time you have to compute both delay values.
Option 2 - you increase or decrease the rate of movement of the read head, so that it goes from the old delay time to the new delay time. This results in pitch up/down effects when changing delay time, unlike option 1. (I think this is the option jupiter 8 is describing.)
If you want the read head to move at the same rate as the write head, then the read index will just be an integer value. However, if you (say) want it to move 20% faster than the write head, then the amount you will want to advance by per sample you output will be 1.2 samples. So say we start at sample 20, the read index will then be 20.2, 21.4, 22.6, 23.8, 24 ... So you need to interpolate between the adjacent samples in the delay buffer to get your samples. (I see whilst writing this Robin has explained this better than me above, but maybe my explanation adds something useful).This sounds like what I'm after, but is also the point of confusion. The index is discrete. How can it have a fractional part? It has to move from one discrete position (current) to a new discrete position in discrete steps. Where is the fraction? Sorry if this sound like a silly question, but at the moment I'm just not seeing it.
Writing this makes me realise how much time and effort it takes to explain stuff. Respect to all the people on the devforum who give up so much time to answer other peoples questions
-
- KVRer
- Topic Starter
- 23 posts since 3 May, 2009
Maybe this is the root of the confusion. My delay value is in terms of samples (bufferSize = sampleRate* MaxDelaySecs). Therefore it is an integer. I can't go any smaller than an integer so never have a fractional part. Are you suggesting breaking up the audio buffer into "frame"? Then the frame would be the "integer part" and the sample within the frame would be the "fractional part". What is the advantage of doing this?Robin from www.rs-met.com wrote: O.K. so you now have a delayline that allows only for integer delays. that's the first step. but depending on your application, you may need/want to allow for non-integer delay times. in that case the delay (in units of samples) will be represented by a float/double variable. the floor of that variable is referred to as the 'integer part' and the remainder is the 'fractional part'. you will need these two parts then for your interpolator.
Thanks again for you help.
Darrell.
-
- KVRer
- Topic Starter
- 23 posts since 3 May, 2009
Ben,Ben [Camel Audio] wrote:When the delay time changes from an old value to a new value, and you want to avoid clicks you have two options.
If you want the read head to move at the same rate as the write head, then the read index will just be an integer value. However, if you (say) want it to move 20% faster than the write head, then the amount you will want to advance by per sample you output will be 1.2 samples. So say we start at sample 20, the read index will then be 20.2, 21.4, 22.6, 23.8, 24 ... So you need to interpolate between the adjacent samples in the delay buffer to get your samples. (I see whilst writing this Robin has explained this better than me above, but maybe my explanation adds something useful).
Writing this makes me realise how much time and effort it takes to explain stuff. Respect to all the people on the devforum who give up so much time to answer other peoples questionsApologies for being brief here and not contributing much - its hard to find the time!
Thanks for your reply and enlightening explanation/example (the penny has finally drop). It make much more sense now. Unfortunately this leads to may more question.
What would be a typical "rate" to change? Some ball park figures will help to give me a starting point. Also presumably the simplest form of interpolation would just be to go to the closest sample. Using Ben previous example 20.2, 21.4, 22.6, 23.8, 24 ... would translate to 20, 21, 23, 24, 25 etc. How acceptable would this be in terms of audio quality?
Thanks again for your help. It is much appreciated.
Darrell.
-
Music Engineer Music Engineer https://www.kvraudio.com/forum/memberlist.php?mode=viewprofile&u=15959
- KVRAF
- 4390 posts since 8 Mar, 2004 from Berlin, Germany
this would be the maximum delay then. but the current delay would be something like:gibsond0120 wrote:Maybe this is the root of the confusion. My delay value is in terms of samples (bufferSize = sampleRate* MaxDelaySecs). Therefore it is an integer.
delayInSamples = delayInSeconds*sampleRate;
and if your value of 'delayInSeconds' can be anything (any non-integer value) then so will be your delayInSamples. it will not - all by itself - be quatized to integer values (unless you use an int variable, of course). read up on fractional delaylines or fractional delay filters, for example here:
http://ccrma.stanford.edu/~jos/Interpol ... lation.pdf
- KVRAF
- 10286 posts since 17 Sep, 2004 from Austin, TX
Tyvm, Oh Brainy OneRobin from www.rs-met.com wrote:http://ccrma.stanford.edu/~jos/Interpol ... lation.pdf
@Ben, you're damned right. The only way to escape is to be a deveoper of payware that has a perfectly understandable reason now to explain anything and everything. I've started a several blogs and even a book so I can just point people to the answers I've already taken the time to explain clearly and not have to re-type it all over again.
I know this is a couple-month old thread, but I'm bumping it for whoever finds my last post and wonders what the answer is...
it seems that .pdf explains what I needed pretty well
-
- KVRist
- 251 posts since 24 May, 2009
(scratches head) I understand the "how" of fractional-sample delay, but I'm having trouble wrapping my head around the "why": what's the benefit of having delay resolution finer than 1/fs? That's like 22us.Robin from www.rs-met.com wrote:this would be the maximum delay then. but the current delay would be something like:
delayInSamples = delayInSeconds*sampleRate;
and if your value of 'delayInSeconds' can be anything (any non-integer value) then so will be your delayInSamples.
(I'm assuming this discussion is based around simple delays rather than something like reverb effects, but even there, 22us only represents something under 1cm of path length, yes?)
-
- KVRer
- 23 posts since 29 Jan, 2010
Hi all!
I decided not to open a new thread, couse this is exactly what I need.
I'm new to the VST programming stuff and I decided to make a delay effect using SDK 2.4. For the beginning I started off from Steinberg's ADelay project and made some changes, added some stuff that I needed and so far is all working
Except..when I am changing delay time parameter i get a cracking, distorted signal. so reading through this thread gave me some ideas how to fix this thing = using interpolation or LPF. but the problem is i dont know exactly how to implement this.
if you take algorithm from ADelay example
how do i implement interpolation? which parameters must I use/define?
or even better when delay time is changing I'd like to get that pitch up/down effect with decreasing the rate of movement of the read head as Ben said in the post above.
I am really confused here. Read a lot from this forum & other interpolation topics, but somehow all things are too theoretical. Just can't figure out how.
So any help would be appreciated.
I decided not to open a new thread, couse this is exactly what I need.
I'm new to the VST programming stuff and I decided to make a delay effect using SDK 2.4. For the beginning I started off from Steinberg's ADelay project and made some changes, added some stuff that I needed and so far is all working
Except..when I am changing delay time parameter i get a cracking, distorted signal. so reading through this thread gave me some ideas how to fix this thing = using interpolation or LPF. but the problem is i dont know exactly how to implement this.
if you take algorithm from ADelay example
Code: Select all
float x = *in++;
float y = buffer[cursor];
buffer[cursor++] = x + y * fFeedBack;
if (cursor >= delay) {
cursor = 0;
}
*out1++ = y;
or even better when delay time is changing I'd like to get that pitch up/down effect with decreasing the rate of movement of the read head as Ben said in the post above.
I am really confused here. Read a lot from this forum & other interpolation topics, but somehow all things are too theoretical. Just can't figure out how.
So any help would be appreciated.
- KVRAF
- 2604 posts since 4 Sep, 2006 from 127.0.0.1
i once did a fractional feedback delay line
in my original code (oldish) when the delay time parameter is changed - a new buffer is alloccated, then the audio from the old buffer gets filled in the new buffer (interpolated) and then the old buffer is discarded
so in other words, for example if the delay time was 2 seconds, and then suddenly you changed it to 1 second, you'll hear the "tail" pitched up
the dumb thing about it is the dynamic alloccation (erm, it's an old code)
the pitch effects are quite nice
in my original code (oldish) when the delay time parameter is changed - a new buffer is alloccated, then the audio from the old buffer gets filled in the new buffer (interpolated) and then the old buffer is discarded
so in other words, for example if the delay time was 2 seconds, and then suddenly you changed it to 1 second, you'll hear the "tail" pitched up
the dumb thing about it is the dynamic alloccation (erm, it's an old code)
the pitch effects are quite nice
It doesn't matter how it sounds..
..as long as it has BASS and it's LOUD!
irc.libera.chat >>> #kvr
..as long as it has BASS and it's LOUD!
irc.libera.chat >>> #kvr
