1 /* Copyright (c) 2014 The Chromium OS Authors. All rights reserved. 2 * Use of this source code is governed by a BSD-style license that can be 3 * found in the LICENSE file. 4 */ 5 #include "math.h" 6 7 #include "cras_util.h" 8 #include "rate_estimator.h" 9 10 /* The max rate skew that considered reasonable */ 11 #define MAX_RATE_SKEW 100 12 13 static void least_square_reset(struct least_square *lsq) 14 { 15 memset(lsq, 0, sizeof(*lsq)); 16 } 17 18 void least_square_add_sample(struct least_square *lsq, double x, double y) 19 { 20 lsq->sum_x += x; 21 lsq->sum_y += y; 22 lsq->sum_xy += x * y; 23 lsq->sum_x2 += x * x; 24 lsq->num_samples++; 25 } 26 27 double least_square_best_fit_slope(struct least_square *lsq) 28 { 29 double num, denom; 30 num = lsq->num_samples * lsq->sum_xy - lsq->sum_x * lsq->sum_y; 31 denom = lsq->num_samples * lsq->sum_x2 - lsq->sum_x * lsq->sum_x; 32 return num / denom; 33 } 34 35 void rate_estimator_destroy(struct rate_estimator *re) 36 { 37 if (re) 38 free(re); 39 } 40 41 struct rate_estimator *rate_estimator_create(unsigned int rate, 42 const struct timespec *window_size, 43 double smooth_factor) 44 { 45 struct rate_estimator *re; 46 47 re = (struct rate_estimator *)calloc(1, sizeof(*re)); 48 if (re == NULL) 49 return NULL; 50 51 re->window_size = *window_size; 52 re->estimated_rate = rate; 53 re->smooth_factor = smooth_factor; 54 55 return re; 56 } 57 58 void rate_estimator_add_frames(struct rate_estimator *re, int fr) 59 { 60 re->level_diff += fr; 61 } 62 63 double rate_estimator_get_rate(struct rate_estimator *re) 64 { 65 return re->estimated_rate; 66 } 67 68 void rate_estimator_reset_rate(struct rate_estimator *re, unsigned int rate) 69 { 70 re->estimated_rate = rate; 71 least_square_reset(&re->lsq); 72 re->window_start_ts.tv_sec = 0; 73 re->window_start_ts.tv_nsec = 0; 74 re->window_frames = 0; 75 re->level_diff = 0; 76 re->last_level = 0; 77 } 78 79 int rate_estimator_check(struct rate_estimator *re, int level, 80 struct timespec *now) 81 { 82 struct timespec td; 83 84 /* TODO(hychao) - is this the right thing to do if level is 0? */ 85 if ((re->window_start_ts.tv_sec == 0) || (level == 0)) { 86 re->window_start_ts = *now; 87 re->window_frames = 0; 88 re->level_diff = 0; 89 re->last_level = level; 90 return 0; 91 } 92 93 subtract_timespecs(now, &re->window_start_ts, &td); 94 re->window_frames += abs(re->last_level - level + re->level_diff); 95 re->level_diff = 0; 96 re->last_level = level; 97 98 least_square_add_sample(&re->lsq, 99 td.tv_sec + (double)td.tv_nsec / 1000000000L, 100 re->window_frames); 101 if (timespec_after(&td, &re->window_size) && 102 re->lsq.num_samples > 1) { 103 double rate = least_square_best_fit_slope(&re->lsq); 104 if (fabs(re->estimated_rate - rate) < MAX_RATE_SKEW) 105 re->estimated_rate = rate * (1 - re->smooth_factor) + 106 re->smooth_factor * re->estimated_rate; 107 least_square_reset(&re->lsq); 108 re->window_start_ts = *now; 109 re->window_frames = 0; 110 return 1; 111 } 112 return 0; 113 } 114