Granular Synth Problem (poly~)

Matthias's icon

Dear all,

I have a question concerning a polyphonic granular synthesizer I am trying to build.

The architecture of the instrument is quite simple and it is basically driven by a grain generator, which is packed into a poly~ object in order to obtain multiple grain streams. In essence it is very similar and based on to the granular tutorial patcher which can be found here: https://cycling74.com/wiki/index.php?title=MSP_Polyphony_Tutorial_3:_Granular_Synthesis

Now, I have tried to nest this polyphonic grain generator into another poly~ abstraction in order to obtain an individual pitches per key that is pressed on the midi keyboard.

However, whatever I try it remains monophonic and fails to deliver the desired results.

I have uploaded the patch with all necessary externals and abstractions. On the top level, you can find the poly~ object, which hosts the "tr_notepoly_0.6" abstraction, which in turn hosts the "tr_grains_0.6" (the grain generator) abstraction.

When I replace the "tr_grains_0.6" poly~ object inside the "tr_notepoly_0.6" poly~ object with a normal saw~ oscillator, the patch behaves as expected. Whenever I hook up the "tr_grains_0.6" poly~ object, the patch is monophonic.

Is there anything I have overlooked? Does anything go wrong with muting and unmuting the poly~ instances in the embedded abstraction?

I would call myself an intermediate user of Max, but I think I am stuck here and can't find the solution to this problem. Maybe someone of you may find the time to look at my patch and tell me what's going on?

I am aware of the discussion in this thread https://cycling74.com/forums/poly-within-poly-for-polyphonic-granular-synth/, where it is the overall opinion to avoid poly~ inside poly~. However, this is just for complex control reasons, which I seem to have control over.

Some help would be much appreciated!

Thanks in advance,
Matthias

Transformationator-Poly.zip
zip
Dave Mollen's icon

Nesting poly~'s in poly's may indeed introduce problems. But I have used it quite a bit.

I'm not sure what's causing your problem. It's a pretty big patch. But you should take note that the send object sends data globally. So the data going through your "send pitch.min" and "send pitch.max" are sent to all poly patches. Maybe this is your problem..

Matthias's icon

Dear Dave,

Thank you very much for your hint, and sorry for the late reply. I had kind of a busy weekend.

I suspected something along these lines. I now had some time to play around with getting discreet pitch information into the nested poly~ and, tadaaaa, it works.

I now have some other issues with voice allocation in the top poly~ object. But this is peanuts.

If anyone's interested, I'll post and share again as soon as I have something worth showing.

Best and thanks again!
Matthias

Dave Mollen's icon

I think there's another problem with your patch. When you send a note-off to your notepoly patch the nested poly patch with the grains is muted automatically. This way you have a grain that is turned off mid-ramp. When you unmute that notepoly patch it finishes that grain first. The problem is that this particular grain is pitched to the previous note.

I have had this problem before. But it may be that this is not an issue in your case.

Anyway to solve this you'd have to stop sending triggers for the grainramps at a note-off message. Then you wait for the last grain to finish and mute your notepoly patch.

Matthias's icon

That's exactly what I'm dealing with at the moment. I am experiencing the exact symptoms you are describing (the nested poly patch finishes the last grains with the previous pitch as soon as I unmute the notepoly).

Your idea seems to make good sense to me. However, stopping the triggers for the grain ramps at the moment of the note-off message would prevent me from having slow releases from the ADSR envelope. I was more thinking of stopping the triggers for the grain ramps as soon as the ADSR finishes (detectable from the mute outlet) and only then muting the notepoly.

The only problem then would be how to detect the moment when the nested poly finishes the last grain, which would be the trigger for muting the notepoly. Would you have any idea how to achive this?

Thanks for your input, Dave, you were a great help with this!

Dave Mollen's icon
Max Patch
Copy patch and select New From Clipboard in Max.

I've used a silence detector for this before. You may need to tweak some parameters to make the silence detector work properly.

Matthias's icon

Thanks a million for that! I'll have a look at it as soon as I get the chance. Might be another two days until then, though.

Best,
Matthias

Matthias's icon

Hey Dave!

I looked at your silence detector and played around with it for a little while. Brilliant idea and I wouldn't have come up with it ;-)

I now finally got the polyphony to work properly. The only tradeoff I had to find is between the rampsmooth value (the 8820 samples you suggested works quite well) and the grain density. When the density parameter is quite low and the grains short, the interval between grains becomes larger, possibly larger than the rampsmooth value and, as a consequence, the voice is muted. I now found a sweet spot between minimum grain density and grain length value so that the voice won't be muted even at the lowest settings.

Thanks again for the inspiring input, Dave! Without you I'd still be stuck.

If anyone's interested, I've uploaded the final result (I have started to rebuild the surrounding patch to make it a bit tidier, so the lfo feature for scrubbing through the sample is still missing).

Best,
Matthias

GrainLabs-Poly.zip
zip
Dave Mollen's icon

Maybe you don't need the silence detection. You can also try to send the bangs from the right outlet of the line~ object through the gate to thispoly~. The same line~ object that mutes the grain poly~ patch.

I'm glad I have been of some help to you.

Matthias's icon

Yes you have indeed, thanks again!

I now had some time to experiment around with the patch over the weekend. And I've had the same idea before. However, the silence detector is actually the better solution to solve the problem when the interval between successive grains becomes larger.

When there is a larger interval (no grain playing, but the next is supposed to be fired) the parent poly~ patch is muted immediatly. With the silence detector I am able to introduce a slight delay (rampsmooth rampdown value) before the bang is sent.

I am now up and running with quite a solid polyphonic granular patch. I am just working out minor details (e.g. to prevent sending additional bangs into the nested poly~ patch at the moment of the note-off message). I'll post and share as soon as it is done.

One more thing, just out of interest: I turned the instrument into a Max For Live patch and noticed that at lower settings (5 - 10 Hz) of the density parameter (a phasor~ object triggering a series of bangs), the bangs triggering the individual grains is much more regular in the Max For Live device than in Max6 standalone. Any idea as to what is the cause for this? Is it the max scheduler (I read some stuff about it but haven't actually fiddled around with it at all) versus the handling of timing in Live? Or is it something else?

Best,
Matthias

Dave Mollen's icon

I'm not sure but I understood that the scheduler is automatically in overdrive in M4L. In Max standalone you can set it to overdrive manually. You can check the box in the audio status window.

Matthias's icon

OK, thanks Dave. And sorry for the late reply, I didn't have a chance to check back lately.

I have the patch up and running now, but it's a huge CPU hog with high density settings and more than 2 voices playing ;-)

It works and makes beautiful sound textures, but it's not easy to balance playability and CPU usage. My idea is now to build myself an external that then replaces the innermost grain poly~. I have looked around for ready made externals (Nathan Wolek's Granular Toolkit and others), but none seem to do what my lowest level poly~ does and with all of them I would need to have 2 levels of poly~.

I'll be off learning C, then ;-)

Thanks again for your help, dave!

Best,
Matthias

Mark Durham's icon

You may also want to check out the Robert Henke Granulator 2 which (I think) uses gen~ inside poly~ and is very efficient.

Matthias's icon

I have looked at Robert's Granulator (version I and II) closely and it doesn't granulate the way I wish. I also checked for other externals and none of them do what I want them to do.

I am really learning to code in C right now, I wanted to do this for a long time. I'll post back as soon as I have something useful, could be a while though ;-) I know exactly what it should do, I just need to know how to build it.

Thanks for all the input in this topic. I'm not sure if I should post the synth discussed here until I have something more efficient, it's really a CPU hog at the moment.