[shared] granular in codebox
Not the most elegant code, but it shows it's possible.
cool
Really looking forward to hacking and learning from this, thanks
Brendan
I'm loving this - I can't believe we're seeing new life in granular synthesis. Whodathunkit?
This is my little hack - manual position + random pos.
Can anyone point me towards how to mess with the pitch here? It's in the way we poke isn't it? (ooer)
great, thank you!
@Andreas
do you think you would have the time/inclination to annotate that code (including jvkr's original) in more detail please? While the method is fairly transparent to me, some of the gen-specific operations/functions (haha, ALL of them) are foreign to me, you know, like the Buffer and Data arrays, and the logic behind the functions. I'd like to see if this approach is suitable for (better than) my own Granary project.
Why?
Enhanced self-learning, and I'd really like to get down to the DNA/sample level of granular synthesis.
Best
Brendan
hey Brendan, I'm terribly sorry, but I can't annotate for shit, really. I'm an idiot who just pokes the right places, I guess.
Me too Andreas, but just pretend like I'm a TOTAL newb.
;-)
That's the spirit.
I'll continue to attempt to break apart this thing. And I won't stop until I accidentally make a hanning window.
I mean, I can hack most MaxMSP patches, freeform and improvised without really knowing how I'm doing it. But gen coding is sadly a closed book. Envy.
Hanning window. Exactly!
jvkr, if you're still with us rambling fools, this is a great example, very nice for the hackers.
If you can find it in your heart to expand it, that'd be amazing.
Right now I'am abroad with reduced access to internet. In a few days I'm back with some more details.
Brilliant.
I'm back. First of all this code is an attempt to create a variable amount of paralel events based on a single unit generator definition. Although this can't really be achieved in max or gen~, a for-loop represent the tool to evaluate multiple instances in the time span of a single sample. The code here is based on keeping a log of all active grains and evaluate their progress on a sample by sample basis. This is indeed not very elegant, but functional for this problem.
This log keeps three values for each grain: the current position, step value, and a threshold value. Every sample the step value is added to the current position (which represents progress through a buffer) until the threshold is reached, which signifies the end of a grain. The interaction with the log, represented by the Data object, requires some flag to signal that a certain entry in this log either represents an active grain, or is free. This is done with the first entry, the current position. When it is set to zero, it means free, otherwise this entry is occupied.
A new entry is added to the log whenever a non-zero value arrives at the input. A for-loop then walks through the log until it finds an empty entry and uses that to store values for a new grain. A second for loop produces that actual grains by again walking through all log entries, test whether they're occupied and if so, use the stored values to read from the buffer, and update the position value for the next run.
The object that is used to read sound from the buffer is sample()
, which requires as arguments the name of a buffer and a phase value between 0 and 1, signifying the start and end of the buffer. This is comparable to the wave~ object in max.
A more abundantly annotated version of the code is added as a max patch.
As noticed, this version does not perform any windowing. Windowing requires at least four entries to log, and is easiest with a phase that goes from 0-1, that is scaled to a duration and offset into the buffer. Values in the log then are: current phase, phase step value, buffer offset, duration.
Hope this all makes it a little more clear.
@jvkr
Many many thanks for taking the time to put this explanation together, much appreciated. I hope that as this thread develops I can make some contribution to your own experiments in gen~ for granulation.
Cheers
Brendan
Thanks jvkr!
edit: This is really well doc'd. There's a high chance I'll learn something tonight.
... I didn't even know you could do for loops in gen! really interesting patch, thank you so much for sharing and I can't wait to have a proper play around with it.
@Wetterberg : Don't know if you ended up succeeding at adding windowing but I finally managed to do it.
I took jvkr's original patch and also added a few other params : random panning amount,random grain duration amount, grain probability.
Here it is:
Nice work, Q.
I took the liberty of converting your text version to one that uses the
Select stuff in patch > "Copy Compressed" (Edit menu) > Paste into patch
approach, which is a lot easier for others to make use of. Hope that's okay.
@GREGORY : No problem at all ! Will use Copy Compressed for the next patches ;)
@RAJA : i think the "floor" object comes from the jasch package which you can find in the package manager but yeak int definitely works too !
I started to build a live granular processor by expanding a bit further the patch. I like the results and it does sound great when the granulator feeds back its output (especially when you go up an octave, nice shimmer effect) to the buffer capturing the audio.
However, I get some kind of artefacts from time to time which I haven't been able to isolate and get rid of. At first I thought it occured when the writing process reaches then end of the buffer and starts again at 0 so I implemented a declicking envelope tp fade in and out at the buffer boundary. But I still get thos artefacts.
Anyway, here's the patch, hope you have fun with it !