Skip to content

Map a Controller

Cc() turns a controller’s CC messages into a signal from 0 to 1, which you can send into any parameter. Cc and Cc_learn are in the signals reference. The controller needs an input port — see Play Through from a Keyboard.

Give the CC number. Add a channel (1–16) when two controllers send the same number:

use "std/signals" { Cc };
let cutoff = Cc(74); // CC 74 on any channel
let wheel = Cc(1, 1); // CC 1 on channel 1 only

Every input port feeds Cc(), whether or not a track uses it.

Run Cc_learn() and move the knob within ten seconds:

use "std/signals" { Cc_learn };
let knob = Cc_learn();
// Waiting for CC input... (10s timeout)
// Learned: Cc(74, 1)

It returns a signal bound to that knob and prints the Cc(...) call for it. Replace Cc_learn() with that call so the script doesn’t stop to learn each time. With no CC in ten seconds, it is an error.

Smooth the steps, scale to the parameter’s range, and send it with <<:

use "std/instruments" { Sampler, Kit };
use "std/effects" { Lowpass, Delay };
use "std/signals" { Cc };
let drums = AudioTrack("drums");
drums.set_instrument(Sampler(Kit("CR-78")));
drums << [bd sd bd sd];
let filter = Lowpass("lowpass", 2000);
let delay = Delay("delay", 0.25, 0.5);
drums.set_effects(#[filter, delay]);
filter.parameter("Cutoff") << Cc(74).smooth(50).range_exp(200, 8000);
delay.parameter("Feedback") << Cc(71).smooth(30).range(0.1, 0.6);
drums.volume << Cc(7).range(-60, 0);
  • .smooth(ms) removes the zipper noise of a CC’s 128 steps. How much to smooth depends on the parameter; see Step a Parameter Through Values.
  • .range_exp() suits frequencies, where the ear hears ratios; .range() suits linear values.

A few CC numbers are near-universal: 1 mod wheel, 7 volume, 10 pan, 11 expression, 64 sustain, 71 resonance, 74 cutoff.