ADSR with per-stage ring mod?

If you want to try out a prototype that’d be 110% cool with me, just if you’re gonna sell the finished work I ask that you toss me a free copy :wink:

1 Like

Sweet! I think this could be an excellent free module and will share the prototype with you first when (hopefully not if) I have something worked up, then we can discuss open development/release options and hopefully get something out into the world!

Did some thinking, and I think there’s a fundamental question about the operation we’ve failed to consider yet.

Say you put a sine as the modulator on the attack stage, does that sine get added directly or get multiplied with that stage?

image

That is, should the amplitude of the modulator be controlled by the amplitude of that stage (green) or should the modulator be added directly (blue)

Something else to consider is what other outputs would be useful? Having the raw ADSR out is obvious, but I think it may make sense to have beginning/end of stage outputs too.

Another thing, should the ADR times be able to be modulated, or does that add too much complexity? What about the ADR slopes (lin/log/exp)?

Obviously, there’s a point of balance between features, usability, and ability to be programmed. I’m not interested in something as complex as shape master for this, and I think it’d be easy to accidently end up making something like the nysthi bezier module instead of the actual goal.

All great questions, and I agree that “oops, we made a bezier” is a potential pitfall!

Off the cuff thoughts:

As to + vs *, probably switchable per stage (maybe even crossfadeable, if that proves interesting?) Some sort of bias control would also be useful for *, since literal multiplication by an unbiased bipolar signal is going to drop the envelope to zero every time there’s a zero-crossing. Half- or full-wave rectification could also come in handy. Since the multi-stage modulation (and smooth transitions thereof) is the quiddity of the plugin, I think lots of options to massage modulators and play well with various inputs is a good thing.

Outputs are so easy to code up and so useful that I think we shouldn’t stint on them. If we want to save HP we can always put EOS triggers, unmodulated outs, etc. on an output expander (pretty common place for them).

In terms of ADR time and S level, maybe along the lines of Fundamental VCA, knobs and CV only? The complexity here is the modulation, and a simpler envelope model might help bring that out.

I think the underlying ADSR should probably start simple, by which I guess I mean linear. My mind’s ear isn’t finding cases where modulation with exp or log would be fundamentally more interesting than modulation with lin (and arguably since exp or log spend more time near extrema than lin does, they’re closer to just applying the modulated signal by itself). Besides, a fancier version or tacked-on options can always come later.

I’ll add a question: I’m assuming we want this to be polyphonic in terms of input gates to output envelopes. If the modulators are monophonic, the ups and downs would be sync’d across all voices, so they would be wobbling in unison for a given stage (I’m imagining the simple case of an ADSR driving a VCA here).

Poly modulation inputs would be trivial to do, and we would of course apply a single-channel mod to all channels of the envelope if that’s all we got.

But getting really wild poly results with unsync’d modulators would require feeding a whole bunch of inputs in a useful configuration. We should assess whether existing poly LFOs are suitable for interesting results.

I can imagine trickery with delays (or a weird application of allpass filters, kind of like the “dome filter” in Bode shifters) to turn one LFO into a bunch for a poly context.

I can also imagine a companion LFO module designed to produce poly LFOs with useful characteristics for this ADSR!

This is turning into an open development thread, so anyone else with thoughts or requests should pile on…

1 Like

re: + vs *, yeah, that sonuds like the right way to go. I’m not sure about crossfade, but having it be per-stage sounds like a good thing, as does rectification. Rectification is cheap anyway, so that shouldn’t be a problem.

I think you’re right, expanders for EOS++ is the way to go. That would let us make something simple then add on as necessary- even the lin/log/exp could be done via expander if it does prove interesting.

As for poly, while your ideas are good - especially pointing out the sync’d modulation - I don’t know if we’d want to deal with the complexity of it at first. It might be easier to just make a mono prototype and then add poly after the fact, that way we can actually optimize for performance in one shot instead of rethinking everything about poly with each change.

I think if we added poly I’d want it to have ADR time and S level control inputs though, that way different envelopes could actually have different times. This would be particularly interesting for feedback-ish patching where one shorter envelope is used as the modulator on a longer envelope’s stage.

Going back to the + vs *, should the envelope’s output be able to drop below 0v? I don’t see any reason it shouldn’t? I’d like to provide an inverted output too, so that would then be able to go above 0v

Had to take a leave of ADSR absence to get this out but pondering your points now and might start coding this weekend :slight_smile:

