[Jack-Devel] Jack's timing functions

PrevNext  Index
DateMon, 26 Dec 2011 17:06:19 +0000
From Fons Adriaensen <[hidden] at linuxaudio dot org>
ToJack Developers <[hidden] at lists dot jackaudio dot org>
Follow-UpFlorian Paul Schmidt Re: [Jack-Devel] Jack's timing functions
Follow-UpRobin Gareus Re: [Jack-Devel] Jack's timing functions
Hello all,

During the weekend I've been working on adaptive resampling
algorithms (as required for e.g. alsa_in and similar apps).

The first tentative result produces around 100 times less
jitter on the dynamic resample ratio than alsa_in. See 
<http://kokkinizita.linuxaudio.org/linuxaudio/resample.html>,
The difference in phase noise levels is roughly 40 dB.

In fact it has reached the point where the performance is no
longer dominated by random timing errors, but also systematic
ones resulting from quantisation of some of the variables
involved in the calculation (mostly frame counts expressed
as integers) start to show up.

This is very visible in the time domain test results, but even
the frequency domain ones start to show this, Note all those
little discrete lines in the first plot (link above) - each
of these is the result of a deterministic error pattern.

This in turn made me look into Jack's timing-related functions.

One of the values I needed was the microseconds time for the
start of the current cycle (the one filtered by the DLL). This
is not available in the public API. It can be computed in two
ways. The first is
jack_frames_to_time (client, jack_last_frame_time (client));
which is a rather convolved way to recompute an already known
value: client->engine->current_time.current_wakeup.

The second involves using jack_frames_since_cycle_start() and
jack_get_time() and compute the result from those. This is even
more convolved and won't produce an accurate result because:

a) the usecs clock is read twice, the values used should be the
   same but they won't,
b) the value returned by jack_frames_since_cycle_start() is
   quantised to an integer and precision is lost,
c) the base usecs time used by this function is apparently not
   the DLL filtered time but the unfiltered one. 

(c) in fact introduces an inconsistency in the set of time-related
functions.

So my first suggestion is to add two functions to transclient.c:

jack_time_t jack_current_wakeup_time (const jack_client_t *client)
{
    return client->engine->frame_timer.current_wakeup;
}

jack_time_t jack_next_wakeup_time (const jack_client_t *client)
{
    return client->engine->frame_timer.next_wakeup;
}

which together with jack_last_frame_time() provide all the 
info a client could need to do its own calculations to full
precision.

Note that it is perfectly safe to read these three values in
the process callback even without the 'guards' used to ensure
an atomic copy in jack_read_frame_time() - a new cycle isn't 
supposed to start at that time (and if it does there is bigger
trouble ahead anyway).

An alternative would be to provide a single function to allow
a client to read all three in a single call, with or without
guards.

Another way to provide better precision would be to let
jack_frames_since_cycle_start() return a double or float,
and to modify the two frames<->usecs conversion functions
to accept/return a float or double frametime referenced
to the start of the cycle (i.e. removing the potentially
large offset, which is given by jack_last_frame_time()
anyway).

Ciao,


-- 
FA

Vor uns liegt ein weites Tal, die Sonne scheint - ein Glitzerstrahl.
PrevNext  Index

1324919191.9270_0.ltw:2,a <20111226170619.GA5065 at linuxaudio dot org>