Pole Mixing and Mutable Instruments Ripples (2020) clone

I’ve been studying the excellent MI Ripples clone which offers LP2, LP4, BP2 and LP4>VCA outputs. My goals are:

  • learn more about filter design / schematics
  • work out how to generate other response outputs
  • build an un-official Ripples v2 clone with HP2

I am not an expert in circuit design or simulation, but aspire to become more knowledgeable!

A number of resources describe how you can employ pole mixing in order to generate novel outputs:

Existing Ripples clone understanding

The existing clone, by @AlrightDevices uses an RK2 integrator to solve the ODEs of a series of 4 low pass cells, including resonant feedback path from the final back to the first. Source code here.

        // The 2164's input terminal is a virtual ground, so we can model the
        // vca-integrator cell like so:
        //                            ___
        //               ┌───────────┤___├───────────┐
        //               │             R             │
        //               │                  ┌───┤├───┤
        //               │           A*ix   │    C   │
        //         ___   │          ┌───┐   │ │╲     │
        //  vin ──┤___├──┤        ┌─┤ → ├───┴─┤-╲____├── vout
        //          R    │ ↓ix    │ └───┘   ┌─┤+╱
        //               ╧        ╧         ╧ │╱
        //
        //  We can see that:
        //    ix = (vin + vout) / R
        //    A*ix = -C * dvout/dt
        //  Thus,
        //    dvout/dt = -A/(RC) * (vin + vout)

Bandpass 4 pole (Warmup)

Bandpass 4 pole Summary

As a warmup, I can modify to output say BP4, by using the coefficients in Emilie’s pole mixing document.

float bp4 = (lp2 + 2*lp3 + lp4) * kBP2Gain;

This works well, and I get good agreement with other BP4 models.

So far so good!

Highpass 2 pole

Highpass 2 pole

First I’ve taken a bunch of measurements of my Ripples v2 (ignore the notch at ~12k, that’s just ES8 doing something weird). It’s a little hard to tell, but whilst there isn’t a constant roll-off slope wrt resonance (this is documented in the manual), there is good attentuation at low frequencies.

Description Image
Resonance 0
Resonance 9 o’clock
Resonance noon
Resonance self osc onset (~2 oclock)

Approach 1:

According to electric druid page, you can get HP2 by the following formula:

Response name {Input, 1-pole, 2-pole, 3-pole, 4-pole}

12dB highpass {1, 2, 1, 0, 0}

The Ripples v2 schematic actually shows something very similar

where (I think!) input is mixed with 2x LP1 cell + LP2 cell (here FILTER_IN is the attenutated input as it enters the 4 LP “cells”). I think the (inverting) mixer is combined with an active LP filter that doesn’t appear to be relevant based on cap and resistor sizes.

In code this looks like:

float hp2 = (inputs[0]*kFilterInputGain + 2*lp1 + lp2);

It produces something sensible at zero resonance, but looks less convincing at higher resosances. It does self-oscillate eventually though.

I am curious as to why this doesn’t work, given that the schematic shows something very similar. Specifically by “doesn’t work”, comparing measured and simulated with resonance at noon.

Approach 2 (not advised!)

Quoting the pinchettes pole mixing doc

A possible solution is to add a switch in the circuit to bypass the first low-pass cell (or more accurately use a lower integration cap to provide a very high cutoff-frequency). Using this approach, transfer functions of the form:

Screenshot 2025-05-26 at 15.10.01

can also be implemented using the same circuit and the same design equations, with the only change that the first inverting 1-pole cell has been bypassed and replaced by a simple inverting cell.

I can manually set the first cell cap value to a very small value to approximate a short in the circuit, meaning the first cell is very close to a pure inverting cell. This is a bad idea though because solving dvout/dt = -A/(RC) * (vin + vout) with tiny C is a very stiff ODE, but as an experiment it is interesting as we at least can observe close to the behaviour expected (except no self-oscillation at lower frequencies < 5k).

Approach 3 (in progress!)

I next will try to explicitly replace the 1st LP cell with a pure inverter stage - my initial attempts to do this (i.e. inject -inputs[0] into the second cell) failed, so I might need to “un-roll/un-simd” the RK2 integrator to properly understand what is happening.

Conclusions/Questions

  • It’s pretty straightforward to generate a subset of filter responses where all 4 LP cells are “enabled”, e.g. BP4
    • Reponses where the First LP stage must be disabled are proving harder.
  • I don’t know why an approach similar to the schematic (Approach 1) doesn’t exhibit the desired roll off behaviour
  • Am I doing anything else stupid / missing an obvious solution to this?

I will continue and update this post but any help from those more knowledgable very welcome!

2 Likes

