Hacking the APC40 part 3: Touching the dials

Akai's APC40

Part 1 – This is the first part of the third part 😉  I’m still in the progress of hacking the APC40, but a friend suggested that it would be better for the blog continuity to post my current findings than risk my blog “drying up”. Makes sense I think, so prepare for an incomplete story. Revealing, but incomplete nonetheless.

So we’re back…  I know it took me a while to come up with the next part, but alas; in the end this is still a hobby for me. Some of you even started to wonder if I had turned into a Reason user and stopped caring for Live all of a sudden 😉  That is not the case; I have been playing a lot with Reason in the past few weeks, but more on that in another post.

So far we’ve roughly covered the matrix, the relationship between components / controls, how to investigate methods and how the identification works. Now we’re going to check out the track and device controls.

Previous APC40 discoveries

  • The control surfaces section uses negative ID values for identification whereas the rest uses positive ID values. ID values are used for everything: components, controls as well as their properties and functions.
  • You can use ‘goto’ to “enter” a function in order to learn about its contents (amount of required parameters, names of the parameters, etc.).
    • When ‘entering’ a function you’re looking for ‘im_func’ followed by ‘func_code’. After that the properties ‘co_nlocals’ give you the amount of parameters and ‘co_varnames’ gives you the names and descriptions.
  • Components represent virtual sections of the APC40 (channel strip, matrix, etc.) whereas controls represent physical sections (buttons, sliders, etc.).
  • Components and controls are often related, and sometimes they even depend on each other.
Chart displaying the control surface section
Control surfaces section of the LOM chart. Click the picture to go to the official Cycling ’74 webpage.

Matrix re-visited

Although I roughly covered the matrix section one reader pointed out that I never explained how you could control the button colours. After all; using the functions turn_on and turn_off only get you the default colour, which is green. First you obviously need to point a [live.object] to a button control, for example: “control_surfaces x controls 20” which is the upper left button in the matrix.

The ‘secret’ is to use the “send_value” function with a parameter which defines what the clip should do. It seems this function can use more parameters, according to ‘nlocals’ it uses up to 6, but lets concentrate on the “value” part (the first parameter):

0 – Turn button off.
1 – Turn button on, colour is green.
2 – Turn button on, button is flashing green.
3 – Turn button on, colour is red.
4 – Turn button on, button is flashing red.
5 – Turn button on, colour is orange.
6 – Turn button on, button is flashing orange.

All values above 6 will turn the button on with its default colour of green. The ‘reset’ function does just that; it rescans the matrix and set the button to the required state. So if you turned it off with a clip present then it will be turned back on.

This works for all buttons which have a led in them (type is ButtonElement). Do note that this only affects the colour, it does not change the way the buttons behave in any way. If you want to know more about that you should read up on a previous APC40 post or wait until an upcoming post where we’re going to ‘mirror the matrix’.

The quest for control

As mentioned earlier; the APC40 basically consists of components which make up the virtual parts as well as controls which represent the physical hardware. When looking at the components we can quickly deduct the scene (‘SceneComponents’) and individual clip slots (‘ClipSlotComponents’) which make up the matrix. Speaking of which; the red square which indicates the active matrix is also a component of its own; the so called PedaledSessionComponent.

The tracks are represented individually; all 10 of them (8 regular tracks and one master track, the ‘SpecialChanStripComponents’, as well as the currently selected track). What makes this component special is that a track consists of multiple sections: 5 buttons in the matrix, clip stop button, track select button, and finally the three track “control” buttons: enable, solo and arm. Last but not least; the fader which is recognized as a volume control.

Then there’s the transport section (‘ShiftableTransportComponent’) which leaves us with the final 2 sections: track control and device control.

Note that I’m ignoring some “system components” such as the mixer (SpecialMixerComponent), I’ll get deeper into those in another post.

Step 1: Search for relationships

Thanks to the deduction I did above we now end up with some very probably targets: Device_Component (‘ShiftableDeviceComponent‘), Detail_View_Control (‘DetailViewCntrlComponent‘) and finally Track_Control_Modes (‘EncModeSelectorComponent‘).

Then we have the Controls section, this leaves very little to guess due to the name: RingedEncoderElement. What is interesting here is that the dials use the same class as the buttons in the matrix: ButtonElement. As such we can try to “turn_on” a dial, which obviously isn’t going to work. However, thanks to previous experiments we now do know that “turn_off” isn’t used to influence the controls’ functionality in any way. Oh, just for completion: the relationship should be obvious: “Track_Control_0_Ring_Mode_Button” and “Track_Control_0” together with the device control section: “Device_Control_0_Ring_Mode_Button” and as expected: “Device_Control_0”.

Step 2: Identification

