granulator patch generating strange high frequencies

matheus leston's icon

hi, everyone.

i'm building a very simple patch to make some extreme time stretching (something about 1000x slower). but as a result, there are some very high frequencies being generated as well. i have noticed that if i changed the audio settings (such as i/o vector size, signal vector size, and sample rate) this high frequency will change.

why is this happening? i must admit that i don't understand quite well how max deals with those vector sizes, but is there something to do with it? if so, is there anyway to avoid it?

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

here is the patch. i must say it is a work in progress!

thanks!

brendan mccloskey's icon

Hi
no-one can test this as you neglected to include the [grao] patcher.......

Amplitude modulation sidebands or frequency artifacts, in granular synthesis, result from the windowing of the grains (size and shape). Try varying this window to see if the frequencies persist, I believe that angular or hard-knee envelopes are often the cause, whereas curved overlapping grain windows can help alleviate them; also, reduce the number of polyphonic voices when testing too, 100 voices is quite numerous.

Brendan

matheus leston's icon

hi, brendan.

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

i'm sorry, i forgot to include the patcher! here it goes:

by windowing of the grains you mean the duration of each one of the grains? now it is at 100.

the envelope is made with a line object. do you think that maybe using a groove~ instead of play~ and using the loop sync outlet of it to control the envelope should work better?

thank you!

Peter McCulloch's icon

