XaiJu
luxcache
luxcache

patreon


REVERB 101 - Part 1: An Introduction to M4L and Reverb Design with niloufar

In this Lux Cache tutorial series, Los Angeles based artist and dsp enthusiast, niloufar, will explore 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 give an overview of key reverb concepts,  and then apply them in the basic Schroeder reverb model. 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, as well as a similar reaktor ensemble, 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.

Accompanying .amxd and .ens files included in the premium subscription, available as a separate post on Patreon.

~

There are myriad approaches to simulating the effects of reverb digitally, from rigid physical models of spaces to abstract configurations, completely removed from any idea of a physical space, but almost all of them center on modeling the basic properties of reflection, absorption, and diffusion with filters and delays.

Reverb is an art and what makes a good reverb is ultimately subjective, despite there being conventions and common principles. Many great sounding reverbs sound nothing like real spaces (or even possible spaces) and it is crucial to remember that no single reverb is ideal for every sound.

In this tutorial, niloufar will provide an overview of concepts which form the basis of understanding reverb design, as well as walk through implementing and modifying two basic reverb designs in Max4Live.

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)

What is reverb anyway?

A brief history of reverb

Digital reverberation

Schroeder reverb

Schroeder Reverb Cont. (Allpass Filter)

What is reverb anyway?

When we hear sound, what we’re hearing is the displacement of air*, typically by some vibrating object which has been excited**. As sound designers, we’re used to representing this displacement as sound waves, and for reverb, this is a good way to think of it as well. When I clap my hands in an enclosed space, like my bedroom, or a cave, or a concert hall, I produce sound waves. Why then, does the result sound different depending on the kind of space I am in? We can answer this question by thinking about where the waves travel after they’ve been produced. If I clap my hands fairly loud, the waves will travel farther, and they may travel far enough to collide with something, usually the walls of the space I am in. So what happens then?

What happens next depends on the kind of surface that the sound waves collide with.

(Hard, flat surfaces produce reflections)

The sound bounces off of the surface and continues to travel, but in a different direction. The sound might bounce back directly to our ears, or it might continue to bounce off of other surfaces. Either way, we hear this reflected sound as echoes. We will later simulate this effect using a delay.

(Soft, porous surfaces absorb sound.)

When a sound wave is absorbed, it ends its journey inside of the material, and will never reach our ears. Recording studios are often set up with this kind of surface, in order to capture the sound with as little coloration as possible, by removing echoes.

Most surfaces are neither perfectly reflective, nor perfectly absorbent, and will therefore perform some combination of absorption and reflection. The effect is that the reflected sounds will be dampened, or lose some energy (usually higher frequencies) as they are reflected. We will later simulate this effect using a lowpass filter.

(Diffusive surfaces reflect sound, but across many different directions.)

The sound energy is thus dispersed more evenly, making diffusion a very useful property in acoustics, as it allows us to build up echoes without strongly emphasizing particular

frequencies. Most rooms are not diffusive, and thus exhibit resonances at particular frequencies often referred to as room modes. We will later simulate the effect of diffusion using an allpass filter.

*specifically, the compression and rarefaction of air molecules, that is to say, the transition of a group of molecules from a state of being packed together and bunched up to a state of being spread apart. In actuality, what we measure when we measure sound is the displacement relative to a position of equilibrium, an undisturbed state. Not only this, but real objects often have more than one degree of freedom (the direction in which they can move.) As you’ll see, our understanding of real sound can be very complex or very simple, it all depends on the model we build to make sense of it. Of course, simple models are the easiest to understand, but also lose a fair bit of nuance.

**see part 3 of sv1’s organic sound design masterclass for a brief overview of excitation.

A brief history of reverb

In a sense, the first reverb designers were the architects of concert halls and other such acoustic spaces throughout musical history. It wasn’t until about the late 1800s, however, until the acoustic attributes of these spaces were understood in depth. A crucial breakthrough in the study of reverberation was provided by Wallace Sabine, who drew a correlation between the reverberant qualities of a room, and the size of that room, as well as the geometry, volume, and reflectivity of its surfaces. The shape of a surface, as well as the relative position of surfaces to each other, determine the angle of the reflections produced by the surfaces. As discussed earlier, the contour of a surface determines its reflective/absorbent/diffusive properties. Sabine also gave us the important notion of “reverberation time”, often referred to as RT60 (the time it takes for reverberation to decay 60dB from its peak amplitude.)

Early attempts at creating artificial reverberation involved allocating dedicated “echo chambers”, which would have dry sound funneled into the room on one end through a speaker, and recorded on the other end by a microphone.

(Capitol Records echo chamber)

The recorded signal would subsequently be mixed with the dry signal. When executed well, this method often produced excellent sounding results but did not offer much in the way of customization.

A more flexible method involved the use of a material medium, such as a spring or a plate, with transducers attached at either end to perform the function of the microphone/speaker in the previous echo chamber example. These reverberators often sounded good, albeit rather artificial, and incapable of producing especially long reverberation times.

