ADC filtering code
  • Hi,

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

    //GRIDS

    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) {

    //FRAMES 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.

    thanks.

  • 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.

    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.