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?
@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.
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.ā
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.)
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.
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.
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:
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.
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.
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.
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.
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;
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.)