Digital reverberation

It is generally recognized that the first experiments with digital simulation of reverberation occurred at Bell Labs in the mid-1950s. Of particular note is the work of one Bell Labs technician, the German physicist Manfred Schroeder. In 1960, Schroeder, along with B.F. Logan, put forward a  filter design, commonly referred to as a Schroeder allpass section, in the paper “Colorless” Artificial Reverberation(cite). In the years since, the allpass filter, and in particular the Schroeder allpass, have come to be an essential building block in digital reverb design.

For a thorough description of allpass filters, see JOS(1). We will limit ourselves here to what is necessary to understand in order to build one. Consequently, we will look at implementing two basic reverb designs, given in Schroeder’s 1962 paper Natural Sounding Artificial Reverberation(2).

Generally speaking, an allpass filter is any filter where for a given input frequency, the amplitude of the corresponding output frequency is the same. In the same sense that a low-pass filter leaves low frequencies unchanged and attenuates high frequencies, and a high-pass filter leaves high frequencies unchanged and attenuates low frequencies, an allpass filter leaves all frequencies unchanged and does not attenuate any frequencies.

(amplitude response of low pass/high pass/all pass filter)

One might say, then, that the most simple allpass filter is a rudimentary delay,

y(n) = x(n-M)

where M is the delay time in samples. It is common at this point to ask, “what is the purpose of a filter if all frequencies pass through unchanged?” We can answer this question by looking at the phase response of the filter, as opposed to the amplitude response. The kinds of allpass filters we’re interested in for digital reverberation, including the Schroeder allpass section, have a flat amplitude response, but an uneven phase response. From now on, when we speak of an “allpass filter”, this is the kind we will be referring to.

(amplitude vs. phase response of 1khz allpass filter)

Phase can be a tricky concept to grasp. We will not give it a full treatment here but instead, consider a useful simplification. For our purposes, we can consider the allpass filter to be a kind of frequency-specific delay. At short delay times, this results in the smearing of transients* across time, which can soften the perceived impact of a sound, and at longer delay times, some frequencies of the signal are strongly displaced in time, resulting in perceived echoes. Allpass filters are generally said to be “colorless” but this displacement of frequencies across time can often cause the output of an allpass to sound pitched.

Recall the earlier explanation of diffusion in a room as a series of echoes with even frequency energy. It should be clear now that the allpass filter we have described is roughly analogous to this simplified view of diffusion.

*a transient is a short, high-energy burst of sound occurring at the start of a sound event

Schroeder Reverb

(The Basic Schroeder Design)

Let us now take a look at the basic Schroeder design. Schroeder proposes a group of 4 parallel comb filters fed into 2 series allpass filters.

The [comb~] and [teeth~] objects provide two implementations of a comb filter in Max/MSP. That said, these implementations are slightly over-complicated for our purposes*. A close examination of the diagram shows that Schroeder’s comb filter is a simple feedback comb filter, also known as a simple tapped delay line with feedback.

(Schroeder’s comb filter, a simple delay of T milliseconds with feedback gain, g)

(the same structure, implemented in Max/MSP)

We will create a [tapin~] object with an argument of 100 to initialize the maximum delay time as 100ms, then connect its outlet to the inlet of a corresponding [tapout~]. This is our basic delay line. In order to add feedback, we scale the output of the [tapout~] using a [*~] and connect it back to the input of our [tapin~]. This is our basic comb filter. In order to control the feedback and delay times, we will add two [live.dial] objects.

(Inspector Window)

We can set the name and output range of these objects using the Inspector Window, accessible through the ‘i’ symbol on the right panel of our patcher window. I have set my delay range to (1-100.) and my feedback range to (0-95.).

(simplest comb filter with delay/feedback control)

We can hook our delay dial directly into the inlet of the [tapout~] to control delay time. For our feedback dial, we can make use of the right outlet, which always outputs values from 0 to 1, and scale it by [* 0.95], before connecting it to [*~] in order to control the amplitude of the feedback signal.

If we test our comb filter by sending it a short burst of noise, we can see that everything is in order, but we may notice that when we change the delay time, we hear an audible discontinuity or clicking. In order to eliminate this, we must smooth the output of our delay dial using interpolation.

The [line~] object does just that. [line~] receives a two-element list; the first element is the destination value, and the second element is the time in milliseconds that it takes to reach that value. The ($1 20) message substitutes $1 for our input. In the case of this screenshot, the input (3.88) causes the message ($1 20) to output (3.88 20) and consequently tells our [line~] to ramp to a value of (3.88) over 20 milliseconds. No more clicks!

Instead of clicking, however, we may now notice an audible shift in the pitch of our output as our delay time changes. This is often a much more musically desirable effect than clicking. It can be made more prominent by increasing the ramp time.

Now that we have our basic comb filter, we simply have to duplicate it four times to create the parallel structure in Schroeder’s design.

Notice that while we have duplicated the delay dial in order to have independent control over the delay time of each comb filter, we have not done so with the feedback dial. Instead, we are using one dial to control the feedback time of every filter. This is ideal for our purposes since we will want to be able to control the decay of our reverb as a whole.