Edit:

  • Agree on mono prototype, let’s iron things out in the simpler case, make sure it’s interesting enough to proceed (pretty sure it will be!), and then add those options…
  • Interesting poly patching case, definitely
  • Inverted output sounds good. I think some downstream stuff might act surprising if envelopes go below 0V, so I wonder if an output rectification option might be a good idea too, as that would play well with modulators as the envelope reaches its lowest level (and the UI and block diagram has rectifiers in it for inputs anyway, so we’re not adding a new concept).
1 Like

Sounds good.

Thinking about checking the ‘equality’ for moving to the next stage, I don’t actually know what bit-depth things in rack are internally? From work I did earlier this semester with steganography I know with 24 bit audio the 8 least significant bits can all be changed with basically no audible artifacts, so maybe we would just check if the first two bytes are equal? If we assume we can do at least a tiny bit of interpolation (and that it doesn’t kill performance) I imagine we could get away with a lot more variance

1 Like

Excellent!

Rack runs on 32-bit floats internally; the typical approach is to establish an epsilon value and treat them as equal if the abs() of their difference is <= epsilon. There are other issues but since typical values in Rack are by convention reasonably well distributed over 0±10 I think we’ll probably be OK with a simple approach. (Any approach will be super-cheap computationally).

Suggest we start with epsilon settable from the right-click menu for development and if we find a value that seems objectively robust in application (i.e. it avoids audible glitches/unpleasant slew swoops but is willing to move from stage to stage) we probably just hard-code it (it’s not the kind of parameter you’d typically expect a user to tweak). I can also imagine a ramping approach where the epsilon automatically broadens if the inter-stage waiting period has gone on long enough.

1 Like

The ramping approach does seem like a really good idea, I hadn’t considered that- It’s entertaining to think about it as the module getting impatient.

But, yeah, I think start simple, just do a manual binary search to see if we can find one good epsilon value and force it to transition regardless of pops and clicks- that way we can hear how bad it might actually be.

1 Like

@gc3 have you started this at all yet? I got busy and this sorta fell of my radar for a bit… oops ._.

haha me too. Other stuff + pushing the keyboard patcher along has held this off for me too. Still very interested and had a few design thoughts here and there; will collect those & start coding once TapPatch is in alpha :slight_smile:

Cool :+1: TapPatch is pretty awesome too

1 Like

Thx @Vega! Love to have you in the alpha or beta round if you’re interested–no time commitment required, just play around with it and share thoughts :slight_smile:

Am looking forward to getting on to this, though. TapPatch is already enough code that refactoring is getting a bit tedious…

Sure, I’m interested in the beta

Yeah, sometimes it helps to write code for more than one thing at once anyway- helps think in multiple ways and not get stuck in a rut, maybe find a new way to approach a bug.

1 Like

I’ve started work on my own collection of modules, learning as I go. Sulafat and γ Lyrae are done, a multi-mode wavefolder and ring-mod with inverted output and offset respectively. Vega (the module, not me) will be this thread’s module.

I was thinking about how to do the switching between stages, and while I may still have to do some interpolation, through-0 detection, or other wizzardry, I think I’m going to see if a simple crossfade of the modulation signals works first.

2 Likes

Would a slew applied on the change not work? Probably won’t be desirable in many circumstances but it could be used only if the difference is more than x and have a slower rate the more difference in voltage there is? I don’t know if that’s even possible though.

it’s possible, and I considered it. The problem is that difference itself is also time varying, and with audio rate modulation makes significantly less sense

1 Like

This has no morphing of stages, not extra modes, no lin/exp controls, etc. - still very early stages but it does work. I’m absolutely going to have to redo the panel a bit as the attenuation knob so close to the input is highly frustrating and a few extra controls are going to be necessary - namely offset and attenuation controls for the global ring input

2 Likes

Nice work! Sorry I had to bail on helping out with this, but you seem to have it well in hand, and I look forward to trying it out as soon as it’s released! It’s a great idea.

Added a few hp, but it’s significantly less cramped, plus I added some indicator lights.

For some reason the global ring attenuation and offset knobs really don’t want to show up. Not sure what’s going on there, but also running out of time and energy to work on this tonight. Here’s the code rn, https://github.com/VegaDeftwing/LyraeModules/tree/VegaADSR it’s a real mess, but I wanted to hammer it out then refine as I go

2 Likes