• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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