This isn’t actually correct. If its loop
argument is set to 0 (which not the default), then BufRd
actually does flip the done bit when it reaches the last frame in the buffer, and this can be seen with Done
. I’ve checked this with a simple trace
on a simple UGen.
The reason why you can’t easily find where this is done in the C++ code is that code is shared with PlayBuf via several macros and functions. The entry is BufRd_next_1
that does
double loopMax = (double)(loop ? bufFrames : bufFrames - 1);
before calling sc_loop
via the LOOP_BODY_1
macro as
phase = sc_loop((Unit*)unit, phase, loopMax, loop);
The latter function renames loopMax
to hi
in its arguments and does this test and set:
inline double sc_loop(Unit* unit, double in, double hi, int loop) {
if (in >= hi) {
if (!loop) {
unit->mDone = true;
return hi;
}
// ...
}
This is why Done
gets signaled from RdBuf only when loop
is false.
There is however a bit of a conceptual problem with the combo of Phasor
and BufRd
plus Done
. Unlike for PlayBuf
, in which the trigger goes directly into it, so it resets the “done” status, there’s no way to reset the “done” status for BufRd
because the trigger goes into the Phasor
. So while you can detect BufRd “done” status once… there is no way to reset it. So you might as well kill the whole thing with an envelope.
Here’s a parred down version of what I use. The infinite-end
Phasor is basically the same as using an Integrator, but re-triggerable.
p = NodeProxy.audio(s, 2);
(p.prime({ arg rate = 1, amp = 0.3, bufnum = 0, t_trig = 1, phafps = 4, interp = 2;
var framidx = Phasor.ar(t_trig, rate * BufRateScale.kr(bufnum), 0, inf);
var playgen = BufRd.ar(2, bufnum, framidx, 0, interp);
var done = framidx >= BufFrames.kr(bufnum);
var env = 1 - done; // could add nicer fade out
SendTrig.ar(env * Impulse.ar(phafps, 0.5), 66, framidx);
SendTrig.ar(done, 77);
env * playgen * amp}))
(o = OSCdef(\track, { arg msg, time;
switch (msg[2])
{66} {
{ ("OSC now playing frame" + msg[3]).postln;}.defer }
{77} {
{ ("OSC buffer play ended.").postln }.defer;
{ p.set(\t_trig, 1); ("OSC buffer play restarted").postln }.defer(3) }
}, '/tr', s.addr))
p.play.edit
Works quite nicely for updating a timeCursorPosition
in a SoundFileView on the 66 message (at phafps
frames per second), changing the buffer when it’s “faded out” by the env
on the 77 message etc. You can vary rate
continuously while playing as well.