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.)