Looping, Ableton and Max for Live Patching
This is a new format of posts, which tries to use my Medium profile as an informal outlet for thoughts and ideas around music and programming.
Less informal content will be published into https://beijaflor.io/blog, but might be less frequent and/or of different nature.
With this text, I hope to present some live-looping gear in a useful manner, present software alternatives and go over some shortcomings. I also would like to talk about very basic Max for Live patching that I've done over the weekend, while trying to work this out.
I’d say I'm more a software engineer and less a musician, but I hope there’s a healthy mix of both points-of-view.
My most trusted set-up consists of a bass guitar, some effects pedals and a Boss RC-300 looper.
The Boss RC-300 is a floor looper with 3 tracks. It has 6 main foot-switches, 2 for each track. One switch will start playback, recording or overdub and the other will stop and clear clips.
There're more secondary functions, but the gist is: there're 3 tracks, looping, stopping, playing, clearing is all one press away with leds indicating the current state. It's really great.
There's great flexibility while recording loops and a simple tactile experience. One may perform completely improvised music with it, rather than perform a planned set, due it having plenty foot-switches for all main operations. There're more details about how it works which are interesting to share later.
Boss RC-300 Tempo sync
At some point, after a bunch of loops, one might want to sync the RC-300 tempo with other equipment. For example, we may want to run drum-loops on a computer and have the loops be quantised to work with them.
While the RC-300 has MIDI input/output, it does not support syncing its tempo to a MIDI host very well. Also, when it's the host, setting the tempo, there are unfortunate glitches in tempo when buttons are pressed, which are noticeable.
The way I've worked around this (found on the internet 🌈) is to have the RC-300’s tempo be set by a MIDI clip, which will essentially press its tap-tempo button on every beat.
The RC-300 tap tempo button must be mapped to some CC message and the DAW must be set-up to send this CC on every beat. For ableton, this can be achieved as above and with the following ALS file: https://github.com/yamadapc/ableton-bits/blob/main/RC-300%20Clock.als
Multi-Track Ableton Looper Device
This is quite some work and it doesn't solve a couple of issues. First, RC-300 is big and a bit of a hassle to carry around. And second, a fully in-the-box approach with Ableton would be more flexible (unlimited tracks, post/pre effects, etc).
But we can try to make an equivalent looper within Ableton.
The start is to create a 3 track looper in Ableton live.
I'm using an Audio Device Rack to have 3 Looper devices in parallel, plus an extra dry channel. My tri-looper rack also has a low-pass filter on track 2 for some added performance effects.
It's useful to have a MIDI foot controller that can replace the 6 buttons on the RC-300. I'm using the MorningStar MC-8. It’s a nicely built, foot MIDI controller with 8 buttons and good support for pages and banks. Mapping is a little tedious, but generally a good experience.
Quantisation behaviour in Ableton
After recording an initial loop, all loops should follow its tempo.
In RC-300, the pedal can be set-up so that if a press is late/early, it'll be intuitively "snapped" to the closest bar boundary.
If I'm on bar 2 & press the button on beat 1.1, the RC-300 will know that I wanted my loop to have started on beat one of bar 2, rather than beat 1 of bar 3.
On Ableton live, the second option happens. Being a little late will delay the press up to the next bar. This is very annoying and the RC-300 is considerably more forgiving with timing, both in terms of avoiding clicks/pops and fixing late/early presses in a way that makes sense while playing.
One big missing feature while looping with a computer is visual feedback. On the hardware looper, there are LEDs that will light-up with red/green lights, which make it easy to see what loops need to be cleared or are playing.
While the looper device has visual feedback, on the screenshot above, you may notice it will only show the currently selected rack, which means I can't see the 3 loopers' states at the same time.
This is important when improvising loops, because you might forget if you recorded the loop on track 2 or 1 and now want to stop it — or worse: forget there's a recorded loop on a track and accidentally start it.
In order to try to fix this issue, I turned to Max for Live.
I found a nice Max device, which implemented basic visuals for looper and "forked" it, modifying it to support multiple loopers at the same time.
My version is on https://maxforlive.com/library/device/7523/multi-looper-visuals-and-window and it looks like this:
Using the dropdowns on the left, we select 3 loopers that we'd like to see.
On the right, 3 HUD panels will show the state of the loopers in both text & color formats.
By pressing the "open" button, we can open a view of the looper state on a separate window, which could, for example, be kept on a separate monitor from the main Ableton window.
I want to show how this is implemented within Max, because it was quite nice for me to understand how to set it up.
The patch starts with track/device selectors, which are from the built-in Max for Live abstractions:
These are wired-up to the UI and let the user select the track where the 3 loopers are, as well as each of the looper devices from dropdowns (more on how to automatically find the looper device IDs later).
The LOM (Live Object Model) refers to each entity (track, device, set, track, clip, etc.) with both a path ("live_set selected_track") and an ID (305).
The dropdowns give the rest of the patch the IDs for the track and devices we'll display the state for. The IDs for the track and the 3 looper are sent into a sub-patcher called "prepare-ids".
This sub-patcher does replacement of the "iterate" prefix to the "id" prefix. It receives "iterate 305" and outputs "id 305".
The track ID is used only to display the track name on the device:
The "set-object-name" sub-patcher is an object that receives a message like:
Fetches the entity name from the Live API and outputs a message:
set "Device name"
Which can be sent into the comment box used in the UI.
In order to turn id 305 into the device name, we use the
live.object object. This max object receives a message and an ID and executes this message over the object.
In this case, the message is "get name", triggered by a bang on the previous step and the ID is passed via the "2" inlet.
The looper device IDs are piped into a similar sub-patch to display their name plus the more important sub-patch that observes and renders the looper state.
The "looper state HUD" sub-patch uses
bpatch, which lets it nicely render some UI, while being abstracted away.
Inside the this patch another "HudLogic" sub-patch is wired into both the colored
panel and another comment that shows the state in text.
The "HudLogic" sub-patch outputs messages to set the comment and set the color of the panel on separate outlets. It looks like this:
First, the patch finds the LOM "path" for the "id 305".
It sends a "getpath" message into the
live.object with "id 305", which outputs something like
path live_set tracks 0 devices 0 chains 0 devices 10 chains 0 devices 0 (in this case, where my loopers are deeply nested within other racks).
Then, we modify this path to get the first parameter of the looper (
parameters 1) of the looper, which will give us the looper state:
By sending this modified
path ... message onto the
live.path object, we find out that the state parameter ID for this looper is "id 97".
We then use the
live.observer object to observe the value of this parameter
select object switches between the states:
- 0 — Stopped
- 1 — Overdubbing
- 2 — Recording
- 3 — Playing
And the states get forwarded to the comment title/background color outlets
Finally, there's +/- a copy of the whole patch on another sub-patcher, which can receive an "open" message by the
pcontrol object, in order to be opened on a separate window.
These things are nicely laid out on presentation mode for the UI:
I think this is really cool. Of course I couldn’t have understood it without the original patch to fork, so that was a great reference.
Automatically finding the Looper IDs
The drop-down experience is not the best, because I can anticipate, I'll find myself constantly having to use these track/device drop-downs to find the loopers and map them to the HUDs.
To address this, I built a version of this patch which does not require me to do it. Here's how it works:
We start by finding the ID of the track the max patch itself is on. So we simply auto select the track based on wherever you drop the device in.
This ID is sent to a sub-patcher which is very similar to the previous.
But instead of getting the looper device ids from the drop-down, before forwarding them to our HUD sub-patchers, we find all of them with a
I tried to implement the recursive search for devices within racks in Max itself with patch cables, but found it to both be impossibly hard to understand and it actually consistently crashed Max.
We start by declaring an
id function at the top-level. This function will be called whenever this
js object receives an
id 1234 message. So
trackId in this case is something like
We then use the LiveAPI class to interact with the entities. For example, given a "parentId", here's how to get all the nested chains or devices in it.
The whole source looks like this:
And then we can just drag-and-drop this device onto any track and it will search for the "Looper 1", "Looper 2" and "Looper 3" devices and display custom UI for them!
Things that could have been better
- Quantisation still isn't as good as the RC-300 (perhaps to fix this I've to finish implementing things at augmented-audio)
- The state does not show whether the looper is cleared or not
- Max crashes when trying to do recursive patches with LOM (?)
- This is still much worse than having LEDs
🌈 🙂 I learned something! I hadn't built patches like this before.
Enjoy your day & see you around.