Brendan, this patch means a lot to me! I've been using grainstretch~ and absolutely loving it, and have been hoping to create a tangible 'instrument' for live-looping with access to any portion of the buffer, with time and pitch shifting available to the extreme. However, grainstretch~ was a somewhat mysterious external that I could not easily learn from nor add to, and I was concerned about it remaining compatible with my system.
My professor at university advised I try and build a grainstretch~ equivalent from vanilla max objects, in gen~, but this project had been difficult for me for quite some time. Finding your patch was like discovering a pot of gold. So elegant, so simple, so powerful! I plan to do quite a bit of hacking with it, and hope to create something really cool for live performances with this excellent tool to help get me started.
Brendan I've really been enjoying your patch! After some study, I have a few questions:
Could you explain the meaning and characteristics of "de-correlated grain pitch and grain size"?
What are the blur and interonset parameters for? Blur seems to have a similar effect to "range". I have not come across those in other granular synthesis patches. You mention in the patch that "In true granulation synthesis, interonset duration and even shape is user-defined." This patch sure sounds like "true granulation synthesis", why do you say it's not?
Also, I have experimented with adding variable grainsize to the patch--I am hoping to produce highly rhythmic loops the length of the grain size (a la grainstretch~), but your instrument is exceptionally smooth no matter what I try. Is it possible to create very rhythmic grains without re-working the heart of the patch?
interesting and pertinent questions; I'll try to address them in point form:
- de-correlated grain pitch and grain size: in many other granular playback/synthesis engines I have tried by other authors, changing the SIZE of the grain (by altering the phasor frequency) also changes its pitch, and vice-versa. I have simply made the grain size fixed, and a function of the pitch scalar. Effectively, changing grain pitch does not alter the size of the grain.
- the 'blur' parameter operates at around 0. ~ 5ms; you're right, it is a 'range' control, but I wanted really precise control at the sub-millisecond level in this domain, hence the two variables. This is because spectral artefacts are unavoidable if playback speed and pitch are anything other than 1. Now, I haven't done enough research to know WHY this is the case, it is a subjective observation from many years of tweaking this algorithm. Effectively, I use the 'blur' control to vary normal playback, and also for some cool comb-filter/AM type resonances when re-pitching or changing playback speed (when blur = 0.); the 'range' control is for more cloud-like textures, which leads me to . . .
- the difference between granular playback and granulation synthesis: "granary" is a hybrid engine - it allows relatively artefact-free repitching and speed control (manual scrubbing), but it also aspires to granulation synthesis textures, through the interonset, spread and range controls. If this was a true granulation synthesis engine, then we would expect control over grain size, window shape, interonset TIME, etc etc. These are the control parameters (amongst several others) fundamental to granulation synthesis (see Robert Henke's Monolake for example). Therefore, 'granary' reveals a hybrid approach. I wanted smooth playback/scrubbing AND some re-synthesis possibilities.
- yes, the fixed grain size is the blue [sig~ 80] inside my [jj.pitchSize] abstraction; mess with this if you like, for variable grain size, but you will lose the buttery goodness of smooth manula scrubbing; as for rhythmic grains, you will have to hack the interonset section of the gen~ abstraction, which I don't have time to do sadly. Try lowering the voice count of the poly~ (to 4 or 2), see how that sounds.
Andreas Wetterberg recently asked about the blur/range thing too, so I hacked a cleaner version for him; here it is attached:
please do point me to any artistic uses (performance audio/video) featuring 'granary'
One question. When turning scrubbing on and off, autoplay always begins where you left off. Where do I send a zero message so when I turn manual scrubbing off autoplay starts at zero?
I tried sending zero to the scrub between the r and s loc_GUI2 but if I add a multi slider set to continuous data output to track the position I can see that the position jumps back to wherever I left off. So it is being stored somewhere and I cannot find it.
Sorry if this is a simple question that I should have been able to figure out on my own.
edit: its the phasor. starts and stops where it left off. what message do I send to make it start at zero?
edit: ahhh, the second inlet of phasor resets the phase. (finger > flick > head)
glad you like it; and also happy that you sussed out where to make your own changes - exactly! Resetting the phase of the scrub/playback phasor (it's inside [jj.scrub] I think) will force playback from 0ms. Here's a more refined and more functional version, which I think sounds even better (attached)
Well-spotted again. Yes, this is the latest version; I forgot to delete jj.panning. I found that panning inside the granulator was introducing additional and unwanted artefacts through low frequency amplitude modulation, so I removed it. You could reinstate it, outside the granulator if you need it.
Nah I dont need it. Using a different type of panning anyway. Was just wondering cause it was different than the older version. Definitely don't want art (e) facts, those are for historians. We need fact free art!!
Final update as of 08/11/15, purely cosmetic with some simple GUI enhancements. Zip attached, and video demo here; as always patch is fully annotated for your learning/hacking pleasure.
1. anything I need to be aware of if I replace my existing granary objects? They're actually called "granny" (top level ) and subFin (gen patch), so I'm guessing that they are a few generations back!
2. Also - I use between 4 & 6 instances of granny in my patches, and at the moment I have to create separate instances of each of the elements (granny1, granny2 etc, subFin1, subFin2 etc) to allow for reach instance to have its own named buffer...
granny1 (top level patcher) contains named buffer & poly~ subFin_1, which contains the gen~ object & a few other bits & pieces. The gen object has been edited to refer to the specific buffer (in this case sampill_1 ).
If this was a patch using an external I would expect to be able to specify the buffer to use either using an argument or by sending a message to the object (e.g. set buffer ...). I can't see any way to do this with a gen~ - is it not possible? (i.e. do I have to keep creating multiple instances of the gen & surrounding patch?)
3. You mentioned you'd like to hear examples of pieces using granary . The sounds in this piece were created entirely using your object...
(earlier pieces on my page will have been created using either granary or elasticx~. I know for sure that All Remains & Last Day used granary)
thanks for continuing to use Granary, and thanks for your questions:
1. I can't recall if the version you currently have (subFin etc) uses my own abstractions (jj.pitchsize, etc.). Can I suggest that you download the zip, extract it and point Max at it. The user controls are now: pitch, pitch rand, grain shape, blur, interonset; playback speed, freeze, loop length and loop start. If these parameter controls are vastly different from the version of granary you have I can only apologise - I haven't been tracking changes and I am constantly improving it (I'm working on new elements as we speak !).
2. Addressing different buffers, for different audio file sources (which is what I guess you're doing) shouldn't be too much of a challenge, I'd need to look at the helpfile for buffer~ in a little more detail; maybe polybuffer~, or dynamic name changes to buffers.
3. Thank you so much for these, very intriguing to see how others use Granary.
Mmm, yes. I will give that a try with my existing setup before I replace the older modules. As long as max doesn't complain about multiple modules with the same named buffer (before the pairing is changed by the message).
this is not expected behaviour; looplength (0.0 ~ 1.0) determines how much of the audio is looped: at 1.0 the whole file is read, at 0.0 one 'grain' is read, effectively freezing playback and allowing loopstart to act as an alternative 'scrub' control. So be aware of any silences in your source audio.
"Any looplength setting other than 0 generates no sound. Is that normal behavior ?"
It seems to be a bug, after a few selection of loop length of 0, the patch stops generating sound for any other length except 0. After restarting the patch everything works for a while, then again the same problem occurs. The problem persist from the first time I played around with granary patch. I did an attempt to find and fix the problem, 'till now no success.
I just spent 20 minutes messing around with loop-length and loop-start parameters and have to confirm that I can indeed recreate this bug. Sorry guys; I strongly suspect it has something to do with the [jj.scrub] abstraction, which uses a separate single phasor to control playback speed/direction/length; the ramp often overruns the end of the buffer size, when loop start is anything other than 0ms into the file. Hmmm. No time tmoro to fix, but I will solve this over the weekend
anecdotally, it seems the playback phasor is getting a freq value of zero from somewhere, and then refusing to accept further freq values. Should be simple to repair. I'm contemplating translating the 2 abstractions (for playback and grain size etc) into gen~ patches, maybe wrap as much as I can up into one single gen grain.
(Check me out, tracking bug fixes! Can I add the title Developer to my CV now hehe?)
thanks for the fast replay! indeed when You reinitiate the phasor~ object in the jj.scrub abstraction everything works fine. very strange bug!
curious of the gen versions of the abstractions and yes you can add Developer to your CV. :)
Your patch was working fine (brilliantly actually, i absolutely LOVE it) and suddenly I'm not seeing any waveforms in the visualiser, not hearing any audio when I add files. It feels like there is a disconnect somewhere and I'm not smart enough to work it out as I'm a pretty newbie max user/coder. Any chance you know what's the problem?
I'm glad this 'older' work is still of value! The interface looks very neat. I did notice an issue with variable amplitude: when playback is normal, with no blurring, the amplitude is 'normal', but it then drops whenever blur is engaged. I considered using some sort of compensatory algorithm that boosted grains. Maybe have a look inside the poly~ or gen~ blocks.
I'd love to see a demo video of how you're using this ;)
(I have ported this to PurrData and I'm in the process of re-writing it for Supercollider, but I really miss gen~ as my licence has expired)