Pulsar Synthesis help...
Hi, I'm working on university project, I'm trying to build a pulsar synthesis patch , including all the features from PulsarGenerator by Curtis Roads + some other things if I can.
The problem is, I don't know Max very well... anyway I managed to recreate the PS patch from the Cipriani/Giri third book and understood it, and even added some things.
I'm now stuck on a task which is keeping me from working on the others cause it requires redefining the very core of the patch. Basically I need to encapsulate the synthesis engine in a poly~ and create a voice routing system (which I've done) and then find a way to crossfade individual pulsars when the pulsaret period is longer than the pulsar (whole grain) period (when pulsaret frequency < whole pulsar frequency). This is because, otherwise, the pulsaret is cut at the start of the next and you get sharp edges in the waveform.
The book, after describing the basic patch, gives this assignment:
"Since pulsaret frequency and grain rate are independent, the pulsaret period may be longer than the grain rate period, and this creates pops and clicks. Roads suggests, in this case, to create a quick crossfade between successive grains. Modify the patch and create a crossfade between grains: this crossfade should have a very brief duration, for example 1ms; when the grain rate frequency is very high (>1000), and its period very short (less than 1ms), the crossfade duration should be equal to the period. To do this the patch should be polyphonic, use the algorithm used in 12_13_grain_stream.maxpat" (other patch from the book).
I've been trying to do this all day without success. Inside gen, outside gen, using buffers, using lines, nothing has worked. I'm gonna paste here my patch and the poly~ patch, stuck at the voice selection stage. This works: the phasor impulses, as you see, cycle between the individual voices of the poly, so the starting point is there. I stripped the patches of the additions I made to make them simpler to understand.
How would you do this?
I tried outside gen (with wave~ objects) and inside gen, with sample operators, always with buffers. My idea was to implement a bell-shaped curve for every grain when pulsaretFreq < grainRate and read that curve with a phasor at 500hz (period 2ms), double the suggested duration cause it's not a real crossfade: it's a double fade that should (in my head, don't know if I'm wrong) act as a crossfade when the grains are really close. The signal of course would be multiplied by this curve. Then I would have done the same thing for grainRate > 1000hz, shortening the reading time.
But reading Microsound (and the book assignment above), it seems like what Roads did was an actual crossfade around the cutting points of each pulsar, scalable by an edge factor. You can find this control in PulsarGenerator and I'd like to implement it in my patch. But I've failed even at the simple attempt I just described... any help is welcome.
Thanks!
The main patch:
The inside poly~:
to add: since I made the patch polyphonic, it seems like it does some kind of crossfade/smoothing between pulsars when pulsaretFreq < grainRate on its own. When it was monophonic, you would hear a lot of artefacts when pF < gR and the waveform looked all edgy. When I enclosed the thing in poly~ I get a smoother waveform and less artefacts. Don't really know why this is cause I didn't implement any kind of crossfade or interpolation.
i haven´t read all, but i´ve read about the requirement to overlap grains, or in other words: making windows.
imho this is yet another case of a "polyphony" requirement and suggests that you use (another) layer of poly~, just as for pitch&key.
because this way you can easily extend it to 5-times overlap and/or save CPU in those moments or presets patches where overlapping is not needed.
when the first overlap happens, you will need a window with 90 degree phase shift (i.e. one complex oscillator or phasor ramp), from the 5th overlap on you will need offsets of 45 in addition and so on)
only this phase offset function for the overlapping windows (and the actual audio streams) will have to reside inside the poly~
Thanks, but what I'm missing is actually how to detect when an overlap happens... I think what you're describing regarding the overlap already happens, there are 8 instances so 8 possible overlaps.
Could you give an example in the patch? It's pretty simple, it could help a lot.
There's a different way of dealing with overlapping grains. Imagine you have a tape loop (modeled via a buffer~), and a process that is continuously reading the next sample and then erasing it. When a new grain happens, you *overdub* that grain into the future of the tape -- not replacing samples in the buffer~, but adding to them.
If you want to dig into gen~ there's a couple of examples of this in the gen examples folder (both start with "gen~.ola") -- one of them actually is a pulsar generator: gen~.ola.pulsar.maxpat
There's also more extensive examples and a detailed walkthrough of the process (and alternative methods) in the gen~ book https://cycling74.com/books/go :-)