This might help: VCV Library - Squinky Labs Stairway / That filter has one, two, three, and four pole options. They are explained in the manual, and the manual also gives descriptions and links about how to do that. Lastly, of course, the source code for the module is freely available.

2 Likes

Yes I used Stairway to sanity check my BP4 implementation, thank you. And I reference Emilie’s pole mixing paper which you also follow.

I think actually you do something quite similar to my suggested approach 2, which i may have hastily written off. But instead of just a large constant (very small cap value), you have the first stage set to possibly 0.9*Nyquist (i say possibly because its a little hard to follow logic through the LUTs), which integrator should be able to handle, nice! Will report back :slight_smile:

ah, I had forgotten about that “bypass first stage”. But I’m pretty sure the rest of it is just mixing the outputs of the four integrators?

Yes exactly. I found it straightforward to mix the integrators when responses don’t need the first stage disabled, I’m trying to work out the trick for disabling the first stage, and your repo is helping with that.

That said, Emilie’s hardware ripples V2 design allows simultaneous HP2 and LP2/4 outputs so she is achieving this pole mixing high pass without disabling a stage.

Please, take everything I say with a grain of salt, my knowledge about these things is pretty vage.

I think you are on the right path with this one, but you should take the voltage for the filter core in[0] as first stage, not inputs[0]*kFilterInputGain. I tried it and it looks close und sounds pretty good…

3 Likes

Ah yes I think you’ve got it, looks promising here too - thank you! And a hint was there all along, because it matched well when resonance was zero.

I’ll update post and code when I get some time :slight_smile:

Getting there, just need to incorporate the diode overdrive/clipping on input 1 :slight_smile:

3 Likes

Little update:

Good parts: Highpass is working well, at low resonances

Approximate soft clipping for input 1 is looking OK

Four pole band-pass available:

However highpass at higher resonances is not at all accurate, looks like it should “dampen” more rapidly somehow. Definitely getting well beyond my expertise for now, so putting it out as a flawed clone/homage. :slight_smile:

Build here: Releases · hemmer/AudibleInstruments · GitHub

1 Like

I don’t know if it fixes it, but you should take a look at the resonance control: it has a different values in V2.

V2

V1

float kResAmpR_ripples2 = 51000.f;
float kResKnobR_ripples2 = 51000.f;
float kResKnobV_ripples2 = 10.f;

// Calculate resonance control current
float i_reso = VtoIConverter(kResAmpR_ripples2, frame.res_cv, kResInputR,
    frame.res_knob * kResKnobV_ripples2, kResKnobR_ripples2);
3 Likes

Oh nice, thanks for this! I’ve updated this, but I think all it is really doing is changing slightly how the resonance knob sets i_reso (i.e. to what degree does a knob setting open the OTA in the feedback path), but the actually resonance characteristics remain the same (I think!). However it has inspired me to start going through component by component, and more crucially the resistors/caps around the OTA have also changed, the highpass in the feedback loop is slightly different etc etc. It’s a great learning excercise trying to decipher these schematics so I will continue to take a look!

4 Likes

@hemmer if you really want to go off the rails with this you could try just outputting the individual filter poles, or have a mix input for them. That’s the approach I took with the Pole Dancer analog hardware filter. Then I’ve a second module that defines the mix levels for various filter modes. That does a couple things: you’re not limited to only a couple filter types, and you can do crossfade/morphing between filter modes. VCV Rack is an ideal environment for this type of thing, as it’d be difficult to do something like this in physical eurorack. You get the full flexibility of using other Rack modules to define the crossfade, and basically unlimited module instantiations for filter types. I posted about it awhile back here: Zoxnoxious analog synth VCV Rack demos - #58 by brer_rabbit

2 Likes

Hey, that’s a fun idea, maybe an expander exposing them would be the way to go?

In terms of progress, the main issue I’m having is with the Q gain compensation.

As I understand it, in both the original and updated Ripples circuits, the OTA in the resonance/feedback loop is effectively mixing some of the original (highpassed) filter input to the resonance signal. Certainly zeroing out the input to the positive terminal makes things worse, so it is helping, but it doesn’t seem quite right. I’m a bit confused because the modelling looks like it explictly tries to capture this effect, but I see a fair drop in the passband. Does anyone with original Ripples hardware see this passband drop?

In my 2020 hardware unit I see very little drop with increasing resonance, and before I try to get the 2020 version working for VCV, I want to establish behaviour for OG Ripples.

If I seem fastidious about this, it’s only because I am curious to learn about these circuits and this type of modelling, not to criticize the implementation. And as usual, just thinking out loud and blogging progress in this thread :slight_smile:

ps. I’m aware you could compensate for this drop with lookup tables etc (as in Squinky’s Stairway), but given the required effect appears to be already in the modelling I’m keen to get it to naturally drop out of the simulation.

1 Like