• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /***
2   This file is part of PulseAudio.
3 
4   PulseAudio is free software; you can redistribute it and/or modify
5   it under the terms of the GNU Lesser General Public License as
6   published by the Free Software Foundation; either version 2.1 of the
7   License, or (at your option) any later version.
8 
9   PulseAudio is distributed in the hope that it will be useful, but
10   WITHOUT ANY WARRANTY; without even the implied warranty of
11   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12   Lesser General Public License for more details.
13 
14   You should have received a copy of the GNU Lesser General Public
15   License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
16 ***/
17 
18 /* The code in this file is based on the theoretical background found at
19  * https://www.freedesktop.org/software/pulseaudio/misc/rate_estimator.odt.
20  * The theory has never been reviewed, so it may be inaccurate in places. */
21 
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25 
26 #include <pulsecore/macro.h>
27 #include <pulse/sample.h>
28 #include <pulse/xmalloc.h>
29 #include <pulse/timeval.h>
30 
31 #include "time-smoother_2.h"
32 
33 struct pa_smoother_2 {
34     /* Values set when the smoother is created */
35     pa_usec_t smoother_window_time;
36     uint32_t rate;
37     uint32_t frame_size;
38 
39     /* USB hack parameters */
40     bool usb_hack;
41     bool enable_usb_hack;
42     uint32_t hack_threshold;
43 
44     /* Smoother state */
45     bool init;
46     bool paused;
47 
48     /* Current byte count start value */
49     double start_pos;
50     /* System time corresponding to start_pos */
51     pa_usec_t start_time;
52     /* Conversion factor between time domains */
53     double time_factor;
54 
55     /* Used if the smoother is paused while still in init state */
56     pa_usec_t fixup_time;
57 
58     /* Time offset for USB devices */
59     int64_t time_offset;
60 
61     /* Various time stamps */
62     pa_usec_t resume_time;
63     pa_usec_t pause_time;
64     pa_usec_t smoother_start_time;
65     pa_usec_t last_time;
66 
67     /* Variables used for Kalman filter */
68     double time_variance;
69     double time_factor_variance;
70     double kalman_variance;
71 
72     /* Variables used for low pass filter */
73     double drift_filter;
74     double drift_filter_1;
75 };
76 
77 /* Create new smoother */
pa_smoother_2_new(pa_usec_t window,pa_usec_t time_stamp,uint32_t frame_size,uint32_t rate)78 pa_smoother_2* pa_smoother_2_new(pa_usec_t window, pa_usec_t time_stamp, uint32_t frame_size, uint32_t rate) {
79     pa_smoother_2 *s;
80 
81     pa_assert(window > 0);
82 
83     s = pa_xnew(pa_smoother_2, 1);
84     s->enable_usb_hack = false;
85     s->usb_hack = false;
86     s->hack_threshold = 0;
87     s->smoother_window_time = window;
88     s->rate = rate;
89     s->frame_size = frame_size;
90 
91     pa_smoother_2_reset(s, time_stamp);
92 
93     return s;
94 }
95 
96 /* Free the smoother */
pa_smoother_2_free(pa_smoother_2 * s)97 void pa_smoother_2_free(pa_smoother_2* s) {
98 
99     pa_assert(s);
100 
101     pa_xfree(s);
102 }
103 
pa_smoother_2_set_rate(pa_smoother_2 * s,pa_usec_t time_stamp,uint32_t rate)104 void pa_smoother_2_set_rate(pa_smoother_2 *s, pa_usec_t time_stamp, uint32_t rate) {
105 
106     pa_assert(s);
107     pa_assert(rate > 0);
108 
109     /* If the rate has changed, data in the smoother will be invalid,
110      * therefore also reset the smoother */
111     if (rate != s->rate) {
112         s->rate = rate;
113         pa_smoother_2_reset(s, time_stamp);
114     }
115 }
116 
pa_smoother_2_set_sample_spec(pa_smoother_2 * s,pa_usec_t time_stamp,pa_sample_spec * spec)117 void pa_smoother_2_set_sample_spec(pa_smoother_2 *s, pa_usec_t time_stamp, pa_sample_spec *spec) {
118     size_t frame_size;
119 
120     pa_assert(s);
121     pa_assert(pa_sample_spec_valid(spec));
122 
123     /* If the sample spec has changed, data in the smoother will be invalid,
124      * therefore also reset the smoother */
125     frame_size = pa_frame_size(spec);
126     if (frame_size != s->frame_size || spec->rate != s->rate) {
127         s->frame_size = frame_size;
128         s->rate = spec->rate;
129         pa_smoother_2_reset(s, time_stamp);
130     }
131 }
132 
133 /* Add a new data point and re-calculate time conversion factor */
pa_smoother_2_put(pa_smoother_2 * s,pa_usec_t time_stamp,int64_t byte_count)134 void pa_smoother_2_put(pa_smoother_2 *s, pa_usec_t time_stamp, int64_t byte_count) {
135     double byte_difference, iteration_time;
136     double time_delta_system, time_delta_card, drift, filter_constant, filter_constant_1;
137     double temp, filtered_time_delta_card, expected_time_delta_card;
138 
139     pa_assert(s);
140 
141     /* Smoother is paused, nothing to do */
142     if (s->paused)
143         return;
144 
145     /* Initial setup or resume */
146     if PA_UNLIKELY((s->init)) {
147         s->resume_time = time_stamp;
148 
149         /* We have no data yet, nothing to do */
150         if (byte_count <= 0)
151             return;
152 
153         /* Now we are playing/recording.
154          * Get fresh time stamps and save the start count */
155         s->start_pos = (double)byte_count;
156         s->last_time = time_stamp;
157         s->start_time = time_stamp;
158         s->smoother_start_time = time_stamp;
159 
160         s->usb_hack = s->enable_usb_hack;
161         s->init = false;
162         return;
163     }
164 
165     /* Duration of last iteration */
166     iteration_time = (double)time_stamp - s->last_time;
167 
168     /* Don't go backwards in time */
169     if (iteration_time <= 0)
170         return;
171 
172     /* Wait at least 100 ms before starting calculations, otherwise the
173      * impact of the offset error will slow down convergence */
174     if (time_stamp < s->smoother_start_time + 100 * PA_USEC_PER_MSEC)
175         return;
176 
177     /* Time difference in system time domain */
178     time_delta_system = time_stamp - s->start_time;
179 
180     /* Number of bytes played since start_time */
181     byte_difference = (double)byte_count - s->start_pos;
182 
183     /* Time difference in soundcard time domain. Don't use
184      * pa_bytes_to_usec() here because byte_difference need not
185      * be on a sample boundary */
186     time_delta_card = byte_difference / s->frame_size / s->rate * PA_USEC_PER_SEC;
187     filtered_time_delta_card = time_delta_card;
188 
189     /* Prediction of measurement */
190     expected_time_delta_card = time_delta_system * s->time_factor;
191 
192     /* Filtered variance of card time measurements */
193     s->time_variance = 0.9 * s->time_variance + 0.1 * (time_delta_card - expected_time_delta_card) * (time_delta_card - expected_time_delta_card);
194 
195     /* Kalman filter, will only be used when the time factor has converged good enough,
196      * the value of 100 corresponds to a change rate of approximately 10e-6 per second. */
197     if (s->time_factor_variance < 100) {
198         filtered_time_delta_card = (time_delta_card * s->kalman_variance + expected_time_delta_card * s->time_variance) / (s->kalman_variance + s->time_variance);
199         s->kalman_variance = s->kalman_variance * s->time_variance / (s->kalman_variance + s->time_variance) + s->time_variance / 4 + 500;
200     }
201 
202     /* This is a horrible hack which is necessary because USB sinks seem to fix up
203      * the reported delay by some millisecondsconds shortly after startup. This is
204      * an artifact, the real latency does not change on the reported jump. If the
205      * change is not caught or if the hack is triggered inadvertently, it will lead to
206      * prolonged convergence time and decreased stability of the reported latency.
207      * Since the fix up will occur within the first seconds, it is disabled later to
208      * avoid false triggers. When run as batch device, the threshold for the hack must
209      * be lower (1000) than for timer based scheduling (2000). */
210     if (s->usb_hack && time_stamp - s->smoother_start_time < 5 * PA_USEC_PER_SEC) {
211         if ((time_delta_system - filtered_time_delta_card / s->time_factor) > (double)s->hack_threshold) {
212             /* Recalculate initial conditions */
213             temp = time_stamp - time_delta_card - s->start_time;
214             s->start_time += temp;
215             s->smoother_start_time += temp;
216             s->time_offset = -temp;
217 
218             /* Reset time factor variance */
219             s->time_factor_variance = 10000;
220 
221             pa_log_debug("USB Hack, start time corrected by %0.2f usec", temp);
222             s->usb_hack = false;
223             return;
224          }
225     }
226 
227     /* Parameter for lowpass filters with time constants of smoother_window_time
228      * and smoother_window_time/8 */
229     temp = (double)s->smoother_window_time / 6.2831853;
230     filter_constant = iteration_time / (iteration_time + temp / 8.0);
231     filter_constant_1 = iteration_time / (iteration_time + temp);
232 
233     /* Temporarily save the current time factor */
234     temp = s->time_factor;
235 
236     /* Calculate geometric series */
237     drift = (s->drift_filter_1 + 1.0) * (1.5 - filtered_time_delta_card / time_delta_system);
238 
239     /* 2nd order lowpass */
240     s->drift_filter = (1 - filter_constant) * s->drift_filter + filter_constant * drift;
241     s->drift_filter_1 = (1 - filter_constant) * s->drift_filter_1 + filter_constant * s->drift_filter;
242 
243     /* Calculate time conversion factor, filter again */
244     s->time_factor = (1 - filter_constant_1) * s->time_factor + filter_constant_1 * (s->drift_filter_1 + 3) / (s->drift_filter_1 + 1) / 2;
245 
246     /* Filtered variance of time factor derivative, used as measure for the convergence of the time factor */
247     temp = (s->time_factor - temp) / iteration_time * 10000000000000;
248     s->time_factor_variance = (1 - filter_constant_1) * s->time_factor_variance + filter_constant_1 * temp * temp;
249 
250     /* Calculate new start time and corresponding sample count after window time */
251     if (time_stamp > s->smoother_start_time + s->smoother_window_time) {
252         s->start_pos += ((double)byte_count - s->start_pos) / (time_stamp - s->start_time) * iteration_time;
253         s->start_time += (pa_usec_t)iteration_time;
254     }
255 
256     /* Save current system time */
257     s->last_time = time_stamp;
258 }
259 
260 /* Calculate the current latency. For a source, the sign must be inverted */
pa_smoother_2_get_delay(pa_smoother_2 * s,pa_usec_t time_stamp,uint64_t byte_count)261 int64_t pa_smoother_2_get_delay(pa_smoother_2 *s, pa_usec_t time_stamp, uint64_t byte_count) {
262     int64_t now, delay;
263 
264     pa_assert(s);
265 
266     /* If we do not have a valid frame size and rate, just return 0 */
267     if (!s->frame_size || !s->rate)
268         return 0;
269 
270     /* Smoother is paused or has been resumed but no new data has been received */
271     if (s->paused || s->init) {
272         delay = (int64_t)((double)byte_count * PA_USEC_PER_SEC / s->frame_size / s->rate);
273         return delay - pa_smoother_2_get(s, time_stamp);
274     }
275 
276     /* Convert system time difference to soundcard time difference */
277     now = (time_stamp - s->start_time - s->time_offset) * s->time_factor;
278 
279     /* Don't use pa_bytes_to_usec(), u->start_pos needs not be on a sample boundary */
280     return (int64_t)(((double)byte_count - s->start_pos) / s->frame_size / s->rate * PA_USEC_PER_SEC) - now;
281 }
282 
283 /* Convert system time to sound card time */
pa_smoother_2_get(pa_smoother_2 * s,pa_usec_t time_stamp)284 pa_usec_t pa_smoother_2_get(pa_smoother_2 *s, pa_usec_t time_stamp) {
285     pa_usec_t current_time;
286 
287     pa_assert(s);
288 
289     /* If we do not have a valid frame size and rate, just return 0 */
290     if (!s->frame_size || !s->rate)
291         return 0;
292 
293     /* Sound card time at start_time */
294     current_time = (pa_usec_t)(s->start_pos / s->frame_size / s->rate * PA_USEC_PER_SEC);
295 
296     /* If the smoother has not started, just return system time since resume */
297     if (!s->start_time) {
298         if (time_stamp >= s->resume_time)
299             current_time = time_stamp - s->resume_time;
300         else
301             current_time = 0;
302 
303     /* If we are paused return the sound card time at pause_time */
304     } else if (s->paused)
305         current_time += (s->pause_time - s->start_time - s->time_offset - s->fixup_time) * s->time_factor;
306 
307     /* If we are initializing, add the time since resume to the card time at pause_time */
308     else if (s->init) {
309         current_time += (s->pause_time - s->start_time - s->time_offset - s->fixup_time) * s->time_factor;
310         current_time += (time_stamp - s->resume_time) * s->time_factor;
311 
312     /* Smoother is running, calculate current sound card time */
313     } else
314         current_time += (time_stamp - s->start_time - s->time_offset) * s->time_factor;
315 
316     return current_time;
317 }
318 
319 /* Convert a time interval from sound card time to system time */
pa_smoother_2_translate(pa_smoother_2 * s,pa_usec_t time_difference)320 pa_usec_t pa_smoother_2_translate(pa_smoother_2 *s, pa_usec_t time_difference) {
321 
322     pa_assert(s);
323 
324     /* If not started yet, return the time difference */
325     if (!s->start_time)
326         return time_difference;
327 
328     return (pa_usec_t)(time_difference / s->time_factor);
329 }
330 
331 /* Enable USB hack */
pa_smoother_2_usb_hack_enable(pa_smoother_2 * s,bool enable,pa_usec_t offset)332 void pa_smoother_2_usb_hack_enable(pa_smoother_2 *s, bool enable, pa_usec_t offset) {
333 
334     pa_assert(s);
335 
336     s->enable_usb_hack = enable;
337     s->hack_threshold = offset;
338 }
339 
340 /* Reset the smoother */
pa_smoother_2_reset(pa_smoother_2 * s,pa_usec_t time_stamp)341 void pa_smoother_2_reset(pa_smoother_2 *s, pa_usec_t time_stamp) {
342 
343     pa_assert(s);
344 
345    /* Reset variables for time estimation */
346     s->drift_filter = 1.0;
347     s->drift_filter_1 = 1.0;
348     s->time_factor = 1.0;
349     s->start_pos = 0;
350     s->init = true;
351     s->time_offset = 0;
352     s->time_factor_variance = 10000.0;
353     s->kalman_variance = 10000000.0;
354     s->time_variance = 100000.0;
355     s->start_time = 0;
356     s->last_time = 0;
357     s->smoother_start_time = 0;
358     s->usb_hack = false;
359     s->pause_time = time_stamp;
360     s->fixup_time = 0;
361     s->resume_time = time_stamp;
362     s->paused = false;
363 
364     /* Set smoother to paused if rate or frame size are invalid */
365     if (!s->frame_size || !s->rate)
366         s->paused = true;
367 }
368 
369 /* Pause the smoother */
pa_smoother_2_pause(pa_smoother_2 * s,pa_usec_t time_stamp)370 void pa_smoother_2_pause(pa_smoother_2 *s, pa_usec_t time_stamp) {
371 
372     pa_assert(s);
373 
374     /* Smoother is already paused, nothing to do */
375     if (s->paused)
376         return;
377 
378     /* If we are in init state, add the pause time to the fixup time */
379     if (s->init)
380         s->fixup_time += s->resume_time - s->pause_time;
381     else
382         s->fixup_time = 0;
383 
384     s->smoother_start_time = 0;
385     s->resume_time = time_stamp;
386     s->pause_time = time_stamp;
387     s->time_factor_variance = 10000.0;
388     s->kalman_variance = 10000000.0;
389     s->time_variance = 100000.0;
390     s->init = true;
391     s->paused = true;
392 }
393 
394 /* Resume the smoother */
pa_smoother_2_resume(pa_smoother_2 * s,pa_usec_t time_stamp)395 void pa_smoother_2_resume(pa_smoother_2 *s, pa_usec_t time_stamp) {
396 
397     pa_assert(s);
398 
399     if (!s->paused)
400         return;
401 
402     /* Keep smoother paused if rate or frame size is not set */
403     if (!s->frame_size || !s->rate)
404         return;
405 
406     s->resume_time = time_stamp;
407     s->paused = false;
408 }
409