The feedback comb filter has the useful property of increasing echo density without allocating additional delay lines (which would be computationally expensive.) Instead, each delay decays exponentially as it is fed back, resulting in a cascading series of echoes. Unfortunately, the amplitude response of this filter is not flat, meaning it disproportionately emphasizes certain frequencies, depending on the feedback amount and delay time. To see this in effect, let’s see what happens when we input white noise into one of our comb filters above.

(comb filter spectrum)

Looking at the output of our comb filter in [spectroscope~] we can see regularly spaced peaks and troughs in the spectrum. Consequently, our series of parallel comb filters will have a very colored frequency response. On top of this, the echo density is quite low without allocating many more comb filters. This is alright for simulating the early reflections of a room, and as previously discussed, most real rooms do exhibit a certain color or room resonances. But what about the diffuse tail?

*deriving the comb filter first will also help us better understand the allpass. after following the tutorial, I encourage you to experiment with replacing the comb filter elements in our finished design with [comb~] and [teeth~], and note the differences.

Schroeder Reverb Cont. (Allpass Filter)

MSP has an [allpass~] object, but it is not the same allpass design Schroeder uses*. Not only this, but we can construct the Schroeder allpass section fairly easily from the comb filter design we have already created. While we will not give in-depth treatment to why this works, paying attention to how we construct a Schroeder allpass will prove a powerful tool in reverb design, as we go on to nesting allpasses**. Schroeder himself does just this later in the paper, which we will examine towards the end.

Let us first duplicate our basic comb filter and add a [+~] before the [tapin~]. And another before the [tapout~].

In the left inlet of the top [+~], we will plug in our input and in the right inlet, we will plug in our feedback signal. We will then take the output of [tapout~] and plug it into the bottom [+~].

Now that we’ve done this, let’s take the output of the top [+~]. This is the combination of our input and the feedback signal. Let’s first scale it by our feedback gain, using [*~]. Then, let’s route it into the inlet of the bottom [+~], like so:

What we have now done is add a feedforward component to our feedback comb filter. In order to make this structure allpass, all we have to do is multiply our feedback gain (not feedforward!) by -1.***

(Schroeder allpass section, implemented in Max/MSP)

Taking a look at Schroeder’s allpass diagram, we can confirm that our structure now resembles his, only with the signs of the feedback/forward gains inverted.****

(Schroeder allpass section)

We can now easily realize the full design by connecting our bank of parallel comb filters to two allpass filters in series.

(basic Schroeder reverb)

Here I have encapsulated our comb and allpass filter realizations by highlighting them and pressing Command + Shift + E (on Mac, Ctrl + Shift + E on Windows.) For cleanliness, I have created send/receive pairs for the feedback parameter. The --- prefix is to ensure that these sends are device-specific when loading multiple instances. Lastly, I have added a simple dry/wet control at the bottom.

For setting the delay times of the comb filters, Schroeder recommends a range of about 1:1.5, meaning the fourth delay should be about 1.5 times the amount of the first. For the allpass delays, he recommends about 5 and 1.7 milliseconds, respectively. Schroeder recommends setting the feedback time of the allpasses and the combs separately. For the combs, he recommends a gain of about 0.85 (85%) and for the allpasses 0.7 (70%.)

Schroeder offers a formula for determining the RT60 (the time it takes for reverb time to decay by 60db),  T = 3*Tn/(-log|g|), where T is the desired decay time, Tn is the delay time, and g is the feedback gain. I will not derive it here, but we can reverse engineer this formula in order to calculate the feedback gain given a specified decay time and delay, g = e-(3Tn)/T

Here is the equation implemented in order to create a meaningful “decay” parameter for the comb filters. The delay should not exceed the decay time, so it is defined as a portion of the decay time. It has also been scaled by 0.1, because I have found that values higher than this start to substantially lose echo density.

Open up the schroeder1.amxd and poke around for yourself! Change the various parameters! Try replacing our allpass and comb components with the [allpass~] and [comb~]/[teeth~] objects in max. What happens when you replace just some of them? What happens when you add more combs in parallel or more allpasses in series? In a later tutorial, we’ll look at a variety of improvements we can make to this basic patch, as well as some other, more interesting, established reverb designs.

*the [allpass~] object implements a general allpass filter.

**as well as making general allpasses.

***the Schroeder allpass section is a combination feedback/feedforward comb filter, where the feedback gain is the negative of the feedforward gain.

****it does not matter whether we invert the feedback or feedforward gain, only that one is the inverse of the other. In this case, we have chosen to invert the feedback gain, due to personal preference and in keeping with examples such as JOS.

~

niloufar is a musician, sound engineer, and visual artist. Her recent record 'other's success is my success loulou' is available on bandcamp.

You can follow her on soundcloud 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.

Comments

Love this, thanks!

Niloufar is amazing! Thank you for this!

nadyakid

incredible


More Creators