The clock of s.waitForBoot and s.doWhenBooted

Hello everyone,

There are mentions of the default clock for { ... }.fork, Routine {}.play and Task.new etc. in the help document.
However, there seems to be no mention of the default clocks for s.waitForBoot and s.doWhenBooted in the help document. I think AppClock is the clock for both. Am I right? I think it would be better to add the clock for s.waitForBoot and s.doWhenBooted. Are there other equivalents where the default clock is not mentioned in the help document?

.fork(clock, quant, stackSize)

Returns a Routine using the receiver as it’s function, and plays it in a TempoClock.

1 Like

@smoge Thanks, but sorry there were typos in my question. I corrected my question. sorry!

Yes, the default clock for {}.fork, Routine and Task is a TempoClock.
However, there seems to be no mention of the default clocks for s.waitForBoot and s.doWhenBooted in the help document.

Yes, it could be included in the documentation.

1 Like

While I agree this would be a good addition to the documentation, there is also a bit of a mental habit here, along the lines of, “the documentation doesn’t say, so, I don’t know.” An alternate mental habit could be, “the documentation doesn’t say – let me test it.”

s.waitForBoot { thisThread.clock.postln };
... prints...
AppClock

s.quit;

s.boot.doWhenBooted { thisThread.clock.postln };
... prints...
AppClock

BTW I’m not suggesting that it’s OK to leave documentation wildly incomplete – I’ve vocally complained in the past about some method documentation where you have to run some code to find out what the return value is likely to be, and I think, in principle, it would be better if method documented expected input and output types.

But there’s also no way that documentation can cover every question that any user will ever have. (For another example here, s.boot.doWhenBooted { thisThread.clock.postln }; posts AppClock after the entire startup sequence, but s.doWhenBooted({ thisThread.clock.postln }).boot; posts AppClock before “Shared memory server interface initialized” – one could ask, “Shouldn’t the documentation explain the server boot sequence in detail?” Sure, it “should” – but this particular question is quite obscure and it’s easy to imagine how a documentation author would fail to anticipate it.)

hjh

I’m not sure.

I think it’s a bit much to ask that the person deduce not only the commands to discover the different Clocks, but also why different clocks are used in those situations in particular. Better would be a doc (or a wiki) where these details are listed but also explained.

In general, SC has a lot of obscure things in the language documentation. I even had the feeling it was on purpose in the past! I think that, compared to other languages, this causes a certain delay in learning.

I’m not sure it it’s still there, but often something was quite confusing, and there was a note like “For more information, check Smalltalk doc” or “read a book on OOP”. Very vague orientations on core concepts, very confusing if you are new to programming.

That’s not what I’m talking about.

Quoting myself: “… I agree this would be a good addition to the documentation…” and “I’m not suggesting that it’s OK to leave documentation wildly incomplete…”

My view is that documentation should strive to be reasonably complete, and organized such that the most relevant details are highlighted, and “fine print” should be available (but not clogging the main thread of discourse) – this is extremely hard to do btw, so I also think there needs to be some patience with documentation lapses. In those cases, it would help if users who have been around for awhile could try to check it out first. Obviously this burden should not fall on beginners!

So it’s a nuanced, in-between position. Online discourse naturally pushes nuanced, in-between positions toward one or the other extreme, but this is a mistake.

I suspect actually that we agree 99% here.

hjh

1 Like

I tested it as follows:

(
s.waitForBoot {
//s.doWhenBooted {
	Window().front
}
)
(
var synth, num, startedAt, current;

num = 30;
synth = { |out|
    var sig, env;
    env = Env.perc.ar(doneAction: Done.freeSelf);
    sig = SinOsc.ar(\freq.kr) * env * 0.1;
    OffsetOut.ar(out, sig)
}.asDefName;
startedAt = Main.elapsedTime;
current = { Main.elapsedTime - startedAt };

{ var clock = SystemClock;
    { num.do { |i|
        [clock.asString.padLeft(11, " "), i, current.()].postcs;
        s.bind {
            Synth(synth, [freq: 69.midicps])
        };
        1.wait
    } }.fork(clock)
}.();

{
    s.waitForBoot { num.do { |i|
        ["s.waitForBoot's default clock", i, current.()].postcs;
    //s.doWhenBooted { num.do { |i|
    //    ["s.doWhenBooted's default clock", i, current.()].postcs;
        s.bind {
            Synth(synth, [freq: 71.midicps, out: 1])
        };
        1.wait
    } }
}.();
)

Of course, to confirm this in sc files, users should inspect the implementation by typing Cmd/ctrl + i after selecting waitForBoot, for example. Then one will see this.doWhenBooted, then statusWatcher.doWhenBooted etc. If one can follow these well, one will see the following lines:

However, to explain AppClock, SystemClock and TempoClock to beginners, I think the codes similar to the two suggested above are more intuitive and user-friendly.
I think that SC help documents are generally written for users who already have some programming experience and expertise in this area, and thus are not as user-friendly not only for beginners, but also for intermediate users… The fact that some similar questions have been asked more than once also shows these characteristics of the help document. I find this very unfortunate and have recently submitted several PRs to try and make up for it, despite my lack of knowledge and clumsiness. For example:

2 Likes

Yes. It helps beginners grasp the different clocks if mentioned in more places.

