I want to modulate the playback rate of a buffer without changing the pitch (i.e. time-stretching). So I am playing it through GrainBuf, and using a SinOsc as an LFO to modulate the phasor rate. This is simple enough, but the tricky part is that I want the period of the LFO to exactly match the period of the modulated buffer so that when it plays in a loop, it always slows down and speeds up at the exact same parts of the buffer. If you run this code, you will hear the effect that I’m going for:
b = Buffer.read(s, Platform.resourceDir +/+ "sounds/a11wlk01.wav");
(
SynthDef(\bufstretch, {|buf|
var bufdur, maxrate, minrate, lfo_period,
phasor_rate, phasor, graintrig, graindur, sig;
bufdur = BufDur.ir(buf); // 4.28329
maxrate = 4.0;
minrate = 0.1;
lfo_period = 2.09;
// I found the correct lfo_period through trial and error.
// Can it be calculated as a function of maxrate, minrate, and bufdur?
phasor_rate = SinOsc.ar(lfo_period.reciprocal, pi/2).range(minrate, maxrate);
phasor = Sweep.ar(1, phasor_rate / bufdur);
graintrig = Impulse.ar(\trigrate.kr(20));
graindur = \trigrate.kr.reciprocal * \overlap.kr(2);
sig = GrainBuf.ar(2, graintrig, graindur, buf, BufRateScale.ir(buf), phasor);
Out.ar(0, sig);
}).add;
)
Synth(\bufstretch, [buf: b]);
You can see in my code that I have hard-coded lfo_period = 2.09
, which I arrived at through trial and error. This is the approximate duration of the buffer after it has undergone one period of modulation. But I would like to be able to calculate lfo_period
as a function of maxrate
, minrate
, and bufdur
so that I can change these values and still maintain perfect sync between the buffer and LFO. I think it is possible to do so with some calculus, but unfortunately my calculus skills are extremely rusty. Does anyone know how to solve this?