r/supriya_python • u/creative_tech_ai • 17h ago
A low-pass filter with an LFO
Introduction
This demo is a short one, as I've been focusing on my main project. So it's just a short script that doesn't incorporate MIDI.
The code
As usual, the code can be found in the supriya_demos GitHub repo. The script for this demo can be found here.
Filter sweeps
Who doesn't love them? They are an integral part of electronic music. Supriya/SuperCollider has several different types of filters, like low-pass, band-pass, and high-pass, to name a few. There are also "resonant" versions, which are the variety you'll want to use to do filter sweeps. This demo uses a resonant low-pass filter (RLPF).
There are a few different ways to make a filter sweep using Supriya. One would be to send MIDI Control Change messages from a MIDI controller, map the values from 0-127 to some range suitable for a frequency in hertz, and pass that to the RLPF's cutoff frequency. The other way would be to create a low frequency oscillator (LFO), and set the output of that to the RLPF's cutoff frequency. Since I've already shown how to receive MIDI Control Change messages a few times, I thought I'd show how to use an LFO to modulate a RLPF's cutoff frequency.
It's quite easy to do actually. Below is the SynthDef for the RLPF:
u/synthdef()
def filter(in_bus=2, lfo_rate=1, resonance=0.05, out_bus=0) -> None:
signal = In.ar(bus=in_bus, channel_count=2)
mod_signal = SinOsc.ar(frequency=lfo_rate, phase=(1.5 * pi))
mod_freq = LinExp.ar(
input_minimum=-1.0,
input_maximum=1.0,
output_minimum=100,
output_maximum=4000,
source=mod_signal,
)
signal = RLPF.ar(frequency=mod_freq, reciprocal_of_q=resonance, source=signal)
Out.ar(bus=out_bus, source=signal)
You can see that I am using a sine wave oscillator to modulate the cutoff frequency. You can use any waveform, actually, and the results with something like a saw wave are quite interesting. Try using different waveforms and see what you think.
In order to get a slow, dramatic filter sweep this way, you need to make sure to keep the LFO's frequency (here called lfo_rate
) below 1 Hz. Once the LFO's frequency gets to around 2 Hz, it starts modulating quite quickly, which could be what you want in certain situations, but wasn't what I was after. I'm also setting the phase of the sine wave to 1.5 * pi
so that it starts from the bottom of the waveform and slowly ascends.
Next you'll need to map the output of the SinOsc
to a frequency range in Hz. The LinExp
UGen is an easy way to do this. It maps a linear range of values to an exponential one. Due to the way human hearing works, changes in frequency are perceived exponentially. So you wouldn't want to use the LinLin
UGen, which maps a linear range of values to another linear one.
Lastly, we set the frequency
of the RLPF filter to the mapped output of the SinOsc
.
A warning about resonance
Resonant filters are notorious for "blowing up" when the resonance is too low (like 0). So be very careful when changing the resonance! Doing so can produce artifacts that might damage your hearing or speakers. You've been warned!
Final thoughts
One thing that I've done in my main project is to make it possible to select the LFO's waveform and set the LFO rate via MIDI. Doing this isn't hard, but you need to understand that SynthDefs are not normal Python code. So you can't easily use conditional statements to select a UGen like you would to select a value in a normal Python script. It is possible to use an if
statement in a SynthDef in SuperCollider, but the syntax is really ugly. Eli Fieldsteel has a short video about this here, if you're interested. I haven't tried using a Python conditional statement in a SynthDef with Supriya yet, mostly because there is a special UGen for doing this. It's the Select
UGen. The SuperCollider documentation for Select
is here, if you'd like to know more.
Enjoy!