XaiJu
luxcache
luxcache

patreon


REVERB 101 - Part 2: Beginnings of Digital Reverb Design with nilofaur

In this Lux Cache tutorial series, Los Angeles based artist and DSP enthusiast, niloufar, explores the principles of building reverb in Max/MSP, from fundamental concepts to more advanced/custom applications, with the aim to give the reader the basic understanding needed to design their own reverbs. In this tutorial, she will continue her journey through the history of digital reverb, and implement designs by Schroeder and Moorer. This tutorial is focused on Max4Live, but the concepts outlined can be applied to other audio programming environments such as Pure Data and Reaktor. We have included the finished M4L patch alongside this tutorial.

This tutorial is available as both a Patreon text post and .pdf document format. We ask you kindly to not share Lux Cache content outside of the Patreon, our contributors rely on your donations.

~

When listening to the output of the basic Schroeder algorithm explored in Part 1 of the series, one is likely to find that it leaves a lot to be desired. The reverberation it produces is not very realistic at all, and there are few perceptually meaningful parameters to be manipulated.

In this tutorial, niloufar will continue her foray into the history of reverb, building on the basic concepts established in Part 1. Readers will learn about algorithms put forward after the “Schroeder Reverb”, as well as another algorithm put forth by Schroeder himself in the same paper.

This tutorial assumes a basic familiarity with Max/MSP. It should be supplemented with a reading of the official documentation, specifically the Max basic tutorials and the MSP tutorials.



Contents

The lesson will be broken up into these segments (jump to content)

Schroeder’s Nested Series Allpass

Moorer

The Lowpass Comb Filter

Tapped Delay Line for Early Reflections

Schroeder’s Nested Series Allpass

In part 1, I presented to you an algorithm consisting of parallel comb filters fed into two allpass in series, referring to it as the “Schroeder Reverb.” That was a bit of a fib. Although this is what the parallel combs/series allpass algorithm has come to be known as, it is neither the only algorithm put forward by Schroeder, nor the first in his paper Natural-sounding artificial reverberation.

Earlier in the paper, Schroeder notes that more than one allpass can be connected in series in order to increase echo density.

This structure is identical to the series allpass in the reverb we built in part 1.

This is especially useful because we can connect an arbitrary number of allpasses in series without violating the all pass principle. This is because each allpass is only connected to another allpass by its input or output, and the output of an allpass is allpass no matter what the input is.

Another useful attribute of allpass filters is that they can be nested. Let’s take a look at some diagrams from JOS to see what this means.

This is our simple allpass filter. This diagram may look unfamiliar, but fret not, it is the same allpass diagram we have dealt with before, but some of the terms have different names. This is a good opportunity to introduce you to some DSP conventions that we’ll be seeing pop up a lot as we look at more reverb algorithms.

Here is the Schroeder diagram we are  familiar with. The k term in the JOS diagram is the same as the gain, g, in this one. The signs are inverted, but this is fine. So long as the feedback gain is the inverse of the feedforward, it doesn’t matter which is which.

The delay, T has been replaced by z-1. This notation is used to describe a delay of a single sample, and we will see it often in DSP diagrams. The delay need not be restricted to a single sample, and can be arbitrary without violating the allpass principle. In the case of an arbitrary delay, we will often use the notation z-M, where M is the delay in samples.

In order to nest an all pass filter of this kind, we take the delay, z-1 and insert another allpass filter between it and its connections.

The output of this structure is still all pass. Why is this relevant to us and Schroeder? Recall that any arbitrary number of allpass filters in series is itself an allpass filter. Thus, rather than simply insert a single allpass filter after the delay, z-1, we can insert many. This allows us to create a feedback loop where each pass of the loop goes through several all-pass filters and each filter adds to the echo density.

The result is a reverberator whose echo density increases as the feedback builds up! This is closer to the response of real spaces, whose echo density also increases with time, as opposed to the parallel combs “Schroeder Reverb”, which has a uniform echo density.

Implementing this structure in Max/MSP turns out to be rather simple. Let us look first at the series allpass.

