line~ gen implementation with variable rate envelope

srs's icon

Does anyone know of a method to implement something similar to the [line~] object but with the ability to change the rate of the envelope in realtime, ie. while the envelope is being output as a ramp signal? So rather than setting the time and speed of the ramp using the initial message, it could be adjusted after the envelope function is triggered.

The use case for this is for a sample buffer player that can have variable rate playback. Rather than trigger it with a (0, sample-length sample-length) message, it could instead be triggered with just a start position and then count in ms from there till it reaches the end of the buffer.

The difference between what is needed and what the [line~] object currently does is that the rate could be adjusted during playback, e.g. it could be sped up or slowed down to arrive at the end of the buffer at an earlier or later time depending on a 'speed' control parameter (1=normal playback, 2=twice as fast, 0.5=half the speed, etc)

I've been trying to implement this in gen using a simple counter and then dividing it by the sample rate but that doesn't work. The sample position jumps backwards when slowed down instead of changing the increment amount.

double_UG's icon

i made this long times ago, I hope it works

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

Bradley Korth's icon

Double_UG, the gen object didn't load correctly in your copy-paste. It's just an addition of the two inlets.

srs's icon

The solution I'm searching for is similar to the [line~] object and that is what is currently being used to playback the audio buffer, the essential difference is that the end time is not set prior to being triggered and the rate of the envelope ramp is variable so it's more like an infinite ramp than a predefined envelope function generator.

It seems like the solution requires gen as it's the counter rate itself that would change. Slowing the rate would increase the delay time between tick and this need to happen at sample rate.

Here's the pseudocode but I'm not sure how this can be implemented in gen (or if it even is the right solution this problem)


mizu's icon

a way for me to learn if-else :

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

https://cycling74.com/forums/feature-request-line-equivalent-in-gen
https://cycling74.com/forums/translate-msp-line-into-gen-counter/
bzzz

srs's icon

Thanks for this patch and the links - all very helpful!

The solution might be simpler than I thought but it's not quite there yet. Following this tutorial, it was possible to adjust the rate of the counter by changing the increment amount and then converting the output to ms. This part works perfectly as a means to index a buffer for audio playback.

https://cycling74.com/tutorials/gen~-for-beginners-part-3-counting-and-a-world-without-bang-messages

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

The final issue is just resetting the counter while it is playing which at first I thought would be simple. For some reason, sending a 0 to reset the counter and then a 1 to start it again doesn't work . If I delay the restart by 25ms via a [del 25] then it does work (see the workaround) which seems strange and the opposite to what I would expect.

Does anyone know why the reset-restart bang at the top of the patch doesn't work? I think I'm missing something fundamental about how signals are processed in gen. I don't see why using [t b b] doesn't work but ideally it would be possible to just reset and restart the counter immediately in one gen operation rather than having to add a 25ms delay...

I have a feeling it might involve [latch] or [sah] but even after going through the tutorials and that thread, none of my attempts worked.

mizu's icon

Hi srs
playing with ( not a gen~ expert...) seems t b b to "fast" for audio timing. 1 ms in Max timing seems working "sometime". Latch in gen~ could be the friend, if a command is needed in audio timing. Here a noob's progress patch using a click~:

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


Roman Thilenius's icon


i´ve only read the the original question:

line~ can be retriggered at any time, so if you only need to do it (the speed change) once, and you dont need sample exact control, you could use its own output to make up a new segment for its input.

other than that, if you have, say, a line~ running from 0 to 1 in 10 seconds, and after 3 seconds you want it to run with double speed, you would simply multiply its output signal:

*~ 2.
clip~ 0. 1.

srs's icon

Thanks for the suggestion Roman but my attempt at implementing that approach ran into an earlier problem where changing the rate while the [line~] output is running causes the playhead to skip around in a way which doesn't work well for indexing an audio file.

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

And thanks Mizu, that solution works great! The bang-restart and rate change are working perfectly.

One last question that might have as simple a solution as the one you just provided - do you think there is any way for the output value of the gen object to hold when it is stopped (as opposed to going back to zero)?

I've tried to do this via a [gate] that I thought would 'close' and stop the output before the counter resets but I'm guessing the synchronous nature of gen doesn't allow this.

An attempt at holding the counter value when it is stopped

Here's the simplified patch in case one any else finds the solutions so far useful or feels like helping out further..

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


mizu's icon

latch ? the friend

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

srs's icon

Amazing - thank you so much for this! You've just completed the very last step of a project that's been weeks in the making and is finally finished.

Hope I can help someone else out one day in the way you have just done!

mizu's icon

welcome !
nb: thanks to everyone here: line~ was not the simplest thing to port in gen~
bzz