ADC filtering code



I’ve had a look at the adc filtering scheme used in mutable instruments:


for (uint8_t i = 0; i < 8; ++i)
int16_t value = adc.Read8(i);
int16_t delta = value - pot_values[i];
if (delta < 0) {
delta = -delta;
if (delta > 32) {
pot_values[i] = value;
switch (i) {

kAdcThreshold = 1 << (16 - 10); // 10 bits

for (uint8_t i = 0; i <= kFrameAdcChannel; ++i)
int32_t value = (31 * adc_filtered_value_[i] + adc_.value(i)) >> 5;
adc_filtered_value_[i] = value;
int32_t current_value = static_cast<int32_t>(adc_value_[i]);
if (value >= current_value + kAdcThreshold ||
value <= current_value - kAdcThreshold)
queue_.AddEvent(CONTROL_POT, i, value);
adc_value_[i] = value;

//DSP board
uint8_t* history = cv_history_ + cv_ptr_;
for (uint8_t i = 0; i < CV_LAST; ++i)
cv_sum_[i] -= *history;
*history = cv_[i];
cv_sum_[i] += cv_[i];
history += 16;
cv_ptr_ = (cv_ptr_ + 1) & 15;

I can’t really get my head around it and i need something to clean up adc reading and avoid multiple reading and sending. There’s the idea of a threshold established between the last read value and the new one i managed to understand but it could be great to have a clearer explanation if someone could. In the meanwhile I’ll try it myself.



a tinny bump if anyone passes around here.


Hi fuzz, I’ll try some explanation, but can you maybe point out the exact lines that you don’t get?

Olivier is systematically using a one pole low-pass filter to filter out the noise coming from the ADCs: each time a new value is read (adc_value_), the actual value of the parameter (adc_filtered_value_) moves in the direction of the reading by an amount proportional to the difference between the last actual value and the new value read. If the proportionality factor is zero, then adc_filtered_value_ never moves and the reading is just ignored; if it is one, adc_filtered_value_ perfectly follows the reading; in between, it “lags behind”. On the upside, this filters out some of the noise from the reading; on the downside, the filtered value now takes some time to reach the reading, which can be noticed (“glide”).

One pole low-pass is very simple to implement but it’s fact not a very good filter for this purpose; now I use boxcar averaging on CV and pot readings, which responds much quicker than one-pole. I can send you my library if you need.

On top of this, you sometimes need a perfectly steady value when the pot is not moving (and allow some noise when the pot is in movement), so we add a “dead band”: to the reading (if reading didn’t move by as much, don’t touch the actual parameter). The analogy here is with backlash in a gear train. I asked the
very same question a while ago on Synth-DIY and “got some nice answers (see the whole thread)”:

Hope it helps!


Hmmm, yes, although in some use-cases, having some time-dependent hysteresis in the smoothed ADC value may be helpful, particularly where there might be noise in the input signal, not just noise from the ADC itself. When a pot is turned, there can be transient relaxation jitter when the knob is released and the wiper of the pot settles to a new position. In other cases, some low-pass filtering may be desirable, such as when moving between frames in Frames (e.g. see “this post on MW”:, or moving about the SOM in Grids. To get such filtering with a boxcar averager, wouldn’t you need to make the boxcar longer, in which case more memory is needed, and memory is in short supply on the MPUs used in the MI modules mentioned in the OP? So, what is best for a particular purpose depends on the purpose.


Boxcar filtering is doing a better job at eliminating impulsive outliers (common on the F4 ADCs), and a worse job at eliminating high frequencies. In terms of CPU it’s slightly more inefficient than a one-pole filter, and it uses RAM (a fast implementation would work like this: pick the last sample in a delay line and remove it from the running sum, add the new sample to the sum and shove it in the delay line).

I also experimented with median filtering on the F4 (along with other adaptative schemes - for example adjusting the filter coefficient depending on the error between the current value and the readout, to allow faster tracking of fast changes), and was surprised that none of them worked much better than 2-pole filtering, which I’m now using on F4 projects.

As for backlash/thresholding, I’m trying to stay away from it as much as possible. Any implementation (including the one mentioned in the thread above) has the downside that it might make the maximum/minimum value of the pot impossible to reach. At the very least, you have to scale the value by (1.0 + 2 * threshold) and offset it by -threshold, then clamp to [0, 1.0] (assuming that’s the range of your parameter) to make sure turning the pot to the minimum/maximum allows you to always reach the minimum and maximum settings. Note that pots already have mechanical backlash, and that their response curve already have a flat zone at the minimum and maximum.


seems like an older post - but about my topic.
i digdug the hardware/code of clouds a little. i see there is only filtering in the cvscaler::read function, dough seems to be enough…

i measured the values i get (int) from the adc - i get 0-65535 steps. for -5V to +5V input. with a noise starting from around ±10 in the small values to ±70 in the >50000 ranges.

is that noise level normal/ok for unfiltered values?

and is it normal that noiselevel rises with the value? ( i changed the psu to a swiching mode 3.3V reg, but i still have a seperate linear regulator for 3.3Vanalog).



Yes. The resolution of the converters is 12-bit so a small jump in noise will cause a jump of +/-16 in value.


ok - so nothing to worry about too much then.
i tried for a first to alter the delayLenght in the 1s delayline example from (i think was ) tom whitwell, the quasi “hello world” clouds.
and that of course that introduced mostly noise, not just when altering the delaylength, but also because the values were flippering constantly from the adc-noise. so playback randomly picks values from around that time… not to hifi. but i could guess a delay…

so then the 65535 returned by the adc.value(CHANNEL) function should be shifted 4 bits anyway? cause HW resolution is 12bits. or does it benefit somehow from the “long” ADC_SampleTime_480Cycles, or the like.
sry all the silly questions.


What would it change? The last 4 bits are 0 anyway.

The hardware resolution is 12-bit, but the ADC is set to return left-aligned values.

adc.value() returns the raw value from the DAC. You need to apply some filtering to get something cleaner. One-pole, running average, hysteresis…


i see there is the ADC_DataAlign_Left in the config. t.b.h. i didn’t check that data just be left aligned in the 16bits.

I tried out different filterings. i like the exp moving average. it’s pretty simple and seems to do a good job. i then decided to tune it a little, by introducing another exp-average of the difference from the read values. this diff i use to set the weight of the new incoming values. big diff - less filtering - faster response. and as the diff gets smaller - filtering increases.

i am happy with this. and so is the delay-line :slight_smile:
thanks for pointing things out!