granary - a hybrid granulation engine

    Built using vanilla MaxMSP and Gen objects.

    • Aug 20 2014 | 6:48 pm
      (see latest post of mine below, for the newerist version)
    • Aug 20 2014 | 8:37 pm
      Great work, Brendan! Thanks for sharing!
    • Oct 07 2014 | 9:47 pm
      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.
      Thank you! Thank you!
    • Oct 10 2014 | 5:54 am
      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?
    • Oct 10 2014 | 8:28 am
      Hallo Jaren
      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.
      ps Andreas Wetterberg recently asked about the blur/range thing too, so I hacked a cleaner version for him; here it is attached:
      pps please do point me to any artistic uses (performance audio/video) featuring 'granary'
      Best regards Brendan
    • Oct 10 2014 | 8:45 am
      And besides Henke's Monolake, here's another example of a highly sophisticated granulation synthesis engine, by Tom Maisey:
      This one is particularly sexy. And to anyone else watching: NO, I am not being typically humble; there are other really good grain engines out there, these are facts.
    • Oct 11 2014 | 7:36 am
      Fantastic explanation, thank you Brendan that clears a lot up (and for an already exceptionally clean patch to begin with!)
      Thanks for pointing me in the right direction for some tinkering, I've already learned a lot from this instrument
    • Jun 16 2015 | 1:51 am
      Hi Brendan,
      Really love this. Especially the interonset.
      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)
    • Jun 16 2015 | 3:36 pm
      Hi Wil 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)
      Best Brendan
    • Jun 17 2015 | 2:15 am
      awesome brendan!
      thanks for this. question: is this the newest version? jj.panning is in the folder (and different than the last version), but not in the actual patch.
    • Jun 17 2015 | 12:30 pm
      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.
    • Jun 17 2015 | 12:44 pm
      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!!
      Thanks again.
    • Jul 21 2015 | 8:24 pm
      And here's a further update (for the users above, I just increased the voice count to 24; hits my CPU for about 30 ~ 50%)
    • Nov 08 2015 | 1:11 pm
      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. Enjoy
    • Nov 08 2015 | 7:19 pm
      A very handsome piece of work, Brendan. Nicely done.
    • Nov 11 2015 | 9:38 pm
      I can't even seem to find where to download the patch to be honest....? HALP
    • Nov 11 2015 | 9:53 pm
      Read up two postings from yours - the bit that says "Final update...."
      Right below Brendan's name, there's a .zip file. Click on it.
    • Nov 11 2015 | 9:57 pm
      struggling to find the patch also, would love to have a look! can anyone point me in the right direction? Apologies if it's really obvious!
    • Nov 11 2015 | 10:11 pm
      Hello! Can you post the zip again? I can not find it. thank you
    • Nov 11 2015 | 10:49 pm
      Let's try this again. In your browser window, search for the string "Final update as of 08/11/15." See that .zip file listed under Brendan's name? Click on it.
    • Nov 12 2015 | 12:03 am
      hahah now I feel silly but to my simple mind it wasn't that easy! Thanks for getting back guys. Gotta love the C74 community.
      Now time to tweak!
    • Nov 12 2015 | 12:46 am
      Awesome Brendan, thanks a lot!
    • Nov 12 2015 | 12:58 pm
      ; )
    • Nov 12 2015 | 2:25 pm
      hi Brendan, some things...
      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)
    • Nov 12 2015 | 7:59 pm
      Hi David 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.
      Gimme a day or two Best Brendan
    • Nov 13 2015 | 11:33 am
    • Nov 13 2015 | 4:32 pm
      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).
    • Nov 23 2015 | 11:11 am
      Any looplength setting other than 0 generates no sound. Is that normal behavior ?
    • Nov 23 2015 | 12:16 pm
      Hi Stephane 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.
      I'll upload a short demo video asap.
    • Nov 27 2015 | 9:23 am
      Does this problem persist
    • Dec 10 2015 | 3:13 pm
      "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.
      .r. -
    • Dec 10 2015 | 10:39 pm
      Hi 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
      Thanks for flagging this
    • Dec 11 2015 | 9:55 am
      FYI 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?)
    • Dec 11 2015 | 12:05 pm
      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. :)
      .r. -
    • Mar 25 2017 | 8:32 am
      Glad you like it, and solved your issue. I currently use only PD and Supercollider as I've abandoned commercial software for a while, so I can't maintain and update Granary. But I'll be back soon ;)
    • Jul 16 2018 | 8:49 am
      Hey Brendan,
      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?
    • Oct 04 2018 | 12:17 am
      Hi Brendan, I use Granary as my sample player of choice in a Max For Live audio effect found here
      It does Spectral Convolution of the Input audio with a Sample loaded into Granary.
      I had to add a bypassable limiter because the results really can be preposterously loud and other times way too quiet. It depends on the Input and the Sample.
    • Oct 04 2018 | 12:50 pm
      Hi Eric 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)
    • Oct 04 2018 | 12:55 pm
      @uresses Sorry to have missed your comment; does this problem persist, and have you recently updated Max or your OS?
    • Oct 04 2018 | 7:37 pm
      It might be older, but at least it works in 64bit. I used to use Grainstretch~ and FFtease but I can't get either of those to work on Win10. I suspect they only work on macs now.
      Unfortunately, Live10 Suite doesn't let you edit gen~. Also, I don't really have the DSP wherewithal to hack that kind of thing.
    • Oct 04 2018 | 7:50 pm
      Also, here is a picture of my fix for the buffer overun problem mentioned back in Dec 10 2015
      Basically, when the scrub counter becomes larger than the filesize, it resets the phasor to 0.
    • Oct 04 2018 | 8:11 pm
      re amplitude, could do something to boost/limit amplitude inside the poly~ ? I could send you my own voice subpatch, via PM if you need/want.
    • Oct 04 2018 | 8:39 pm