As far as the structure, we have simply placed 5 allpass filters in series. For the feedback gain, Schroeder suggests a uniform value of about 0.7. Since we won’t be changing this value, we can use a [live.thisdevice] to trigger a message of 0.7 when the device loads. For the delay time of each allpass, Schroeder suggests a value of about 1/3rd of the previous delay, as well as delay ratios which are not equal.

For the sake of convenience, let us now encapsulate this structure. Now we can think of it as a single input, single output allpass filter, with only one parameter to set all the delay times.

Take a look at this. It’s the inside of the [p allpass] subpatcher! I’ve added some [live.dial]s to control the delay and feedback gain. As of now, this is just a standard allpass. However, to arrive at Schroeder’s design, we need simply to insert the [p seriesAllpass] we just constructed after the delay (in this case, between the output of [tapout~] and the things it’s connected to.)

Voila! Now we have our fully allpass reverberator. Open up the schroeder2.amxd file and play with it yourself. Experiment with different configurations of the 3 values. Can you imagine some different versions of this patch? What if there are more or less allpass in series? What if the allpasses inside the series are nested? What are the benefits of this design? What are its deficiencies?

Moorer

In his 1979 paper About This Reverberation Business, James Moorer set about remedying some of the problems with the earlier Schroeder designs, such as ringing in the late reverb tail, poor response to short impulsive sounds, and unnatural modulation.

To remedy these issues, he first takes a look at the unit reverberators at the core of Schroeder’s designs, the comb filter and the allpass filter. Moorer proposes 4 new unit reverbator designs, but for all but one of them, he deems the difference mostly negligible when substituted for Schroeder’s unit reverberators.

The Lowpass Comb Filter

One property of real rooms is that they exhibit a reverberation time which decreases as the frequency increases. This attenuation of high frequencies occurs as sound travels through the air. There are many factors at play determining the particular attenuation (such as the humidity, temperature, pressure, and the frequency being attenuated), but as a general rule, the further the sound travels, the more the high frequencies are dampened.

In order to simulate this process (albeit, crudely) Moorer proposes a simple feedback comb filter with a lowpass in the feedback loop, like so:

(Comb filter with delay time m, filter H(z), and feedback gain g)

More specifically, Moorer proposes this design

This is the same as the above diagram, the term g2 now represents the feedback gain, and the terms g1 and z-1 describe the lowpass filter, a simple one pole lowpass. Since this filter requires a single sample delay to implement (the z-1 term), we must move into gen~ to implement it. Thus, for a full implementation in gen~, see the Appendix and then skip to “Moorer’s Parallel Combs. For those without gen~, we will proceed forward with an approximation, using [onepole~]

Let’s start our basic comb from Part 1, the [p comb] subpatcher.

This is exactly the same as our above diagram, just with the top clipped off.

The lowpass portion of the diagram overlaps this, it consists of everything above the delay, including the g2 term.

The input enters on the right here, and is output on the left. This is a simple one pole lowpass filter, explained in more depth in the Appendix. Since we do not have access to a single sample delay, we will have to make do with an approximation. The [onepole~] object implements a filter which is, for the most part, identical. Thus we can transform our simple feedback comb filter into a lowpass feedback comb filter, by inserting [onepole~] into the feedback loop, like this:

Now all that’s left to do is set the filter coefficients. We will use a scale object to transform the g1 values of the range 0 to 1 to frequency values for [onepole~]. The delay time and the feedback gain, f, remain the same.

We will position our inlets like so, from left to right, input, delay time, lowpass amount, and feedback amount.

Moorer’s Parallel Combs

Moorer’s reverb is a similar design to Schroeder’s, consisting of parallel combs into an allpass filter (in this case, just one.) For sufficient echo density, he suggests six combs in parallel, with the familiar delay ratios of about 1:1.5. Moorer advises setting the f value as a constant. As for the g1 values, I will set them here according to a table provided by Moorer, based on measurements of humidity in concert halls. These are generally good sounding values, but I encourage you to play with setting these values arbitrarily. High values of g1 damp the sound whereas low values tend towards the sound of no filter in the loop at all.

Here are the combs, with the values set accordingly. For the allpass, the delay time is very sensitive. Short delays create an undesirable “puff-puff” sound, but delays longer than six milliseconds tend to produce audible periodic repetitions (we should recognize this having played with longer delay times in our earlier allpass designs.) Thus the delay is best set to 6ms and the gain about 0.7.

