Ever incrementing time loop

Thank you as always for your time James,

I’m simply asking for a clarification on the exact setting of the tempo parameter which would fill in the value for this line of code:

TempoClock.default.tempo_( ) // ?

Coming from earlier when you explained:

…also when I mentioned a function it’s because I’ve been using:

{
	|bpm q|

	clear

	(
		TempoClock
	)

	.tempo_

	(
		bpm / 60
	)

	.sched

	(
		0 , q . r
	)
}

…ultimately ending with 1.wait time equialling the value of a quarter note for a given bpm.

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