Feedback Delay Network (FDN) Reverb

DSP, Plugin and Host development discussion.
RELATED
PRODUCTS

Post

I've got a few more questions regarding FDN if mystran or anyone wants to hop in.

1) the delay module I built the FDN out of is multi-tap and it seems a waste not to use a few more taps. But when I do so the FDN that used to run an hour without growing or shrinking more than about 6dB total, instead mushrooms several dB or tens of dB a second. OK, so I cut the feedback by 50% but it dies out. I guessed dividing by sqrt(2) might work but no, also doesn't restore the infinite-but-not-growing reverb. Any ideas? I thought of adding a positive and negative tap to the (positive) tap I already have. I'll try that in a bit.



2) I only in the last couple days realized that half the literature on reverbs refers to what I'd call a "delay" as a comb filter. I mean, I know they comb filter, but I thought the "comb filter" term was used for short delays with the intent of filtering as opposed to delaying. So now I'm wondering the same about "all pass." Is there a way to write a delay such that the "read head" does NOT comb filter? And if so, shouldn't we use that all the time for "digital delay" purposes? (Except possibly for delays with changing times?) Why would you not? And can you do so in an FDN?



3) Can I just take an entire FDN and place it as one of the four delays of an FDN? For instance have 4 4-line FDNs in parallel and feeding back with the FDN math? Or does that actually just give you the same thing as a 16-line FDN? (The reason I ask is that the 4-way is giving at least 1/2 each line's output to the other lines, and as you increase lines they clearly start decoupling so that most lines' input is mostly their own output. It sounds like they wouldn't build diffusion as quickly.)



4) I don't know the symbols for the various matrix math operations I see in the literature and don't know how to make FDN matrices. But I do know what to do with them. My 4-line FDN is based off the Householder/Jot matrix

.5 -.5 -.5 -.5
-.5 .5 -.5 -.5
-.5 -.5 .5 -.5
-.5 -.5 -.5 .5

Do I understand that for say N=5 the diagonal would be 1-2/N = .6, while the other entries would be -2/N = -.4?



5) I've been reading https://ccrma.stanford.edu/~jos/pasp/Ho ... atrix.html . The author's quite often cited in other papers I note.

However I don't understand his focus on primes such as at https://ccrma.stanford.edu/~jos/pasp/Ch ... ngths.html and https://ccrma.stanford.edu/~jos/pasp/Pr ... ngths.html .

I mean, I know we don't want common factors. But, why stick to integers at all? They actually all have a common factor of 1.
Why not irrational numbers? I'm getting good results with sqrt( prime ). The only advantage I can see to primes is that it avoids the overhead and filtering effect of interpolation, but if you're going to time-vary your taps you need to have that anyway...

Post

Swiss Frank wrote: Thu Dec 20, 2018 3:28 am 1) the delay module I built the FDN out of is multi-tap and it seems a waste not to use a few more taps. But when I do so the FDN that used to run an hour without growing or shrinking more than about 6dB total, instead mushrooms several dB or tens of dB a second. OK, so I cut the feedback by 50% but it dies out. I guessed dividing by sqrt(2) might work but no, also doesn't restore the infinite-but-not-growing reverb. Any ideas? I thought of adding a positive and negative tap to the (positive) tap I already have. I'll try that in a bit.
A fully satisfactory answer would be quite complicated, but the practical version is that having a bunch of parallel delays (or more generally: a bunch of parallel all-pass filters) and a unitary feedback matrix will rotate/reflect the energy from one delay to another, but doesn't really change the total amount of energy that's recirculating in the system: the poles of the undamped system are all on the unit circle. Once you add more feedback taps, you break this delicate balance and you're pretty much back to trying to make sense of the transfer functions.

So the short version is: just don't do it.
2) I only in the last couple days realized that half the literature on reverbs refers to what I'd call a "delay" as a comb filter. I mean, I know they comb filter, but I thought the "comb filter" term was used for short delays with the intent of filtering as opposed to delaying. So now I'm wondering the same about "all pass." Is there a way to write a delay such that the "read head" does NOT comb filter? And if so, shouldn't we use that all the time for "digital delay" purposes? (Except possibly for delays with changing times?) Why would you not? And can you do so in an FDN?
You get a "feedforward comb filter" from a delay when you mix it with the dry (non-delayed) signal. You get a "feedback comb filter" from a delay when you introduce feedback. In either case the spectrum has a comb-like shape, hence the name. The length of the delay doesn't change the shape except as far as making it more or less dense.

