Download as txt, pdf, or txt
Download as txt, pdf, or txt
You are on page 1of 4

Precise timing in SC: latency, bundles, OffsetOut

See also [ServerTiming]

In usual operation, when you ask for a Synth, SuperCollider tries to instantiate it
as fast as possible:

Synth(\makemenow); //won't do anything unless you had a \makemenow SynthDef : )

There is a slight latency, of some milliseconds.

-------------------------------------------------
Optional partial explanation:
This is caused by the block boundary of calculation (SC works internally by default
at 64 samples at a time, the control rate, and can only create new synths on the
control period boundaries) and message passing overheads. There is a network
connection between the localhost server and the language with communication by Open
Sound Control protocol messages; this network communication takes some time
(perhaps a few milliseconds on the same machine). If you control a synthesis server
somewhere else on the internet outside a local area, it can take much longer!
-------------------------------------------------

This almost-immediate instantiation is fine for reactive response when you want to
minimise delay, say to an incoming MIDI message, a GUI button press, an onset
trigger from audio...

BUT, it can lead to ragged timing within sequencing. Since expert percussionists
can reputedly hear inaccuracies in timing on the order of milliseconds, a little
jitter in realisation time for supposedly isochronous sequences is noticeable in
some circumstances. Investigation of complex rhythmic structures only realisable by
machine may also depend on really accurate timing.

To get very accurate timing, instructions to the sound synthesis server can be
time-stamped, where they are given a strongly marked future time of occurrence.
This is at the cost of not having them happen immediately, but at some known delay,
the latency.

SuperCollider has a latency setting, which defaults to 0.2 seconds

s.latency //s global variable points to the default server

You can change this; I often go more for 50 milliseconds or so

s.latency = 0.05

btw, if you set the latency too small, so messages don't get through before their
future time required, you may see 'late' messages in the post window which look
like:
late 0.009407656
late 0.014832689
late 0.005836474
...
In order to use the latency rather than have an 'as soon as possible' Synth, you
wrap the message to make the Synth in a bundle (essentially, you make a time
stamped packet, which in general use can contain multiple instructions for things
that have to happen at exactly the same time, such as coincident musical events).
The shortcut way is to write

s.bind { Synth(\makemesoon); };

If you had two things which had to happen simultaneously:

s.bind { Synth(\makemesoon); Synth(\makemesoontooatthesametime); };

Let's try and compare. We may find it difficult to hear the difference with the
example next unless you concentrate, but as you make more complicated rhythmic
structures and have more and more going on under control (for example, granulation
effects), you may need to be careful about this.

//sound recipe, required


SynthDef(\testbleep,
{ Out.ar(0,Pan2.ar(Line.kr(1,0,0.1,doneAction:2)*SinOsc.ar(440)*0.1,0.0))}).add;

//no use of latency, immediate, timing slightly more ragged

(
{
inf.do {|i|
Synth(\testbleep);
[0.5,0.25,0.02].wrapAt(i).wait;
}
}.fork;
)

(
{
inf.do {|i|
s.bind { Synth(\testbleep); };
[0.5,0.25,0.02].wrapAt(i).wait;
}
}.fork;
)

try changing latency with the later loop to see the effect (the first loop would
not be affected)
s.latency = 0.2
s.latency = 0.01

See also this help file for more on bundles and alternative ways to create Bundles
like makeBundle:
[bundledCommands]

Even with time stamps, there may be issues with acheiving absolute sample accuracy
on the server. If you want to schedule two Synths, one to follow the other at an
arbitrary position measured in samples (rather than relative time), you will find
this is impossible; the synthesis server does not share a sample clock with the
language (sampling rates are advertised as immutable, but sample clocks on audio
interface hardware drift a little each second, so sample accuracy can be very
difficult to achieve).

(It is possible to acheive sample-based timing within Synths, using server-side


scheduling; see the Server-side Sequencing and Triggers tutorial for some
examples.)

However, you can do better than the control period boundary for starting new
Synths, if you use the OffsetOut UGen rather than Out. This makes sure that the
scheduled start position of a Synth leads to an accurate sample start position
within a control period, by adding a fixed delay in samples as needed.

[OffsetOut]

OffsetOut is used exactly like Out, and just does its stuff for you.

//sound recipe; only difference is OffsetOut instead of Out


SynthDef(\testbleep2,
{ OffsetOut.ar(0,Pan2.ar(Line.kr(1,0,0.1,doneAction:2)*SinOsc.ar(440)*0.1,0.0))}).a
dd;

//with OffsetOut
(
{
inf.do {|i|
s.bind { Synth(\testbleep2); };
[0.5,0.25,0.001].wrapAt(i).wait;
//note 0.001 milliseconds wait is under control period size, 64/44100 =
0.0014512471655329
}
}.fork;
)

//without
(
{
inf.do {|i|
s.bind { Synth(\testbleep); };
[0.5,0.25,0.001].wrapAt(i).wait;
//note 0.001 milliseconds wait is under control period size, 64/44100 =
0.0014512471655329
}
}.fork;
)

You might also like