/* Copyright (c) 2014 The Chromium OS Authors. All rights reserved.
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "math.h"
#include "cras_util.h"
#include "rate_estimator.h"
/* The max rate skew that considered reasonable */
#define MAX_RATE_SKEW 100
static void least_square_reset(struct least_square *lsq)
{
memset(lsq, 0, sizeof(*lsq));
}
void least_square_add_sample(struct least_square *lsq, double x, double y)
{
lsq->sum_x += x;
lsq->sum_y += y;
lsq->sum_xy += x * y;
lsq->sum_x2 += x * x;
lsq->num_samples++;
}
double least_square_best_fit_slope(struct least_square *lsq)
{
double num, denom;
num = lsq->num_samples * lsq->sum_xy - lsq->sum_x * lsq->sum_y;
denom = lsq->num_samples * lsq->sum_x2 - lsq->sum_x * lsq->sum_x;
return num / denom;
}
void rate_estimator_destroy(struct rate_estimator *re)
{
if (re)
free(re);
}
struct rate_estimator *rate_estimator_create(unsigned int rate,
const struct timespec *window_size,
double smooth_factor)
{
struct rate_estimator *re;
re = (struct rate_estimator *)calloc(1, sizeof(*re));
if (re == NULL)
return NULL;
re->window_size = *window_size;
re->estimated_rate = rate;
re->smooth_factor = smooth_factor;
return re;
}
void rate_estimator_add_frames(struct rate_estimator *re, int fr)
{
re->level_diff += fr;
}
double rate_estimator_get_rate(struct rate_estimator *re)
{
return re->estimated_rate;
}
void rate_estimator_reset_rate(struct rate_estimator *re, unsigned int rate)
{
re->estimated_rate = rate;
least_square_reset(&re->lsq);
re->window_start_ts.tv_sec = 0;
re->window_start_ts.tv_nsec = 0;
re->window_frames = 0;
re->level_diff = 0;
re->last_level = 0;
}
int rate_estimator_check(struct rate_estimator *re, int level,
struct timespec *now)
{
struct timespec td;
/* TODO(hychao) - is this the right thing to do if level is 0? */
if ((re->window_start_ts.tv_sec == 0) || (level == 0)) {
re->window_start_ts = *now;
re->window_frames = 0;
re->level_diff = 0;
re->last_level = level;
return 0;
}
subtract_timespecs(now, &re->window_start_ts, &td);
re->window_frames += abs(re->last_level - level + re->level_diff);
re->level_diff = 0;
re->last_level = level;
least_square_add_sample(&re->lsq,
td.tv_sec + (double)td.tv_nsec / 1000000000L,
re->window_frames);
if (timespec_after(&td, &re->window_size) &&
re->lsq.num_samples > 1) {
double rate = least_square_best_fit_slope(&re->lsq);
if (fabs(re->estimated_rate - rate) < MAX_RATE_SKEW)
re->estimated_rate = rate * (1 - re->smooth_factor) +
re->smooth_factor * re->estimated_rate;
least_square_reset(&re->lsq);
re->window_start_ts = *now;
re->window_frames = 0;
return 1;
}
return 0;
}