Sample and loop

    Mar 22 2012 | 7:13 pm
    While working on something, I needed a way to capture a sample and then loop it, but have the loop length dependent on the length of the recording. I whipped up a solution in Gen~ and thought it might be useful for someone else.

    • Mar 22 2012 | 7:57 pm
      Fantastic bit of code. I just added functionality for length of loop and a reverse button.
    • Mar 22 2012 | 11:15 pm
      Also wouldn't be hard to add in a rate control (just multiply the sample playback increment before %). fun stuff
    • Mar 22 2012 | 11:52 pm
      Like this?
      I seem to get either silence or white noise when speed isn't 1.
    • Mar 23 2012 | 2:02 am
      It looks very interesting but (I'm a total noob with gen) what is the advantage of doing it in gen~ rather than with the classic groove~ object ?
      I'm currently building something for doing, more or less, the same thing and I didn't think of using gen~
      Thanks for any light.
    • Mar 23 2012 | 5:58 pm
      Close, but the * should go before the + actually. You are multiplying the increment amount to change speed. With the speed control, I also switched to a higher-quality interpolation mode so I can buttery smooth slow-downs. Try this:
      @Spip The advantage - for me at least - is the ability to have a fairly fine-grained control over how it works, and be able to expand and alter that at will. The groove~ object is a little bit of a black box that just works. Also, with my version, the phase of the loop is in sync with the recording. I could also expand this to do overdubbing with just a few more parts. Also, the @interp options in the Gen~ objects sound really good.
    • Mar 23 2012 | 6:46 pm
      Ah I see where I was going wrong. Sounds great!
      I just added a sync output like groove~ and an output for length of sample.
      I'm trying to change the start point of the sample though without too much luck.
      (it's getting messy!)
    • Mar 23 2012 | 7:12 pm
      I think this is what you want. This doesn't protect you from reading past the end of the buffer though, so you might want to further complicate that calculation by scaling the range based on length, or passing through % or something.
    • Mar 23 2012 | 7:25 pm
      Thanks for the explanation !
      New homeworks to do :)
    • Mar 23 2012 | 9:07 pm
      Thanks for posting this Andrew. Added a couple of simple things- stop, stutter, restart and overdub. I tried to use splat for the overdub and couldn't make it work. Could you post an example using splat?
    • Mar 23 2012 | 9:50 pm
      With splat you just divide the play position (in samples) by the buffer size, which you can get from the left outlet of the gen~ buffer object. Otherwise it's basically the same as poke (from what I can tell), but with linear interpolation if you're playing at different speeds.
    • Mar 24 2012 | 12:12 am
      >I could also expand this to do overdubbing with just a few more parts
      ... well... if it isn't too much bother... I'd sure like to get off and running with a looper like that... :) Pretty please?
    • Mar 24 2012 | 1:07 am
      Here's a patch with all the above contributions including overdub...
    • Mar 25 2012 | 4:56 pm
    • Mar 26 2012 | 11:58 pm
      It's such a privilege to be part of a community with so many smart, creative people. Not to mention generous. Thanks!!!
    • Mar 27 2012 | 2:38 am
      I know how to do this outside of gen~, but is there a way to ramp the attack/release inside gen~ to minimize clicks?
    • Mar 27 2012 | 5:32 pm
      There's no rule that EVERYTHING has to happen inside the gen~ object. ;) That said, here is a simple way to do linear ramps between 0 and 1 for an A/R envelope. Hope that helps.
    • Mar 28 2012 | 1:21 am
      Thank you, Andrew. :-) Just trying to keep learning all this wonderful new stuff.
    • Jul 19 2012 | 6:59 pm
      Interpolation using [splat] seems to not work when referencing a buffer in I doing something wrong, or is this not supported? If not, is there a possibility that it will be, or am I going to have to build something that does this myself? I'm trying to get rid of the need for ipoke~ in my tape looper patch.
    • Jul 21 2012 | 7:56 pm
      Hi Guys, This is a really sweet patch.
      I'm trying to fold it into a larger patch and i'm running into a couple noob roadblocks. First I'm trying to convert the gen looper to record in stereo (with the option of then controlling playback of each channel independently) so for example on play back the left channel could play forward and the right channel in reverse, or setting different start and end loop point. different speeds per channel etc.... I tried duplicating the inside of the gen~ changing the inputs on the copy but it's not wanting to record to a two channel buffer.
      The second question is, this will all live in a bpatcher of which i will load several instances of in a parent patcher and I've done this before by prefixing names in the bpatcher with #0. but when I try to use the #0 in the gen object to rename the buffer name i get a gen compile error saying that #0_lp is not a valid variable name. So I'm not sure how to get around that.
      Thanks for the help and the inspiring code to start with!
      Attached is a copy of my hacked version which is not working correctly.
    • Jul 25 2012 | 3:58 am
      Hi there jacobgolden,
      There's a couple of problems with that genpatcher -- first of all, the #0- prefixing doesn't work in gen patchers (and probably never will...). Instead, create a named buffer object (e.g. [buffer mybuf]) and in the parent Max patcher send a message "mybuf #0_lp" to the gen~. Also, [splat] does not support different @interp modes at the moment.
      I hope that helps. Here's the gen~ with those changes made:
    • Jul 31 2012 | 9:00 pm
      Thanks Graham i will check that out and report back. Best!
    • Jul 10 2013 | 12:55 am
      I tried to modify the patch (Rick's version with overdub), so the loop will sync to the transport. After a few hilarious attempts, I realized I may need help from people wiser than myself.
      Can anyone suggest a way to do this?
    • Dec 14 2013 | 9:08 pm
      But never work overdub for me!!!
    • Mar 18 2014 | 9:45 pm
      i would like to run 6 instances of this simultaneously but do not have gen!
      would someone kindly post 6 versions of this that address buffers with different names?
      ex. dummy, dummy1, dummy2, etc
      thank you so much!
    • May 27 2014 | 1:57 am
      Ever once in a while I come back to this patch and try and figure why the overdub part stopped working.
      [Splat] use to need different timing information than [Poke]. From what I found that's no longer true.
      They now seem to be the same except that [Splat] writes with linear interpolation between samples and
      overdubs the new and old samples.
      Anyway... here's a new working version and I added a new function, multiply I guess is the best name.
      It's basically a new loop that runs in sync with the original recording, except that the new loop can be
      two or three times or however many times as long as the original loop.
    • Dec 22 2014 | 4:22 pm
      Hi all, first post here.
      I’m very new to gen~ and trying to build a varispeed looper with overdub (i.e. what this patch does). I’ve got Max 7 and overdub wasn’t working properly, which I assume is because a) the new splat takes a sample or phase index just like the poke object (which Rick already noticed) and b) it automatically overdubs, i.e. mixes the incoming signal with the existing buffer (according to the mix value sent to the fourth inlet and the @overdubmode attribute). So I just stopped feeding the peak output into splat and it works like a charm (pasted below).
      But I’ve run into a problem: I get quite a bit of high-frequency digital noise when overdubbing at any speed other than 1. A similar effect happens with the groove~/poke~ combination I was using before, but it’s much subtler and only appears after multiple overdubs. Can anyone reproduce this problem – and any ideas how to solve it?
      (Audio interface is an Apogee One running @ 44.1 kHz, which usually gives pretty clean conversion.)
    • Dec 22 2014 | 5:06 pm
      From what you're describing that sounds like interpolation issues. So if you record (or overdub) at a rate higher than 1. you aren't writing every consecutive sample, hence leaving empty samples. Which when you go to play back, gives you that alias-y type sound.
      I've not mess with the gen objects/options too much but interpolation seems like it would be an attribute for the buffer/playback objects in gen.
    • Dec 23 2014 | 5:52 pm
      Yes, skipped frames may very well be the problem. Except that I do have peek (the playback object) set to spline interpolation. Splat is supposed to write with interpolation as well. What’s strange is that this combo sounds _worse_ than my groove~/poke~ setup, even though gen~ has higher-quality interpolation. And with groove~/poke~, smearing happens gradually, after several passes (which is exactly what you’d expected with skipped frames), whereas in this ~gen patch, the signal gets severely distorted on the very first overdub. (I also tried the trick of writing 3 samples ahead/behind of playback in case the problem was caused by writing to the same buffer.)
      Several other threads on this forum suggest ping-ponging between two buffers/pokes; i.e. always writing at sample-rate (without interpolation) and mixing that signal with the output from the interpolated, tempo-shifted playback object. I’ve almost got this ~gen patch working that way, though I’m not quite sure how best to build the pingpong mechanism. I’ll post that too when it’s ready.
      Also, Rodrigo: a huge thanks for the groove~/poke~ looper you posted. I used it as a template for my own, but now I’ve put everything inside polybuffer~ and poly~ objects (which you also suggested on another thread) so I can add buffer~ and groove~ objects on the fly, keeping each overdub layer separate so I can warp them non-destructively. A little CPU-intensive, but otherwise works quite well. I’d be happy to share it if anyone’s interested.
    • Dec 23 2014 | 6:14 pm
      That's a cool idea for the polybuffer. I would imagine easy to setup an 'undo' kind of thing by doing that.
      I've recently commissioned a custom looper external that I will share for free(source and all) once it's ready. It does all the bells-and-whistels looping that I want/need, and nice and click free.
    • Dec 23 2014 | 7:06 pm
      I'm very much looking forward to that, Rodrigo - will it be able to loop something that isn't free-jazz drums, too? :)
    • Dec 23 2014 | 7:28 pm
      Hehe, yeah.
    • Dec 23 2014 | 9:55 pm
      fuck yeah. I'm all over it, then. I'm the worst drummer on the planet.
    • Dec 24 2014 | 5:48 pm
      Hi Ian, may you post your patch ? I struggle with poly~ and am curious to see working bits of patching using it on this looper stuff.
    • Jan 02 2015 | 7:46 pm
      Here you go, Stephane. Sorry for the delay (needed to clean up the patching quite a bit). Not sure how to bundle abstractions with pasted code so I’ve just attached the patch as a .zip. This is very much a prototype: no undo yet (but easy to implement); no record in reverse. Should give you an idea how to go about using poly~ for a looper though. Built entirely in Max 7 so watch out for compatibility issues.
      Rodrigo, really looking forward to seeing your external. Very generous of you to offer to share it with the community :).
    • Jan 07 2015 | 3:48 pm
      Any ideas how I could implement an "undo overdub" function in this patch?
      - justin
    • Feb 24 2015 | 8:23 pm
      I have prepared a M4L audio effect starting from Rick's patch. I have eliminated a click coming from gen out4 mix and automated the cleaning of the buffers for every new record action.
      Now it works great!! Thanks Rick
      Would be great to add a undo button. Any idea?
    • Mar 08 2015 | 8:02 pm
      BTW, about the skipped frames / ipoke~ replacement, here's something I was playing with:
    • Mar 15 2015 | 10:54 pm
      Wow you people are so awesome! This patch is exactly what I was trying to figure out how to do with groove~ but was failing as it seems the buffer~ that the groove~ reads from has to have a specified length and this totally solves that!
      Curious question, would it be possible to run more than one of these simultaneously but independently? I'm looking at the simplest version, without any overdubs (unnecessary because I want to run three or so of them independently so i can pitch/speed them around live individually)
      I'm also a bit confused by the buffer, since nothing is running into it. I tried doubling the patch and adding a buffer named dummy2 30000 (is the 30000 just to have a maximum length?) but it doesn't really work...
      New to this but so excited with the opportunities!!(I'm a violinist at Oberlin Conservatory)
    • Mar 16 2015 | 3:36 am
      Here's the patcher I'm currently running
    • Mar 16 2015 | 5:22 am
      "Curious question, would it be possible to run more than one of these simultaneously but independently?" Yes.
      "I’m also a bit confused by the buffer, since nothing is running into it."
      The poke message inside the gen writes to the buffer.
      "I tried doubling the patch and adding a buffer named dummy2 30000 but it doesn’t really work…" Did you change the buffer name inside the gen to dummy2?
      "(is the 30000 just to have a maximum length?)" Yes
    • May 29 2015 | 3:37 pm
      hey guys,
      that's some fantastic work floating around in this thread, kudos!
      i wanted to wrap my head around the patches based on Anrews code, but there seemed to me a simple problem for real life usage:
      if you load up the patch without having used the record-function, the gen~-code will pass through the audio. after recording it won't. is there a solution to even that out: either bypass all the time, especially when overdubbing?
    • Sep 19 2015 | 3:14 pm
      Amazing work ladies and gentlemen. I have been enjoying the fruits of your labor, thoroughly! Thank you.
      But, of course the curious mind wants to rework the formula.
      I would love to have multiple loops that are synced. Is this possible with this beautiful patch? I had an idea to take the length of loop one and port it into the length of loop two after loop one was done being recorded, thus determining the boundaries within which you could record for loop two. Is this sensible?
      Below is what I am working with if anyone has any suggestions. In this state the loops appear to be synced, but the recording of the second loop shifts position initially, moving from when you actually recorded it against the first loop.
      Much appreciated!