Regular FDN can be made into an all-pass (in the MIMO sense). I don't remember if I ever tried that, but there's some old FDN papers that describe how to do that; it's not exactly a new idea.
3) Can I just take an entire FDN and place it as one of the four delays of an FDN? For instance have 4 4-line FDNs in parallel and feeding back with the FDN math? Or does that actually just give you the same thing as a 16-line FDN? (The reason I ask is that the 4-way is giving at least 1/2 each line's output to the other lines, and as you increase lines they clearly start decoupling so that most lines' input is mostly their own output. It sounds like they wouldn't build diffusion as quickly.)
The "delays" in an FDN structure need to be all-pass (which pure delay itself is trivially), so you would have to use the all-pass version of FDN for such embedding.
4) I don't know the symbols for the various matrix math operations I see in the literature and don't know how to make FDN matrices. But I do know what to do with them. My 4-line FDN is based off the Householder/Jot matrix
If I was you I would probably try to decipher the formulas with the help of either Google or some basic mathematics textbook. While it will be rather painful until you get the basics down, it actually will not take that long to learn the basics and it will save you a ton of time in the long run. Actually learning the basics of linear algebra and complex analysis is also worth spending the time, but even just learning the notation will make your life a lot easier.
Do I understand that for say N=5 the diagonal would be 1-2/N = .6, while the other entries would be -2/N = -.4?
Looks just about right.
5) I've been reading https://ccrma.stanford.edu/~jos/pasp/Ho ... atrix.html . The author's quite often cited in other papers I note.

However I don't understand his focus on primes such as at https://ccrma.stanford.edu/~jos/pasp/Ch ... ngths.html and https://ccrma.stanford.edu/~jos/pasp/Pr ... ngths.html .

I mean, I know we don't want common factors. But, why stick to integers at all? They actually all have a common factor of 1.
The primes thing is indeed about common factors. The reason to choose integers is to avoid having to implement fractional delays. A lot of signal processing literature and research starts from the assumption that your primary, secondary and tertiary goal is to avoid spending any more CPU cycles than you absolute have to.

If you don't mind irrationals then you could pick the minimum delay you want, then incrementally multiply it by the golden ratio to get successive delay lengths until you have as many as you need (and because I'm feeling evil, I'll leave it as an exercise to the reader to figure out the rationale behind this approach).

Post

> but doesn't really change the total amount of energy that's recirculating in the system

OK, but if one positive tap works, would two positive plus one negative tap also work for instance? (I still haven't tried it.)

> it's not exactly a new idea

Right, its just that I think some things I'm reading are either telling me I must or at least I can do that.

> which pure delay itself is trivially), so you would have to use the all-pass version of FDN for such embedding.

I see I see.

> If I was you I would probably try to decipher the formulas with the help of either Google or some basic mathematics textbook.

You're right of course. I'm sure Wiki has a lot of this.

> primes... irrational

I've seen a problem already. For instance, 5 and 10 sample delays are bad to use as the second is exactly twice the first.

You can't just move to irrationals and think it's a solved problem, because sqrt(5) and sqrt(20) sample delays ALSO have the second exactly twice the first. (because sqrt(20) is 2 * sqrt(5)...) So, simply sqrt'ing everything in sight, even after making sure its not a integer squared, is no panacea.

Post

Swiss Frank wrote: Thu Dec 20, 2018 11:25 pm You can't just move to irrationals and think it's a solved problem, because sqrt(5) and sqrt(20) sample delays ALSO have the second exactly twice the first. (because sqrt(20) is 2 * sqrt(5)...) So, simply sqrt'ing everything in sight, even after making sure its not a integer squared, is no panacea.
That's why the suggestion to use powers of the golden ratio. :)

Post

Hello! This seems to be the latest FDN Reverb related topic. I'm currently stuck in my reverb design and would certainly need help. I would like to add a simple reverb to my synth that I'm building.

So far, I've managed to get a very simple N (8 selected now) delay line FDN work with a decent very short room sound. The FDN is quite simple, it simply feeds back the lines in order (processs line 1 -> feedback to line 2 and so forth) and it wraps back with the last line feeding it's output to line 1 input. I've also tried to add a feedback delay line that feeds the whole output to the input.

The schematic that I'm stuck right now is this:

https://drive.google.com/open?id=1TFPx5 ... 0oV7arm2Jk

I think the problem is also the way the FDN line delays and gains behave and I've tried changing and controlling the but to no reasonable results. What kind of delay times would you recommend using in the FDN? Currently I'm setting up the first line as 0.08sec and the next line is simply multiplied by the previous line with a factor of 1.25f.
The feedforward/feedback gains gN are controlled by a knob and each gain factor is -6 dB less than the previous one.

