Strategies to loop/timestretch audio samples in Patterns

Hi! I don’t think that it entirely solves the problem. However, I’ve learned quite a lot about miSCellaneous_lib and I might use it for granulation! Impressive work, it sounds quite nice. It might be helpful to describe my specific setup a little more. I am hacking a very terse syntax for live improvisation with audio samples. My current non-functional audio looper is invoked like this:

// All the following is running in ProxySpace.push(s.boot)

// Starts looping the amen1 sound from sample library
~test == [sp: "amen1", nb: 0, db: 0, dur: 8];

// Adding a fixed lowpass filter on the given sound
~test.fx1(1, {arg in; LPF.ar(in, 1500)});

I am using quite a lot of operators (>>, ->, =>) added to NodeProxy to handle different flavours of Pbind (Pmono, simple sampling, loop sampling). Under the hood, this system is using a specific type of Event that handles sample allocation, defines the quant, deals with syntax shortcuts, etc.

/* Audio Looper (sample playback) */
== {
  arg pattern;
  pattern = EventShortener.findShortcuts(pattern);
  pattern = pattern ++ [\type, \buboLoopEvent];
  this[0] = Pbind(*pattern);
  this.quant = pattern[pattern.indexOf('dur') + 1];
  // fadeTime is important for loops
  this.fadeTime = 2;
  this.play;
  ^this
}

The buboLoopEvent is defined like this:

      Event.addEventType(\buboLoopEvent, {
        arg server;
        if (~sp.notNil && ~nb.notNil, {
          ~sp = ~sp ?? 'default';
          ~nb = ~nb ?? 0;
          ~buf = Bank(~sp)[~nb % Bank(~sp).paths.size];
          if (Bank(~sp).metadata[~nb % Bank(~sp).size][\numChannels] == 1) {
              ~instrument = \looperMono;
          } {
              ~instrument = \looperStereo;
          };
        });
        ~type = \note;
        currentEnvironment.play;
      });

This is very hacky but more than enough for simple beat/rhythm oriented improvisations. I can manually deal with more complex use cases by using the regular SC syntax. The final piece in the equation is the SynthDef itself, borrowed and adapted from the tutorial mentioned above:

z = SynthDef(\looperStereo,
      {arg out, buf = 0;
          var sig,env ;
          sig = Mix.ar(
            PlayBuf.ar(
              2,
              buf,
              BufRateScale.ir(buf) * ((BufFrames.ir(buf) / s.sampleRate) * p.clock.tempo / \dur.kr(8)
            ),
            1,0,doneAction:2)
          );
          env = EnvGen.ar(
            Env.linen(
              0.0,
              \dur.kr,0
            ),doneAction:2
          );
          sig = sig * env;
          sig = sig * \amp.kr(-6.dbamp);
          OffsetOut.ar(out,Pan2.ar(sig,\pan.kr(0)));
  }).add;

I can play my amen breaks and the playback speed is determined by dur. So far, so good. However, there are some problems that make it unusable for live improv:

  • audio overlaps: the head and tail of the sample are somehow overlapping.
  • fadeTime can be quite ugly. It would be better to just cut the playback and start with a fresh sound, keeping the playback position between audio samples.