In fact, you can’t.
As an experiment, let’s take three different envelopes, all with two segments of equal lengths (i.e. dividing the time in half). The first envelope is your envelope = 1 second. The second is 1 millionth of a second, and the third is 1 million seconds.
Then we will do the standard practice for preparing an envelope buffer – discretize – and plot these results together.
(
[
Env([0, 1, 0], [0.5, 0.5], [8, -8]),
Env([0, 1, 0], [0.0000005, 0.0000005], [8, -8]),
Env([0, 1, 0], [500000, 500000], [8, -8])
].collect { |env|
env.discretize.as(Array)
}
.lace(1024*3)
.plot(numChannels: 3);
)
… and the three look the same (even though the long envelope is a trillion times longer than the shortest).
.discretize
stretches or squeezes the envelope to fit in the number of requested samples. At that moment, the envelope’s duration is lost forever. GrainBuf has no idea about 1 second or other duration in the Env. What you’re left with is an abstract shape which preserves the ratios between the segments’ times. (That is, if you repeat the experiment with times [1, 3], and [0.000001, 0.000003], and [1000000, 3000000], they will all discretize to segments where the first takes 1/4 of the size, and the second takes 3/4.)
Having, now, an abstract, “timeless” envelope shape, GrainBuf applies graindur to it by scaling the shape to match the grain time. We can visualize this by granulating audio content = 1.0 and making sure there’s no overlap between grains, so that each grain is (audio = 1.0) * (env shape). Here, the envelope shape gets longer with each successive grain.
// weirdo grain shape
e = Env([0, 1, 0.1, 0.6, 0], [1, 3, 1, 5], \sin);
e.plot;
~envBuf = Buffer.sendCollection(s, e.discretize, 1);
~unityBuf = Buffer.alloc(s, 40000, 1, completionMessage: { |buf| buf.fillMsg(0, 40000, 1) });
(
{
var trig = Impulse.ar(50); // should be 5 in the plot
var dur = PulseCount.ar(trig) * 0.004;
GrainBuf.ar(1, trig,
dur: dur,
sndbuf: ~unityBuf,
envbufnum: ~envBuf
)
}.plot(duration: 0.1);
)
One might also imagine that the envbuf’s number of samples determines the grain duration. That would be inflexible – different durations per grain is a reasonable thing to support. You wouldn’t want to create a different buffer for every grain duration you’d ever want.
hjh