rampsmooth~ in gen

sono's icon

Hi forum,

I am looking for a rampsmooth~ implementation in gen~ (preferably code inside codebox).

What I want to do is to use buffer~ as a send/receive for audio from msp to gen~ by modulating 1 buffer sample. However, when reading this sample with either index~ in msp or peek within gen~, it needs interpolation equal to the vector size. Therefore, a rampsmooth~ with ramps set to the vector size works perfect. But how to manage this in gen~?

Note: I need linear interpolation, so no smoothing with a low pass filter or alike.

I can imagine it needs to be somehow similar to send~ and receive~ as they apply a vector size delay to tackle the same problem - I assume.

Find a patch attached to further explain my problem.

Thanks in advance!

Sono

PS I've searched the forum and found similar questions but no satisfying solutions

rampsmooth_in_gen_NOTWORKING.maxpat
Max Patch
stkr's icon

you seem a bit confused about what it will do for you.

Max Patch
Copy patch and select New From Clipboard in Max.

but anyway, here are the msp smoothers in gen~:

hope it helps.

Peter McCulloch's icon

Thanks, STKR for these. They will become snippets...

@Sono: you can think of ramp smooth in conceptual terms as doing a crossfade between the old value and the new. Every time the input changes, the crossfade restarts using the previous value as the base and the new value. This is easiest to see in a stepped signal, e.g. from SAH.

Maurizio Giri's icon
Max Patch
Copy patch and select New From Clipboard in Max.

Hi, unfortunately the gen~ rampsmooth doesn't work like the MSP rampsmooth~...

sono's icon

Thanks for the input. Indeed, unfortunately, as Maurizio already pointed out, the gen~ solution is not completely similar to rampsmooth~. Is there a way to know exactly what is inside rampsmooth?

stkr's icon

oops. it was quite a good guess though ;-)

Max Patch
Copy patch and select New From Clipboard in Max.

this one with switchable modes seems to work:

sono's icon

Great STKR, many thanks!

Ruslan's icon

Hi SONO, set @interp attribute of peek operator inside gen to linear.

Maurizio Giri's icon

Hi, sorry for the late reply.
I was looking for a gen~ rampsmooth too, and I found it is unexpectedly tricky to patch in gen...

@STKR your last version works with smooth signals like sine waves, but unfortunately not for signals with sudden jumps, like noise or square waves.

So I decided to program it in genexpr, and it seems to me it is very similar to the MSP rampsmooth~ (if not identical). Probably my code needs some optimization, but it does its job.

Max Patch
Copy patch and select New From Clipboard in Max.

Here it is:
`

stkr's icon

nice job maurizio!

my one i guess is more like the reaktor / supercollider 'slew' things.

it does seem strange that rampsmooth would be as expensive as that (4 memories) when slide~ just uses one.

i'm gonna start using your version anyway of course...

Peter McCulloch's icon

Slide~ only has to remember its previous value and know the direction of change so that it can choose the correct coefficient, so it's relatively stateless. The tradeoff is that it's a royal pain to calculate the amount of time it will take to actually arrive at its destination even for simple input like a pulse wave since it decays asymptotically towards 0. In BEAP's AHD envelope I have some code that demonstrates a cheat for calculating this (a Hal Chamberlin technique) by using overshoot and clipping.

Rampsmooth~, on the other hand, has to remember where it came from and where it's heading. The timing is, however, really simple to calculate. Gotta say, I'm still surprised how much of a pain it is to implement.

Maurizio Giri's icon

Yep, with rampsmooth you have to check if the input signal is changing or not, if it is changing you have to compare the current input signal with the previous OUTPUT signal to see if you have to go downward or upward, then divide the difference by the number of ramp steps and perform the first step.
If the signal is not changing you have to know how much is the increment you calculated the last time the input signal changed, how many steps you have already done, if you are using rampup or rampdown, AND what was the last output value (hence the 4 memory cells).
Also if the rampup or rampdown value changes while the algorithm is performing a ramp, you have to reset the counters, calculate the new step value and start a new ramp.
So, not at all trivial...

Peter McCulloch's icon

This made me wonder what's going on in a linear analog slew limiter. It looks like the output signal is fed back into the negative input on op-amp to convert log slew to linear. Not sure how this would work in DSP land...

cédric's icon

maurizio thank!! exactly what i need!!

Graham Wakefield's icon

BTW there's a Maurizio-inspired rampsmooth in the gen~.slide.maxpat example.

toothpaste's icon

Unfortunately Maurizio-inspired rampsmooth in the gen~slide.maxpat example doesn't work properly either.

Problem:
When the input is in the range of 0-1, the output sometimes escapes this range, and when it does, it keeps climbing forever to numbers like 892732808129083924098232....

I could try and debug the code but I really don't have any time for this at the moment.
Also, since it is in the 'official' examples folder, I kinda expect it to function as is and not require any fixing by me :)

Where do you think is the bug in the code? Or has someone already come up with an alternative rampsmooth code that functions properly?

Thanks in advance.

Maurizio Giri's icon

Hi Toothpaste, can you post a patch that shows the bug?

toothpaste's icon

I am afraid I can't. I can't consistently reproduce it. If i could, I could perhaps solve the problem with the code

Both the input and the rampup, rampdown values are modulated at signal rate by chaotic signals so it's difficult to tell at which state (and with which set of coefficients) this happens. It looks like the counter keeps counting up although the new value has already been reached. (god only knows what that value may be...)

All I can say is that this happens every each time after running any patch that uses this rampsmooth, for some time. And I am sure rampsmooth is the culprit.

Maurizio Giri's icon

Can you check this "unoptimized" version and see if the bug is still present?

Max Patch
Copy patch and select New From Clipboard in Max.

toothpaste's icon

I sure can when i have time.
Could you please tell me though where you think the problem is with the optimized code? Or do you just want me to randomly test this one for no reason? If you could tell me the possible cause of the problem i can at least help brainstorm for a solution
The problem is that I am not really comfortable with the codebox syntax

Maurizio Giri's icon

I'm not sure, but I noticed that the unoptimized code gives a slightly different output which is practically identical to the MSP rampsmooth~ output (if you subtract the two outputs you get 0)

Graham Wakefield's icon

Hi all,

I have no idea what the problem with the gen~ example might be, it seems to work find with my testing here. Maybe something happens because that ramp restarts if the up or down parameters change? Not sure if rampsmooth~ does that. But it's easy to remove from the gen~ example if so (just remove the || delta(rampup) II delta(rampdown) from the codebox.

Anyway I thought it would be fun to knock up a non-codebox variant, which is below (gen~ rampsmooth2) -- maybe try this one and see if it works for you? This one doesn't reset when rampup or rampdown change, but that could easily be added by adding a change after each parameter and mixing that with the other change signal.

Max Patch
Copy patch and select New From Clipboard in Max.

However, unlike what rampsmooth~ does, often what I really want in slew limiting is something that will limit how steep a signal can ever be (limiting the slope, rather than limiting the duration). This is a much easier and also very useful circuit:

Max Patch
Copy patch and select New From Clipboard in Max.

It's also easy to make a 2nd order version of this (basically, put another slope limiter inside the first), that will also limits the 2nd derivative, not just the 1st, which will soften those sharp corners.

Graham

toothpaste's icon

Sorry for this late response Graham. The rampsmooth you posted works perfectly for my situation cause I latch the new parameters and the input only when the old destination has been reached anyway!

I didn't try adding the updatable parameter functionality, so I cannot comment on whether the bug I mentioned above will persist then. I also had a hunch that it was that part of the circuit that caused the issue though.

Thank you!