OK… and you’re citing the correct formula. Beats per second will always be 1/60th of beats per minute, and… it really can’t be any other way…?
As for the function… it looks to me like you’re assuming that the clock should only ever be playing one thing at a time (clear
). Personally, I wouldn’t make that assumption. It might be useful in very specific testing scenarios, but in a musical-performance context, if I start a harmony layer and it causes the bassline to be stopped, that’s entirely impractical.
Also clear
is a bit dangerous:
// your function
(
f = { |bpm, q|
TempoClock.clear
.tempo_(bpm / 60)
.sched(0, Routine(q))
};
)
// run some long notes
(
f.value(60, {
loop {
(degree: rrand(-7, 7), sustain: 3).play;
1.wait;
}
});
)
// switch it out with something else
(
f.value(60, {
loop {
"just something non-audio".scramble.postln;
1.wait;
}
});
)
… and, on the second invocation of f
, the note releases from the first routine have been cleared out of the clock queue, leaving several stuck notes behind, forcing you to hit ctrl-., which you were probably trying to avoid. So I disagree with clear
.
Then… do you want to change the tempo globally for every f
call? In many, many cases, you would set the tempo once at the top, and the layers would simply follow that. (Or, in a poly-tempo situation, you might have one clock per tempo.) So it’s not certain that setting the tempo in the function is a generally-applicable idea either.
Then the function is left with sched
… in which case, why not just call sched
directly?
IMO the use case evaporates under sunlight.
I guess I’d do it more like this? But tbh, in my own use cases, I wouldn’t use a function like this at all, because I need polyphonic layers. (That is, the following is meant a bit tongue-in-cheek as somewhat a counterexample… the effort outweighs the usefulness.)
(
var tasks = IdentityDictionary.new;
f = { |bpm, q|
var clock, entry, thread, watcher;
if(tasks[bpm].isNil) {
// except that this doesn't sync with other clocks
// ... other problem, another day
clock = TempoClock(bpm / 60);
// in this version, 'items' will only ever have one
// item. But you might want polyphony later
entry = (clock: clock, items: Array.new);
tasks[bpm] = entry;
} {
entry = tasks[bpm];
clock = entry[\clock];
};
entry[\items].do(_.stop);
thread = Task(q).refresh;
watcher = SimpleController(thread)
.put(\stopped, {
entry[\items].remove(thread);
watcher.remove;
});
entry[\items] = entry[\items].add(thread);
clock.sched(0, thread)
};
)
hjh