play~ should be totally fine. Check out the "start" message for play~ if you want to skip the line~ before it. (dunno if there's a savings, but there might be a small one) t

My two cents on granular poly~ patches: I like the actual poly~ voice to be as dumb as possible (as in there's no control/random logic in it, it's just following instructions and playing the grain you provide). This allows you to have multiple streams of grain generators all using the same underlying engine, for instance, which you can't do if the control logic is inside the poly~ voice. You can make a big list using something like this:

pack 2 0. 0.2 0. 100. 0. 0. 0. 0. .... etc. for your various parameters where they map onto things like:
| bufferNumber transposition volume startTime duration pan envelopeShape etc.
| (The bufferNumber parameter is a great place to use a banged itable. (to give weighted probabilities for the different buffers)
|
|
prepend note
|
poly~

You can use zl nth within the poly~ voice to extract individual items. (you can use unpack, but you have to keep track of the positions, and certain values need to arrive before others, so zl nth and t l l l l ... saves you lots of trouble since you can pick and choose)

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

Here are some very small tweaks that may tighten up the performance for your poly~ voice:

matheus leston's icon

Peter, thank you very much for your suggestions. I was actually using your approach, sending a larger message do the poly~ object and unpacking it. For some reason I can't remember I have changed it, but you are right, it doesn't make much sense. I have reorganized the patch and used a bunch of zl.nth, as you said. I also didn't know about this behavior of line~ with messages like (0, 1 100) and it was really helpful, thank you. I changed the message to control the playback position as you suggested, but to control the envelope I decided to use a curve~, as Brendan suggested.

But to eliminate the high frequency that was being generated, I had to increase the randomness of the playback position (to a window of 100 ms instead of 10ms). It solved the problem, but the result was a drone that was a little "blurry" (that's the best word I could think of). So I created a small expression to control the level and size of the grains: as closer a grain is to the middle of the window, the louder and longer it is. Does it makes sense?

Brendan, I'm still using 100 voices because my metro is set to 1 and the duration of each grain is 100. If the number of the voices is lower, I would get some missing grains. If the metro is higher, the sound is not that rich and I need to narrow the window again, resulting in that high pitched frequency. This allowed me to remove the reverbs.

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

Here is the new patch:

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

and this is inside the poly~~

Peter McCulloch's icon

I'm guessing that the high frequency that you were experiencing was comb filtering. If the grains are coming at very regular intervals, you're apt to get comb filtering, especially if the grain parameters are similar. You could also try randomly inverting the phase of some of the grains (* 1. or * -1. for the volumes) to break that up a little more.

You can use more than one metro, each set with a different rate, to generate bangs. (e.g. metro 1, metro 1.17, metro 1.83) You can also only pass x percent of bangs from each, and that will further break up the regularity.

Floating Point's icon

Random phase inversion--clever.
Also check your audio settings- as small an IO vector as possible may help with the combing--also try enabling audio in interrupt and overdrive and see if that helps.

matheus leston's icon

Peter, your suggestions made the patch sound much better! I decided to break the regularity of the metro with a random value at each bang. I have included the random phase inversion (indeed, really clever): the comb filtering is gone and the result is sounding much fuller and denser. I also noticed that now I can reduce the grain window again, and even if I remove the randomness, playing all the grains in order, it sounds perfectly fine - even better when playing some transients - thanks to the phase inversions.

Terry, with those improvements and with a brand new comb-filter-free patch, changing the IO Vector doesn't change the sound. But in the previous versions of the patch my experience was the opposite of what you said: as lower it is, the stranger it sounds. As I said before, this is something that I don't understand quite well. Can you please tell me where I can find more information about vector and buffer sizes? I guess I need to understand it better and study it for a while...

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

The new patch:

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

And the poly~

Thank you all!

Peter McCulloch's icon

Glad to hear it's working better. Very small things: you can save a multiply if you multiply the output of curve~ by your gain factor first; also, decide works better than random 2 in terms of uniformity. (decide is essentially random 2 but with an algorithm that's designed for that particular case)

Floating Point's icon

@matheusleston
an explanation of audio vectors etc is here:
https://cycling74.com/docs/max5/tutorials/msp-tut/mspaudioio.html

if you are using metro to trigger an audio process, especially if the timing resolution is close to or below the vector size, then strange things can happen, like metro timing being quantized to the vector size, which could explain your earlier observations.

This thread outlines my recent experience of this problem:
https://cycling74.com/forums/triggering-audio-grains-timing-inaccuracies-how-to-fix

matheus leston's icon

Terry, thank you for the reference. I read the thread and this is really interesting stuff. I'll make some tests with line~, got really curious about that.

Peter, once again, thank you for the advice. And the patch is working perfectly now. That random phase inversion ideia is brilliant! I'm starting to sketch a strange "freeze" patch based on it. I'll let you know if it works.

(By the way, I just downloaded you M4L devices, and they are amazing!)

Peter McCulloch's icon

This thread gave me the idea for the phase inversion--or rather, made me connect the dots and see it as a tool for reducing the comb-filtering--I hadn't tried it before for that purpose, so thanks for that. Glad you like the M4L devices. I'm working on a small commercial project for that, so there's more in the pipeline...

brendan mccloskey's icon

Hi Peter
if I may chip in (while deferring to your expertise); I'm interested in this sub-thread too - a system I have used randomizes the phase offset of two overlapping windows, constrained between approx 0.2 and 0.8 (anything beyond these limits places grains too close together) for 4 grain-windows per single voice (using an [allpass~ 50 randVal 0.] as well). This gives me minimum combing with optimum blurring or smearing. But my engine uses a far smaller number of voices than the OP.

2c

Brendan

Peter McCulloch's icon

Hi Brendan, that sounds like an interesting approach. Pretty much any irregularity you can introduce into the system should reduce the comb-filtering. Are you doubling the grain, or is this for independently chosen grains? If this is a polyphonic system, do you lose throughput with the extra latency? (i.e. voice x can't play yet because it's waiting for the previous grain on that voice to come out the other end of the pipe) I guess that needn't be the case if you're not doing muting.

Do you mind posting code? Curious to see, and it's always nice to expand the bag 'o tricks especially with granular.

brendan mccloskey's icon

Hi
for each voice (inside a poly) a central phasor generates one window - this phasor ramp is offset by a random phase amount and generates another window. Both windows are independently duplicated and offset using the allpass filter, whose feedback is set to zero, with a random delay time based on the randomized/constrained grainsize. Nothing that hasn't been done before I guess, but as this forms a small part of my PhD thesis, I'd rather PM it to you than broadcast it; I'd like to see/hear an example of your own approach too ; )

Coupla years ago Tim Lloyd, Chris Muir, Alex Harker and Luke Hall helped out with the basics of this for me - further research led me to this approach. I guess we could just multiply the number of offset phasors......but I like the subjective brightness that the allpass filter imparts......waffle-drone-blah

At the moment I have 3 x 24 grains, no muting and around 30-40% CPU usage.

Brendan

Peter McCulloch's icon

Sure thing. In the midst of a dissertation here, so I understand...

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

Not sure if this is of interest, but here's an example of doing allpass interpolation in gen~. It's part of a comb filter (PM.Comb~) with built-in saturation, but you could just strip out the allpass portion. I'm also curious about doing allpass interpolation on the playhead itself, since you're not using feedback.

brendan mccloskey's icon

Thanks, I'll get a look at this when I'm on my Max6 machine.

One question that persists, about the [allpass] approach. I originally intended it to function as a true allpass filter as found in reverberation systems - blurring sharp transients without colouration, but I now understand that this only happens at smaller delay times (< c. 30ms??), with a degree of feedback and in series. I am using constrained-random delay times of around 50% of the random grainsize (which is between c.50-100ms, fbk gain @ 0.), so I guess I'm not getting the true allpass effect, more of a shifted/delayed window. My question is, is there a text-book 'cutoff' value for the allpass filter - at what time-value does the transient blurring effect disappear, eg, > 30ms?

I had previously used smaller [allpass~] delay values with feedback on each grain voice, but didn't like the combing pseudo-reverberant effect this produced, but liked the idea of a cheap way of getting further duplicate windows.

Is your dissertation Max-related too?

Best
Brendan

brendan mccloskey's icon

ps

4329.4wins.png
png
Peter McCulloch's icon

No, it's having to do with music notation using JMSL. I'm looking at ways of analyzing creative behavior using data mining.

Peter McCulloch's icon

In Max 6, check out the filter-response sections of the help files for allpass~ and phaseshift~. Allpass~ is set using delay times, so it's got a tendency to look like comb filtering (which it is, even though it's smearing it a little each time), whereas phaseshift~ is set using frequency. In general, I'd say that if you're looking for a time effect, use allpass~, but if you're looking for a frequency effect, use phaseshift~.

You can also use filtercoeff~ to filtergraph~ coefficients similar to phaseshift~ for use with biquad~. The thing about phaseshifting is that it's generally only audible in cancellation or extreme situations, so I would imagine that having less of a delay would increase the effect that it has on frequency. The delay, however, could be having a nice thickening effect on the sound, especially depending on what you're doing with panning. Here's a discussion on allpass filter coefficients that could be helpful:
http://www.kvraudio.com/forum/viewtopic.php?p=3624633