Feedback Delay Network (FDN) Reverb

DSP, Plugin and Host development discussion.
RELATED
PRODUCTS

Post

mystran wrote: Wed Mar 20, 2019 10:04 am Even if you throw in a permutation, it doesn't mix (or "diffuse") the signals very well, unless the number of delays is very small.
General question: how much do you rely on the feedback matrix for diffusion?

It seems to me that even a dense mixing matrix like Hadamard takes a few loops for the echo density to become decent, so you get a very spattery start. Of course, you fix this by then adding diffusion before/after the feedback - but if you have a good diffuser, you're not as desperate to build up the echo density in the feedback section either, right?

I've had generally positive results from Householder, if there's good diffusion in the chain as well.

Post

signalsmith wrote: Sat May 29, 2021 1:50 pm It seems to me that even a dense mixing matrix like Hadamard takes a few loops for the echo density to become decent, so you get a very spattery start. Of course, you fix this by then adding diffusion before/after the feedback - but if you have a good diffuser, you're not as desperate to build up the echo density in the feedback section either, right?
It's about trade-offs: with faster diffusion in the FDN itself you can get away with a cheaper pre-diffuser and/or use longer delays in the FDN.

Post

Has anyone ever tried matrices that have fractional values where the contribution from, say, shorter delays is a bit more quiet than the contribution from longer delays, and does that actually make any sense?

Post

Urs wrote: Wed Jun 02, 2021 1:46 pm Has anyone ever tried matrices that have fractional values where the contribution from, say, shorter delays is a bit more quiet than the contribution from longer delays, and does that actually make any sense?
I've pretty much always done this, although giving smaller contributions to shorter delays isn't possible as such if you want the matrix to remain unitary.

What you can do though is take the skew-variant of the fast Hadamard transform where you have n*log(n) rotations by pi/4 for equal coefficients, but you can also use other angles (at the cost of slightly more expensive rotations). If you use a slightly larger angle, then you'll pass slightly more signal to the "other" delay of the rotation pair, which allows you to emphasize feedback paths involving multiple delays.

I've never been super-scientific about this, but basically if you take the ordering of the delays into account you can arrange things in such a way that the longest delays tend to push most of their signal to the shortest delays and vice-versa, essentially giving preference to two-delay combinations that are close to the average of the possible two-delay combinations and least weight on self-feedback, while still feeding some of the signal to all the delays.

edit:

Upon further thought, I guess you can do this with reflection just the same, although I've always used rotations I think: you have cosines for self-feed and sines for cross-feed and if you invert one of the sines it's a rotation and if you invert one of the cosines it's a reflection. With Hadamard you have cos=sin=sqrt(.5), but any other angle still gives you a unitary transform.

I also want to observe that while we need to satisfy cos^2+sin^2=1 one doesn't necessarily need to pick an angle as such. Instead one can pick two arbitrary weights and then normalize the 2D vector, or pick the amount of desired self-feedback compute the other weight by sqrt(1-x^2), etc..

Post

a couple of days ago, i realized, that what i called "generalized Hadamard transform" here:

http://www.rs-met.com/documents/dsp/Gen ... nsform.pdf

can be generalized much further by starting with an arbitrary NxN seed matrix, not necessarily 2x2. and what you get then is a transform defined by some....hmm...let's call it "Kronecker power", i.e. an iterated Kronecker product of a matrix with itself. and if the seed matrix is unitary, then so is its Kronecker power:

https://en.wikipedia.org/wiki/Kronecker ... properties

eigenvalues on the unit circle multiplied by other eigenvalues on the unit circle remain on the unit circle. here is some code to compute the original Hadamard trafo, the generalized one (as in my pdf - i now call it (fast) kronecker2x2-transform) and the version using a 3x3 seed matrix:

Code: Select all

template<class T>
void rsLinearTransforms::hadamard(T* A, int N)
{
  rsAssert(rsIsPowerOfTwo(N), "N must be a power of 2");
  int h = 1;
  while(h < N) {
    for(int i = 0; i < N; i += 2*h) {
      for(int j = i; j < i+h; j++) {
        T x = A[j];
        T y = A[j+h];
        A[j]   = x + y;
        A[j+h] = x - y;  }}
    h *= 2;  }
}

template<class T>
void rsLinearTransforms::kronecker2x2(T* A, int N, T a, T b, T c, T d)
{
  rsAssert(rsIsPowerOfTwo(N), "N must be a power of 2");
  int h = 1;
  while(h < N) {
    for(int i = 0; i < N; i += 2*h) {
      for(int j = i; j < i+h; j++) {
        T x = A[j];
        T y = A[j+h];
        A[j]   = a*x + b*y;
        A[j+h] = c*x + d*y;  }}
    h *= 2;  }
}

