X-Git-Url: https://code.delx.au/pulseaudio/blobdiff_plain/33b186e74dc2de6fa363d10d3450c354ec99f864..8534149fbe87c63a5af85f5610c0f62b45500d90:/src/pulsecore/time-smoother.c diff --git a/src/pulsecore/time-smoother.c b/src/pulsecore/time-smoother.c index 6a2ffaa6..0696cabc 100644 --- a/src/pulsecore/time-smoother.c +++ b/src/pulsecore/time-smoother.c @@ -78,17 +78,26 @@ struct pa_smoother { /* Cached parameters for our interpolation polynomial y=ax^3+b^2+cx */ double a, b, c; - pa_bool_t abc_valid; + pa_bool_t abc_valid:1; pa_bool_t monotonic:1; pa_bool_t paused:1; + pa_bool_t smoothing:1; /* If FALSE we skip the polonyomial interpolation step */ pa_usec_t pause_time; unsigned min_history; }; -pa_smoother* pa_smoother_new(pa_usec_t adjust_time, pa_usec_t history_time, pa_bool_t monotonic, unsigned min_history) { +pa_smoother* pa_smoother_new( + pa_usec_t adjust_time, + pa_usec_t history_time, + pa_bool_t monotonic, + pa_bool_t smoothing, + unsigned min_history, + pa_usec_t time_offset, + pa_bool_t paused) { + pa_smoother *s; pa_assert(adjust_time > 0); @@ -99,25 +108,11 @@ pa_smoother* pa_smoother_new(pa_usec_t adjust_time, pa_usec_t history_time, pa_b s = pa_xnew(pa_smoother, 1); s->adjust_time = adjust_time; s->history_time = history_time; - s->time_offset = 0; + s->min_history = min_history; s->monotonic = monotonic; + s->smoothing = smoothing; - s->px = s->py = 0; - s->dp = 1; - - s->ex = s->ey = s->ry = 0; - s->de = 1; - - s->history_idx = 0; - s->n_history = 0; - - s->last_y = s->last_x = 0; - - s->abc_valid = FALSE; - - s->paused = FALSE; - - s->min_history = min_history; + pa_smoother_reset(s, time_offset, paused); return s; } @@ -201,6 +196,13 @@ static double avg_gradient(pa_smoother *s, pa_usec_t x) { int64_t ax = 0, ay = 0, k, t; double r; + /* FIXME: Optimization: Jason Newton suggested that instead of + * going through the history on each iteration we could calculated + * avg_gradient() as we go. + * + * Second idea: it might make sense to weight history entries: + * more recent entries should matter more than old ones. */ + /* Too few measurements, assume gradient of 1 */ if (s->n_history < s->min_history) return 1; @@ -279,6 +281,7 @@ static void estimate(pa_smoother *s, pa_usec_t x, pa_usec_t *y, double *deriv) { pa_assert(y); if (x >= s->px) { + /* Linear interpolation right from px */ int64_t t; /* The requested point is right of the point where we wanted @@ -294,7 +297,22 @@ static void estimate(pa_smoother *s, pa_usec_t x, pa_usec_t *y, double *deriv) { if (deriv) *deriv = s->dp; + } else if (x <= s->ex) { + /* Linear interpolation left from ex */ + int64_t t; + + t = (int64_t) s->ey - (int64_t) llrint(s->de * (double) (s->ex - x)); + + if (t < 0) + t = 0; + + *y = (pa_usec_t) t; + + if (deriv) + *deriv = s->de; + } else { + /* Spline interpolation between ex and px */ double tx, ty; /* Ok, we're not yet on track, thus let's interpolate, and @@ -302,10 +320,8 @@ static void estimate(pa_smoother *s, pa_usec_t x, pa_usec_t *y, double *deriv) { calc_abc(s); - tx = (double) x; - /* Move to origin */ - tx -= (double) s->ex; + tx = (double) (x - s->ex); /* Horner scheme */ ty = (tx * (s->c + tx * (s->b + tx * s->a))); @@ -313,7 +329,7 @@ static void estimate(pa_smoother *s, pa_usec_t x, pa_usec_t *y, double *deriv) { /* Move back from origin */ ty += (double) s->ey; - *y = ty >= 0 ? (pa_usec_t) lrint(ty) : 0; + *y = ty >= 0 ? (pa_usec_t) llrint(ty) : 0; /* Horner scheme */ if (deriv) @@ -348,7 +364,6 @@ void pa_smoother_put(pa_smoother *s, pa_usec_t x, pa_usec_t y) { * we can adjust our position smoothly from this one */ estimate(s, x, &ney, &nde); s->ex = x; s->ey = ney; s->de = nde; - s->ry = y; } @@ -359,12 +374,19 @@ void pa_smoother_put(pa_smoother *s, pa_usec_t x, pa_usec_t y) { s->dp = avg_gradient(s, x); /* And calculate when we want to be on track again */ - s->px = s->ex + s->adjust_time; - s->py = s->ry + (pa_usec_t) lrint(s->dp * (double) s->adjust_time); + if (s->smoothing) { + s->px = s->ex + s->adjust_time; + s->py = s->ry + (pa_usec_t) llrint(s->dp * (double) s->adjust_time); + } else { + s->px = s->ex; + s->py = s->ry; + } s->abc_valid = FALSE; -/* pa_log_debug("put(%llu | %llu) = %llu", (unsigned long long) (x + s->time_offset), (unsigned long long) x, (unsigned long long) y); */ +#ifdef DEBUG_DATA + pa_log_debug("%p, put(%llu | %llu) = %llu", s, (unsigned long long) (x + s->time_offset), (unsigned long long) x, (unsigned long long) y); +#endif } pa_usec_t pa_smoother_get(pa_smoother *s, pa_usec_t x) { @@ -395,7 +417,9 @@ pa_usec_t pa_smoother_get(pa_smoother *s, pa_usec_t x) { s->last_y = y; } -/* pa_log_debug("get(%llu | %llu) = %llu", (unsigned long long) (x + s->time_offset), (unsigned long long) x, (unsigned long long) y); */ +#ifdef DEBUG_DATA + pa_log_debug("%p, get(%llu | %llu) = %llu", s, (unsigned long long) (x + s->time_offset), (unsigned long long) x, (unsigned long long) y); +#endif return y; } @@ -405,7 +429,9 @@ void pa_smoother_set_time_offset(pa_smoother *s, pa_usec_t offset) { s->time_offset = offset; -/* pa_log_debug("offset(%llu)", (unsigned long long) offset); */ +#ifdef DEBUG_DATA + pa_log_debug("offset(%llu)", (unsigned long long) offset); +#endif } void pa_smoother_pause(pa_smoother *s, pa_usec_t x) { @@ -414,13 +440,15 @@ void pa_smoother_pause(pa_smoother *s, pa_usec_t x) { if (s->paused) return; -/* pa_log_debug("pause(%llu)", (unsigned long long) x); */ +#ifdef DEBUG_DATA + pa_log_debug("pause(%llu)", (unsigned long long) x); +#endif s->paused = TRUE; s->pause_time = x; } -void pa_smoother_resume(pa_smoother *s, pa_usec_t x) { +void pa_smoother_resume(pa_smoother *s, pa_usec_t x, pa_bool_t fix_now) { pa_assert(s); if (!s->paused) @@ -429,10 +457,22 @@ void pa_smoother_resume(pa_smoother *s, pa_usec_t x) { if (x < s->pause_time) x = s->pause_time; -/* pa_log_debug("resume(%llu)", (unsigned long long) x); */ +#ifdef DEBUG_DATA + pa_log_debug("resume(%llu)", (unsigned long long) x); +#endif s->paused = FALSE; s->time_offset += x - s->pause_time; + + if (fix_now) + pa_smoother_fix_now(s); +} + +void pa_smoother_fix_now(pa_smoother *s) { + pa_assert(s); + + s->px = s->ex; + s->py = s->ry; } pa_usec_t pa_smoother_translate(pa_smoother *s, pa_usec_t x, pa_usec_t y_delay) { @@ -454,14 +494,33 @@ pa_usec_t pa_smoother_translate(pa_smoother *s, pa_usec_t x, pa_usec_t y_delay) if (s->dp > nde) nde = s->dp; -/* pa_log_debug("translate(%llu) = %llu (%0.2f)", (unsigned long long) y_delay, (unsigned long long) ((double) y_delay / nde), nde); */ +#ifdef DEBUG_DATA + pa_log_debug("translate(%llu) = %llu (%0.2f)", (unsigned long long) y_delay, (unsigned long long) ((double) y_delay / nde), nde); +#endif - return (pa_usec_t) lrint((double) y_delay / nde); + return (pa_usec_t) llrint((double) y_delay / nde); } -void pa_smoother_reset(pa_smoother *s) { +void pa_smoother_reset(pa_smoother *s, pa_usec_t time_offset, pa_bool_t paused) { pa_assert(s); + s->px = s->py = 0; + s->dp = 1; + + s->ex = s->ey = s->ry = 0; + s->de = 1; + + s->history_idx = 0; s->n_history = 0; + + s->last_y = s->last_x = 0; + s->abc_valid = FALSE; + + s->paused = paused; + s->time_offset = s->pause_time = time_offset; + +#ifdef DEBUG_DATA + pa_log_debug("reset()"); +#endif }