153a5a1b3Sopenharmony_ci/***
253a5a1b3Sopenharmony_ci  This file is part of PulseAudio.
353a5a1b3Sopenharmony_ci
453a5a1b3Sopenharmony_ci  Copyright 2007 Lennart Poettering
553a5a1b3Sopenharmony_ci
653a5a1b3Sopenharmony_ci  PulseAudio is free software; you can redistribute it and/or modify
753a5a1b3Sopenharmony_ci  it under the terms of the GNU Lesser General Public License as
853a5a1b3Sopenharmony_ci  published by the Free Software Foundation; either version 2.1 of the
953a5a1b3Sopenharmony_ci  License, or (at your option) any later version.
1053a5a1b3Sopenharmony_ci
1153a5a1b3Sopenharmony_ci  PulseAudio is distributed in the hope that it will be useful, but
1253a5a1b3Sopenharmony_ci  WITHOUT ANY WARRANTY; without even the implied warranty of
1353a5a1b3Sopenharmony_ci  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1453a5a1b3Sopenharmony_ci  Lesser General Public License for more details.
1553a5a1b3Sopenharmony_ci
1653a5a1b3Sopenharmony_ci  You should have received a copy of the GNU Lesser General Public
1753a5a1b3Sopenharmony_ci  License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
1853a5a1b3Sopenharmony_ci***/
1953a5a1b3Sopenharmony_ci
2053a5a1b3Sopenharmony_ci#ifdef HAVE_CONFIG_H
2153a5a1b3Sopenharmony_ci#include <config.h>
2253a5a1b3Sopenharmony_ci#endif
2353a5a1b3Sopenharmony_ci
2453a5a1b3Sopenharmony_ci#include <stdio.h>
2553a5a1b3Sopenharmony_ci#include <math.h>
2653a5a1b3Sopenharmony_ci
2753a5a1b3Sopenharmony_ci#include <pulse/sample.h>
2853a5a1b3Sopenharmony_ci#include <pulse/xmalloc.h>
2953a5a1b3Sopenharmony_ci
3053a5a1b3Sopenharmony_ci#include <pulsecore/macro.h>
3153a5a1b3Sopenharmony_ci
3253a5a1b3Sopenharmony_ci#include "time-smoother.h"
3353a5a1b3Sopenharmony_ci
3453a5a1b3Sopenharmony_ci#define HISTORY_MAX 64
3553a5a1b3Sopenharmony_ci
3653a5a1b3Sopenharmony_ci/*
3753a5a1b3Sopenharmony_ci * Implementation of a time smoothing algorithm to synchronize remote
3853a5a1b3Sopenharmony_ci * clocks to a local one. Evens out noise, adjusts to clock skew and
3953a5a1b3Sopenharmony_ci * allows cheap estimations of the remote time while clock updates may
4053a5a1b3Sopenharmony_ci * be seldom and received in non-equidistant intervals.
4153a5a1b3Sopenharmony_ci *
4253a5a1b3Sopenharmony_ci * Basically, we estimate the gradient of received clock samples in a
4353a5a1b3Sopenharmony_ci * certain history window (of size 'history_time') with linear
4453a5a1b3Sopenharmony_ci * regression. With that info we estimate the remote time in
4553a5a1b3Sopenharmony_ci * 'adjust_time' ahead and smoothen our current estimation function
4653a5a1b3Sopenharmony_ci * towards that point with a 3rd order polynomial interpolation with
4753a5a1b3Sopenharmony_ci * fitting derivatives. (more or less a b-spline)
4853a5a1b3Sopenharmony_ci *
4953a5a1b3Sopenharmony_ci * The larger 'history_time' is chosen the better we will suppress
5053a5a1b3Sopenharmony_ci * noise -- but we'll adjust to clock skew slower..
5153a5a1b3Sopenharmony_ci *
5253a5a1b3Sopenharmony_ci * The larger 'adjust_time' is chosen the smoother our estimation
5353a5a1b3Sopenharmony_ci * function will be -- but we'll adjust to clock skew slower, too.
5453a5a1b3Sopenharmony_ci *
5553a5a1b3Sopenharmony_ci * If 'monotonic' is true the resulting estimation function is
5653a5a1b3Sopenharmony_ci * guaranteed to be monotonic.
5753a5a1b3Sopenharmony_ci */
5853a5a1b3Sopenharmony_ci
5953a5a1b3Sopenharmony_cistruct pa_smoother {
6053a5a1b3Sopenharmony_ci    pa_usec_t adjust_time, history_time;
6153a5a1b3Sopenharmony_ci
6253a5a1b3Sopenharmony_ci    pa_usec_t time_offset;
6353a5a1b3Sopenharmony_ci
6453a5a1b3Sopenharmony_ci    pa_usec_t px, py;     /* Point p, where we want to reach stability */
6553a5a1b3Sopenharmony_ci    double dp;            /* Gradient we want at point p */
6653a5a1b3Sopenharmony_ci
6753a5a1b3Sopenharmony_ci    pa_usec_t ex, ey;     /* Point e, which we estimated before and need to smooth to */
6853a5a1b3Sopenharmony_ci    double de;            /* Gradient we estimated for point e */
6953a5a1b3Sopenharmony_ci    pa_usec_t ry;         /* The original y value for ex */
7053a5a1b3Sopenharmony_ci
7153a5a1b3Sopenharmony_ci                          /* History of last measurements */
7253a5a1b3Sopenharmony_ci    pa_usec_t history_x[HISTORY_MAX], history_y[HISTORY_MAX];
7353a5a1b3Sopenharmony_ci    unsigned history_idx, n_history;
7453a5a1b3Sopenharmony_ci
7553a5a1b3Sopenharmony_ci    /* To even out for monotonicity */
7653a5a1b3Sopenharmony_ci    pa_usec_t last_y, last_x;
7753a5a1b3Sopenharmony_ci
7853a5a1b3Sopenharmony_ci    /* Cached parameters for our interpolation polynomial y=ax^3+b^2+cx */
7953a5a1b3Sopenharmony_ci    double a, b, c;
8053a5a1b3Sopenharmony_ci    bool abc_valid:1;
8153a5a1b3Sopenharmony_ci
8253a5a1b3Sopenharmony_ci    bool monotonic:1;
8353a5a1b3Sopenharmony_ci    bool paused:1;
8453a5a1b3Sopenharmony_ci    bool smoothing:1; /* If false we skip the polynomial interpolation step */
8553a5a1b3Sopenharmony_ci
8653a5a1b3Sopenharmony_ci    pa_usec_t pause_time;
8753a5a1b3Sopenharmony_ci
8853a5a1b3Sopenharmony_ci    unsigned min_history;
8953a5a1b3Sopenharmony_ci};
9053a5a1b3Sopenharmony_ci
9153a5a1b3Sopenharmony_cipa_smoother* pa_smoother_new(
9253a5a1b3Sopenharmony_ci        pa_usec_t adjust_time,
9353a5a1b3Sopenharmony_ci        pa_usec_t history_time,
9453a5a1b3Sopenharmony_ci        bool monotonic,
9553a5a1b3Sopenharmony_ci        bool smoothing,
9653a5a1b3Sopenharmony_ci        unsigned min_history,
9753a5a1b3Sopenharmony_ci        pa_usec_t time_offset,
9853a5a1b3Sopenharmony_ci        bool paused) {
9953a5a1b3Sopenharmony_ci
10053a5a1b3Sopenharmony_ci    pa_smoother *s;
10153a5a1b3Sopenharmony_ci
10253a5a1b3Sopenharmony_ci    pa_assert(adjust_time > 0);
10353a5a1b3Sopenharmony_ci    pa_assert(history_time > 0);
10453a5a1b3Sopenharmony_ci    pa_assert(min_history >= 2);
10553a5a1b3Sopenharmony_ci    pa_assert(min_history <= HISTORY_MAX);
10653a5a1b3Sopenharmony_ci
10753a5a1b3Sopenharmony_ci    s = pa_xnew(pa_smoother, 1);
10853a5a1b3Sopenharmony_ci    s->adjust_time = adjust_time;
10953a5a1b3Sopenharmony_ci    s->history_time = history_time;
11053a5a1b3Sopenharmony_ci    s->min_history = min_history;
11153a5a1b3Sopenharmony_ci    s->monotonic = monotonic;
11253a5a1b3Sopenharmony_ci    s->smoothing = smoothing;
11353a5a1b3Sopenharmony_ci
11453a5a1b3Sopenharmony_ci    pa_smoother_reset(s, time_offset, paused);
11553a5a1b3Sopenharmony_ci
11653a5a1b3Sopenharmony_ci    return s;
11753a5a1b3Sopenharmony_ci}
11853a5a1b3Sopenharmony_ci
11953a5a1b3Sopenharmony_civoid pa_smoother_free(pa_smoother* s) {
12053a5a1b3Sopenharmony_ci    pa_assert(s);
12153a5a1b3Sopenharmony_ci
12253a5a1b3Sopenharmony_ci    pa_xfree(s);
12353a5a1b3Sopenharmony_ci}
12453a5a1b3Sopenharmony_ci
12553a5a1b3Sopenharmony_ci#define REDUCE(x)                               \
12653a5a1b3Sopenharmony_ci    do {                                        \
12753a5a1b3Sopenharmony_ci        x = (x) % HISTORY_MAX;                  \
12853a5a1b3Sopenharmony_ci    } while(false)
12953a5a1b3Sopenharmony_ci
13053a5a1b3Sopenharmony_ci#define REDUCE_INC(x)                           \
13153a5a1b3Sopenharmony_ci    do {                                        \
13253a5a1b3Sopenharmony_ci        x = ((x)+1) % HISTORY_MAX;              \
13353a5a1b3Sopenharmony_ci    } while(false)
13453a5a1b3Sopenharmony_ci
13553a5a1b3Sopenharmony_cistatic void drop_old(pa_smoother *s, pa_usec_t x) {
13653a5a1b3Sopenharmony_ci
13753a5a1b3Sopenharmony_ci    /* Drop items from history which are too old, but make sure to
13853a5a1b3Sopenharmony_ci     * always keep min_history in the history */
13953a5a1b3Sopenharmony_ci
14053a5a1b3Sopenharmony_ci    while (s->n_history > s->min_history) {
14153a5a1b3Sopenharmony_ci
14253a5a1b3Sopenharmony_ci        if (s->history_x[s->history_idx] + s->history_time >= x)
14353a5a1b3Sopenharmony_ci            /* This item is still valid, and thus all following ones
14453a5a1b3Sopenharmony_ci             * are too, so let's quit this loop */
14553a5a1b3Sopenharmony_ci            break;
14653a5a1b3Sopenharmony_ci
14753a5a1b3Sopenharmony_ci        /* Item is too old, let's drop it */
14853a5a1b3Sopenharmony_ci        REDUCE_INC(s->history_idx);
14953a5a1b3Sopenharmony_ci
15053a5a1b3Sopenharmony_ci        s->n_history --;
15153a5a1b3Sopenharmony_ci    }
15253a5a1b3Sopenharmony_ci}
15353a5a1b3Sopenharmony_ci
15453a5a1b3Sopenharmony_cistatic void add_to_history(pa_smoother *s, pa_usec_t x, pa_usec_t y) {
15553a5a1b3Sopenharmony_ci    unsigned j, i;
15653a5a1b3Sopenharmony_ci    pa_assert(s);
15753a5a1b3Sopenharmony_ci
15853a5a1b3Sopenharmony_ci    /* First try to update an existing history entry */
15953a5a1b3Sopenharmony_ci    i = s->history_idx;
16053a5a1b3Sopenharmony_ci    for (j = s->n_history; j > 0; j--) {
16153a5a1b3Sopenharmony_ci
16253a5a1b3Sopenharmony_ci        if (s->history_x[i] == x) {
16353a5a1b3Sopenharmony_ci            s->history_y[i] = y;
16453a5a1b3Sopenharmony_ci            return;
16553a5a1b3Sopenharmony_ci        }
16653a5a1b3Sopenharmony_ci
16753a5a1b3Sopenharmony_ci        REDUCE_INC(i);
16853a5a1b3Sopenharmony_ci    }
16953a5a1b3Sopenharmony_ci
17053a5a1b3Sopenharmony_ci    /* Drop old entries */
17153a5a1b3Sopenharmony_ci    drop_old(s, x);
17253a5a1b3Sopenharmony_ci
17353a5a1b3Sopenharmony_ci    /* Calculate position for new entry */
17453a5a1b3Sopenharmony_ci    j = s->history_idx + s->n_history;
17553a5a1b3Sopenharmony_ci    REDUCE(j);
17653a5a1b3Sopenharmony_ci
17753a5a1b3Sopenharmony_ci    /* Fill in entry */
17853a5a1b3Sopenharmony_ci    s->history_x[j] = x;
17953a5a1b3Sopenharmony_ci    s->history_y[j] = y;
18053a5a1b3Sopenharmony_ci
18153a5a1b3Sopenharmony_ci    /* Adjust counter */
18253a5a1b3Sopenharmony_ci    s->n_history ++;
18353a5a1b3Sopenharmony_ci
18453a5a1b3Sopenharmony_ci    /* And make sure we don't store more entries than fit in */
18553a5a1b3Sopenharmony_ci    if (s->n_history > HISTORY_MAX) {
18653a5a1b3Sopenharmony_ci        s->history_idx += s->n_history - HISTORY_MAX;
18753a5a1b3Sopenharmony_ci        REDUCE(s->history_idx);
18853a5a1b3Sopenharmony_ci        s->n_history = HISTORY_MAX;
18953a5a1b3Sopenharmony_ci    }
19053a5a1b3Sopenharmony_ci}
19153a5a1b3Sopenharmony_ci
19253a5a1b3Sopenharmony_cistatic double avg_gradient(pa_smoother *s, pa_usec_t x) {
19353a5a1b3Sopenharmony_ci    unsigned i, j, c = 0;
19453a5a1b3Sopenharmony_ci    int64_t ax = 0, ay = 0, k, t;
19553a5a1b3Sopenharmony_ci    double r;
19653a5a1b3Sopenharmony_ci
19753a5a1b3Sopenharmony_ci    /* FIXME: Optimization: Jason Newton suggested that instead of
19853a5a1b3Sopenharmony_ci     * going through the history on each iteration we could calculated
19953a5a1b3Sopenharmony_ci     * avg_gradient() as we go.
20053a5a1b3Sopenharmony_ci     *
20153a5a1b3Sopenharmony_ci     * Second idea: it might make sense to weight history entries:
20253a5a1b3Sopenharmony_ci     * more recent entries should matter more than old ones. */
20353a5a1b3Sopenharmony_ci
20453a5a1b3Sopenharmony_ci    /* Too few measurements, assume gradient of 1 */
20553a5a1b3Sopenharmony_ci    if (s->n_history < s->min_history)
20653a5a1b3Sopenharmony_ci        return 1;
20753a5a1b3Sopenharmony_ci
20853a5a1b3Sopenharmony_ci    /* First, calculate average of all measurements */
20953a5a1b3Sopenharmony_ci    i = s->history_idx;
21053a5a1b3Sopenharmony_ci    for (j = s->n_history; j > 0; j--) {
21153a5a1b3Sopenharmony_ci
21253a5a1b3Sopenharmony_ci        ax += (int64_t) s->history_x[i];
21353a5a1b3Sopenharmony_ci        ay += (int64_t) s->history_y[i];
21453a5a1b3Sopenharmony_ci        c++;
21553a5a1b3Sopenharmony_ci
21653a5a1b3Sopenharmony_ci        REDUCE_INC(i);
21753a5a1b3Sopenharmony_ci    }
21853a5a1b3Sopenharmony_ci
21953a5a1b3Sopenharmony_ci    pa_assert(c >= s->min_history);
22053a5a1b3Sopenharmony_ci    ax /= c;
22153a5a1b3Sopenharmony_ci    ay /= c;
22253a5a1b3Sopenharmony_ci
22353a5a1b3Sopenharmony_ci    /* Now, do linear regression */
22453a5a1b3Sopenharmony_ci    k = t = 0;
22553a5a1b3Sopenharmony_ci
22653a5a1b3Sopenharmony_ci    i = s->history_idx;
22753a5a1b3Sopenharmony_ci    for (j = s->n_history; j > 0; j--) {
22853a5a1b3Sopenharmony_ci        int64_t dx, dy;
22953a5a1b3Sopenharmony_ci
23053a5a1b3Sopenharmony_ci        dx = (int64_t) s->history_x[i] - ax;
23153a5a1b3Sopenharmony_ci        dy = (int64_t) s->history_y[i] - ay;
23253a5a1b3Sopenharmony_ci
23353a5a1b3Sopenharmony_ci        k += dx*dy;
23453a5a1b3Sopenharmony_ci        t += dx*dx;
23553a5a1b3Sopenharmony_ci
23653a5a1b3Sopenharmony_ci        REDUCE_INC(i);
23753a5a1b3Sopenharmony_ci    }
23853a5a1b3Sopenharmony_ci
23953a5a1b3Sopenharmony_ci    r = (double) k / (double) t;
24053a5a1b3Sopenharmony_ci
24153a5a1b3Sopenharmony_ci    return (s->monotonic && r < 0) ? 0 : r;
24253a5a1b3Sopenharmony_ci}
24353a5a1b3Sopenharmony_ci
24453a5a1b3Sopenharmony_cistatic void calc_abc(pa_smoother *s) {
24553a5a1b3Sopenharmony_ci    pa_usec_t ex, ey, px, py;
24653a5a1b3Sopenharmony_ci    int64_t kx, ky;
24753a5a1b3Sopenharmony_ci    double de, dp;
24853a5a1b3Sopenharmony_ci
24953a5a1b3Sopenharmony_ci    pa_assert(s);
25053a5a1b3Sopenharmony_ci
25153a5a1b3Sopenharmony_ci    if (s->abc_valid)
25253a5a1b3Sopenharmony_ci        return;
25353a5a1b3Sopenharmony_ci
25453a5a1b3Sopenharmony_ci    /* We have two points: (ex|ey) and (px|py) with two gradients at
25553a5a1b3Sopenharmony_ci     * these points de and dp. We do a polynomial
25653a5a1b3Sopenharmony_ci     * interpolation of degree 3 with these 6 values */
25753a5a1b3Sopenharmony_ci
25853a5a1b3Sopenharmony_ci    ex = s->ex; ey = s->ey;
25953a5a1b3Sopenharmony_ci    px = s->px; py = s->py;
26053a5a1b3Sopenharmony_ci    de = s->de; dp = s->dp;
26153a5a1b3Sopenharmony_ci
26253a5a1b3Sopenharmony_ci    pa_assert(ex < px);
26353a5a1b3Sopenharmony_ci
26453a5a1b3Sopenharmony_ci    /* To increase the dynamic range and simplify calculation, we
26553a5a1b3Sopenharmony_ci     * move these values to the origin */
26653a5a1b3Sopenharmony_ci    kx = (int64_t) px - (int64_t) ex;
26753a5a1b3Sopenharmony_ci    ky = (int64_t) py - (int64_t) ey;
26853a5a1b3Sopenharmony_ci
26953a5a1b3Sopenharmony_ci    /* Calculate a, b, c for y=ax^3+bx^2+cx */
27053a5a1b3Sopenharmony_ci    s->c = de;
27153a5a1b3Sopenharmony_ci    s->b = (((double) (3*ky)/ (double) kx - dp - (double) (2*de))) / (double) kx;
27253a5a1b3Sopenharmony_ci    s->a = (dp/(double) kx - 2*s->b - de/(double) kx) / (double) (3*kx);
27353a5a1b3Sopenharmony_ci
27453a5a1b3Sopenharmony_ci    s->abc_valid = true;
27553a5a1b3Sopenharmony_ci}
27653a5a1b3Sopenharmony_ci
27753a5a1b3Sopenharmony_cistatic void estimate(pa_smoother *s, pa_usec_t x, pa_usec_t *y, double *deriv) {
27853a5a1b3Sopenharmony_ci    pa_assert(s);
27953a5a1b3Sopenharmony_ci    pa_assert(y);
28053a5a1b3Sopenharmony_ci
28153a5a1b3Sopenharmony_ci    if (x >= s->px) {
28253a5a1b3Sopenharmony_ci        /* Linear interpolation right from px */
28353a5a1b3Sopenharmony_ci        int64_t t;
28453a5a1b3Sopenharmony_ci
28553a5a1b3Sopenharmony_ci        /* The requested point is right of the point where we wanted
28653a5a1b3Sopenharmony_ci         * to be on track again, thus just linearly estimate */
28753a5a1b3Sopenharmony_ci
28853a5a1b3Sopenharmony_ci        t = (int64_t) s->py + (int64_t) llrint(s->dp * (double) (x - s->px));
28953a5a1b3Sopenharmony_ci
29053a5a1b3Sopenharmony_ci        if (t < 0)
29153a5a1b3Sopenharmony_ci            t = 0;
29253a5a1b3Sopenharmony_ci
29353a5a1b3Sopenharmony_ci        *y = (pa_usec_t) t;
29453a5a1b3Sopenharmony_ci
29553a5a1b3Sopenharmony_ci        if (deriv)
29653a5a1b3Sopenharmony_ci            *deriv = s->dp;
29753a5a1b3Sopenharmony_ci
29853a5a1b3Sopenharmony_ci    } else if (x <= s->ex) {
29953a5a1b3Sopenharmony_ci        /* Linear interpolation left from ex */
30053a5a1b3Sopenharmony_ci        int64_t t;
30153a5a1b3Sopenharmony_ci
30253a5a1b3Sopenharmony_ci        t = (int64_t) s->ey - (int64_t) llrint(s->de * (double) (s->ex - x));
30353a5a1b3Sopenharmony_ci
30453a5a1b3Sopenharmony_ci        if (t < 0)
30553a5a1b3Sopenharmony_ci            t = 0;
30653a5a1b3Sopenharmony_ci
30753a5a1b3Sopenharmony_ci        *y = (pa_usec_t) t;
30853a5a1b3Sopenharmony_ci
30953a5a1b3Sopenharmony_ci        if (deriv)
31053a5a1b3Sopenharmony_ci            *deriv = s->de;
31153a5a1b3Sopenharmony_ci
31253a5a1b3Sopenharmony_ci    } else {
31353a5a1b3Sopenharmony_ci        /* Spline interpolation between ex and px */
31453a5a1b3Sopenharmony_ci        double tx, ty;
31553a5a1b3Sopenharmony_ci
31653a5a1b3Sopenharmony_ci        /* Ok, we're not yet on track, thus let's interpolate, and
31753a5a1b3Sopenharmony_ci         * make sure that the first derivative is smooth */
31853a5a1b3Sopenharmony_ci
31953a5a1b3Sopenharmony_ci        calc_abc(s);
32053a5a1b3Sopenharmony_ci
32153a5a1b3Sopenharmony_ci        /* Move to origin */
32253a5a1b3Sopenharmony_ci        tx = (double) (x - s->ex);
32353a5a1b3Sopenharmony_ci
32453a5a1b3Sopenharmony_ci        /* Horner scheme */
32553a5a1b3Sopenharmony_ci        ty = (tx * (s->c + tx * (s->b + tx * s->a)));
32653a5a1b3Sopenharmony_ci
32753a5a1b3Sopenharmony_ci        /* Move back from origin */
32853a5a1b3Sopenharmony_ci        ty += (double) s->ey;
32953a5a1b3Sopenharmony_ci
33053a5a1b3Sopenharmony_ci        *y = ty >= 0 ? (pa_usec_t) llrint(ty) : 0;
33153a5a1b3Sopenharmony_ci
33253a5a1b3Sopenharmony_ci        /* Horner scheme */
33353a5a1b3Sopenharmony_ci        if (deriv)
33453a5a1b3Sopenharmony_ci            *deriv = s->c + (tx * (s->b*2 + tx * s->a*3));
33553a5a1b3Sopenharmony_ci    }
33653a5a1b3Sopenharmony_ci
33753a5a1b3Sopenharmony_ci    /* Guarantee monotonicity */
33853a5a1b3Sopenharmony_ci    if (s->monotonic) {
33953a5a1b3Sopenharmony_ci
34053a5a1b3Sopenharmony_ci        if (deriv && *deriv < 0)
34153a5a1b3Sopenharmony_ci            *deriv = 0;
34253a5a1b3Sopenharmony_ci    }
34353a5a1b3Sopenharmony_ci}
34453a5a1b3Sopenharmony_ci
34553a5a1b3Sopenharmony_civoid pa_smoother_put(pa_smoother *s, pa_usec_t x, pa_usec_t y) {
34653a5a1b3Sopenharmony_ci    pa_usec_t ney;
34753a5a1b3Sopenharmony_ci    double nde;
34853a5a1b3Sopenharmony_ci    bool is_new;
34953a5a1b3Sopenharmony_ci
35053a5a1b3Sopenharmony_ci    pa_assert(s);
35153a5a1b3Sopenharmony_ci
35253a5a1b3Sopenharmony_ci    /* Fix up x value */
35353a5a1b3Sopenharmony_ci    if (s->paused)
35453a5a1b3Sopenharmony_ci        x = s->pause_time;
35553a5a1b3Sopenharmony_ci
35653a5a1b3Sopenharmony_ci    x = PA_LIKELY(x >= s->time_offset) ? x - s->time_offset : 0;
35753a5a1b3Sopenharmony_ci
35853a5a1b3Sopenharmony_ci    is_new = x >= s->ex;
35953a5a1b3Sopenharmony_ci
36053a5a1b3Sopenharmony_ci    if (is_new) {
36153a5a1b3Sopenharmony_ci        /* First, we calculate the position we'd estimate for x, so that
36253a5a1b3Sopenharmony_ci         * we can adjust our position smoothly from this one */
36353a5a1b3Sopenharmony_ci        estimate(s, x, &ney, &nde);
36453a5a1b3Sopenharmony_ci        s->ex = x; s->ey = ney; s->de = nde;
36553a5a1b3Sopenharmony_ci        s->ry = y;
36653a5a1b3Sopenharmony_ci    }
36753a5a1b3Sopenharmony_ci
36853a5a1b3Sopenharmony_ci    /* Then, we add the new measurement to our history */
36953a5a1b3Sopenharmony_ci    add_to_history(s, x, y);
37053a5a1b3Sopenharmony_ci
37153a5a1b3Sopenharmony_ci    /* And determine the average gradient of the history */
37253a5a1b3Sopenharmony_ci    s->dp = avg_gradient(s, x);
37353a5a1b3Sopenharmony_ci
37453a5a1b3Sopenharmony_ci    /* And calculate when we want to be on track again */
37553a5a1b3Sopenharmony_ci    if (s->smoothing) {
37653a5a1b3Sopenharmony_ci        s->px = s->ex + s->adjust_time;
37753a5a1b3Sopenharmony_ci        s->py = s->ry + (pa_usec_t) llrint(s->dp * (double) s->adjust_time);
37853a5a1b3Sopenharmony_ci    } else {
37953a5a1b3Sopenharmony_ci        s->px = s->ex;
38053a5a1b3Sopenharmony_ci        s->py = s->ry;
38153a5a1b3Sopenharmony_ci    }
38253a5a1b3Sopenharmony_ci
38353a5a1b3Sopenharmony_ci    s->abc_valid = false;
38453a5a1b3Sopenharmony_ci
38553a5a1b3Sopenharmony_ci#ifdef DEBUG_DATA
38653a5a1b3Sopenharmony_ci    pa_log_debug("%p, put(%llu | %llu) = %llu", s, (unsigned long long) (x + s->time_offset), (unsigned long long) x, (unsigned long long) y);
38753a5a1b3Sopenharmony_ci#endif
38853a5a1b3Sopenharmony_ci}
38953a5a1b3Sopenharmony_ci
39053a5a1b3Sopenharmony_cipa_usec_t pa_smoother_get(pa_smoother *s, pa_usec_t x) {
39153a5a1b3Sopenharmony_ci    pa_usec_t y;
39253a5a1b3Sopenharmony_ci
39353a5a1b3Sopenharmony_ci    pa_assert(s);
39453a5a1b3Sopenharmony_ci
39553a5a1b3Sopenharmony_ci    /* Fix up x value */
39653a5a1b3Sopenharmony_ci    if (s->paused)
39753a5a1b3Sopenharmony_ci        x = s->pause_time;
39853a5a1b3Sopenharmony_ci
39953a5a1b3Sopenharmony_ci    x = PA_LIKELY(x >= s->time_offset) ? x - s->time_offset : 0;
40053a5a1b3Sopenharmony_ci
40153a5a1b3Sopenharmony_ci    if (s->monotonic)
40253a5a1b3Sopenharmony_ci        if (x <= s->last_x)
40353a5a1b3Sopenharmony_ci            x = s->last_x;
40453a5a1b3Sopenharmony_ci
40553a5a1b3Sopenharmony_ci    estimate(s, x, &y, NULL);
40653a5a1b3Sopenharmony_ci
40753a5a1b3Sopenharmony_ci    if (s->monotonic) {
40853a5a1b3Sopenharmony_ci
40953a5a1b3Sopenharmony_ci        /* Make sure the querier doesn't jump forth and back. */
41053a5a1b3Sopenharmony_ci        s->last_x = x;
41153a5a1b3Sopenharmony_ci
41253a5a1b3Sopenharmony_ci        if (y < s->last_y)
41353a5a1b3Sopenharmony_ci            y = s->last_y;
41453a5a1b3Sopenharmony_ci        else
41553a5a1b3Sopenharmony_ci            s->last_y = y;
41653a5a1b3Sopenharmony_ci    }
41753a5a1b3Sopenharmony_ci
41853a5a1b3Sopenharmony_ci#ifdef DEBUG_DATA
41953a5a1b3Sopenharmony_ci    pa_log_debug("%p, get(%llu | %llu) = %llu", s, (unsigned long long) (x + s->time_offset), (unsigned long long) x, (unsigned long long) y);
42053a5a1b3Sopenharmony_ci#endif
42153a5a1b3Sopenharmony_ci
42253a5a1b3Sopenharmony_ci    return y;
42353a5a1b3Sopenharmony_ci}
42453a5a1b3Sopenharmony_ci
42553a5a1b3Sopenharmony_civoid pa_smoother_set_time_offset(pa_smoother *s, pa_usec_t offset) {
42653a5a1b3Sopenharmony_ci    pa_assert(s);
42753a5a1b3Sopenharmony_ci
42853a5a1b3Sopenharmony_ci    s->time_offset = offset;
42953a5a1b3Sopenharmony_ci
43053a5a1b3Sopenharmony_ci#ifdef DEBUG_DATA
43153a5a1b3Sopenharmony_ci    pa_log_debug("offset(%llu)", (unsigned long long) offset);
43253a5a1b3Sopenharmony_ci#endif
43353a5a1b3Sopenharmony_ci}
43453a5a1b3Sopenharmony_ci
43553a5a1b3Sopenharmony_civoid pa_smoother_pause(pa_smoother *s, pa_usec_t x) {
43653a5a1b3Sopenharmony_ci    pa_assert(s);
43753a5a1b3Sopenharmony_ci
43853a5a1b3Sopenharmony_ci    if (s->paused)
43953a5a1b3Sopenharmony_ci        return;
44053a5a1b3Sopenharmony_ci
44153a5a1b3Sopenharmony_ci#ifdef DEBUG_DATA
44253a5a1b3Sopenharmony_ci    pa_log_debug("pause(%llu)", (unsigned long long) x);
44353a5a1b3Sopenharmony_ci#endif
44453a5a1b3Sopenharmony_ci
44553a5a1b3Sopenharmony_ci    s->paused = true;
44653a5a1b3Sopenharmony_ci    s->pause_time = x;
44753a5a1b3Sopenharmony_ci}
44853a5a1b3Sopenharmony_ci
44953a5a1b3Sopenharmony_civoid pa_smoother_resume(pa_smoother *s, pa_usec_t x, bool fix_now) {
45053a5a1b3Sopenharmony_ci    pa_assert(s);
45153a5a1b3Sopenharmony_ci
45253a5a1b3Sopenharmony_ci    if (!s->paused)
45353a5a1b3Sopenharmony_ci        return;
45453a5a1b3Sopenharmony_ci
45553a5a1b3Sopenharmony_ci    if (x < s->pause_time)
45653a5a1b3Sopenharmony_ci        x = s->pause_time;
45753a5a1b3Sopenharmony_ci
45853a5a1b3Sopenharmony_ci#ifdef DEBUG_DATA
45953a5a1b3Sopenharmony_ci    pa_log_debug("resume(%llu)", (unsigned long long) x);
46053a5a1b3Sopenharmony_ci#endif
46153a5a1b3Sopenharmony_ci
46253a5a1b3Sopenharmony_ci    s->paused = false;
46353a5a1b3Sopenharmony_ci    s->time_offset += x - s->pause_time;
46453a5a1b3Sopenharmony_ci
46553a5a1b3Sopenharmony_ci    if (fix_now)
46653a5a1b3Sopenharmony_ci        pa_smoother_fix_now(s);
46753a5a1b3Sopenharmony_ci}
46853a5a1b3Sopenharmony_ci
46953a5a1b3Sopenharmony_civoid pa_smoother_fix_now(pa_smoother *s) {
47053a5a1b3Sopenharmony_ci    pa_assert(s);
47153a5a1b3Sopenharmony_ci
47253a5a1b3Sopenharmony_ci    s->px = s->ex;
47353a5a1b3Sopenharmony_ci    s->py = s->ry;
47453a5a1b3Sopenharmony_ci}
47553a5a1b3Sopenharmony_ci
47653a5a1b3Sopenharmony_cipa_usec_t pa_smoother_translate(pa_smoother *s, pa_usec_t x, pa_usec_t y_delay) {
47753a5a1b3Sopenharmony_ci    pa_usec_t ney;
47853a5a1b3Sopenharmony_ci    double nde;
47953a5a1b3Sopenharmony_ci
48053a5a1b3Sopenharmony_ci    pa_assert(s);
48153a5a1b3Sopenharmony_ci
48253a5a1b3Sopenharmony_ci    /* Fix up x value */
48353a5a1b3Sopenharmony_ci    if (s->paused)
48453a5a1b3Sopenharmony_ci        x = s->pause_time;
48553a5a1b3Sopenharmony_ci
48653a5a1b3Sopenharmony_ci    x = PA_LIKELY(x >= s->time_offset) ? x - s->time_offset : 0;
48753a5a1b3Sopenharmony_ci
48853a5a1b3Sopenharmony_ci    estimate(s, x, &ney, &nde);
48953a5a1b3Sopenharmony_ci
49053a5a1b3Sopenharmony_ci    /* Play safe and take the larger gradient, so that we wakeup
49153a5a1b3Sopenharmony_ci     * earlier when this is used for sleeping */
49253a5a1b3Sopenharmony_ci    if (s->dp > nde)
49353a5a1b3Sopenharmony_ci        nde = s->dp;
49453a5a1b3Sopenharmony_ci
49553a5a1b3Sopenharmony_ci#ifdef DEBUG_DATA
49653a5a1b3Sopenharmony_ci    pa_log_debug("translate(%llu) = %llu (%0.2f)", (unsigned long long) y_delay, (unsigned long long) ((double) y_delay / nde), nde);
49753a5a1b3Sopenharmony_ci#endif
49853a5a1b3Sopenharmony_ci
49953a5a1b3Sopenharmony_ci    return (pa_usec_t) llrint((double) y_delay / nde);
50053a5a1b3Sopenharmony_ci}
50153a5a1b3Sopenharmony_ci
50253a5a1b3Sopenharmony_civoid pa_smoother_reset(pa_smoother *s, pa_usec_t time_offset, bool paused) {
50353a5a1b3Sopenharmony_ci    pa_assert(s);
50453a5a1b3Sopenharmony_ci
50553a5a1b3Sopenharmony_ci    s->px = s->py = 0;
50653a5a1b3Sopenharmony_ci    s->dp = 1;
50753a5a1b3Sopenharmony_ci
50853a5a1b3Sopenharmony_ci    s->ex = s->ey = s->ry = 0;
50953a5a1b3Sopenharmony_ci    s->de = 1;
51053a5a1b3Sopenharmony_ci
51153a5a1b3Sopenharmony_ci    s->history_idx = 0;
51253a5a1b3Sopenharmony_ci    s->n_history = 0;
51353a5a1b3Sopenharmony_ci
51453a5a1b3Sopenharmony_ci    s->last_y = s->last_x = 0;
51553a5a1b3Sopenharmony_ci
51653a5a1b3Sopenharmony_ci    s->abc_valid = false;
51753a5a1b3Sopenharmony_ci
51853a5a1b3Sopenharmony_ci    s->paused = paused;
51953a5a1b3Sopenharmony_ci    s->time_offset = s->pause_time = time_offset;
52053a5a1b3Sopenharmony_ci
52153a5a1b3Sopenharmony_ci#ifdef DEBUG_DATA
52253a5a1b3Sopenharmony_ci    pa_log_debug("reset()");
52353a5a1b3Sopenharmony_ci#endif
52453a5a1b3Sopenharmony_ci}
525