Advanced Looper (question(s))

    Apr 03 2014 | 7:54 pm
    So I've been rebuilding the core parts of my main performance max patch slowly and I've finally come to the gnarliest part of my code, that needs the most amount of reworking. The main looper/sampler.
    I've been using a groove/poke combo for a few years now as the main looper and it kind of does what I want, but it's not perfect and adding some new features puts me into new territory for me, so I decided to ask the forum for help here.
    First, what the basic spec is: -a varispeed dynamically sized looper than can overdub -can change speed/direction while recording -can jump arbitrarily while recording
    So my looper does that now, though it doesn't declick all of those things.
    Things I want to add, and have no idea how to approach: -undo layers -oversampled (so halfspeed still sounds crisp) -play "past zero" (as in play forward from 0.9 to 0.1 in a loop) -not click anywhere (at start/end, while jumping/recording)
    -The undo layers I have literally now idea how to go about that. I'm presuming you record to separate buffers and have them playback simultaneously but that seems quite messy and hard to sync.
    -Oversampling I kind of understand, just stick it in a poly~, but where I'm fuzzy about is the math. In my looper below I'm driving poke via groove's sync outlet by multiplying it by the total samples in the buffer. When oversampling, do you put the buffer inside the poly~ and adjust the math so it's twice as much? (ie a 1sec buffer would have 88200 samples in it?).
    -The "play past zero" thing has kicked my butt for a bit as groove does not work that way at all, which leads me to believe that I need a different playback/interpolator. But which?
    -The 'declicking' is a multilayered problem, so no clear answer here.
    Any help/suggestions in terms of objects/approaches would be welcome.
    Here's a basic version of the looper I use. This is quite old and the code has changed a bit since then, but it's so entangled with other bits of code it's easier to just put this isolated code here.

    • Apr 04 2014 | 12:43 am
      "The undo layers I have literally now idea how to go about that."
      if you have a set amount of maximum layers(say up to 4), you could also write to separate buffers, then copy/mix to one buffer for playback only(there's an mxj object that copies from buffer to buffer)... if that makes sense(separate your design thinking between playback and record stages... buffers used to keep recording don't necessarily need to be the same ones plaid back from).
      also, what if you created a recording mechanism, whereby every new recording simply created a new buffer on the fly. using gen~ you can switch to newly created buffers(and then destroy old unused ones)... processing or mixing them into even newer ones... meh... i'm daydreaming. just a random thought... maybe not worth the effort :p but this might solve clicking where random recording points are chosen then plaid back from other random least at the recording stage(one window for each on-the-fly sized buffer... or declicking at the playback stage only if these on-the-fly buffers were used in full... so many options to think about). declicking at playback stage is easier, you'll figure that out :) (i'm assuming you want freedom to go wherever in the buffer~ at both recording and playback stages... so declicking at recording stage maybe necessary... otherwise, with quantization boundaries, you wouldn't need any of this and could just declick at playback stage...)
      For play-passed-zero: ya, something other than groove~... signal-based play-position control... play~ and wave~ have some kind of interpolation :p index~ does not... depends on how you want to send control signal... BUT! i highly recommend gen~! in that case, check out the 'sample' object(the index attribute opens it up for different control options too)... and this way you could build in your own amp-envelope for declicking too... either way, whether in gen~ or using play~/wave~/index~(or even 2dwave~ if you feel adventurous), play-passed-zero is just a wraparound function: in gen~ you can use 'wrap' or roll your own fully organic grass fed solution, or in generic msp, pong~ is pretty helpful for wrapping, else roll your own using something like this logic: if x > max, x = x - max, else etc. etc. etc. :D
      (no idea about oversampling :p)
      just my 2cents
      edit: must keep track of that "keep a log of this edit" thing always checked on default now on these forums... weird :p
    • Apr 04 2014 | 7:38 am
      Thanks for that!
      I was thinking, after making the post, about using something like polybuffer and a poly-based playback object. A bit memory expensive, but not so big a deal nowadays. So recording 4 layers and playing back 4 layers would be 4 individual playback objects going at once (via poly). I didn't think to mix/bounce as I went.
      That makes more sense than having a single long buffer to play non-linear bits from.
      And thinking of declicking for recording and playback stages independently is a good idea. There will have to be declicking at both as jumping arbitrarily will happen at both (without quantization). There will be some mlr-style 8 slice jumping, but most of it won't be that.
      For most of this stuff I will likely have to be in gen as I'll need tight sample control to declick all the jumping around. I'm getting better at straight code now too (learning a lot of js/ruby lately) so that isn't as scary as it once appeared.
    • Apr 04 2014 | 8:37 am
      ye ! with bpolybuffer you can add remove buffer easily, and keep one buffer to write the sum of the other buffers in polybuffer ! maybe the first polybuffer's buffer - it's possible that no poly~ based playback is needed ; even so you cna add poly voices on the fly, this might be not a so good idea though because the totality of poly voices need be reconstructed when one does that iirc. i'm also trying to create a similar mechanism so this topic is welcome :) for oversampling : math is right afaik, one second at 88200 would allow twice the quality in case of a slowing down. an "up 2" argument will do hte trick. Then i don't know however, if you play htat back form your main patcher, you buffer will have 88200 samples so you will have to play it at twice the speed to have normal speed... i think... this needs testing...
    • Apr 04 2014 | 10:34 am
      So in that scenario, every time an undo layer is peeled away it would require instantly re-summing all other layers to the 'playback' buffer?
      I tried doing some testing with the upsampling thing a while back but didn't manage to figure out. My poly knowledge was shittier then, so I wasn't able to determine wether I was fucking up or it wasn't working like I thought (ie buffer outside of the poly).
    • Jul 01 2014 | 7:46 pm
      Hey Rodrigo, how's the progress going with this? I'm determined to make my own looper/live-sampler for performances with acoustic instruments, and I'm learning more about Max (I'm novice) to this end. I came across your trusty old looper last year, on the thread you posted when you originally made it, and it was an inspiration. I'm looking forward to seeing what you come up with now in 2014.
      Here are some ideas i've been thinking about for the last few months: a workstation that will allow me to control the pich/speed/size/amplitude/etc parameters of ~3 separate loopers, with each looper capable of overdubbing. This is so the loopers can each be seperate sizes, relating to one another in ratios (or not). This way the rhythms and pitches in the seperate loop banks interact in interesting ways (e.g. evolving counterpoint) over time. Also, I think it'd be great if a looper was able to start playback while it was still recording, e.g. in Bach's canons when halfway through the exposition of a melody, another permutation of it is looped under the original, but at a different pitch. Last, I'd like to make minute real-time adjustments to the pitch of my loopers, for example raise the pitch of the sample in a looper up by a semitone for a second so that it temporarily changes how it harmonizes with another loop, and then have the option of saving my parameter changes to the loops or making them temporary.
      Any thoughts on this?
    • Jul 01 2014 | 11:59 pm
      I've started some work on it, but it will be slow going for the time being. Going to have to do a lot in gen, for all the driving stuff, and have to figure out the logic for it all before anything else.
    • Jul 02 2014 | 5:01 pm
      Good luck!
    • Oct 09 2014 | 1:36 am
      Any progress in this? I've been studying gen~ and have found a bunch of gen~ looper examples on the forums, and would be interested in how you are organizing yours to achieve this high-quality timestretching and de-clicking.
    • Oct 09 2014 | 7:20 am
      Not really. At the moment I'm commissioning something to build something like what I want as an external (or gen patch), as quite a bit of it is beyond my knowledge (and definitely my free time!).
      I will share what I have when it's done too either way.
    • Oct 09 2014 | 7:33 am
      Wow that's really generous of you--thank you.