The APC40 is a great controller, but it does basically one thing: launch clips. Hans Petrov decided to improve on this diminutive feature set by writing the APC_64-40 remote script in Python to add new control modes for the clip grid, faders, and encoders. When Live 9 came out, the closed-source API for these scripts changed, and Fabrizio Poce took over the project to keep it up-to-date with the latest changes. In January of 2014 he stopped maintaining the project, but it continues to be useful to many APC40 owners, as well as other writers of custom scripts.
The original script makes the controller much more useful for producing as well as performing by adding note trigger modes as well as step sequencer controls. However, once you make the move to the arrange view while producing a song, the controller is less than useful even with the new modes. However, I felt the hardware could still be helpful for mixing and arranging, because one feature I’ve always wanted from the APC 40 is live VU metering of channels on the clip grid. So, it’s my pleasure to introduce a new version: the APC_64-40_9 Remote Script (VU Meter edition):
Download and install
To install the script, just unzip the archive and copy the resulting “APC_64_40_9” folder to the Ableton Remote Scripts directory. On Windows, it should be at:
“C:\Program Files\Ableton\Live 8.x.x\Resources\MIDI Remote Scripts\”.
On Mac OS X, find the Ableton application file and right click -> “Show Package Contents”. You’ll find the Remote Script folder in
“Contents/App-Resources/MIDI Remote Scripts”.
Once the script is in place, launch Live and open the preferences window. In the “MIDI / Sync” tab, you’ll find a listing of your connected MIDI gear. Find your APC40, and in the left column, change the default selection to “APC 64 40 vu” as shown. Hold the shift button and press the various Track Selection buttons to switch modes. If the clip grid changes to show new layouts, it’s installed correctly.
How it works
The basic idea behind remote scripts is simple enough: the Ableton application contains a folder called “Remote Scripts” that contains python files for mapping popular controllers to Ableton’s native features. Since python is a full programming language and not just a markup script, you can also create interactive modes and make the controls dynamic by writing a few classes and enabling them based on the control input. Hanz Petrov’s script already implements the shift-mode selection function, and I was able to get a head start by lifting a few functions from Will Marshall’s APCAdvanced script to add observers to the channel meters.
The main mode selection function (abridged) is below. It simply listens to the number corresponding to the button pushed, enables or disables the session view, and enables or disables the metering functions. When the 8th mode is selected, it creates a new instance of the
VUMeters class, which contains the code to listen to the audio channels and outputs their signal to the button matrix.
def _set_modes(self): # Clip Launch if (self._mode_index == 0): self._session_zoom._on_zoom_value(1) #zoom # Session Overview elif (self._mode_index == 1): self._session_zoom.set_zoom_button(None) self._session_zoom.set_enabled(True) self._session_zoom._is_zoomed_out = True .... # VU Meters elif (self._mode_index == 7): self._session.set_enabled(False) self._update_vu_meters()
VUMeters class, we first gather all of the visible tracks (those not collapsed) into an array for ease of access. Then we iterate on that array (offset by current session view’s position on the screen) and add
VUMeter objects to our list of meters until we have 8 meters (if the channel is audio and not MIDI). Then we use the native Live API function
add_output_meter_left_listener to call back into the
observe function of our
VUMeter object with the audio level every time it updates.
class VUMeters(ControlSurfaceComponent): def observe(self, session_offset): self.master_meter = VUMeter(self, self.song().master_track) self.song().master_track.add_output_meter_left_listener(self.master_meter.observe) visible_tracks =  i = 0 while i < len(self.song().tracks): track = self.song().tracks[i] if track.is_visible: visible_tracks.append(track) i +=1 for row_index in range(NUM_METERS): self._tracks[row_index] = visible_tracks[row_index + session_offset] if self._tracks[row_index].has_audio_output: self._meters[row_index] = VUMeter(self, self._tracks[row_index]) self._tracks[row_index].add_output_meter_left_listener(self._meters[row_index].observe)
Finally, in the
VUMeter class, we listen to the level reported by the listener function, calculate the root-mean-square (RMS) value of the signal based on a few frames of audio so we don’t unnecessarily overload the APC with update messages, and convert that value into a series of messages to the controller which turn the corresponding LEDs on and off.
class VUMeter(): def observe(self): level = self.rms(self.frames) self.set_leds(self.matrix, level) def rms(self, frames): return math.sqrt(sum(frame*frame for frame in frames)/len(frames)) def set_leds(self, matrix, level): for index in range(6): button = matrix[index] if index >= (6 - level): if index < 1: button.send_value(LED_RED, True) elif index < 2: button.send_value(LED_ORANGE, True) else: button.send_value(LED_ON, True) else: button.send_value(LED_OFF, True)
(Other functions omitted here for clarity include some simple value scaling to convert the floating point volume value from Live’s API to an integer from 0-6 (because we have 6 lights to represent volume), calculating mean peaks on the master channel and lighting up all the buttons red to warn us of clipping, and utility functions to disconnect the listeners when the mode is switched back to session view or the channel offset is changed an the meters must be re-initialized.)
I’m delighted to be able to share this script with you, and I hope you have as much fun using it as I did writing it. Enjoy!