Are you latching the envelope selection per grain? I think that came up earlier in the thread.
It’s mandatory – if you’ve got one envbuf control being split out to multiple simultaneous grains, then you have to hold the value steady for the complete duration of one grain. If you don’t do this, then the envelope selection for grain 2 will also change grain 1 in the middle. This will sound like “sliding out of time” but it may be a totally different issue.
So, taking your kr version:
(
SynthDef(\pulseDivider, {
var trig = K2A.ar(\trig.tr(0));
var tFreq = \tFreq.kr(1);
var grainRate = tFreq / \overlap.kr(1);
var maxOverlap = 4;
var envBufNums = 4;
var envBufs = \envBufs.kr(Array.fill(envBufNums, 0));
var envIndex = \localEnvBufIndex.kr(1);
var sig = Array.fill(maxOverlap, { |i|
var localTrig, hasTriggered;
var phase, grainEnvs, grainEnv;
localTrig = PulseDivider.ar(trig, maxOverlap, i);
hasTriggered = PulseCount.ar(localTrig) > 0;
phase = Sweep.ar(localTrig, grainRate * hasTriggered);
// because this is all kr, you don't need the multiplex
// also -- here -- you must not allow envIndex to change mid-grain
grainEnv = BufRd.ar(1, Select.kr(envBufs, Latch.kr(envIndex, localTrig)), phase * BufFrames.kr(envBuf), loop: 0, interpolation: 4);
sig = SinOsc.ar(\freq.kr(440));
sig = sig * grainEnv;
}).sum;
sig = Pan2.ar(sig, \pan.kr(0), \amp.kr(0.10));
Out.ar(\out.kr(0), sig);
}).add;
)
If you’re going to use Demand units to choose envelopes, then this is instead of the pattern doing it. It strikes me as not quite meaningful to try to do both at once. (That is, don’t put synth controls inside the demand structure.)
(
SynthDef(\pulseDivider, {
var tFreq = \tFreq.kr(1);
var trig = Impulse.ar(tFreq);
var grainRate = tFreq / \overlap.kr(1);
var maxOverlap = 4;
var envBufs = \envBufs.kr(#[1,1,1,1]);
// here: use this 100% to choose envelopes
// now think about this... you want *one* stream
// of envelope selection. So this must be outside the loop
var envIndex = Demand.ar(trig, 0, Dseq([0, 1], inf));
var sig = Array.fill(maxOverlap, { |i|
var localTrig, hasTriggered;
var phase, grainEnvs, grainEnv, envBufSelect;
localTrig = PulseDivider.ar(trig, maxOverlap, i);
hasTriggered = PulseCount.ar(localTrig) > 0;
phase = Sweep.ar(localTrig, grainRate * hasTriggered);
grainEnvs = envBufs.collect { |envBuf|
BufRd.ar(1, envBuf, phase * BufFrames.kr(envBuf), loop: 0, interpolation: 4)
};
// it is still necessary to latch
grainEnv = Select.ar(Latch.ar(envBufSelect, localTrig), grainEnvs);
sig = SinOsc.ar(\freq.kr(440));
sig = sig * grainEnv;
}).sum;
sig = Pan2.ar(sig, \pan.kr(0), \amp.kr(0.10));
Out.ar(\out.kr(0), sig);
}).add;
)
hjh