Closed elanhickler closed 2 years ago
hmmm...ok...i see. so you want to use it as an envelope generator? i wonder what meaningful user parameters could be. the raw "increment" is certainly not convenient from a user's perspective. when something is called "bouncillator" i'd suppose the user parameters to something oscillator-like - like the frequency of oscillation...so maybe we need setSampleRate, setFrequency, etc...but for an envelope generator, i'd rather suppose things like setAttackTime, setDecayTime etc.
should it be some sort of mix between oscillator and envelope generator, like something that procudes a periodic succession of attack and decay curves attack-decay-attack-decay-....
i mean, the basic bouncillator actually produces a sort of asymmetric triangle/sawtooth waveform - basically, the "TriSaw" waveform for which we already have code. various shape could be introduced by just passing the TriSaw wavefrom through suitable waveshaper ...but maybe not just a simple waveshaper but one that applies a different shaping function to the upward and downward parts - it should be easy too keep track of where we are and just switch between two functions for attack and decay
my idea is to just start with TriSaw waveform: (you can imagine this to oscillate between 0..1, too). and then apply separate waveshaping curves to up and down sections, maybe functions of such a kind: for the sigmoid shape and just one half of them for the (pseudo) exp/anti-exp shape (and if such a pseudo exp is not good enough, we can also have a real exp - you can compare both shapes in the meta-mapper - rational is your rational function formula and there's also a true exp available)
You cannot use an oscillator because of the floor/ceiling paradigm. Envelope generator is a time-based/trigger based function with "speed" instead of "frequency". You can't use an oscillator paradigm because: what if the user modulates ceiling down as the envelope is rising up. You need to know when the signal hits the ceiling to then bounce off and go to decay phase. How would you simulate that with an oscillator? How would you accomplish attack/deay speed modulation or a sustain stage?
to modulate floor and ceiling, i would just take the osc-output, scale it with a factor and add a DC offset (formulas for the required factor and offset can be computed simply from desired floor and ceil). of course, the response to such a modulation would be different than here, but i would actually think, in a way that's preferable since the timing of hitting floor and ceil would be naturally decoupled from the values of floor and ceil....unless you find such a coupling desirable - but why would you? ...but even in this case, i think, it could be fixed by scaling the increments by ceil-floor
How would you accomplish attack/decay speed modulation or a sustain stage?
for the former, i would introduce a symmetry parameter that determines the ratio of the attack-length vs the decay-length (actually, this is the symmetry parameter of the trisaw). sustain....hmm...well....maybe by clipping the osc output - just cut off the upper and/or lower parts of the triangle wave, giving a sort of trapezoidal waveform
I'd want attack time or speed vs decay time or speed like an envelope, not an attack/decay ratio.
i just edited my former reply. i think, you could re-couple them by scaling oscillator increments by the difference ceil-floor - the father they are apart, the higher the increments
ah wait - you mean a coupling between attack/decay, not between them and floor/ceil - this can also be done by scaling the increments appropriately (maybe by 1/(attack+decay) or something)
so, it's mostly about different parametrizations, i.e. what is coupled vs what is decoupled. but i think, all such couplings/decouplings can also be achieved in the oscillator with a little bit of trickery
if you just want attack- and decay times instead of osc-frequency and attack/decay ratio, it's a simple transformation, something like:
1/freq = period = attack+decay some-formula-with-asymmetry-parameter = attack/decay -> solve for asymmetry parameter
It needs to be non-loopable as well like a normal ADSR. I need to modulate shape for example, go from expo to Lin on decay. The difference between oscillator and envelope is that with oscillator you know where you are based on phase. With envelope you don't know, like, you modulate parameters and you would not know when you will hit the floor or ceiling until you cross the threshold
Maybe it's ok to use a shapable oscillator. I am just concerned that modulation will not produce the desired effect.
Oops. Didn't mean to close. Another example of the problem, oh this may be THE problem with an oscillator. What if you change attack time while in decay stage? Wouldn't the decay stage be affected because you are recalculating the entire signal / shape?
hmmm...well...yeah - it's certainly a different parametrization, but as said, with some little tweaks and formulas, i would think, that we can translate one parameterization into another. i just don't know what exactly the desired effects are. ...i mean, for example, should the distance between floor and ceiling affect the timing or not? normally, in bouncillator, the farther they are apart, the longer it takes to go from one to another with a given speed/increment. ...may be a desirable effect - or not. depending on the application. but it should be straightforward to implement it one or the other way or both in the true osc.
What if you change attack time while in decay stage? Wouldn't the decay stage be affected because you are recalculating the entire signal / shape?
i think so, yes. hmm...but maybe i can find a tweak for that, too. how do you actually achieve non-looped envelopes in bouncillator? i mean, it repeats itself over and over again as well. in a sense, it actually is a sort of oscillator as well (that's why i named it the way i did)
the thing is, this whole "predict-when-to-hit-the-wall" leads to a horribly messy (and expensive to compute) formula in the case of a curved transition. and the curve isn't even very flexible yet
An oscillator might work........
Ok let's try an oscillator. As long as you can seamlessly switch between loopable and non loopable with it. And I need separate atk/Dec shapes and decay time with expo/log for the first iteration.
Edit: with curvature so you can go to lin-exp or lin-log. Lin-log-exp in one shaper is not necessary although it would be nice.
OH WAIT. Can you modulate frequency to 0 to induce sustain? Yes it should work. Sustain would be a matter of "how fast to go to 0hz" instead of exact sustain level. When you increase decay time, the curve down to 0hz needs to scale with decay time.
Then for release, modulate back to original frequency.
Hmm the problem with going to 0hz for sustain is that if the attack phase is near instant and you wanted a sustain level of 100% the signal would be in decay phase a bit before 0hz could be reached. The faster the attack phase the more unpredictable the sustain level.
wait wait wait. first of all, why does attack/decay time on bouncillator only update after bounce?
aaahh...i see - i use a variable "dx" (for delta-x) in the per-sample update equation and i switch dx to be inc or dec in the bounce but don't update dx in setIn/Decrement. i will fix that tomorrow
Can you modulate frequency to 0 to induce sustain?
well, that would assume that we have a frequency parameter in the first place. in an oscillator, i'd probably give the user a frequency and a shape parameter - but these can be translated to attack and decay parameters which seem to be what you want. so - without introducing redundant parameters - i'd either provide frequency-and-shape (attack/decay ratio) or attack-and-decay. but i could certainly introduce some kind of redundant overall speed-scaler variable (proportional to an osc frequency) along with attack and decay which you could then set to zero to stop the osc. such that the actual attack and decay time would the reciprocal of that scaler times the dialed in values....and if that scaler is zero, the osc halts
....hmmm....but if we do that, i wonder if i could support negative values for that, too
Definitely fix the increment/decrement not updating, I think we are not done exploring this.
TriSaw parameters:
yes - i've already started with this, just need to fill in a few things. i'm currently exploring some functions that could be useful for the shapes. i used your one-parametric rational mapping function as starting point but i want to introduce a second parameter that somehow controls the s-shape. ...it's a bit of exploration and trial-and-error...but i have a good candidate already....need to walk through some math to get a nice and convenient parametrization. i want to make it seamless
...maybe something like this: https://www.desmos.com/calculator/lhk7brjm9h with b=0, it is the well-known bilinear rational mapping with "a" being the curvature parameter. ...i'm not yet happy with the behavior of "b"....needs more experimentation (ideally, i'd like it to range between -1..+1 and introduce s-shape for b>1 and spike-shape for b<1 - and also work in a reasonable way together with a)
this seems nice: https://www.desmos.com/calculator/tsjvtyrcoh "a" controls curve (as usual) and "s" controls s-ness - with a=0, s=1 it could produce a waveform that looks quite sinusoidal. together with clipping, we could also make rectangular waves (turn the triangle into rectangle by driving a clipper that sits before the shaper - with asymmetric triangles, we may also control pulse-width). ...soo looks like we'll be getting an osc that can produce saw/triangle/pulse/sine and anything in between
ok, there's a new TriSawOscillator module available in ToolChain (under Sources) that can be used to experiment with the various parameters. it's currently working with the parametrization suitable for an osc (frequency and asymmetry) (set the frequency by hitting a midi note). that's good for experimenting. i think, i'll do the re-parametrization via attack/decay times in a subclass, so we can use the baseclass as regular osc. it can even do pulsewave without any further ado like introducing clippers - by setting both bending parameters to full scale (1 or -1) and use asymmetry for pulse-width. ...but maybe with an additional clipper, we can do even more useful shapes...dunno....but such a thing could be useful for hold/sustain stuff...
Ok, we need attack/decay time parameters for TriSaw and fix the Bouncillator update problem and you are done with Envelope-related stuff for now. I need to experiment now.
What I am beginning to think about is to create envelope SPECIFICALLY for a use case rather than, with bouncillator or TriSaw, trying to fulfill everything in one. Example: I may want your help creating a "pluck/guitar" envelope where the attack is expo, decay is expo then transitions to linear, where parameters would be:
PLUCK ENVELOPE
REED ENVELOPE
Then we can really hone in, finely craft the parameters and equations, come up with new ways to create really nice envelopes that are smooth, organic, and responsive.
By modulating decay speed by hand I was able to achieve this desired shape, which I want for my Pluck envelope:
The next step for Bouncillator is the get expo attack expo decay. Right now it's expo attack log decay or vice versa.
OH LOOK!
I just registered a feedback modulation to the modulation manager and by messing with limits of the feedback modulation I am getting a number of useful curves on the decay. I also used negative feedback on the attack to create the exponential attack. The feedback source works much better than the Shape parameter, I mean... maybe it's just the clipping that is helping. Maybe soft clipping would be even more interesting.
Here's another example, these are really good pluck envelopes and could be used to emulate vactrol voltage curves.
feedback applied to shape parameter:
To parameterize this I might add these controls:
Feedback to Attack (-1 to +1) Floor clipping feedback to attack (0.000001 to 1 exponential) Ceiling clipping feedback to attack (0.000001 to 1 exponential) Feedback to Decay (-1 to +1) Floor clipping feedback to decay (0.000001 to 1 exponential) Ceiling clipping feedback to decay (0.000001 to 1 exponential)
Definitely fix the increment/decrement not updating, I think we are not done exploring this.
fixed already yesterday. should work now.
TriSaw parameters:
attack time decay time full envelope time scaler
there's now a class rsTriSawModulator in rosic that is set up in terms of these parameters. i also has adjustable floor/ceil
(in response to my own post) No this won't work. The feedback modulation suffers from "samplerate pitch locking". I might as well use a less complex Attack/Decay design (not Bouncillator) for this unless you put more work into Bouncillator (put limits on infinities and decouple shape and timing).
Can you enable those things in Toolchain so I can test quickly?
do you mean, i should add the TriSawModulator as modulation source in toolchain?...because for the TriSawOscillator it wouldn't make sense to replace frequency/asymmetry parameters with attack/decay. you also wouldn't be able to do feedback modulation in this case
sure, can you do that now? Otherwise I will make a plugin.
ok, i'll do it
done. it's under Modulators. there seems to be bug with the TimeScale parameter...oh - and the shape params still missing...
uhhhhhh I need the output, I'm not using it as a modulator yet.
ok im getting output by modulating DebugAudioModule. All good! Yes except for time scale.
TimeScale is fixed and i'm having some fun with feedback modulation already! :-) play some low notes!
<?xml version="1.0" encoding="UTF-8"?>
<ToolChain PatchFormat="1" ActiveSlot="1">
<Slot Type="TriSawOscillator">
<Slot1-TriSawOscillator PatchFormat="1" AttackSigmoid="0.5" DecaySigmoid="0.585227"/>
</Slot>
<Slot Type="Limiter">
<Slot2-Limiter PatchFormat="1" OutLevel="-7"/>
</Slot>
<Slot Type="TriSawModulator">
<Slot3-TriSawModulator PatchFormat="1" Attack="53.367" Decay="519.887" TimeScale="2.43424"/>
</Slot>
<Slot Type="None">
<Slot4-None PatchFormat="1"/>
</Slot>
<Modulations>
<Connection Source="Slot3-TriSawModulator" Target="Slot1-TriSawOscillator.Asymmetry"
Depth="0.66964285714285787243" DepthMin="-1" DepthMax="1" Mode="Absolute"/>
<Connection Source="Slot3-TriSawModulator" Target="Slot3-TriSawModulator.Floor"
Depth="0.2946428571428572063" DepthMin="-1" DepthMax="1" Mode="Absolute"/>
<Connection Source="Slot3-TriSawModulator" Target="Slot3-TriSawModulator.Ceiling"
Depth="0.40178571428571463464" DepthMin="-1" DepthMax="1" Mode="Absolute"/>
<Connection Source="Slot3-TriSawModulator" Target="Slot3-TriSawModulator.TimeScale"
Depth="-0.72321428571428603149" DepthMin="-1" DepthMax="1" Mode="Absolute"/>
</Modulations>
</ToolChain>
...will add missing shape parameters now
shape parameters added (took so long to upload because i was distracted by playing with the new toys)
good. what are you playing with? that same patch above?
no, i was experimenting with the new shape parameters
no longer relevant and will revisit topic elsewhere.
Here's the previous issue with formulas and such: https://github.com/RobinSchmidt/RS-MET/issues/80
I created a plugin for this, see:
Soundemote\AudioPlugins\Envelope Submodule
, compile for your testing.Could you work on this when you have some spare minutes? I'll pay for this as well. Start with these, these should be easy:
Need a "setSampleRate" function so that when I increase oversampling, the frequency remains relatively the same. It might not remain perfectly the same, that's ok.
Can you make it so that when increment/decrement speed is changed, the update happens right away? Currently, updates only happen after a reflection.
When Floor/Ceiling are equal, we get infinite hang.
OR do you have a new idea for a bouncillator and want to start over? Let me restate the goals just in case: