posRate and playbackRate control with BufRd

hey, can someone tell me how i can implement BufRd with playback rate and position rate control for granulation, similiar to GrainBuf? I think its more straightforward with Playbuf, but i would like to do this with BufRd.
Phasor should ramp from 0 to number of frames in the Buffer with a specific position rate, for every trigger
you multiply each grain with the hanning window driven by Sweep, the duration of each grain is the time Sweep needs to go from 0 to 1 scaled by overlap (to overlap grains inside the SynthDef you would need to use collect and PulseDivider here, i left it out for simplicity). I cant make sense of the combination of the modulus Phasor and the triggered Sweep atm and where to implement the posRate and the playBackRate when using BufRd. thanks.


b = Buffer.read(s, Platform.resourceDir +/+ "sounds/a11wlk01.wav");

(
var hanningWindow = { |phase|
	(1 - (phase * 2pi).cos) / 2 * (phase < 1);
};

SynthDef(\granular, { |sndBuf|

	var tFreq = \tFreq.kr(10);
	var trig = Impulse.ar(tFreq);
	var phase = Sweep.ar(trig, tFreq);

	var grainWindow = hanningWindow.(phase / \overlap.kr(1));

	var pos = Phasor.ar(
		trig: 0,
		rate: \posRate.kr(1) * BufRateScale.kr(sndBuf) * SampleDur.ir / BufDur.kr(sndBuf),
		start: \posLo.kr(0),
		end: \posHi.kr(1)
	);

	var sig = PlayBuf.ar(
		numChannels: 1,
		bufnum: sndBuf,
		rate: \playBackRate.kr(1),
		trigger: trig,
		startPos: pos * BufFrames.kr(sndBuf),
		loop: 1
	);

	//var sig = BufRd.ar(1, sndBuf, pos * BufFrames.kr(sndBuf), loop: 1, interpolation: 4);
	//var sig = BufRd.ar(1, sndBuf, phase * BufSampleRate.kr(sndBuf) + pos, loop: 1, interpolation: 4);

	sig = sig * grainWindow * \amp.kr(-10.dbamp);

	sig = Pan2.ar(sig, \pan.kr(0));

	sig = LeakDC.ar(sig);
	OffsetOut.ar(\out.kr(0), sig);
}).add;
)

Synth(\granular, [\sndBuf, b]);

You might consider refactoring so that you’re running everything time-dependent in your grain from a single Sweep, Phasor, or Env. For me, the calculations become clearer when you have a single normalized time pointer that you can scale depending on what it’s driving, something roughly like this:

grainDuration = \grainDuration.ir;
phase = Env([0, 1], [grainDuration]).ar(gate:1, doneAction:2);
grainWindow = hanningWindow.(phase.linlin(0, 1, 0, 2pi));
startTime = \startTime.kr * BufSampleRate.ir(sndBuf);
endTime = startTime + (grainDuration * BufSampleRate.ir(sndBuf) * \grainRate.ir);
sig = grainWindow * BufRd.ar(2, sndBuf, phase.linlin(0, 1, startTime, endTime));

thanks, i think the problem is for the window function i need a triggered phase and the phase for the BufRd should be independent of the trigger and wrap at BufFrames. with Playbuf i get the desired behaviour:


b = Buffer.read(s, Platform.resourceDir +/+ "sounds/a11wlk01.wav");

(
var hanningWindow = { |phase|
	(1 - (phase * 2pi).cos) / 2 * (phase < 1);
};

SynthDef(\playBuf, { |sndBuf|

	var tFreq = \tFreq.kr(10);
	var trig = Impulse.ar(tFreq);

	var phase = Sweep.ar(trig, 1);
	var grainDur = \overlap.kr(1) / tFreq;

	var grainWindow = hanningWindow.(phase / grainDur);

	var posRate = \posRate.kr(1) * BufRateScale.kr(sndBuf) * SampleDur.ir / BufDur.kr(sndBuf);
	var pos = Phasor.ar(0, posRate, \posLo.kr(0), \posHi.kr(1));
	var sig = PlayBuf.ar(1, sndBuf, \playBackRate.kr(1), trig, pos * BufFrames.kr(sndBuf), loop: 1);

	sig = sig * grainWindow * \amp.kr(-10.dbamp);

	sig = Pan2.ar(sig, \pan.kr(0));

	sig = LeakDC.ar(sig);
	OffsetOut.ar(\out.kr(0), sig);
}).add;
)

Synth(\playBuf, [\sndBuf, b]);

compared to BufRd with Sweep the buffer always starts at 0 frames when beeing triggered:

(
var hanningWindow = { |phase|
	(1 - (phase * 2pi).cos) / 2 * (phase < 1);
};

SynthDef(\bufRd_sweep, { |sndBuf|

	var tFreq = \tFreq.kr(10);
	var trig = Impulse.ar(tFreq);

	var phase = Sweep.ar(trig, 1);
	var grainDur = \overlap.kr(1) / tFreq;

	var grainWindow = hanningWindow.(phase / grainDur);

	var startPhase = \startPhase.kr(0) * BufSampleRate.kr(sndBuf);
	var sig = BufRd.ar(1, sndBuf, startPhase + (phase * BufSampleRate.kr(sndBuf) * \grainRate.kr(1)), loop: 1);

	sig = sig * grainWindow * \amp.kr(-10.dbamp);

	sig = Pan2.ar(sig, \pan.kr(0));

	sig = LeakDC.ar(sig);
	OffsetOut.ar(\out.kr(0), sig);
}).add;
)

Synth(\bufRd_sweep, [sndBuf: b]);

when i use an additonal Phasor i get the desired behaviour, but the question is where should I implement \playBackRate then?

(
var hanningWindow = { |phase|
	(1 - (phase * 2pi).cos) / 2 * (phase < 1);
};

SynthDef(\bufRd_phasor, { |sndBuf|

	var tFreq = \tFreq.kr(10);
	var trig = Impulse.ar(tFreq);

	var phase = Sweep.ar(trig, 1);
	var grainDur = \overlap.kr(1) / tFreq;

	var grainWindow = hanningWindow.(phase / grainDur);

	var posRate = \posRate.kr(1) * BufRateScale.kr(sndBuf) * SampleDur.ir / BufDur.kr(sndBuf);
	var pos = Phasor.ar(0, posRate, \posLo.kr(0), \posHi.kr(1));
	var sig = BufRd.ar(1, sndBuf, pos * BufFrames.kr(sndBuf), loop: 1);

	sig = sig * grainWindow * \amp.kr(-10.dbamp);

	sig = Pan2.ar(sig, \pan.kr(0));

	sig = LeakDC.ar(sig);
	OffsetOut.ar(\out.kr(0), sig);
}).add;
)

Synth(\bufRd_phasor, [sndBuf: b]);

i actually thought this would be really straight forward mhh…