Tutorials

Advanced Max: An Inquiry into Max’s Slide Objects

Introduction

Max is graced with many filters, taking on many guises - some of which we don’t even think of as filters in the classical sense. The help patcher for the slide object says that it “smooths values logarithmically” - indeed, it does. It is, together with the slide∼ and jit.slide objects, a lowpass filter.

In the process of exploring the algorithm used in these objects, we'll see how they relate to canonical filters with which you may be more familiar. Along the way, we'll step through the mathematical procedures, one step at a time. In the interest of being thorough, I'll try to minimize assumptions and introduce even basic mathematics explicitly.

Note: Readers desiring a quick brush-up to help with following the mathematical expressions are urged to do so before proceeding. Khan Academy is a tremendous resource for reviewing (or learning) algebra by starting with the short videos. If you want a quicker jump-start (or something more basic than algebra) check out the the pre-algebra videos here.

Conventions and Presuppositions

The following descriptions use the standard filter nomenclature where:

n references the current sample location

n−1 references the previous sample location (meaning delay of 1 sample), etc.

x(n) is the current input.

y(n) is the current output.

So, x(n−1) is the immediately previous (or delayed) input, and y(n−1) is the immediately previous (delayed) output, etc.

a₀ is a coefficient to be applied to the FIR side of a canonical filter. As examples, a₀ is the coefficient applied to x(n−0), a₁ is applied to x(n−1), etc.

Likewise, b₁ is a coefficient to be applied to the IIR side of a canonical filter - that is, b₁ is the coefficient applied to y(n−1), b₂ is applied to y(n−2), and so on. There generally is no b₀ because the current output is unknown at the time the calculation is being performed.

The constant fs is the sampling frequency (also known as the sample rate).

Background and Applications

There many ways to smooth a stream of numbers. Perhaps the simplest is to average them. Classic averaging is a lowpass filter with a “finite impulse response”. This is discussed in videos and provided in complementary gen∼ patchers published by Cycling ’74.

Averaging has a number of benefits, including linear phase response and the straight-forwardness of the calculation. The slide objects are nowhere near as easy to describe as the average∼ object, but they are far faster to compute.

All three of the slide objects - slide, slide∼, and jit.slide objects calculate their output identically, so the formula given in the reference pages for each of the objects is the same. For the purposes of this discussion, I'll focus on the slide∼ object for processing audio, because it simplifies the relationship to real time.

Besides generalized smoothing for parameter control, a good use for this type of filter is to slow down the decay part in

audio rate envelope following. Algorithms akin to this are often used in compressors/limiters as well as visualizers for music playback software.

slide∼

The slide∼ object implements a “logarithmic” filter with a slide parameter to smooth out discontinuities in an input signal, such as for performing envelope following. The slide∼ object actually contains two filters: one filter for changes to the input that increase in value (‘slideup’) and one filter for changes to the input that decrease in value (‘slidedown’). Often these are set to the same value, which simplifies some of our expressions, so for this discussion I'll take this to be the case and simply use the term slide for both up and down.

Given a slide value of 1, the output will always equal the input. Given a slide value of 10, the output will only change 1/10th as quickly as the input - this description of the slide∼ object’s behavior can be found in the object’s help patcher and reference page. Putting this in terms of time is complicated, or even exactly in terms of an average of N samples, since that wouldn’t be precise either. Nor is it exactly easy to describe in terms of weighting (the current incoming value is weighted by 1/N and then you have an exponentially decaying curve as you look towards samples from the past).

Mathematically, the filter can be expressed with the equation

slide

This equation expresses the filter in terms that relate to the original description of the object. However, this equation also obscures a number of characteristics of this filter, making it difficult to relate to other filters in Max. If we rewrite the equation in the terms of a standard difference equation we can glean some insights into the operation of the slide∼ object. We start with the equation as originally expressed

Letting a₀ = 1/slide, we can continue to rewrite the equation as

Equation 2 re-expresses equation 1 in terms that of the general difference equation. One immediate use of this is that we can plug the numbers into Max’s biquad∼ object to perform the same filtering, or use the filtergraph∼ object to plot the frequency and phase response. With the equation in this form, we can also see that there are two coefficients: one for the gain of the filter’s input and one to control the gain of a first-order pole, meaning that all we have is a simple one-pole filter. So, we could also plug the coefficient into the onepole∼ filter object to do the same thing.

Frequency Response

Now that we know that the slide∼ object is simply a one-pole filter, we can calculate its frequency response for any given value of the slide parameter. Remember that we have two coefficients derived from the slide value.