template<class T>
void rsLinearTransforms::kronecker3x3(T* v, int N, const rsMatrix3x3<T>& A)
{
  rsAssert(rsIsPowerOfN(N, 3), "N must be a power of 3");
  int h = 1;
  while(h < N) {
    for(int i = 0; i < N; i += 3*h) {
      for(int j = i; j < i+h; j++) {
        T x = v[j+0*h];
        T y = v[j+1*h];
        T z = v[j+2*h];
        v[j+0*h] = A(0,0) * x + A(0,1) * y + A(0,2) * z;
        v[j+1*h] = A(1,0) * x + A(1,1) * y + A(1,2) * z;
        v[j+2*h] = A(2,0) * x + A(2,1) * y + A(2,2) * z; }}
    h *= 3;  }
}
they all work in place (that may be better than the matlab code in my pdf which needed a temporary array) and are based on the code for the fast hadamard trafo on wikipedia: https://en.wikipedia.org/wiki/Fast_Wals ... _transform. the pattern to generalize to arbitrary N should be obvious*. i think, i'll next experiment a bit with using 3D rotation matrices as seed for the 3x3 kronecker-power transform. a bit disadvantageous with 3x3 seed matrices is that the choices for the number of delaylines are spaced further apart (namely as powers of 3, i.e. 9,27,81,...) among which only 9 and 27 seem to be reasonable in practice - at least for reverb (i found that with FDNs - especially really large ones, like 128x128 - one can also synthesize nice electronic snares). it's also possible to efficiently kronecker-multiply matrices of different sizes, allowing even more flexibility in the choice of the number of delaylines. in my experiments with power-of-2 sizes, so far, i found 16 to be the sweet spot, with 8 and 32 being also still okayish. ...maybe i should try to combine 2 3x3 matrix stages with 1 2x2 stage, for a total of 3^2 * 2 = 18 delaylines...with this, i could operate at the sweet spot while still having the more flexible parametrization of a 3D rotation (maybe in terms of Euler angles or whatever)

(*) i included this unnecessary +0*h, +1*h stuff to make the pattern more obvious and i would expect any halfway decent compiler to optimize these away - but i didn't check. i will, before this code goes into production, though
My website: rs-met.com, My presences on: YouTube, GitHub, Facebook

Post

sorry for the necro - what does rotation mean in this context? what effect does it have on the sound? Does it produce a phase rotation, eg each frequency is rotated by 90 degrees inside a fdn matrix?

Post

j wazza wrote: Wed Feb 15, 2023 6:25 pm sorry for the necro - what does rotation mean in this context? what effect does it have on the sound? Does it produce a phase rotation, eg each frequency is rotated by 90 degrees inside a fdn matrix?
If we take one sample in time of an N-channel signal, then we can treat this as an N-vector and rotation here means "abstract" rotation of this N-vector. Rotations (and reflections) are unitary transforms, so they preserve the length (Euclidean norm, the good old Pythagorean sum of squares) of the vector and this property is sufficient to make sure that the N-channel signal as a whole does not gain or lose energy. It is this energy preservation that is required for a feedback structure such as an FDN to be lossless.

In terms of sound, all it really does is spread signals to the channels in a different way such that no information is lost. For example in the 2-channel case LR->MS and MS->LR conversions are such rotations: if we do a traditional 2D phase plot then what happens is we're basically changing the coordinate axis.. but the same principle works for arbitrary dimensions thanks to how we can compose unitary linear transforms rotating two components at a time.

Post

mystran wrote: Thu Feb 16, 2023 7:44 am
If we take one sample in time of an N-channel signal, then we can treat this as an N-vector and rotation here means "abstract" rotation of this N-vector. Rotations (and reflections) are unitary transforms, so they preserve the length (Euclidean norm, the good old Pythagorean sum of squares) of the vector and this property is sufficient to make sure that the N-channel signal as a whole does not gain or lose energy. It is this energy preservation that is required for a feedback structure such as an FDN to be lossless.

In terms of sound, all it really does is spread signals to the channels in a different way such that no information is lost. For example in the 2-channel case LR->MS and MS->LR conversions are such rotations: if we do a traditional 2D phase plot then what happens is we're basically changing the coordinate axis.. but the same principle works for arbitrary dimensions thanks to how we can compose unitary linear transforms rotating two components at a time.
Thanks for the help! I'm coming back to trying this. I think you might mean something like if we take the amplitude values from 2 channels like left and right, and we think of the 2 values like x and y coordinates in a vector, and use a formula to apply rotations to it, is that right? Which formula could you use?

Do you apply this formula to the moving signal directly, or to the level control?
Does this just change the relative levels of the channel? Or does the rotation have other effects?

Post Reply

Return to “DSP and Plugin Development”