granular stretching in gen~ (issues)

billyz123's icon

hey everyone :)

I dove right into gen~ by trying some granular time stretching. I ran into some issues when adding more than one peek~ object. My temporary solution was to just use a delay which works fine, but is there any reason my other idea isn't possible? ...or am I doing something wrong?

Thanks :)

4104.genStretch2.maxpat
Max Patch
Graham Wakefield's icon

Hi there,

The problem is that the second peek jumps position in the buffer at the high point of the envelope, rather than the zero point; hence the clicking. What you'll want instead is to ensure that the each peek switches position only when the envelope is at the zero point.

Also for using peek you probably want to use @interp linear or better to avoid noisy aliasing.

It's probably easier to run a phasor for the first peek, and using it to drive the envelope as well as the grain index. The offset into the buffer (according to speed) should be independent, and you want to only sample this when the envelope is at zero, i.e. when the phasor wraps. A [sah] could be one way to do this.

Then a second phasor can be derived from the first simply by adding 0.5, mod 1, and duplicating the rest of the envelope/peek processing.

If you want more concrete examples, let me know.

Graham

billyz123's icon

Thanks for your help Graham!
I ended using phasors outside gen and that worked. I was trying to avoid using [sah] because even within gen you still have to set it to trigger at 0.001(i think) for it to work with phasors... no matter where the phasor is (inside gen or outside). My main goal was to use strictly samples, so that's why I converted everything to integers inside gen. This works but not completely. Either way I'm happy with the results :)
Also, is there a way to trigger the phase offset of the phasor within gen?

4105.Genstretchbz01.maxpat
Max Patch
Graham Wakefield's icon

There isn't a way to reset the phasor phase currently, but a phasor is basically just an accumulator, so it shouldn't be too difficult to construct from a [+= @max 1].

The [sah] can be be triggered easily using [delta] -> [sah 0], it will trigger when the phasor loops. Take a look at the gen~.pitchshift.maxpat in /examples/gen, or the gen~.slicer.maxpat, both of which are similar to what you're doing.

billyz123's icon

Thank you sir, i will check that out because I didn't even know it was there :)
I'm still confused about how you would reset an accumulator but I guess it doesn't matter because using the phasor from outside gen seems to work amazingly.

Obviously there's a lot going on now with Max/Msp/Gen ... I've never really had a clear understanding of the precision of it all which is probably why I was excited that Gen would work on the sample level. I made a version of this stretcher using only phase and floats and it works great.
So my question now:
Is there some kind of precision conversion from outside Gen into Gen? ...or can you direct me to an explanation/breakdown of the signal level of Max/Msp. I understand all the Max Scheduler stuff perfectly fine as I've been working with this program for many years, but when it comes to :
I/0 vector size
Signal vector size
Vector Optimization
...and now Gen, well I'm afraid you've lost me :)

Thank you!

tegid o's icon

Thanks for posting this, V interesting. One question, though, is it me or is the mirrored stuff on the right hand side of the gen object (the overlapping grain stuff?) actually outputting anything?

billyz123's icon

I'm guessing you don't own gen so here is a simple test to prove it does output something... in fact, without it's output we are all doomed... dooooommmmed!

4148.Genstretchbz00.maxpat
Max Patch
Graham Wakefield's icon

Hi there,

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

Try this one - the second gen~ contains all the phasor & gate functionality embedded within it. I built phasors out of history, + and wrap. Because the history is named, it can be used as a parameter, which is how it can be reset:

MuShoo's icon

Graham - I love you. I've been looking for wrap and didn't even realize it (I'm building a very specialized phasor, myself) - this is perfect. Thank you!

billyz123's icon

Thanks Graham, great ideas!
Don't forget to subtract the grain-size right before the [peek] :)
Plus, I get clipping sounds or whatever when changing grain size... I'm guessing it's an {sah] thing but I haven't figured it out yet.
I'm not really sure there's a point to having the phasor inside gen, at least in this case... but it is cool :) Something I'll definitely use. Thanks!

MuShoo's icon

OK, I'm going to mildly hijack this thread, I hope that's OK, Billyz. The phasor I've been working on, needs a 'stall time' as well as phase control similar to the built in MSP phasor. Stall time is set in MS - essentially, the phasor does one ramp, then sits at 0 until the stall time has passed, then does another ramp from 0 to 1.

Included in the patch are two possible options - the first one doesn't work completely, but it's nice and clean. Sadly, doing the phase merely as a [+] before wrapping, makes the stalltime function screw up majorly.

The second option is MUCH more complex, but it was the only way I could come up with to make phase work, as well as stall time. But it's just... so ugly! And the phase isn't clean, nor is it elegant - it's essentially a delay, but I'm sure after a while it would cause multiple stall-phasors to fall out of sync a touch. (Also I never thought to use a named history object before, so the 'reset' function on it is still clunky).

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

Anyone have any great insights on how to make the simpler patch do what I need it to do?

billyz123's icon

I'm all done with this thread btw :)
I am curious though... what is point/purpose of this "stall time?"

Graham Wakefield's icon

@billyz123: yes, I forgot to add the [- grain_size] there. To avoid the clipping sounds when changing grain_size, yes, need to use another [sah] and trigger it at the envelope end. Be careful to set @min and @max on grain_size then; e.g. it won't handle negative sizes very well...
Having the phasor inside might be slightly more efficient, but also opens up the possibilities of doing other things to it...

@MuShoo: You were on the right track with the first one. Try the following patcher. (I took out the [param phase] stuff for now to keep it simple, I wasn't 100% sure what you wanted it to do). The main logic is to use the reset inputs of the [+=] objects to ensure that only one of them is accumulating at any time. While the first accumulator is doing the 0..1 ramp, it keeps sending a '1' to the second accumulator to keep it at zero. Once the first accumulator goes above 1, the gate closes and output becomes zero, and the second accumulator is allowed to start counting. Once the second accumulator reaches the stall time, it triggers a reset on the first accumulator again (same as you had it), which will feed back to reset the second. It's super useful to have a couple of extra [out] objects while debugging gen~ patchers to see what signals are passing through. I've left one in that shows the reset trigger from the second accumulator.

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

BTW there's a feature coming in the next update (v. soon) which will offer a very different and probably much easier way of doing this.

MuShoo's icon

Graham - I'll likely wait until the next update, then! I'd programmed this exact thing in Java, and it was rather simple (mainly because I could do multi-requirement if/then statements, and such. I'm sure there's a way to do them in Gen~ but logic gates confuse me sometimes.)

Sadly I need the phase function to work almost exactly like it does with phasor~ - if I send one phasor~ a 0, and another one .5, simultaneously, their ramps will be .5 apart, always. The second of my examples allows for this, but it handles it by having 3 accumulators - one for the main ramp, one for stall time, and one to phase-lock (if I remember correctly, at least. It's been a while since I really looked at that one). So when phase is changed, it forces the other two accumulators to 0, until the phase accumulator has counted (at the current main-phasor frequency) to the number specified as the phase.

Which, honestly, works 'okay' - it's just ugly and in-elegant.

Your patch seems to be functionally the same (though with different objects, and for some reason I can't have a stall time of 0) as my latest attempt (minus the phase-locking). I do, however, like the use of += for the main accumulator in yours, because I feel like there's something that could be done with the @min and @max options, though I'm not completely certain how to make them work.

5ibl1's icon

I'm resurrecting this old topic as I'm trying to make a time stretch patch with an instrument input and want this to work completely inside gen so I'll need to use a data object instead of a buffer. I'm not really familiar with working with real time data buffers inside gen and can't seem to make it work.

Any help?