The onepole∼ object uses these same coefficients, but typically calculates from a desired cutoff frequency in hertz fʜᴢ. The first step is converting fʜᴢ, which depends upon the sample rate fs, into a sample rate independent form expressed in radians fʀᴀᴅ.

One potential confusion regarding radians is the fact that, in Max, some objects have a ‘radian’ mode which is expressed in something resembling radians but does not map to frequency with a linear relationship. Examples include onepole∼ and svf∼. It is unfortunate that this special mode was named ‘radian’ since it is misleading and confusing. Their use should be considered deprecated.

As an example, we can let fʜᴢ = 1000 and fs = 44100. Then

Given the frequency in radians, we can now calculate the coefficients

To simplify things, let’s combine the conversion into radians into the coefficient calculation

The relationship between a₀ and b₁ is the same for the onepole∼ coefficients as it is for the slide∼ coefficients, reinforcing the fact that these are really the same filter. We now have enough information to relate onepole∼’s fʜᴢ parameter to slide∼’s slide parameter through a₀.

To solve for slide in terms of hertz we can flip the fractions, resulting in

or to solve for the cutoff frequency in terms of slide

See the table below for some example values to see how the slide parameter relates to the cutoff frequency when using a sample rate of fs = 44100.

Further Exploration

Smoothing algorithms analogous to slide in other environments include Faust. Further exploration of this class of filter is left as an exercise to the reader. If you're interested in pursuing this topic, the Moving Average article on Wikipedia serves as a reasonable point of departure.

by Timothy Place on December 6, 2016

Creative Commons License
daddymax's icon

Interesting stuff - it made my brain hurt, but is a good explanation of some of the math and notation i've often seen used on the forum but never previously attempted to interpret. Thanks.

Max Gardener's icon

The slide~ and onepole~ objects doing the same thing? Boggle boggle. Thanks for the grey matter exercise regimen.

Jean-Francois Charles's icon

Well done, very clear, thanks!

Jean-Francois Charles's icon

So, slide is the Max equivalent to slide~, a low-pass filter.
What would be a Max equivalent to a MSP hi-pass filter or DC-remover?

Roman Thilenius's icon

jfc: hipass can be easily be done by substracting the input from the output of slide~ (since it is phase linear) but i guess for a DC filter it is not steep enough. (i use 2 biquads, 5 would be optimal)

brendan mccloskey's icon

Subscribing to this one too. Maths I can understand, and more importantly, apply and exploit.

Jean-Francois Charles's icon

Thanks Roman. Actually, my question would be for a "Max" equivalent, like "not MSP". Similar to [slide], but the hipass version.

Roman Thilenius's icon

oh pardon, i was already wondering :)

hm, as i did not get it right during the last 20 seconds it must be complicated.

you would probably have to initialise the output to zero, and i think it will also somehow involve 1/n at a point.

Roman Thilenius's icon

hehe, no it works for numbers just as it does for signals.

it is only, like often, a bit difficult to use a proper test setup which actually compares to processing signals.

Max Patch
Copy patch and select New From Clipboard in Max.

Roman Thilenius's icon

uh, watch out, when you paste this into max 7, the minus object turns into a hyphen object. :D

Jean-Francois Charles's icon

Very nice. That's a clever one, thanks Roman!

Dan McAnulty's icon

I think the equations are not loading with the rest of the page.

Candace Hazelwood's icon

no equations in text

Roman Thilenius's icon


if it is urgent, look into the html source to find them; the images still exist, they are just not displayed correctly.

Timothy Place's icon

Images have been fixed now. Thanks for pointing that out!

Norman Freund's icon

Thanks Timothy for taking the time to post this knowledge base article. I am trying this filter out on a cycle~ object output with it's default as a sine wave. So as I sweep through the sine wave input frequencies and pass this through to the slide~ object with a constant slide UP and slide DOWN parameter value of 2.7 which according to your theory should give a corner frequency of:

fc = fsample / (2*slide)
= 44100 / (2*2.7)
= 8167 Hz
but when I plot the response on a bode diagram, I get a corner frequency of 2066 Hz.
So if the above equation is cast into:
fc = fsample / (n * slide)
gives
n = fsample / (slide * fc)

gives
n = 4100/ (2.7 * 2066) = 7.9
which probably rounds to n = 8

So it appears your factor of 2 is out by a factor of 4?
Despite this possible error, your write-up is still very valuable.

Has anyone else run through the numbers to bench test this? If so what were your findings?

Here is a screen grab of the bode plot:

Raw numbers of sine sweep through slide~

bode plot of signal amplitude (db) versus log frequency

The corner frequency taken from the above graph as the horizontal axis intercept.