1 <!DOCTYPE html PUBLIC
"-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
4 <meta http-equiv=
"Content-Type" content=
"text/html; charset=UTF-8">
5 <meta http-equiv=
"Content-Style-Type" content=
"text/css">
7 <meta name=
"Generator" content=
"Cocoa HTML Writer">
8 <meta name=
"CocoaVersion" content=
"949.54">
9 <style type=
"text/css">
10 p
.p1
{margin: 0.0px 0.0px 0.0px 0.0px; font: 20.0px Helvetica
}
11 p
.p2
{margin: 0.0px 0.0px 0.0px 0.0px; font: 12.0px Helvetica
; min-height: 14.0px}
12 p
.p3
{margin: 0.0px 0.0px 0.0px 0.0px; font: 12.0px Helvetica
}
13 p
.p4
{margin: 0.0px 0.0px 0.0px 0.0px; font: 9.0px Monaco
}
14 p
.p5
{margin: 0.0px 0.0px 0.0px 0.0px; font: 9.0px Monaco
; min-height: 12.0px}
15 p
.p6
{margin: 0.0px 0.0px 0.0px 0.0px; font: 9.0px Monaco
; color: #0a22ac}
16 p
.p7
{margin: 0.0px 0.0px 0.0px 0.0px; font: 9.0px Monaco
; color: #90261d}
17 span
.s1
{color: #000000}
18 span
.s2
{color: #456c26}
19 span
.s3
{color: #0a22ac}
20 span
.s4
{color: #90261d}
21 span
.Apple-tab-span
{white-space:pre
}
25 <p class=
"p1"><b>Bundling latency
<span class=
"Apple-converted-space"> </span></b></p>
26 <p class=
"p2"><br></p>
27 <p class=
"p3">To ensure correct timing of events on the server, OSC messages may be sent with a time stamp, indicating the precise time the sound is expected to hit the hardware output.
<span class=
"Apple-converted-space"> </span></p>
28 <p class=
"p2"><br></p>
29 <p class=
"p3">In the SuperCollider language, the time stamp is generated behind the scenes based on a parameter called
"<b>latency</b>."<span class=
"Apple-converted-space"> </span></p>
30 <p class=
"p2"><br></p>
31 <p class=
"p3">To understand how latency works, we need to understand the concepts of
<b>logical time
</b> and
<b>physical time
</b>.
<span class=
"Apple-converted-space"> </span></p>
32 <p class=
"p2"><br></p>
33 <p class=
"p3">Every clock in SuperCollider has both a logical time and physical time.
<span class=
"Apple-converted-space"> </span></p>
34 <p class=
"p2"><br></p>
35 <p class=
"p3"><b>Physical time:
</b> always advances, represents real time.
<span class=
"Apple-converted-space"> </span></p>
36 <p class=
"p3"><b>Logical time:
</b> advances only when a scheduling thread wakes up.
<span class=
"Apple-converted-space"> </span></p>
37 <p class=
"p2"><br></p>
38 <p class=
"p3">While a scheduled function or event is executing, logical time holds steady at the
"expected" value. That is, if the event is scheduled for
60 seconds exactly, throughout the event's execution, the logical time will be
60 seconds. If the event takes
2 seconds to execute (very rare), at the end of the event, the logical time will still be
60 seconds but the physical time will be
62 seconds. If the next event is to happen
3 seconds after the first began, its logical time will be
63 seconds. Logical time is not affected by fluctuations in system performance.
</p>
39 <p class=
"p2"><br></p>
40 <p class=
"p3">This sequencing example illustrates the difference. It's written deliberately inefficiently to expose the problem more clearly. Two copies of the same routine get started at the same time. On a theoretically perfect machine, in which operations take no time, we would hear both channels in perfect sync. No such machine exists, and this is obviously not the case when you listen. The routines also print out the logical time (clock.beats) and physical time (clock.elapsedBeats) just before playing a grain.
</p>
41 <p class=
"p2"><br></p>
42 <p class=
"p4">s.boot;
</p>
43 <p class=
"p5"><br></p>
44 <p class=
"p6">SynthDef
<span class=
"s1">(
</span><span class=
"s2">\sinGrain
</span><span class=
"s1">, {
</span>|out =
0, freq =
440, amp =
0.5, dur =
1|
</p>
45 <p class=
"p4"><span class=
"Apple-tab-span"> </span><span class=
"s3">Out
</span>.ar(out,
<span class=
"s3">SinOsc
</span>.ar(freq,
0, amp) *
<span class=
"s3">EnvGen
</span>.kr(
<span class=
"s3">Env
</span>.sine(
1), timeScale:dur, doneAction:
2));
</p>
46 <p class=
"p4">}).add;
</p>
47 <p class=
"p5"><br></p>
48 <p class=
"p4">2.do({
<span class=
"s3">|chan|
</span></p>
49 <p class=
"p4"><span class=
"Apple-tab-span"> </span><span class=
"s3">var
</span> rout;
</p>
50 <p class=
"p4"><span class=
"Apple-tab-span"> </span>rout =
<span class=
"s3">Routine
</span>({
</p>
51 <p class=
"p4"><span class=
"Apple-tab-span"> </span><span class=
"Apple-tab-span"> </span><span class=
"s3">var
</span> freq;
</p>
52 <p class=
"p4"><span class=
"Apple-tab-span"> </span><span class=
"Apple-tab-span"> </span>{
<span class=
"Apple-tab-span"> </span>freq =
0;
</p>
53 <p class=
"p4"><span class=
"Apple-tab-span"> </span><span class=
"Apple-tab-span"> </span><span class=
"Apple-tab-span"> </span>rrand(
400,
1000).do({ freq = freq +
1 });
</p>
54 <p class=
"p4"><span class=
"Apple-tab-span"> </span><span class=
"Apple-tab-span"> </span><span class=
"Apple-tab-span"> </span>[
<span class=
"s3">thisThread
</span>.clock.beats,
<span class=
"s3">thisThread
</span>.clock.elapsedBeats].postln;
</p>
55 <p class=
"p4"><span class=
"Apple-tab-span"> </span><span class=
"Apple-tab-span"> </span><span class=
"Apple-tab-span"> </span><span class=
"s3">Synth
</span>(
<span class=
"s2">\sinGrain
</span>, [
<span class=
"s2">\out
</span>, chan,
<span class=
"s2">\freq
</span>, freq,
<span class=
"s2">\dur
</span>,
0.005]);
</p>
56 <p class=
"p4"><span class=
"Apple-tab-span"> </span><span class=
"Apple-tab-span"> </span><span class=
"Apple-tab-span"> </span>0.1.wait;
</p>
57 <p class=
"p4"><span class=
"Apple-tab-span"> </span><span class=
"Apple-tab-span"> </span>}.loop;
</p>
58 <p class=
"p4"><span class=
"Apple-tab-span"> </span>});
</p>
59 <p class=
"p4"><span class=
"Apple-tab-span"> </span><span class=
"s3">TempoClock
</span>.default.schedAbs(
<span class=
"s3">TempoClock
</span>.default.elapsedBeats.roundUp(
1), rout);
</p>
61 <p class=
"p5"><br></p>
62 <p class=
"p5"><br></p>
63 <p class=
"p4"><span class=
"Apple-converted-space"> </span>Left channel
<span class=
"Apple-converted-space"> </span>Right channel
</p>
64 <p class=
"p4">Logical vs Physical time
<span class=
"Apple-converted-space"> </span>Logical vs Physical time
</p>
65 <p class=
"p4">95 <span class=
"Apple-converted-space"> </span>95.001466112<span class=
"Apple-converted-space"> </span>95 <span class=
"Apple-converted-space"> </span>95.002988196</p>
66 <p class=
"p4">95.1 <span class=
"Apple-converted-space"> </span>95.101427968<span class=
"Apple-converted-space"> </span>95.1 <span class=
"Apple-converted-space"> </span>95.103152311</p>
67 <p class=
"p4">95.2 <span class=
"Apple-converted-space"> </span>95.201250057<span class=
"Apple-converted-space"> </span>95.2 <span class=
"Apple-converted-space"> </span>95.202905826</p>
68 <p class=
"p4">95.3 <span class=
"Apple-converted-space"> </span>95.301592755<span class=
"Apple-converted-space"> </span>95.3 <span class=
"Apple-converted-space"> </span>95.303724638</p>
69 <p class=
"p4">95.4 <span class=
"Apple-converted-space"> </span>95.401475486<span class=
"Apple-converted-space"> </span>95.4 <span class=
"Apple-converted-space"> </span>95.403289141</p>
70 <p class=
"p5"><br></p>
71 <p class=
"p4">Average physical latency:
</p>
72 <p class=
"p4">0.00144247559999830<span class=
"Apple-converted-space"> </span>.0032120224000039</p>
73 <p class=
"p2"><br></p>
74 <p class=
"p3">Notice that even though the left and right channel patterns were scheduled for exactly the same time, the events don't complete executing at the same time. Further, the Synth(...) call instructs the server to play the synth immediately on receipt, so the right channel will be lagging behind the left by about
2 ms each event--and not by the same amount each time. Timing, then, is always slightly imprecise.
<span class=
"Apple-converted-space"> </span></p>
75 <p class=
"p2"><br></p>
76 <p class=
"p3">This version is the same, but it generates each synth with a
1/
4 second latency parameter:
</p>
77 <p class=
"p2"><br></p>
78 <p class=
"p4">2.do({
<span class=
"s3">|chan|
</span></p>
79 <p class=
"p4"><span class=
"Apple-tab-span"> </span><span class=
"s3">var
</span> rout;
</p>
80 <p class=
"p4"><span class=
"Apple-tab-span"> </span>rout =
<span class=
"s3">Routine
</span>({
</p>
81 <p class=
"p4"><span class=
"Apple-tab-span"> </span><span class=
"Apple-tab-span"> </span><span class=
"s3">var
</span> freq;
</p>
82 <p class=
"p4"><span class=
"Apple-tab-span"> </span><span class=
"Apple-tab-span"> </span>{
<span class=
"Apple-tab-span"> </span>freq =
0;
</p>
83 <p class=
"p4"><span class=
"Apple-tab-span"> </span><span class=
"Apple-tab-span"> </span><span class=
"Apple-tab-span"> </span>rrand(
400,
1000).do({ freq = freq +
1 });
</p>
84 <p class=
"p4"><span class=
"Apple-tab-span"> </span><span class=
"Apple-tab-span"> </span><span class=
"Apple-tab-span"> </span>[
<span class=
"s3">thisThread
</span>.clock.beats,
<span class=
"s3">thisThread
</span>.clock.elapsedBeats].postln;
</p>
85 <p class=
"p4"><span class=
"Apple-tab-span"> </span><span class=
"Apple-tab-span"> </span><span class=
"Apple-tab-span"> </span>s.makeBundle(
0.25, {
<span class=
"s3">Synth
</span>(
<span class=
"s2">\sinGrain
</span>, [
<span class=
"s2">\out
</span>, chan,
<span class=
"s2">\freq
</span>, freq,
<span class=
"s2">\dur
</span>,
0.005]); });
</p>
86 <p class=
"p4"><span class=
"Apple-tab-span"> </span><span class=
"Apple-tab-span"> </span><span class=
"Apple-tab-span"> </span>0.1.wait;
</p>
87 <p class=
"p4"><span class=
"Apple-tab-span"> </span><span class=
"Apple-tab-span"> </span>}.loop;
</p>
88 <p class=
"p4"><span class=
"Apple-tab-span"> </span>});
</p>
89 <p class=
"p4"><span class=
"Apple-tab-span"> </span><span class=
"s3">TempoClock
</span>.default.schedAbs(
<span class=
"s3">TempoClock
</span>.default.elapsedBeats.roundUp(
1), rout);
</p>
91 <p class=
"p2"><br></p>
92 <p class=
"p3">By using makeBundle with a time argument of
0.25, the \s_new messages for the left and right channel are sent with the same timestamp: the clock's current logical time plus the time argument. Note in the table that both channels have the same logical time throughout, so the two channels are in perfect sync.
<span class=
"Apple-converted-space"> </span></p>
93 <p class=
"p2"><br></p>
94 <p class=
"p3">These routines are written deliberately badly. If they're made maximally efficient, the synchronization will be tighter even without the latency factor, but it can never be perfect. You'll also see this issue, however, if you have several routines executing and several of them are supposed to execute at the same time. Some will execute sooner than others, but
<i>their logical time will all be the same
</i>. If they're all using the same amount of latency, you will still hear them at the same time.
<span class=
"Apple-converted-space"> </span></p>
95 <p class=
"p2"><br></p>
96 <p class=
"p3">In general, all synths that are triggered by live input (MIDI, GUI, HID) should specify no latency so that they execute as soon as possible. All sequencing routines should use latency to ensure perfect timing.
<span class=
"Apple-converted-space"> </span></p>
97 <p class=
"p2"><br></p>
98 <p class=
"p3">The latency value should allow enough time for the event to execute and generate the OSC bundle, and for the server to interpret the message and render the audio in time to reach the hardware output on time. If the client and server are on the same machine, this value can be quite low. Running over a network, you must allow more time. (Latency compensates for network timing jitter also.)
</p>
99 <p class=
"p2"><br></p>
100 <p class=
"p3">Pbind automatically imports a latency parameter from the server's latency variable. You can set the default latency for event patterns like this:
</p>
101 <p class=
"p2"><br></p>
102 <p class=
"p4">myServer.latency =
0.2;
<span class=
"Apple-converted-space"> </span><span class=
"s4">//
0.2 is the default
</span></p>
103 <p class=
"p2"><br></p>
104 <p class=
"p3">Here are three ways to play a synth with the latency parameter:
</p>
105 <p class=
"p2"><br></p>
106 <p class=
"p7">// messaging style
</p>
107 <p class=
"p7">// s.nextNodeID is how to get the next unused node ID from the server
</p>
108 <p class=
"p4">s.sendBundle(latency, [
<span class=
"s2">\s_new
</span>, defName, s.nextNodeID, targetID, addAction, arguments]);
</p>
109 <p class=
"p5"><br></p>
110 <p class=
"p7">// object style, asking the object for the message
</p>
111 <p class=
"p4">synth =
<span class=
"s3">Synth
</span>.basicNew(defName, s);
</p>
112 <p class=
"p4">s.sendBundle(latency, synth.newMsg(target, arguments, addAction));
</p>
113 <p class=
"p5"><br></p>
114 <p class=
"p7">// object style, using automatic bundling
</p>
115 <p class=
"p7">// like the previous example, when this finishes you'll have the Synth object in the synth variable
</p>
116 <p class=
"p4">s.makeBundle(latency, { synth =
<span class=
"s3">Synth
</span>(defName, arguments, target, addAction); });
</p>