gen~ and sah~ and phase-shifting

Hi team
in response to a recent request, leafcutter kindly provided me with a small gen~ thing which does the following: generate a phasor ramp of variable duration, with independent control over interonset time – it’s for a granulator I’m working on.

The common method of creating an overlapping grain window would be via [+~ 0.5] –> [%~ 1] and thence to the window generator (in my case, [cos~] –> [!-~ 1] –> [*~ 0.5]), but due to the imposition of an interonset value in the phasor ramp, this is not possible – the "off" period translates to 0.5 in the shifted copy.

Therefore, I’ve added a variable delay inside the gen, which works well, except that deriving a window from this shifted ramp results in spikes in the window, so I have resorted to shifting the original window *outside* the gen object, using an allpass delay. The small excerpt patch below describes the problem; as I am a newcomer to gen it is possible I’ve made a simple error – although it looks clean to me.

– Pasted Max Patch, click to expand. –

Copy all of the following text. Then, in Max, select New From Clipboard.

Attachments:

Hello Brendan!
Hapy to hear about this topic, since i ran into similar problems recently. I will look at your patch more closely if i have time but i wanted to let you know that i came to the conlcusion that working with the oscilloscope will cause problemse if it’s about single samples that could cause problems. e.g. in your case, you have a spike in the window function because when the phasor goes down, it does not do
y[n]=1, y[n+1]=0, so without anything in bewteen. There is one sample with the value of about 0.5, that’s the reason for the spike.

you can see this if you record your signlas and view them closely with [waveform]( i have pasted it down there.)
Iseem to remember this problem and hope i can write you soon what solution i came up with..
cheres!

– Pasted Max Patch, click to expand. –

Copy all of the following text. Then, in Max, select New From Clipboard.

As gen is running at the single sample level, should I try to avoid mixing up my timings then? I tried to build my windows INSIDE the gen object, using the same cos function but they came out all wonky (a technical term meaning "wonky")

I was just about to post a patch, with the windowing etc done in gen, with an annotation suggesting that the problem lies in the delay object (not a fan of delay/allpass in this situation – it always seemed a bit heavy-handed or amateur).

Works like a charm, thanks a million.

A final minor issue remains, in that it is still possible to interrupt the phase-shifted ramp by rapidly varying the grain size parameter; I’ve added sah and history to the gen code, and the scope illustrates the ramp interruption. I’m going to now patch this revision into the larger granulator and if the issue isn’t audible then all well and good; but the new version is below fwiw.

– Pasted Max Patch, click to expand. –

Copy all of the following text. Then, in Max, select New From Clipboard.

Ok, thanks – the ramp interruptions have disappeared thanks to your "interp off" solution but an annoying issue persists, mainly due to my limited grasp of the minutiae of DSP.

It is a wider issue, and does not result from the current discussion but perhaps you have some thoughts? We often rely on overlapping hanning windows to help ameliorate amplitude modulation artefacts in granular playback engines, correct? The subtle yet persistent ringMod/Dalek effect that is the BANE of my working life!!! Anyhoo, on my system, where I use 2 windows (phase-shift by half period) ramp/grain durations of 40, 80ms have the desired effect – no ampMod, really clean, but any other small duration (c. 20~100) and the noise returns. If I use 3 overlapping windows (source –>shift 1/3 –>shift 1/3) the result is exactly the same, but substitute 60 and 90ms. I am referring of course to normal speed playback at original pitch. Naturally I would expect *some* artefacts at varied pitch/speed, but I’ve solved that issue, via some subtle random noise.

Previous related threads on this topic have suggested switching between "regular" playback for normal speed/pitch then shifting to grains for the granular stuff……nah, not for me, seems a bit heavy-handed; plus, this seems to be a phenomenon related to granular synthesis which I would like to understand better and perhaps solve. Alchemy, arcana or merely n00bosity?

and after some patching/reading/patching, the subjective nature of the sidebands/overtones in the spectra appears to depend largely on the source material – ie different soundfiles. I could spend all day choosing the "best" grain duration for one file, only to find it doesn’t have the same smoothing effect on another soundfile. I’m avoiding percussive/rhythmic audiofiles anyway. I may well have to go down the "two playback sources" option I referred to above. I’ll also try a variety of windowing functions too.