I’m unsure if a short note on why this clock is used could be better or make the documentation lose focus. Maybe it would be good. I don’t know. Or a new help document explaining why AppClock is used in this situation, and TempoClock is that situation, etc, would be more effective for the general experience.

2 Likes

Yes!

And…, sometimes I thought AppClock might be deprecated because graphical things can be done inside { ... }.defer. Could this be possible?

Look inside:

	defer { arg delta;
		if (delta.isNil and: {this.canCallOS}) {
			this.value
		}{
			AppClock.sched(delta ? 0, { this.value; nil })
		}
	}
1 Like

In the past, I’ve stated an opinion that SC documentation focuses too much on class references, at the expense of “topic” documentation. “What are clocks and why to use which one” would be a great topic document. The relative lack of “topic documents” is part of what contributes to confusion.

Once a document exists, it can be linked from other relevant places in the help system.

hjh

2 Likes

Ah, it cannot be deprecated…

We need really topic documents…

side note: I’m not totally sure but I think the only reason the AppClock exists is that Gui primitives can’t be scheduled on the SystemClock - this is an annoying complication and there may be a technical fix for this which might simplify this for the user.

1 Like

Side note 2:

From another perspective, I believe users can mainly achieve their goals by using TempoClock and SystemClock with defer { .. }. Would this also mean that there is almost no significant reason for users to know about AppClock?

my understanding is that you need to schedule tasks that include GUI operations on the AppClock. Timing on the AppClock is not as accurate as the SystemClock and importantly can’t be exactly in sync with tasks scheduled on the SystemClock. So given these limitations, once users are scheduling musical and graphical events they do need to understand all three Clock types.

This was a big PITA for me when I was learning/building my system and the limitation on sync between graphic and musical components is unfortunate (but that is a matter for another thread!)

From a usage perspective, AppClock is for any activities that are not time sensitive, but which need to be paced out over time.

GUI activities are in this category because drawing graphics is slower than, say, manipulating an array of 3 pitches. And, SC isn’t meant to be a game engine (drawing is not especially optimized, and it has no logic to drop graphics frames when the CPU is too busy).

The scheduling restriction for GUI activities is because: in cases where a sequencing activity and a GUI activity are scheduled at the same time, you do not want the musical sequencing activity to be delayed for UI updates. (Thinking back also to the old Cocoa app – I don’t know Cocoa but if Cocoa required UI stuff to happen in a specific thread, and JMc wanted better timing for sequencing, then the solution would be to create his own threads for sequencing, and require UI updates to go onto the Cocoa app thread. Something like that…? But that’s ages ago and I never dug into the Cocoa side.)

As prko suggested, the typical approach here is to run the time-sensitive UI control activity on System- or TempoClock, and defer the “physical” UI updates over to AppClock. The control process then remains in sync, and graphics updates catch up as well as they can. On modern systems, this is practically instantaneous.

Honestly it’s not that much of a burden (although you’re correct that, in the absence of documentation, you could easily be misled into scheduling routines on AppClock).

There’s a bit of an edge here, like “the only reason for AppClock is to annoy users” which I think is not a fair characterization. Not everything is time sensitive (e.g. server boot!), and it’s valid to have a way to run those activities in the cracks between time sensitive operations.

hjh

1 Like

My current piece pops windows up with still images in them - no animation . In theory they are in sync with note attacks. Sadly the sync is quite noticeably poor.

Not aware of any workaround save something like @lnihlen ‘s Scintillator…

Popping up windows is a relatively heavy task, depending on window manager. I would not assume a whole window would pop up instantly. (Actually it wasn’t bad on my system, but XFCE is lighter-weight and faster than many other window managers.)

Replacing an image in a pre-existing window can sync pretty well.

// whichever folder
p = ("~/Pictures/misc/*.jpg".standardizePath).pathMatch.scramble.keep(15);

~img = p.collect { |path| Image.open(path) };

~queue = LinkedList.new;

(
w = Window("test", Rect(800, 200, 500, 400)).front;	
w.drawFunc = {
	if(~queue.first.notNil) {
		~queue.first.drawInRect(w.view.bounds.moveTo(0, 0));
		~queue.popFirst;
	};
};
)

(
p = Pbind(
	\freq, Pexprand(200, 800, inf),
	\dur, Pexprand(0.08, 0.8, inf),
	\amp, Pexprand(0.05, 0.25, inf),
	\legato, Pexprand(0.6, 3.0, inf),
	\chooseImg, Pxrand(~img, inf),
	// after event calculations
	// but here it doesn't matter, so \finish would work too
	\callback, {
		// `topEnvironment` is a bit ugly
		// but we have to handle event vs top-level environment scope
		topEnvironment[\queue].add(~chooseImg);
		{ w.refresh }.defer(s.latency)
	}
).play;
)

p.stop;

hjh

1 Like

Thinking back also to the old Cocoa app – I don’t know Cocoa but if Cocoa required UI stuff to happen in a specific thread

Yes, in Cocoa the UI event loop must run on the main thread! Since the actual GUI operations must happen on the UI thread, we need to defer them to the AppClock.

AFAICT, the main reason for the different clocks is that the GUI runs on a typical event loop which blocks on user input, while musical sequencing requires a scheduler. These two models are not really compatible with each other, hence the different threads and different clocks.

(In theory, you could do musical scheduling on the GUI thread, e.g. with timers or with periodic polling, like in a game engine, but the performance would be terrible.)