# Reconstructing the Ableton velocity compand curve in Max for Live

Posted on January 3, 2021

The Velocity effect in Ableton Live allows us to apply some transformations to the velocity of incoming MIDI notes. In this brief blog post we will look at how we might recreate this velocity curve; specifically, the `Comp` part of the curve. The goal won’t necessarily be to recreate the curve exactly, but rather to have something that functions similarly.

# Companding

`Comp` stands for companding, which is a combination of two words compression and expanding. It is a standard algorithm in signal processing. There are many algorithms for doing this, but for our purposes a suitable choice is μ-law companding.

## Compression

Compression happens when we concentrate the range of values near the two extremes (very loud or very soft), rather than near the middle. In the Velocity effect, this is presented by a positive value of the `Comp` parameter, but we will focus on the standard formulas first before relating it back to the `Comp` parameter.

The formula for μ-law compression is pretty straight-forward. The intuition is that to compress a signal `x`, we take the logarithm in base μ (`log`μ`(x)`); to expand it again, we compute `μ`x. The most important difference between this and the actual formulas is that they add some twiddling to avoid artefacts near zero.

The standard formula for μ-law compression is or in `gnuplot` code:

``sgn(x) * log(1 + mu * abs(x)) / log(1 + mu)``

(Note that `log` can be logarithm in any base; we’re effectively computing a logarithm in base `1 + mu` here.)

The parameter μ in the formula serves a similar function to the `Comp` knob: small values of μ compress the curve barely at all; large values compress it a lot. The following graphs shows the effect of this formula for different values of μ: The range of this graph is from `-1` to `+1` on both axis; see side note on the original use of this formula in signal processing below. However, if we squint a bit and think of this graph as representing a mapping from velocity values between `0` and `127` on both axis (the MIDI velocity range), observe the effect the compression has: most values will either up really loud or really soft, with little nuance in the middle.

Although larger values of μ do compress more, we have to increase μ quite quickly; in this graph we increased it by an order of magnitude at each step. We will map this back to a parameter in the range `0` to `1` (like the Velocity effect does) below.

### Side note: use in signal processing

If you think of the compression graph above in terms of what it does to a wave form, then small changes in the input signal near zero have a large effect on the output signal, but small changes the input signal near one only have a small effect on the output signal.

If we then represent this signal as, say, 8 bits samples, we are effectively using more bits to distinguish values near zero and fewer bits to distinguish values near one. This is useful, because the human ear is more sensitive to small changes in soft sounds than it is in small changes in loud sounds.

In this case, the translation is not done as an effect, but as an encoding technique. Compression is applied before the signal is encoded, and at the decoder side, expansion is applied before the signal is played back.

## Expansion

If compression concentrates velocity values near the extremes, expansion concretates velocity near the middle (neither very loud nor very soft). It’s what happens when we dial to the `Comp` knob in the Velocity effect to negative values.

The standard formula for μ-law expansion is or in `gnuplot` code:

``sgn(x) * (1.0/mu) * (((1.0 + mu) ** abs(x)) - 1.0)``

Here is a graph of what this function looks like for various values of μ: # Velocity curves

In order to translate the above formulas/graphs into something more similar to Ableton’s velocity curves, we need to do two things: we need to work in a range of `[0:127]` instead of `[-1:+1]`, and we need to translate from a parameter in the range `[0:1]` (the value of the `Comp` knob) to the `mu` parameter.

To map a value in the range `[0:127]` to `[-1:+1]`, we can just translate

``````in(x)  = -1 + (x / 127) * 2
out(y) = (y + 1) / 2 * 127``````

(Notice that `out` is the inverse of `in`: `out(in(x)) = x`). To define `mu` in terms of the `Comp` parameter (let’s call it `f`), we need to map a value between `[0:1]` to, say, `[0:100]`. One way we could do that is as follows:

``mu(f) = 10.0 ** (2 * f)``

## Compression

This means we end up with the following `gnuplot` code for compression

``out(sgn(in(x)) * log(1 + mu(f) * abs(in(x))) / log(1 + mu(f)))``

with corresponding graph ## Expansion

For expansion, we end up with the `gnuplot` code

``out(sgn(in(x)) * (1.0/mu(f)) * (((1.0 + mu(f)) ** abs(in(x))) - 1.0))``

and graph # Putting it all together

To turn all this into a Max for Live patcher, we can just implement this math in JavaScript (or in Max for Live itself, if you prefer), and set up an `itable` that can serve as a lookup table for velocity translations, and is updated by the JavaScript code every time the `Comp` parameter is changed.

The device itself can just consist of a `live.dial` to serve as a knob to change the `Comp` parameter, a `js` object to run the JavaScript code, and the `itable` itself: As for the JavaScript code itself, you can download velocity.js; just place it in the same directory as your device, and you should be good to go.