It might be interesting to not only randomize the start position but also the grainsize. I don’t know if you’ve tried that already. Lately I find these grainsize below 40ms very useful. They’re indeed useful for percussive sounds, but also for sounds with a lot of texture. I think granular synthesis is really appropriate for those type of sounds.

Anyway, thanks for the genpatch! I never used the interonset parameter.

Hi Dave
yes I’ve been experimenting with various types of noise sources, at both the grainsize signal: cosine wave with randomized frequency, noise~, rampsmooth~ etc, and also on the final output (using an allpass filter @ +/- 20ms) but there is a "threshold" beyond which (depending on the shape of the noise) chorus/phasing type effects become evident – not unpleasant, just not needed. Bearing in mind that this is going to be a sort of hybrid quasi-synchronous/time-based granular engine, I’m probably fixating on the normal-speed/normal-pitch side of things too much. Puritanical.

The gen interonset solution is leafcutter’s, and as you can see from this thread pid contributed too.

Thanks for your thoughts

Brendan

ps
re the choice of windowing function, for pure granular synthesis hanning/triangle windows may be applicable for various reasons, but for time-based granular playback (time/pitch stretching and grain/loop dispersal) a "half-sine" window works best *for me* – it is the most open window I have found, letting more of the original samples through, particularly with three overlaps: phasor–>trapezoid 0.5 0.5–>*1.5–>sinx.

FWIW, there’s an example of doing this in the Max Gen examples: gen~.pitchshift. (randomizing timing to reduce amp modulation sidebands)

Another way of doing the wrapping for the offset is to do something like >=~ 1 into right inlet of -~. (so subtract one when >=1) Only works within a constrained range, but it should be faster than modulo.

ps
Peter, I’ve tried, unsuccessfully, to add phase-modulating noise to my own patch, using the gen example you suggested. Can’t seem to get my head round how to properly add noise/sah/history inside gen, I end up generating either audible noise or pitch modulation. Would you be able to help out if I supplied a small simple example patch?

pps
I am still using delay to phase shift the grain ramp, as [>=~ 1] . . . [-~] won’t work with my (read: leafcutter’s) "sleepy" phasor; works fine with phasor though.

Essentially, I’m trying to apply the blurring effect found in the gen-pitchshift example, via noise-based phase-modulation and I’m not sure that I’ve used sah/history correctly. I haven’t used gen enough to become fluent yet, it does appear to require a slightly enhanced or different skill set and, as my PhD rapidly hurtles towards conclusion, I fear I just don’t have time to self-train in gen.

I think the problem is that you’re trying to randomly phase-shift factoring in the duration of the phasor.

Imagine that two phasors are 180 degrees out of phase with no pause in their cycle. If the offset phasor can only change when it’s done playing, it can’t change its offset to anything less without creating a discontinuity! — you could, however, come up with some algorithm to have it "sit out" a cycle if it would create a discontinuity. (at least, that’s what I think is the case…)

You can jump around in the time after the phasor, but not during, so if you change your random to use that range, instead of the phasor dur, you should get the correct result.

I think I understand your explanation, and to demonstrate I have here a simple MSP patch showing what I wish to do inside gen, modulate the offset/shift amount using noise. In MSP it does so without clicks-pops-n-farts, in gen I think I must be doing something wrong with sah because it doesn’t work as anticipated. Forgive my crushing ignorance of gen, but I simply don’t have time to crash-course it.

– Pasted Max Patch, click to expand. –

Copy all of the following text. Then, in Max, select New From Clipboard.

You’re using rand~, and that changes at a much slower rate! It’s working visibly because it’s changing over time, though if you see what’s happening with the phasor, you’ll notice that it has the effect of changing its rate too, since continuous phase changes produce frequency change. Here’s code that demonstrates. You’ll hear the slowing up and speeding down of the drum, which I think isn’t what you want, correct?

– Pasted Max Patch, click to expand. –

Copy all of the following text. Then, in Max, select New From Clipboard.