ola.granular help me understand
I really like the sound of the ola.granular patch in the gen examples and would love to adapt it for my own uses. However, I'm not yet very good at reading gen code. Can someone help me undertsand what's going on?
Specifically, I/m wondering why I get clicks/artifacts sometimes when frecency is low and the density is high (and also sometimes when there "ahead" param has a large value). I can still hear the windowing working, too... hmm
Hi there,
The way the OLA (overlap-add) examples work is by using a kind of look-ahead buffer to write the grains into, which is constantly read and erased. Every time a new grain is spawned, the waveform of the entire future grain is written into this buffer, using overlap-add (so that other future grains are not overwritten). This all happens in the codebox. It's a gen~ patcher, so the codebox runs on every sample of the real-time audio process. It first checks whether on the current sample, we should spawn a new grain. Then it has a for-loop to write each successive sample of the grain, including computing its window, its content, the stereo pan, etc., and where in the overlap-add buffer it should be written. The parameters specify the density of grains, their duration, and other properties for the synthesis of each one.
A separate process continuously reads out the result of this buffer, erasing as it goes (a bit like a tape head). This is the max patching code to the right of the codebox.
The clicks when density is high and frequency is low (duration is long) will be because of overloading the audio thread in this case (making dropouts). There's lots of different ways of writing granular synths, and OLA is just one of them. The gen~ examples are particularly well-suited to the use of very short duration grains. As the grain duration gets bigger, there's a higher chance of it causing a dropout, as it is trying to compute and write a large number of values within a single audio thread buffer (computing the entire grain in one shot). For working with longer durations you'd probably want to use an algorithm that keeps track of active grains and synthesizes them in chunks as time passes instead. The code for that would be longer and harder to understand, which is why I thought that the simpler one-shot OLA method would be better for the gen~ examples folder.
Hope that helps!
Graham
Hello Graham
I don't suppose you have any references for the algorithm you describe in the latter half of your reply?
All the best, Ben.
It's basically the same as a note allocator connecting two parts: a note/grain scheduler, and the note/grain algorithms themselves.
For synchronous granular, the scheduler will be something like a counter or phasor, triggering a new note each time it cycles. For asynchronous, you could use a probability test, e.g. [noise] -> [abs] -> [< probability/samplerate], where probability is a [param] representing the chance per second to spawn a grain. In both cases, the output is a single-sample trigger to request a new note/grain.
For the note/grain events themselves, you could do them as grainplayer subpatchers/abstractions, with a bunch of them in the gen patcher (this defines your maximum polyphony, or maximum grain overlap). Each event will have an envelope player, driven by some accumulator such as [counter] or [+=] routing into the envelope function. You can use the position of this accumulator to check whether the note/grain is "busy": that is, if the accumulator position is greater than zero and less than the grain duration.
For the allocation itself, one way I have been doing this is to pass the new event trigger to the first grainplayer subpatch, and internally, if the note is busy, it passes the trigger back out, and on to the next grainplayer subpatch. If the grain is not busy, it will reset its grain counter to zero and set the grain parameters, thus starting a new grain.
The outputs of all the grain subpatchers are then just added together and output.
Thank you that's a good explanantion I'll give it a go.
Hi! I'm really interested in understanding the .ola gen objects. However, I haven't been able to find any examples that include .ola.granular or .ola.pulsar. Do you know where I can find them? Thanks!
help->examples->gen