The ‘RingedEncoderElement’ with the associated ButtonElements (dials) leaves very little to guess. But when looking for the right component then it becomes a completely different ballgame. To start from top to bottom I decided to go look for the track control section first, but couldn’t find it. Both “Track_Control_Modes” as well as “Detail_View_Control” has a function “is_enabled” and “set_enabled”. However, enabling or disabling these sections seems to have no effect.

But when checking out the “Device_Control” component on the other hand we’ve hit the jackpot: the moment you use the function “set_enabled” with 0 as parameter then the device control section stops functioning in its entirety.

That is… The buttons below (the arrow buttons, device on/off, clip/track, etc.) all still work as normal. But the section with the dials and ring leds stops responding.

1 section; 2 components

I started experimenting a bit more, especially since its easy to check what happens when you use the “set_enabled” function. And that is how I discovered that the “Detail_View_Control” component is actually the second part of the Device control section on the APC40.

So… The dials and ring leds form one component called “Device_Control” whereas the two rows of buttons below that is another individual component which is called “Detailed_View_Control”. On the Ableton forum I learned some time ago that the scripts to control all this were programmed before Max for Live came into play, and I think this small inconsistency (device control vs. detailed view control) is some proof of that.

So what about the track control section ?

Well, here is where it gets more awkward…

If you scroll up you’ll notice that the only component left is “Track_Control_Modes”. And trial & error learns me that this component makes up the 4 buttons below the dials of the track control section.

But what about those dials and ring leds themselves?

I’ve done quite some experimenting but didn’t manage to control that particular section. In fact; it seems very plausible that this section has no component assigned to it at all…


Right now I think that the only section of dials and ring leds which we can actually control is the device control section. It consists of 2 components which can be completely turned off. My theory is that this is because of the relationship between tracks and devices. The minimum amount of tracks in a Live set is 1, not counting the master track or any optional return tracks. But the minimum amount of devices on a track is 0.

Or in other words: you can have a track with 0 devices, but you can’t have a Live set with 0 tracks.

I think this is why the track control section was made dedicated. You will always have a pan control in your live set, thus you can’t turn this control off.

I know this also creates some illogical issues… After all; you can turn off all track strip components. The only explanation for that which I can think of is the previously mentioned warning: the software which controls both the track control as well as the device control was created before Max for Live existed. Thus wasn’t made with access in mind.

Step 3: Controlling the leds

Controlling the leds themselves isn’t all that difficult. After all; once you established the right path all it takes is a mere ‘send_value’. There is a little more to this; using the techniques I described earlier I discovered that the method ‘send_value’ requires 2 parameters; the value and an indicator if this is to be send out immediately or not.

So to control the leds all you’d have to do is to send a value of, say, 64 into it followed by a boolean, something like this:

Controlling the APC40 ringled
Patch controlling the ring led.

If you use this patch you’ll notice that the led ring around the first device control dial will have changed.

However, there are several problems which needs to get sort out first before we can really use this approach.

First the led display itself; if you try moving the knob you’ll notice that it only shows one led, two when its shifting position.

However; there are several ways a value can be displayed: one where a ring led gets “filled”, one where you have a pan display (left / right get filled), the on / off display (only the left or right most led is lit) and the value as it is displayed now. So; there are several modes which can be set.

Another problem is that the leds will continue to update. Easily shown after you used this patch; try switching to a track which has some devices sitting on its device chain. Chances are high the leds now display different values. And when you switch back the previously set value is lost.

The first idea was to disable the device control “component” so that it would stop updating. While that approach works it does more than that: it also stops you from being able to control the values of the ring leds. So this isn’t a usable approach.

An extension to this problem is that it isn’t enough to simply disable the control parts on the APC40; even switching tracks using the keyboard or mouse will result in the led display to get changed.

My current findings

And this is where I am right now. These are some facts I discovered through trial & error and right now I’m still working on getting more information. My next hacking session is going to be tomorrow evening (or the day after on Friday evening):

  • Disabling the device control ‘component’ disables everything; the leds don’t respond to different devices (aren’t updated) but they also don’t respond to ‘send_value’ function calls.
  • Both the knob and the ringled controls are linked. Using ‘send_value’ on either one results in the ring led getting updated. However; the position heavily differs from time to time, most likely because of the current position of the knob.
  • There are several hints which suggest that you can break the link between the knob and ringled as well as the connection between “Live” and the APC40 (put differently: prevent the device control section to receive and process updates on device settings, thus effectively turning off the led controls).
  • There is a possibility that the same routine which might be usable to set the right modes for the led display can also be used to “free” the track controls. But this is a very wild guess right now, most likely one which isn’t true.
  • It is possible that the update behaviour cannot be blocked. I’m not sure yet, but I think it needs to be kept in mind.

And there you have it so far.

As said; work in progress but to make sure you guys realize I’m still hard on the case I decided to sent out part 1 first.