Here is the completed patch. Open up the moorer.amxd file. Play around with changing the g1/lowpass coefficient. Compare and contrast this with the Schroeder design we built earlier. What are the advantages and disadvantages? How might they be combined?

Exercise

Moorer offers one final amendment to Schroeder’s design, the introduction of a tapped delay line to simulate early reflections. I will leave the implementation of this to the reader, as an exercise.

Here is the diagram of the early reflections simulator. The summed output is then sent to the reverberator we constructed above. The reflections are simulated through a simple tapped delay line of N taps. How might you go about constructing this? (hint: tapout~ takes more than one argument.)

Appendix

Let’s start by implementing our basic comb in gen~.

This is exactly the same as our above diagram, just with the top clipped off.

The z-M term is our delay. We’ve given it an argument of 44100 to set the maximum delay in samples (this is the same as setting the maximum delay to 1 second, as there are 44,100 samples per second at our standard sampling rate of 44.1khz) We’ve also set the @feedback attribute to 1, in order to allow feedback connections. The g2 term is simply our feedback gain. We’ve created an inlet/outlet pair for our incoming/outgoing signal, and a second inlet to control the delay time. So far, there is no difference between this and the [p comb] subpatcher from Part 1.

Let’s look at the top part of the diagram that we just sliced off.

Believe it or not, this is our one pole lowpass filter!

When a signal flows through at the top, it is delayed by a single sample z-1 (using the history operator.) Then it is scaled by our filter coefficient g1 and passed back to the top to be added to the input. We will not get into the mathematics of why this works here, but the result is a spectrum like this, at the highest value of g1 = 0.99:

On its own, this is a problem for us. It may be hard to see, but the gain at the lower frequencies is above unity (some low frequencies are being scaled by a factor of greater than 1, and therefore exiting louder than when they entered the filter.) This won’t do for our comb, because a feedback gain of greater than 1 for any frequency will quickly cause the amplitude to converge to infinity.

In fact, the clipped portion of our diagram is missing a term, in order to resemble a common one pole low pass filter, such as [onepole~]. In order to restore our gain to unity, we must scale it down. Fortunately, there is a convenient way to do this, using the complement of our g1 term (1 - g1). Thus, we will set the g2 term to equal (1 - g1). Thus, in order to transform our simple feedback comb filter into a lowpass feedback comb filter, all we have to do is insert our one pole lowpass into the feedback path, like so:

Here is the spectrum of the lowpass after the scaling, g2, has been applied, for a value of g1 = 0.99:

Now all that’s left to do is set the filter coefficients. For the lowpass coefficient g1, a value greater than 0 but not exceeding 1 will ensure stability. The value of g2 should be set as (f * (1-g1)) where f is between 0 and 1. The (1-g1) portion returns our filtered signal to unity gain, as mentioned before, and the f allows us to scale our filtered signal between 0 and unity, before feeding it back into the input of our comb.

Here are the parameters, set accordingly. As you can see on the right, the g1 coefficient is used to calculate the g2 coefficient. The [!- 1.] operator subtracts its input from 1 (1-g1). The result is scaled by our f parameter and set as g2.

This is our completed gen~ patch. As a final convenience, we can add a ms to samples conversion for our delay time, so that we can set it in ms from outside the patch.

Stepping outside of gen~, we can create a subpatcher like so, with four inlets corresponding to input, delay time, lowpass amount, and feedback amount, respectively.

(See Moorer’s Parallel Combs for continuation)



~

niloufar is a musician, sound engineer, and visual artist. Her recent record 'memory machine loulou' is available on Bandcamp.

You can follow her on Sound/Cloud and Instagram

Citations

  1. Smith, J.O. Physical Audio Signal Processing, http://ccrma.stanford.edu/~jos/pasp/, online book, 2010 edition, accessed January 1, 2021.
  2. M. R. Schroeder, ``Natural-sounding artificial reverberation,'' Journal of the Audio Engineering Society, vol. 10, no. 3, pp. 219-223, 1962.
  3. Moorer, James. (1985). About This Reverberation Business. 10.2307/3680280.


More Creators