FX in Practice

Although they look deceptively simple on the outside, FX are actually quite complex beasts internally. Their simplicity often entices people to overuse them in their pieces. This may be fine if you have a powerful machine, but if - like me - you use a Raspberry Pi to jam with, you need to be careful about how much work you ask it to do if you want to ensure the beats keep flowing.

Consider this code:

loop do
  with_fx :reverb do
    play 60, release: 0.1
    sleep 0.125
  end
end

In this code we’re playing note 60 with a very short release time, so it’s a short note. We also want reverb so we’ve wrapped it in a reverb block. All good so far. Except…

Let’s look at what the code does. First we have a loop which means everything inside of it is repeated forever. Next we have a with_fx block. This means we will create a new reverb FX every time we loop. This is like having a separate FX reverb pedal for every time you pluck a string on a guitar. It’s cool that you can do this, but it’s not always what you want. For example, this code will struggle to run nicely on a Raspberry Pi. All the work of creating the reverb and then waiting until it needs to be stopped and removed is all handled by with_fx for you, but this takes CPU power which may be precious.

How do we make it more similar to a traditional setup where our guitarist has just one reverb pedal which all sounds pass through? Simple:

with_fx :reverb do
  loop do
    play 60, release: 0.1
    sleep 0.125
  end
end

We put our loop inside the with_fx block. This way we only create a single reverb for all notes played in our loop. This code is a lot more efficient and would work fine on a Raspberry Pi.

A compromise is to use with_fx over an iteration within a loop:

loop do
  with_fx :reverb do
    16.times do
      play 60, release: 0.1
      sleep 0.125
    end
  end
end

This way we’ve lifted the with_fx out of the inner part of the loop and we’re now creating a new reverb every 16 notes.

This is such a common pattern that with_fx supports an opt to do exactly this but without having to write the 16.times block:

loop do
  with_fx :reverb, reps: 16 do
    play 60, release: 0.1
    sleep 0.125
  end
end

Both the reps: 16 and 16.times do examples will behave identically. The reps: 16 essentially repeats the code in the do/end block 16 times so you can use them both interchangeably and choose the one that feels best for you.

Remember, there are no mistakes, just possibilities. However, some of these approaches will have a different sound and also different performance characteristics. So play around and use the approach that sounds best to you whilst also working within the performance constraints of your platform.