Adding the feedback delay line certainly helps achieving a longer "reverb" (If you can call this mess a reverb). But all the late echoes are too enhanced and sound extremely metallic and if the whole feedback gain is too large, the ringing sounds simply overbearing.

Are there any good rule of thumbs to this design? I'd also like to know if by simply using the feedback matrix type of reverb would help me achieve things quicker?

P.S. What about modulating the delay line times? I've noticed that when I abruptly change the delay lines with a knob, there is an annoying "krhhhh" sound since the delay line read pointer changes quickly. How do I avoid this?

My delay line function read/write is following in pseudocode:
// GET POINTERS
const float* readInput = inputBuffer.getReadPointer(channel);
float* delayWrite = delayLine.getWritePointer(channel);
etc...

int readStart = static_cast<int>(delayLineLen + writeStart - (lastSampleRate*FDNDelayMatrix[numLine][ch])) % delayLineLen;
if (writeStart + blockSize < delaylineLength){
write input and feedback buffer to delay line}
else {wrap around delay line}

if (readStart + blockSize < delayLineLength){
write delay line at read pointer to output buffer}
else{wrap around...}

Post

processs line 1 -> feedback to line 2 and so forth) and it wraps back with the last line feeding it's output to line 1 input.
https://drive.google.com/file/d/1TFPx5w ... rm2Jk/view
this is not really a feedback delay network - or, well it is one but a very specific one. its feedback matrix looks something like

Code: Select all

