View this PageEdit this PageUploads to this PageHistory of this PageTop of the SwikiRecent ChangesSearch the SwikiHelp Guide

latency jerkiness

HJH wrotes:

Samuel Pluta <spluta at mail.utexas.edu> wrote:
> what exactly does the openBundle, closeBundle thingy
> do (as in, why 
> does the studder go away)?  This studder has been a
> "feature" of some 
> of my software for a while, and I'd like it to go
> away.


If you use Synth.new by itself, here's the sequence:

Create the synth object, populate nodeID etc., build the OSC message, send it .... .... server receives it and does it "now."

Each step takes time, or rather, an unpredictable amount of time.
Hence some notes happen pretty much on time, others slightly later than expected.
That's where the stutter comes from.

Latency compensates for this by telling the server, play this note at the clock's logical time + latency.
Say the routine kicks off at clock time 10.0, latency is 0.1 (physical times are made up and could be anything, within reason):

Logical time Physical time Event
10.0 10.0 create synth object
10.0 10.001 build OSC message
10.0 10.0015 send message
10.0 10.0023 message received

the message says, play the sound at time
 10.0 (logical time) + 0.1 (latency) == 10.1

10.0 10.1 sound comes out

If the routine yields 0.2, the process repeats:

10.2 10.2 create synth object
... ... ...
10.2 10.3 sound comes out

If you use latency, it doesn't matter if it takes 0.0023 seconds to generate, send and receive the message or 0.078 seconds.
The time sent along with the bundle will always be consistent.
You just have to make sure the latency is big enough for your code to run in the virtual machine, plus the time it takes for the message to go over the network.

Synth.new sends its OSC message immediately, without a time stamp. Thus it is executed ASAP.

To set a precise latency (and thus ensure synchronous execution) use the makeBundle method with a function:
s.makeBundle(0.2, {Synth.new("help-Rand")});


Till



The above timing scheme still does not take into account a certain amout of
jitter that can occur on the server within one control period.

// the interval you can calculate like this:
s.options.blockSize / s.actualSampleRate;

// currently it is something like 0.0013 s, 1.3 ms.
(during such a full block, sound travels about 40 cm)
you can change the block size in s.options - down to 16 the cpu load stays fairly reasonable.


This happens when using an Out / XOut / ReplaceOut UGen to write on the bus.
In order to keep this delay constant, you can use an OffsetOut:

SynthDef("grain", { arg freq=1000, out; Out.ar(out, Line.ar(0.1, 0, 0.01, doneAction:2) * SinOsc.ar(freq)) });

SynthDef("grain", { arg freq=1000, out; ReplaceOut.ar(out, Line.ar(0.1, 0, 0.01, doneAction:2) * SinOsc.ar(freq)) });


OffsetOut places each node in a fixed place of one block calculation.
It requires about 40 % more CPU than Out.


jrh


Link to this Page