1 // Copyright 2019 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 pub mod rate_estimator_bindings; 6 7 use std::error; 8 use std::fmt; 9 use std::time::Duration; 10 11 #[derive(Debug)] 12 pub enum Error { 13 InvalidSmoothFactor(f64), 14 } 15 16 impl error::Error for Error {} 17 18 impl fmt::Display for Error { fmt(&self, f: &mut fmt::Formatter) -> fmt::Result19 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 20 use Error::*; 21 match self { 22 InvalidSmoothFactor(sf) => write!(f, "Smooth factor {} is not between 0.0 and 1.0", sf), 23 } 24 } 25 } 26 27 type Result<T> = std::result::Result<T, Error>; 28 29 const MAX_RATE_SKEW: f64 = 100.0; 30 31 /// Hold information to calculate linear least square from 32 /// several (x, y) samples. 33 #[derive(Debug, Default)] 34 struct LeastSquares { 35 sum_x: f64, 36 sum_y: f64, 37 sum_xy: f64, 38 sum_x2: f64, 39 num_samples: u32, 40 } 41 42 impl LeastSquares { new() -> Self43 fn new() -> Self { 44 Self::default() 45 } 46 add_sample(&mut self, x: f64, y: f64)47 fn add_sample(&mut self, x: f64, y: f64) { 48 self.sum_x += x; 49 self.sum_y += y; 50 self.sum_xy += x * y; 51 self.sum_x2 += x * x; 52 self.num_samples += 1; 53 } 54 best_fit_slope(&self) -> f6455 fn best_fit_slope(&self) -> f64 { 56 let num = self.num_samples as f64 * self.sum_xy - self.sum_x * self.sum_y; 57 let den = self.num_samples as f64 * self.sum_x2 - self.sum_x * self.sum_x; 58 num / den 59 } 60 } 61 62 /// An estimator holding the required information to determine the actual frame 63 /// rate of an audio device. 64 /// 65 /// # Members 66 /// * `last_level` - Buffer level of the audio device at last check time. 67 /// * `level_diff` - Number of frames written to or read from audio device 68 /// since the last check time. Rate estimator will use this 69 /// change plus the difference of buffer level to derive the 70 /// number of frames audio device has actually processed. 71 /// * `window_start` - The start time of the current window. 72 /// * `window_size` - The size of the window. 73 /// * `window_frames` - The number of frames accumulated in current window. 74 /// * `lsq` - The helper used to estimate sample rate. 75 /// * `smooth_factor` - A scaling factor used to average the previous and new 76 /// rate estimates to ensure that estimates do not change 77 /// too quickly. 78 /// * `estimated_rate` - The estimated rate at which samples are consumed. 79 pub struct RateEstimator { 80 last_level: i32, 81 level_diff: i32, 82 window_start: Option<Duration>, 83 window_size: Duration, 84 window_frames: u32, 85 lsq: LeastSquares, 86 smooth_factor: f64, 87 estimated_rate: f64, 88 } 89 90 impl RateEstimator { 91 /// Creates a rate estimator. 92 /// 93 /// # Arguments 94 /// * `rate` - The initial value to estimate rate from. 95 /// * `window_size` - The window size of the rate estimator. 96 /// * `smooth_factor` - The coefficient used to calculate moving average 97 /// from old estimated rate values. Must be between 98 /// 0.0 and 1.0 99 /// 100 /// # Errors 101 /// * If `smooth_factor` is not between 0.0 and 1.0 try_new(rate: u32, window_size: Duration, smooth_factor: f64) -> Result<Self>102 pub fn try_new(rate: u32, window_size: Duration, smooth_factor: f64) -> Result<Self> { 103 if smooth_factor < 0.0 || smooth_factor > 1.0 { 104 return Err(Error::InvalidSmoothFactor(smooth_factor)); 105 } 106 107 Ok(RateEstimator { 108 last_level: 0, 109 level_diff: 0, 110 window_start: None, 111 window_size, 112 window_frames: 0, 113 lsq: LeastSquares::new(), 114 smooth_factor, 115 estimated_rate: rate as f64, 116 }) 117 } 118 119 /// Resets the estimated rate 120 /// 121 /// Reset the estimated rate to `rate`, and erase all collected data. reset_rate(&mut self, rate: u32)122 pub fn reset_rate(&mut self, rate: u32) { 123 self.last_level = 0; 124 self.level_diff = 0; 125 self.window_start = None; 126 self.window_frames = 0; 127 self.lsq = LeastSquares::new(); 128 self.estimated_rate = rate as f64; 129 } 130 131 /// Adds additional frames transmitted to/from audio device. 132 /// 133 /// # Arguments 134 /// * `frames` - The number of frames written to the device. For input, 135 /// this should be negative to indicate how many samples 136 /// were read. add_frames(&mut self, frames: i32)137 pub fn add_frames(&mut self, frames: i32) { 138 self.level_diff += frames; 139 } 140 141 /// Gets the estimated rate. get_estimated_rate(&self) -> f64142 pub fn get_estimated_rate(&self) -> f64 { 143 self.estimated_rate 144 } 145 146 /// Check the timestamp and buffer level difference since last check time, 147 /// and use them as a new sample to update the estimated rate. 148 /// 149 /// # Arguments 150 /// * `level` - The current buffer level of audio device. 151 /// * `now` - The time at which this function is called. 152 /// 153 /// # Returns 154 /// True if the estimated rate is updated and window is reset, 155 /// otherwise false. update_estimated_rate(&mut self, level: i32, now: Duration) -> bool156 pub fn update_estimated_rate(&mut self, level: i32, now: Duration) -> bool { 157 let start = match self.window_start { 158 None => { 159 self.window_start = Some(now); 160 return false; 161 } 162 Some(t) => t, 163 }; 164 165 let delta = match now.checked_sub(start) { 166 Some(d) => d, 167 None => return false, 168 }; 169 self.window_frames += (self.last_level - level + self.level_diff).abs() as u32; 170 self.level_diff = 0; 171 self.last_level = level; 172 173 let secs = (delta.as_secs() as f64) + delta.subsec_nanos() as f64 / 1_000_000_000.0; 174 self.lsq.add_sample(secs, self.window_frames as f64); 175 if delta > self.window_size && self.lsq.num_samples > 1 { 176 let rate = self.lsq.best_fit_slope(); 177 if (self.estimated_rate - rate).abs() < MAX_RATE_SKEW { 178 self.estimated_rate = 179 rate * (1.0 - self.smooth_factor) + self.estimated_rate * self.smooth_factor; 180 } 181 self.lsq = LeastSquares::new(); 182 self.window_start = Some(now); 183 self.window_frames = 0; 184 return true; 185 } 186 false 187 } 188 } 189