0 0 0 a
b 0 0 0
0 c 0 0
0 0 d 0
each delayline feeds only into one other (the next) delayline (and the last back into the first). ..plus an additional global feedback path (i think, that's also pretty nonstandard - and may mess up any otherwise carefully tuned unitary feedback matrix). a "proper" FDN suitable for reverb would have each delayline feed into each other - you typically don't want zeros in the matrix and certainly not that many. a dense feedback matrix is what creates the quick build up of the density of delayed reflections. you also probably don't want to have each delayline's length just be 1.25 times the previous but rather some sort of more "irrational" ratios of the lengths...perhaps adjusted to the next prime

(...although i tend to think, prime numbers may be overrated since the feedback damping filter will smear the delayed impulses anyway - so what's the point of preventing *ideal* impulses from coinciding at sample-instants, when in reality, they are smeared out in time anyway - but i don't really have much experience with that - it's just a gut feeling)
Last edited by Music Engineer on Tue Mar 19, 2019 3:46 pm, edited 2 times in total.
My website: rs-met.com, My presences on: YouTube, GitHub, Facebook

Post

HammieTime wrote: Tue Mar 19, 2019 1:42 pm
The schematic that I'm stuck right now is this:

https://drive.google.com/open?id=1TFPx5 ... 0oV7arm2Jk
That doesn't look like a typical FDN, it looks like a multitap delay with 1 extra feedback path. Though, technically speaking any reverb can probably described as an FDN. Typically a FDN would have some kind of unitary matrix, for size 8, a Hadamard matrix is common in the literature.
HammieTime wrote: Tue Mar 19, 2019 1:42 pm I think the problem is also the way the FDN line delays and gains behave and I've tried changing and controlling the but to no reasonable results. What kind of delay times would you recommend using in the FDN? Currently I'm setting up the first line as 0.08sec and the next line is simply multiplied by the previous line with a factor of 1.25f.
Read this thread, Mystran gives a good starting point about times. I do 'em by ear.
HammieTime wrote: Tue Mar 19, 2019 1:42 pm P.S. What about modulating the delay line times? I've noticed that when I abruptly change the delay lines with a knob, there is an annoying "krhhhh" sound since the delay line read pointer changes quickly. How do I avoid this?
You'd be amazed how many commercial reverbs have that behaviour, it's a CPU trade-off, interpolate all the delays and you'll be zipper noise free at the price of more CPU(depends on interpolation type). Modulating delays in reverbs are as old as the hills, typically a user-adjustable 'feature' in some reverbs, it can help curb some of the ringing.

Post

Music Engineer wrote: Tue Mar 19, 2019 3:24 pm
processs line 1 -> feedback to line 2 and so forth) and it wraps back with the last line feeding it's output to line 1 input.
https://drive.google.com/file/d/1TFPx5w ... rm2Jk/view
this is not really a feedback delay network - or, well it is one but a very specific one. its feedback matrix looks something like

Code: Select all

0 0 0 a
b 0 0 0
0 c 0 0
0 0 d 0
each delayline feeds only into one other (the next) delayline (and the last back into the first). ..plus an additional global feedback path (i think, that's also pretty nonstandard - and may mess up any otherwise carefully tuned unitary feedback matrix). a "proper" FDN suitable for reverb would have each delayline feed into each other - you typically don't want zeros in the matrix and certainly not that many. a dense feedback matrix is what creates the quick build up of the density of delayed reflections. you also probably don't want to have each delayline's length just be 1.25 times the previous but rather some sort of more "irrational" ratios of the lengths...perhaps adjusted to the next prime

(...although i tend to think, prime numbers may be overrated since the feedback damping filter will smear the delayed impulses anyway - so what's the point of preventing *ideal* impulses from coinciding at sample-instants, when in reality, they are smeared out in time anyway - but i don't really have much experience with that - it's just a gut feeling)
Thank you. I feared it would become too complicated for me to process all the delay line outputs and send them to the inputs but I guess If I create an extra function to process the output buffers of each FDN line and then multiply it by the Hadamard matrix and sum this output at the input, I should (at least theoretically) get a decent tail? - How do people usually do this anyway? I'm still learning to code efficiently as I go.

I don't yet understand this Matrix multiplication, So basically the first row of the Matrix tells from which lines it get's it's input from? By using the 4x4 Hadamard, It looks like the first line feedback buffer would receive input's from all the lines with factors of 1,1,1,1 and the second line 1,-1,1-1 and so on. So Line1 input would be simply Line1_out+Line2_out+ Line3_out+ Line4_out and second line input: Line1_out - Line2_out + Line3_out - Line4_out. Feedback seems quite immense.

Do people usually even use feed-forward gain instead of feedback gain before summing at the input? I've tried both but somehow the feed-forward gain seems to work slightly better.

Also, if the FDN is used to create the dense output echoes, how do you achieve these kind of lush, long reverbs? Whole system feedback seems like the obvious choice at first, but is it possible, for example to create a less dense FDN (or parallel feed-forward delay lines) at the input and then manipulate these lengths for longer reverb times (like +0.5 sec).
Last edited by HammieTime on Tue Mar 19, 2019 4:51 pm, edited 2 times in total.

Post

Ichad.c wrote: Tue Mar 19, 2019 3:34 pm
HammieTime wrote: Tue Mar 19, 2019 1:42 pm
The schematic that I'm stuck right now is this:

https://drive.google.com/open?id=1TFPx5 ... 0oV7arm2Jk
That doesn't look like a typical FDN, it looks like a multitap delay with 1 extra feedback path. Though, technically speaking any reverb can probably described as an FDN. Typically a FDN would have some kind of unitary matrix, for size 8, a Hadamard matrix is common in the literature.

Thanks. Yea I guess I understood the whole FDN concept slightly wrong. I'll try the Hadamard first.
HammieTime wrote: Tue Mar 19, 2019 1:42 pm I think the problem is also the way the FDN line delays and gains behave and I've tried changing and controlling the but to no reasonable results. What kind of delay times would you recommend using in the FDN? Currently I'm setting up the first line as 0.08sec and the next line is simply multiplied by the previous line with a factor of 1.25f.
Read this thread, Mystran gives a good starting point about times. I do 'em by ear.
HammieTime wrote: Tue Mar 19, 2019 1:42 pm P.S. What about modulating the delay line times? I've noticed that when I abruptly change the delay lines with a knob, there is an annoying "krhhhh" sound since the delay line read pointer changes quickly. How do I avoid this?
You'd be amazed how many commercial reverbs have that behaviour, it's a CPU trade-off, interpolate all the delays and you'll be zipper noise free at the price of more CPU(depends on interpolation type). Modulating delays in reverbs are as old as the hills, typically a user-adjustable 'feature' in some reverbs, it can help curb some of the ringing.
Alright so this is called 'zipper noise'. This is too much for me now, so I will leave it be. Out of curiosity, which commerical reverbs have this property?

Post

I don't yet understand this Matrix multiplication, So basically the first row of the Matrix tells from which lines it get's it's input from?
yes, the first row is populated by the coeffs by which all the delaylines feed into the input of the first delayline. conceptually, you would have a matrix of coefficients, say a(i,j) that determines how much of the output of the j-th delayline feeds into the input of the i-th delayline (or maybe the other way around, depending on your conventions). the catch here is that matrix-vector multiplication is in general an O(N^2) process where N is the dimensionality of the vector (which in this case maps to the number of delaylines). that's why it's popular to use very specific matrices, for which this matrix-vector product can be computed more efficiently - for example in O(N*log(N)) in the case of a hadamard matrix. instead of straightforward, naive matrix multiplication, you do a fast hadamard transform. wikipedia even has python code for it:

https://en.wikipedia.org/wiki/Fast_Wals ... _transform

you would need to post-multiply the output by 1/sqrt(N), though - in this case, you get a unitary (i.e. energy preserving) transform. your FDN output signal would not decay away with this. if you feed in a unit impulse, it should produce some sort of white noise. ..a little more and it even blows up, a little less and you get a nice, exponential decay. so, in reality, you would use a factor s/sqrt(N) where s is some scaling factor < 1 that determines the decay time. and as next step then, you would throw in damping filters that make the decay time frequency dependent

disclaimer: this is a very simplified explanation - you may actually want a different scaling factor for each delayline, depending on its length and maybe a different filter, too - not sure anymore - it's long ago that i implemeneted an fdn
My website: rs-met.com, My presences on: YouTube, GitHub, Facebook

Post

HammieTime wrote: Tue Mar 19, 2019 4:33 pm
Alright so this is called 'zipper noise'. This is too much for me now, so I will leave it be. Out of curiosity, which commerical reverbs have this property?
Naming names is considered bad form, let's just say every commercial reverb I own (including hardware). Usually the size parameter. You can easily make a reverb that doesn't have it, it's just a matter of CPU usage, and generally a 150% to 400% CPU increase is usually a dealbreaker. I just softly mute the input+feedback+output paths when somebody moves the size parameter in my current unreleased project, I'd rather have silence than zipper noise; it's sometimes easier to make the decay parameter automatable for not much CPU increase but it's topology dependant.

Edit: Some fun brain-melting stuff if you have not read it: https://www.gearslutz.com/board/geekslu ... lture.html

Post

Music Engineer wrote: Tue Mar 19, 2019 4:58 pm for example in O(N*log(N)) in the case of a hadamard matrix. instead of straightforward, naive matrix multiplication, you do a fast hadamard transform. wikipedia even has python code for it:

https://en.wikipedia.org/wiki/Fast_Wals ... _transform
I'd just like to observe that this is also probably one of the easiest to compute matrices that doesn't sound completely terrible (unlike for example the super-cheap O(n) householder reflections).

Post

mystran wrote: Wed Mar 20, 2019 5:03 am I'd just like to observe that this is also probably one of the easiest to compute matrices that doesn't sound completely terrible (unlike for example the super-cheap O(n) householder reflections).
It's not a "matrix" in a strict sense though. I like to call these operators "matrix-like" objects, and it makes a good abstraction. Basically, anything that can left-multiply a vector is a "matrix-like" object. FFTs, Householder reflections, Givens rotations, LU inverses, permutations, PDE operators, you name it.

Post

I'd just like to observe that this is also probably one of the easiest to compute matrices that doesn't sound completely terrible
i actually never really bothered to try anything else (*) (aside from maybe some sign-flips) because from a theoretical point of view - if i'm not mistaken - these are some sort of optimum in the sense of most quickly spreading the energy between the delaylines because each feeds into each other with the same strength. right? or wrong?

(*) unless this straightforward generalization:
http://www.rs-met.com/documents/dsp/Gen ... nsform.pdf
counts as "something else". i never fully developed the idea into a product, though. not yet, at least. it's one of the many loose ends lying around in my codebase :hihi:
My website: rs-met.com, My presences on: YouTube, GitHub, Facebook

Post

Ichad.c wrote: Naming names is considered bad form, let's just say every commercial reverb I own (including hardware).
Yeah I didn't think about that the devs might be here. I haven't spend too much with time reverbs and I'm quite happy with the SoundToys Little Plate and couple of Waves I've used and didn't notice anything drastic.
Ichad.c wrote: Usually the size parameter. You can easily make a reverb that doesn't have it, it's just a matter of CPU usage, and generally a 150% to 400% CPU increase is usually a dealbreaker. I just softly mute the input+feedback+output paths when somebody moves the size parameter in my current unreleased project, I'd rather have silence than zipper noise; it's sometimes easier to make the decay parameter automatable for not much CPU increase but it's topology dependant.
Sounds like a good trcik to keep in mind. Thanks.
Ichad.c wrote: Edit: Some fun brain-melting stuff if you have not read it: https://www.gearslutz.com/board/geekslu ... lture.html
I have not.

Post Reply

Return to “DSP and Plugin Development”