[pulseaudio-commits] [Git][pulseaudio/pulseaudio][master] 10 commits: pulsecore: Add alternative time smoother implementation
PulseAudio Marge Bot (@pulseaudio-merge-bot)
gitlab at gitlab.freedesktop.org
Wed Nov 3 21:17:42 UTC 2021
PulseAudio Marge Bot pushed to branch master at PulseAudio / pulseaudio
Commits:
156b5729 by Georg Chini at 2021-11-03T21:13:38+00:00
pulsecore: Add alternative time smoother implementation
This patch adds an alternative time smoother implementation based on the theory
found at https://www.freedesktop.org/software/pulseaudio/misc/rate_estimator.odt.
The functions were written to replace the current smoother functions nearly on
a one-to-one basis, though there are a few differences:
- The smoother_2_put() function takes a byte count instead of a sound card
time as argument. This was changed because in most places a sample count
was converted to a time before passing it to the smoother.
- The smoother needs to know sample rate and frame size to convert byte
counts to time.
- A smoother_2_get_delay() function was added to directly retrieve the stream
delay from the smoother.
- A hack for USB devices was added which works around an issue in the alsa
latency reports for USB devices.
The smoother delivers much better precision than the current implementation.
For results, see the document referenced above.
The new functions are still unused. The following patches will convert all
callers of the smoother functions so that they can use both smoother
implementations, depending on a configure option.
Part-of: <https://gitlab.freedesktop.org/pulseaudio/pulseaudio/-/merge_requests/55>
- - - - -
6f162031 by Georg Chini at 2021-11-03T21:13:38+00:00
Add configuration option enable-smoother-2 to enable alternative smoother code
Part-of: <https://gitlab.freedesktop.org/pulseaudio/pulseaudio/-/merge_requests/55>
- - - - -
8db67737 by Georg Chini at 2021-11-03T21:13:38+00:00
alsa sink/source: Allow alsa to use alternative smoother code
Part-of: <https://gitlab.freedesktop.org/pulseaudio/pulseaudio/-/merge_requests/55>
- - - - -
84ec08e6 by Georg Chini at 2021-11-03T21:13:38+00:00
bluetooth: Allow bluetooth to use alternative smoother code
Part-of: <https://gitlab.freedesktop.org/pulseaudio/pulseaudio/-/merge_requests/55>
- - - - -
1b5f3fc5 by Georg Chini at 2021-11-03T21:13:38+00:00
stream: Allow stream.c to use alternative smoother code
Part-of: <https://gitlab.freedesktop.org/pulseaudio/pulseaudio/-/merge_requests/55>
- - - - -
79aaec7b by Georg Chini at 2021-11-03T21:13:38+00:00
combine-sink: Allow module-combine-sink to use alternative smoother code
Part-of: <https://gitlab.freedesktop.org/pulseaudio/pulseaudio/-/merge_requests/55>
- - - - -
a7c970e2 by Georg Chini at 2021-11-03T21:13:38+00:00
tunnel: Allow module-tunnel to use alternative smoother code
Part-of: <https://gitlab.freedesktop.org/pulseaudio/pulseaudio/-/merge_requests/55>
- - - - -
74585e16 by Georg Chini at 2021-11-03T21:13:38+00:00
esound-sink: Allow module-esound-sink to use alternative smoother code
This is untested.
Part-of: <https://gitlab.freedesktop.org/pulseaudio/pulseaudio/-/merge_requests/55>
- - - - -
a92f566e by Georg Chini at 2021-11-03T21:13:38+00:00
raop-sink: Allow module-raop-sink to use alternative smoother code
This is untested.
Part-of: <https://gitlab.freedesktop.org/pulseaudio/pulseaudio/-/merge_requests/55>
- - - - -
0497821a by Georg Chini at 2021-11-03T21:13:38+00:00
solaris: Allow module-solaris to use alternative smoother code
This is untested.
Part-of: <https://gitlab.freedesktop.org/pulseaudio/pulseaudio/-/merge_requests/55>
- - - - -
16 changed files:
- meson.build
- meson_options.txt
- po/POTFILES.in
- src/meson.build
- src/modules/alsa/alsa-sink.c
- src/modules/alsa/alsa-source.c
- src/modules/bluetooth/module-bluez5-device.c
- src/modules/module-combine-sink.c
- src/modules/module-esound-sink.c
- src/modules/module-solaris.c
- src/modules/module-tunnel.c
- src/modules/raop/raop-sink.c
- src/pulse/internal.h
- src/pulse/stream.c
- + src/pulsecore/time-smoother_2.c
- + src/pulsecore/time-smoother_2.h
Changes:
=====================================
meson.build
=====================================
@@ -615,6 +615,10 @@ if get_option('running-from-build-tree')
cdata.set('HAVE_RUNNING_FROM_BUILD_TREE', 1)
endif
+if get_option('enable-smoother-2')
+ cdata.set('USE_SMOOTHER_2', 1)
+endif
+
alsa_dep = dependency('alsa', version : '>= 1.0.24', required : get_option('alsa'))
if alsa_dep.found()
cdata.set('HAVE_ALSA', 1)
=====================================
meson_options.txt
=====================================
@@ -162,6 +162,9 @@ option('valgrind',
option('x11',
type : 'feature', value : 'auto',
description : 'Optional X11 support')
+option('enable-smoother-2',
+ type : 'boolean', value : 'true',
+ description : 'Use alternative time smoother implementation')
# Echo cancellation
=====================================
po/POTFILES.in
=====================================
@@ -170,6 +170,7 @@ src/pulsecore/thread-mq.c
src/pulsecore/thread-posix.c
src/pulsecore/thread-win32.c
src/pulsecore/time-smoother.c
+src/pulsecore/time-smoother_2.c
src/pulsecore/tokenizer.c
src/pulsecore/x11prop.c
src/pulsecore/x11wrap.c
=====================================
src/meson.build
=====================================
@@ -62,6 +62,7 @@ libpulsecommon_sources = [
'pulsecore/strlist.c',
'pulsecore/tagstruct.c',
'pulsecore/time-smoother.c',
+ 'pulsecore/time-smoother_2.c',
'pulsecore/tokenizer.c',
'pulsecore/usergroup.c',
'pulsecore/sndfile-util.c',
@@ -141,6 +142,7 @@ libpulsecommon_headers = [
'pulsecore/tagstruct.h',
'pulsecore/thread.h',
'pulsecore/time-smoother.h',
+ 'pulsecore/time-smoother_2.h',
'pulsecore/tokenizer.h',
'pulsecore/usergroup.h',
'pulsecore/sndfile-util.h',
=====================================
src/modules/alsa/alsa-sink.c
=====================================
@@ -52,7 +52,12 @@
#include <pulsecore/thread.h>
#include <pulsecore/thread-mq.h>
#include <pulsecore/rtpoll.h>
+
+#ifdef USE_SMOOTHER_2
+#include <pulsecore/time-smoother_2.h>
+#else
#include <pulsecore/time-smoother.h>
+#endif
#include <modules/reserve-wrap.h>
@@ -78,11 +83,15 @@
#define TSCHED_MIN_SLEEP_USEC (10*PA_USEC_PER_MSEC) /* 10ms -- Sleep at least 10ms on each iteration */
#define TSCHED_MIN_WAKEUP_USEC (4*PA_USEC_PER_MSEC) /* 4ms -- Wakeup at least this long before the buffer runs empty*/
+#ifdef USE_SMOOTHER_2
+#define SMOOTHER_WINDOW_USEC (15*PA_USEC_PER_SEC) /* 15s -- smoother windows size */
+#else
#define SMOOTHER_WINDOW_USEC (10*PA_USEC_PER_SEC) /* 10s -- smoother windows size */
#define SMOOTHER_ADJUST_USEC (1*PA_USEC_PER_SEC) /* 1s -- smoother adjust time */
#define SMOOTHER_MIN_INTERVAL (2*PA_USEC_PER_MSEC) /* 2ms -- min smoother update interval */
#define SMOOTHER_MAX_INTERVAL (200*PA_USEC_PER_MSEC) /* 200ms -- max smoother update interval */
+#endif
#define VOLUME_ACCURACY (PA_VOLUME_NORM/100) /* don't require volume adjustments to be perfectly correct. don't necessarily extend granularity in software unless the differences get greater than this level */
@@ -156,11 +165,18 @@ struct userdata {
pa_rtpoll_item *alsa_rtpoll_item;
+#ifdef USE_SMOOTHER_2
+ pa_smoother_2 *smoother;
+#else
pa_smoother *smoother;
+#endif
uint64_t write_count;
uint64_t since_start;
+
+#ifndef USE_SMOOTHER_2
pa_usec_t smoother_interval;
pa_usec_t last_smoother_update;
+#endif
pa_idxset *formats;
@@ -480,9 +496,13 @@ static void hw_sleep_time(struct userdata *u, pa_usec_t *sleep_usec, pa_usec_t*p
/* Reset smoother and counters */
static void reset_vars(struct userdata *u) {
+#ifdef USE_SMOOTHER_2
+ pa_smoother_2_reset(u->smoother, pa_rtclock_now());
+#else
pa_smoother_reset(u->smoother, pa_rtclock_now(), true);
u->smoother_interval = SMOOTHER_MIN_INTERVAL;
u->last_smoother_update = 0;
+#endif
u->first = true;
u->since_start = 0;
@@ -955,7 +975,10 @@ static void update_smoother(struct userdata *u) {
snd_pcm_sframes_t delay = 0;
int64_t position;
int err;
- pa_usec_t now1 = 0, now2;
+ pa_usec_t now1 = 0;
+#ifndef USE_SMOOTHER_2
+ pa_usec_t now2;
+#endif
snd_pcm_status_t *status;
snd_htimestamp_t htstamp = { 0, 0 };
@@ -978,13 +1001,16 @@ static void update_smoother(struct userdata *u) {
if (now1 <= 0)
now1 = pa_rtclock_now();
+ position = (int64_t) u->write_count - ((int64_t) delay * (int64_t) u->frame_size);
+
+#ifdef USE_SMOOTHER_2
+ pa_smoother_2_put(u->smoother, now1, position);
+#else
/* check if the time since the last update is bigger than the interval */
if (u->last_smoother_update > 0)
if (u->last_smoother_update + u->smoother_interval > now1)
return;
- position = (int64_t) u->write_count - ((int64_t) delay * (int64_t) u->frame_size);
-
if (PA_UNLIKELY(position < 0))
position = 0;
@@ -995,18 +1021,26 @@ static void update_smoother(struct userdata *u) {
u->last_smoother_update = now1;
/* exponentially increase the update interval up to the MAX limit */
u->smoother_interval = PA_MIN (u->smoother_interval * 2, SMOOTHER_MAX_INTERVAL);
+#endif
}
static int64_t sink_get_latency(struct userdata *u) {
int64_t delay;
- pa_usec_t now1, now2;
+ pa_usec_t now1;
+#ifndef USE_SMOOTHER_2
+ pa_usec_t now2;
+#endif
pa_assert(u);
now1 = pa_rtclock_now();
+#ifdef USE_SMOOTHER_2
+ delay = pa_smoother_2_get_delay(u->smoother, now1, u->write_count);
+#else
now2 = pa_smoother_get(u->smoother, now1);
delay = (int64_t) pa_bytes_to_usec(u->write_count, &u->sink->sample_spec) - (int64_t) now2;
+#endif
if (u->memchunk.memblock)
delay += pa_bytes_to_usec(u->memchunk.length, &u->sink->sample_spec);
@@ -1036,7 +1070,11 @@ static void suspend(struct userdata *u) {
if (!u->pcm_handle)
return;
+#ifdef USE_SMOOTHER_2
+ pa_smoother_2_pause(u->smoother, pa_rtclock_now());
+#else
pa_smoother_pause(u->smoother, pa_rtclock_now());
+#endif
/* Close PCM device */
close_pcm(u);
@@ -1784,12 +1822,22 @@ static void sink_reconfigure_cb(pa_sink *s, pa_sample_spec *spec, bool passthrou
int i;
bool format_supported = false;
bool rate_supported = false;
+#ifdef USE_SMOOTHER_2
+ pa_sample_spec effective_spec;
+#endif
pa_assert(u);
+#ifdef USE_SMOOTHER_2
+ effective_spec.channels = s->sample_spec.channels;
+#endif
+
for (i = 0; u->supported_formats[i] != PA_SAMPLE_MAX; i++) {
if (u->supported_formats[i] == spec->format) {
pa_sink_set_sample_format(u->sink, spec->format);
+#ifdef USE_SMOOTHER_2
+ effective_spec.format = spec->format;
+#endif
format_supported = true;
break;
}
@@ -1799,11 +1847,17 @@ static void sink_reconfigure_cb(pa_sink *s, pa_sample_spec *spec, bool passthrou
pa_log_info("Sink does not support sample format of %s, set it to a verified value",
pa_sample_format_to_string(spec->format));
pa_sink_set_sample_format(u->sink, u->verified_sample_spec.format);
+#ifdef USE_SMOOTHER_2
+ effective_spec.format = u->verified_sample_spec.format;
+#endif
}
for (i = 0; u->supported_rates[i]; i++) {
if (u->supported_rates[i] == spec->rate) {
pa_sink_set_sample_rate(u->sink, spec->rate);
+#ifdef USE_SMOOTHER_2
+ effective_spec.rate = spec->rate;
+#endif
rate_supported = true;
break;
}
@@ -1812,8 +1866,15 @@ static void sink_reconfigure_cb(pa_sink *s, pa_sample_spec *spec, bool passthrou
if (!rate_supported) {
pa_log_info("Sink does not support sample rate of %u, set it to a verified value", spec->rate);
pa_sink_set_sample_rate(u->sink, u->verified_sample_spec.rate);
+#ifdef USE_SMOOTHER_2
+ effective_spec.rate = u->verified_sample_spec.rate;
+#endif
}
+#ifdef USE_SMOOTHER_2
+ pa_smoother_2_set_sample_spec(u->smoother, pa_rtclock_now(), &effective_spec);
+#endif
+
/* Passthrough status change is handled during unsuspend */
}
@@ -1947,7 +2008,11 @@ static void thread_func(void *userdata) {
pa_log_info("Starting playback.");
snd_pcm_start(u->pcm_handle);
+#ifdef USE_SMOOTHER_2
+ pa_smoother_2_resume(u->smoother, pa_rtclock_now());
+#else
pa_smoother_resume(u->smoother, pa_rtclock_now(), true);
+#endif
u->first = false;
}
@@ -1981,7 +2046,11 @@ static void thread_func(void *userdata) {
/* Convert from the sound card time domain to the
* system time domain */
+#ifdef USE_SMOOTHER_2
+ cusec = pa_smoother_2_translate(u->smoother, sleep_usec);
+#else
cusec = pa_smoother_translate(u->smoother, pa_rtclock_now(), sleep_usec);
+#endif
#ifdef DEBUG_TIMING
pa_log_debug("Waking up in %0.2fms (system clock).", (double) cusec / PA_USEC_PER_MSEC);
@@ -2281,7 +2350,11 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca
bool volume_is_set;
bool mute_is_set;
pa_alsa_profile_set *profile_set = NULL;
- void *state;
+ void *state = NULL;
+#ifdef USE_SMOOTHER_2
+ snd_pcm_info_t* pcm_info;
+ const char *id;
+#endif
pa_assert(m);
pa_assert(ma);
@@ -2394,6 +2467,7 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca
goto fail;
}
+#ifndef USE_SMOOTHER_2
u->smoother = pa_smoother_new(
SMOOTHER_ADJUST_USEC,
SMOOTHER_WINDOW_USEC,
@@ -2403,6 +2477,7 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca
pa_rtclock_now(),
true);
u->smoother_interval = SMOOTHER_MIN_INTERVAL;
+#endif
/* use ucm */
if (mapping && mapping->ucm_context.ucm)
@@ -2613,6 +2688,25 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca
goto fail;
}
+#ifdef USE_SMOOTHER_2
+ u->smoother = pa_smoother_2_new(SMOOTHER_WINDOW_USEC, pa_rtclock_now(), frame_size, u->sink->sample_spec.rate);
+
+ /* Check if this is an USB device, see alsa-util.c
+ * USB devices unfortunately need some special handling */
+ snd_pcm_info_alloca(&pcm_info);
+ if (snd_pcm_info(u->pcm_handle, pcm_info) == 0 &&
+ (id = snd_pcm_info_get_id(pcm_info))) {
+ if (pa_streq(id, "USB Audio")) {
+ uint32_t hack_threshold;
+ /* USB device, set hack parameter */
+ hack_threshold = 2000;
+ if (!u->use_tsched)
+ hack_threshold = 1000;
+ pa_smoother_2_usb_hack_enable(u->smoother, true, hack_threshold);
+ }
+ }
+#endif
+
if (u->ucm_context) {
pa_device_port *port;
unsigned h_prio = 0;
@@ -2816,7 +2910,11 @@ static void userdata_free(struct userdata *u) {
pa_hashmap_free(u->mixers);
if (u->smoother)
+#ifdef USE_SMOOTHER_2
+ pa_smoother_2_free(u->smoother);
+#else
pa_smoother_free(u->smoother);
+#endif
if (u->formats)
pa_idxset_free(u->formats, (pa_free_cb_t) pa_format_info_free);
=====================================
src/modules/alsa/alsa-source.c
=====================================
@@ -47,7 +47,12 @@
#include <pulsecore/thread.h>
#include <pulsecore/thread-mq.h>
#include <pulsecore/rtpoll.h>
+
+#ifdef USE_SMOOTHER_2
+#include <pulsecore/time-smoother_2.h>
+#else
#include <pulsecore/time-smoother.h>
+#endif
#include <modules/reserve-wrap.h>
@@ -71,11 +76,15 @@
#define TSCHED_MIN_SLEEP_USEC (10*PA_USEC_PER_MSEC) /* 10ms */
#define TSCHED_MIN_WAKEUP_USEC (4*PA_USEC_PER_MSEC) /* 4ms */
+#ifdef USE_SMOOTHER_2
+#define SMOOTHER_WINDOW_USEC (15*PA_USEC_PER_SEC) /* 15s */
+#else
#define SMOOTHER_WINDOW_USEC (10*PA_USEC_PER_SEC) /* 10s */
#define SMOOTHER_ADJUST_USEC (1*PA_USEC_PER_SEC) /* 1s */
#define SMOOTHER_MIN_INTERVAL (2*PA_USEC_PER_MSEC) /* 2ms */
#define SMOOTHER_MAX_INTERVAL (200*PA_USEC_PER_MSEC) /* 200ms */
+#endif
#define VOLUME_ACCURACY (PA_VOLUME_NORM/100)
@@ -140,10 +149,17 @@ struct userdata {
pa_rtpoll_item *alsa_rtpoll_item;
+#ifdef USE_SMOOTHER_2
+ pa_smoother_2 *smoother;
+#else
pa_smoother *smoother;
+#endif
uint64_t read_count;
+
+#ifndef USE_SMOOTHER_2
pa_usec_t smoother_interval;
pa_usec_t last_smoother_update;
+#endif
pa_reserve_wrapper *reserve;
pa_hook_slot *reserve_slot;
@@ -452,9 +468,13 @@ static void hw_sleep_time(struct userdata *u, pa_usec_t *sleep_usec, pa_usec_t*p
/* Reset smoother and counters */
static void reset_vars(struct userdata *u) {
+#ifdef USE_SMOOTHER_2
+ pa_smoother_2_reset(u->smoother, pa_rtclock_now());
+#else
pa_smoother_reset(u->smoother, pa_rtclock_now(), true);
u->smoother_interval = SMOOTHER_MIN_INTERVAL;
u->last_smoother_update = 0;
+#endif
u->read_count = 0;
u->first = true;
@@ -462,7 +482,12 @@ static void reset_vars(struct userdata *u) {
/* Called from IO context */
static void close_pcm(struct userdata *u) {
+
+#ifdef USE_SMOOTHER_2
+ pa_smoother_2_pause(u->smoother, pa_rtclock_now());
+#else
pa_smoother_pause(u->smoother, pa_rtclock_now());
+#endif
/* Let's suspend */
snd_pcm_close(u->pcm_handle);
@@ -877,7 +902,10 @@ static void update_smoother(struct userdata *u) {
snd_pcm_sframes_t delay = 0;
uint64_t position;
int err;
- pa_usec_t now1 = 0, now2;
+ pa_usec_t now1 = 0;
+#ifndef USE_SMOOTHER_2
+ pa_usec_t now2;
+#endif
snd_pcm_status_t *status;
snd_htimestamp_t htstamp = { 0, 0 };
@@ -900,12 +928,16 @@ static void update_smoother(struct userdata *u) {
if (now1 <= 0)
now1 = pa_rtclock_now();
+ position = u->read_count + ((uint64_t) delay * (uint64_t) u->frame_size);
+
+#ifdef USE_SMOOTHER_2
+ pa_smoother_2_put(u->smoother, now1, position);
+#else
/* check if the time since the last update is bigger than the interval */
if (u->last_smoother_update > 0)
if (u->last_smoother_update + u->smoother_interval > now1)
return;
- position = u->read_count + ((uint64_t) delay * (uint64_t) u->frame_size);
now2 = pa_bytes_to_usec(position, &u->source->sample_spec);
pa_smoother_put(u->smoother, now1, now2);
@@ -913,18 +945,27 @@ static void update_smoother(struct userdata *u) {
u->last_smoother_update = now1;
/* exponentially increase the update interval up to the MAX limit */
u->smoother_interval = PA_MIN (u->smoother_interval * 2, SMOOTHER_MAX_INTERVAL);
+#endif
}
static int64_t source_get_latency(struct userdata *u) {
int64_t delay;
- pa_usec_t now1, now2;
+ pa_usec_t now1;
+#ifndef USE_SMOOTHER_2
+ pa_usec_t now2;
+#endif
pa_assert(u);
now1 = pa_rtclock_now();
+
+#ifdef USE_SMOOTHER_2
+ delay = - pa_smoother_2_get_delay(u->smoother, now1, u->read_count);
+#else
now2 = pa_smoother_get(u->smoother, now1);
delay = (int64_t) now2 - (int64_t) pa_bytes_to_usec(u->read_count, &u->source->sample_spec);
+#endif
return delay;
}
@@ -1596,12 +1637,22 @@ static void source_reconfigure_cb(pa_source *s, pa_sample_spec *spec, bool passt
int i;
bool format_supported = false;
bool rate_supported = false;
+#ifdef USE_SMOOTHER_2
+ pa_sample_spec effective_spec;
+#endif
pa_assert(u);
+#ifdef USE_SMOOTHER_2
+ effective_spec.channels = s->sample_spec.channels;
+#endif
+
for (i = 0; u->supported_formats[i] != PA_SAMPLE_MAX; i++) {
if (u->supported_formats[i] == spec->format) {
pa_source_set_sample_format(u->source, spec->format);
+#ifdef USE_SMOOTHER_2
+ effective_spec.format = spec->format;
+#endif
format_supported = true;
break;
}
@@ -1611,11 +1662,17 @@ static void source_reconfigure_cb(pa_source *s, pa_sample_spec *spec, bool passt
pa_log_info("Source does not support sample format of %s, set it to a verified value",
pa_sample_format_to_string(spec->format));
pa_source_set_sample_format(u->source, u->verified_sample_spec.format);
+#ifdef USE_SMOOTHER_2
+ effective_spec.format = u->verified_sample_spec.format;
+#endif
}
for (i = 0; u->supported_rates[i]; i++) {
if (u->supported_rates[i] == spec->rate) {
pa_source_set_sample_rate(u->source, spec->rate);
+#ifdef USE_SMOOTHER_2
+ effective_spec.rate = spec->rate;
+#endif
rate_supported = true;
break;
}
@@ -1624,7 +1681,15 @@ static void source_reconfigure_cb(pa_source *s, pa_sample_spec *spec, bool passt
if (!rate_supported) {
pa_log_info("Source does not support sample rate of %u, set it to a verfied value", spec->rate);
pa_source_set_sample_rate(u->source, u->verified_sample_spec.rate);
+#ifdef USE_SMOOTHER_2
+ effective_spec.rate = u->verified_sample_spec.rate;
+#endif
}
+
+#ifdef USE_SMOOTHER_2
+ pa_smoother_2_set_sample_spec(u->smoother, pa_rtclock_now(), &effective_spec);
+#endif
+
}
static void thread_func(void *userdata) {
@@ -1658,7 +1723,11 @@ static void thread_func(void *userdata) {
pa_log_info("Starting capture.");
snd_pcm_start(u->pcm_handle);
+#ifdef USE_SMOOTHER_2
+ pa_smoother_2_resume(u->smoother, pa_rtclock_now());
+#else
pa_smoother_resume(u->smoother, pa_rtclock_now(), true);
+#endif
u->first = false;
}
@@ -1686,7 +1755,11 @@ static void thread_func(void *userdata) {
/* Convert from the sound card time domain to the
* system time domain */
+#ifdef USE_SMOOTHER_2
+ cusec = pa_smoother_2_translate(u->smoother, sleep_usec);
+#else
cusec = pa_smoother_translate(u->smoother, pa_rtclock_now(), sleep_usec);
+#endif
/* pa_log_debug("Waking up in %0.2fms (system clock).", (double) cusec / PA_USEC_PER_MSEC); */
@@ -2085,6 +2158,7 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p
goto fail;
}
+#ifndef USE_SMOOTHER_2
u->smoother = pa_smoother_new(
SMOOTHER_ADJUST_USEC,
SMOOTHER_WINDOW_USEC,
@@ -2094,6 +2168,7 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p
pa_rtclock_now(),
true);
u->smoother_interval = SMOOTHER_MIN_INTERVAL;
+#endif
/* use ucm */
if (mapping && mapping->ucm_context.ucm)
@@ -2293,6 +2368,10 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p
goto fail;
}
+#ifdef USE_SMOOTHER_2
+ u->smoother = pa_smoother_2_new(SMOOTHER_WINDOW_USEC, pa_rtclock_now(), frame_size, u->source->sample_spec.rate);
+#endif
+
if (u->ucm_context) {
pa_device_port *port;
unsigned h_prio = 0;
@@ -2454,7 +2533,11 @@ static void userdata_free(struct userdata *u) {
pa_hashmap_free(u->mixers);
if (u->smoother)
+#ifdef USE_SMOOTHER_2
+ pa_smoother_2_free(u->smoother);
+#else
pa_smoother_free(u->smoother);
+#endif
if (u->supported_formats)
pa_xfree(u->supported_formats);
=====================================
src/modules/bluetooth/module-bluez5-device.c
=====================================
@@ -46,7 +46,12 @@
#include <pulsecore/socket-util.h>
#include <pulsecore/thread.h>
#include <pulsecore/thread-mq.h>
+
+#ifdef USE_SMOOTHER_2
+#include <pulsecore/time-smoother_2.h>
+#else
#include <pulsecore/time-smoother.h>
+#endif
#include "a2dp-codecs.h"
#include "a2dp-codec-util.h"
@@ -138,7 +143,13 @@ struct userdata {
uint64_t read_index;
uint64_t write_index;
pa_usec_t started_at;
+
+#ifdef USE_SMOOTHER_2
+ pa_smoother_2 *read_smoother;
+#else
pa_smoother *read_smoother;
+#endif
+
pa_memchunk write_memchunk;
const pa_bt_codec *bt_codec;
@@ -528,8 +539,13 @@ static int bt_process_push(struct userdata *u) {
}
u->read_index += (uint64_t) memchunk.length;
- pa_smoother_put(u->read_smoother, tstamp, pa_bytes_to_usec(u->read_index, &u->decoder_sample_spec));
- pa_smoother_resume(u->read_smoother, tstamp, true);
+#ifdef USE_SMOOTHER_2
+ pa_smoother_2_resume(u->read_smoother, tstamp);
+ pa_smoother_2_put(u->read_smoother, tstamp, u->read_index);
+#else
+ pa_smoother_put(u->read_smoother, tstamp, pa_bytes_to_usec(u->read_index, &u->decoder_sample_spec));
+ pa_smoother_resume(u->read_smoother, tstamp, true);
+#endif
/* Decoding of data may result in empty buffer, in this case
* do not post empty audio samples. It may happen due to algorithmic
@@ -589,7 +605,11 @@ static void teardown_stream(struct userdata *u) {
}
if (u->read_smoother) {
+#ifdef USE_SMOOTHER_2
+ pa_smoother_2_free(u->read_smoother);
+#else
pa_smoother_free(u->read_smoother);
+#endif
u->read_smoother = NULL;
}
@@ -746,7 +766,11 @@ static int setup_stream(struct userdata *u) {
u->stream_setup_done = true;
if (u->source)
+#ifdef USE_SMOOTHER_2
+ u->read_smoother = pa_smoother_2_new(5*PA_USEC_PER_SEC, pa_rtclock_now(), pa_frame_size(&u->decoder_sample_spec), u->decoder_sample_spec.rate);
+#else
u->read_smoother = pa_smoother_new(PA_USEC_PER_SEC, 2*PA_USEC_PER_SEC, true, true, 10, pa_rtclock_now(), true);
+#endif
return 0;
}
@@ -812,13 +836,19 @@ static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t off
switch (code) {
case PA_SOURCE_MESSAGE_GET_LATENCY: {
+#ifndef USE_SMOOTHER_2
int64_t wi, ri;
+#endif
if (u->read_smoother) {
+#ifdef USE_SMOOTHER_2
+ *((int64_t*) data) = u->source->thread_info.fixed_latency - pa_smoother_2_get_delay(u->read_smoother, pa_rtclock_now(), u->read_index);
+#else
wi = pa_smoother_get(u->read_smoother, pa_rtclock_now());
ri = pa_bytes_to_usec(u->read_index, &u->decoder_sample_spec);
*((int64_t*) data) = u->source->thread_info.fixed_latency + wi - ri;
+#endif
} else
*((int64_t*) data) = 0;
@@ -863,8 +893,11 @@ static int source_set_state_in_io_thread_cb(pa_source *s, pa_source_state_t new_
transport_release(u);
if (u->read_smoother)
+#ifdef USE_SMOOTHER_2
+ pa_smoother_2_pause(u->read_smoother, pa_rtclock_now());
+#else
pa_smoother_pause(u->read_smoother, pa_rtclock_now());
-
+#endif
break;
case PA_SOURCE_IDLE:
@@ -1042,17 +1075,26 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
switch (code) {
case PA_SINK_MESSAGE_GET_LATENCY: {
- int64_t wi = 0, ri = 0;
+ int64_t wi, ri, delay = 0;
if (u->read_smoother) {
+#ifdef USE_SMOOTHER_2
+ /* This is only used for SCO where encoder and decoder sample specs are
+ * equal and output timing is based on the source. Therefore we can pass
+ * the write index without conversion. */
+ delay = pa_smoother_2_get_delay(u->read_smoother, pa_rtclock_now(), u->write_index + u->write_block_size);
+#else
ri = pa_smoother_get(u->read_smoother, pa_rtclock_now());
wi = pa_bytes_to_usec(u->write_index + u->write_block_size, &u->encoder_sample_spec);
+ delay = wi - ri;
+#endif
} else if (u->started_at) {
ri = pa_rtclock_now() - u->started_at;
wi = pa_bytes_to_usec(u->write_index, &u->encoder_sample_spec);
+ delay = wi - ri;
}
- *((int64_t*) data) = u->sink->thread_info.fixed_latency + wi - ri;
+ *((int64_t*) data) = u->sink->thread_info.fixed_latency + delay;
return 0;
}
@@ -1795,7 +1837,11 @@ static void stop_thread(struct userdata *u) {
}
if (u->read_smoother) {
+#ifdef USE_SMOOTHER_2
+ pa_smoother_2_free(u->read_smoother);
+#else
pa_smoother_free(u->read_smoother);
+#endif
u->read_smoother = NULL;
}
=====================================
src/modules/module-combine-sink.c
=====================================
@@ -43,7 +43,13 @@
#include <pulsecore/thread.h>
#include <pulsecore/thread-mq.h>
#include <pulsecore/rtpoll.h>
+
+#ifdef USE_SMOOTHER_2
+#include <pulsecore/time-smoother_2.h>
+#else
#include <pulsecore/time-smoother.h>
+#endif
+
#include <pulsecore/strlist.h>
PA_MODULE_AUTHOR("Lennart Poettering");
@@ -169,7 +175,11 @@ struct userdata {
pa_atomic_t running; /* we cache that value here, so that every thread can query it cheaply */
pa_usec_t timestamp;
bool in_null_mode;
- pa_smoother *smoother;
+#ifdef USE_SMOOTHER_2
+ pa_smoother_2 *smoother;
+#else
+ pa_smoother *smoother;
+#endif
uint64_t counter;
uint64_t snapshot_counter;
@@ -404,8 +414,13 @@ static void process_render_null(struct userdata *u, pa_usec_t now) {
/* pa_log_debug("Ate in sum %lu bytes (of %lu)", (unsigned long) ate, (unsigned long) nbytes); */
- pa_smoother_put(u->thread_info.smoother, now,
- pa_bytes_to_usec(u->thread_info.counter, &u->sink->sample_spec) - (u->thread_info.timestamp - now));
+#ifdef USE_SMOOTHER_2
+ pa_smoother_2_put(u->thread_info.smoother, now,
+ u->thread_info.counter - pa_usec_to_bytes(u->thread_info.timestamp - now, &u->sink->sample_spec));
+#else
+ pa_smoother_put(u->thread_info.smoother, now,
+ pa_bytes_to_usec(u->thread_info.counter, &u->sink->sample_spec) - (u->thread_info.timestamp - now));
+#endif
}
static void thread_func(void *userdata) {
@@ -860,9 +875,15 @@ static int sink_set_state_in_io_thread_cb(pa_sink *s, pa_sink_state_t new_state,
if (running) {
u->thread_info.render_timestamp = 0;
+#ifdef USE_SMOOTHER_2
+ pa_smoother_2_resume(u->thread_info.smoother, pa_rtclock_now());
+ } else
+ pa_smoother_2_pause(u->thread_info.smoother, pa_rtclock_now());
+#else
pa_smoother_resume(u->thread_info.smoother, pa_rtclock_now(), true);
} else
pa_smoother_pause(u->thread_info.smoother, pa_rtclock_now());
+#endif
return 0;
}
@@ -1010,15 +1031,20 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
switch (code) {
case PA_SINK_MESSAGE_GET_LATENCY: {
- pa_usec_t x, y, c;
int64_t *delay = data;
+#ifdef USE_SMOOTHER_2
+ *delay = pa_smoother_2_get_delay(u->thread_info.smoother, pa_rtclock_now(), u->thread_info.counter);
+#else
+ pa_usec_t x, y, c;
+
x = pa_rtclock_now();
y = pa_smoother_get(u->thread_info.smoother, x);
c = pa_bytes_to_usec(u->thread_info.counter, &u->sink->sample_spec);
*delay = (int64_t)c - y;
+#endif
return 0;
}
@@ -1040,6 +1066,12 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
return 0;
case SINK_MESSAGE_UPDATE_LATENCY: {
+#ifdef USE_SMOOTHER_2
+ size_t latency;
+
+ latency = pa_usec_to_bytes((pa_usec_t)offset, &u->sink->sample_spec);
+ pa_smoother_2_put(u->thread_info.smoother, pa_rtclock_now(), (int64_t)u->thread_info.counter - latency);
+#else
pa_usec_t x, y, latency = (pa_usec_t) offset;
/* It may be possible that thread_info.counter has been increased
@@ -1054,6 +1086,7 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
y = 0;
pa_smoother_put(u->thread_info.smoother, x, y);
+#endif
return 0;
}
@@ -1472,6 +1505,7 @@ int pa__init(pa_module*m) {
u->resample_method = resample_method;
u->outputs = pa_idxset_new(NULL, NULL);
+#ifndef USE_SMOOTHER_2
u->thread_info.smoother = pa_smoother_new(
PA_USEC_PER_SEC,
PA_USEC_PER_SEC*2,
@@ -1480,6 +1514,7 @@ int pa__init(pa_module*m) {
10,
pa_rtclock_now(),
true);
+#endif
adjust_time_sec = DEFAULT_ADJUST_TIME_USEC / PA_USEC_PER_SEC;
if (pa_modargs_get_value_u32(ma, "adjust_time", &adjust_time_sec) < 0) {
@@ -1586,6 +1621,11 @@ int pa__init(pa_module*m) {
goto fail;
}
+#ifdef USE_SMOOTHER_2
+ /* The smoother window size needs to be larger than the time between updates */
+ u->thread_info.smoother = pa_smoother_2_new(u->adjust_time + 5*PA_USEC_PER_SEC, pa_rtclock_now(), pa_frame_size(&u->sink->sample_spec), u->sink->sample_spec.rate);
+#endif
+
u->sink->parent.process_msg = sink_process_msg;
u->sink->set_state_in_main_thread = sink_set_state_in_main_thread_cb;
u->sink->set_state_in_io_thread = sink_set_state_in_io_thread_cb;
@@ -1727,7 +1767,11 @@ void pa__done(pa_module*m) {
u->core->mainloop->time_free(u->time_event);
if (u->thread_info.smoother)
+#ifdef USE_SMOOTHER_2
+ pa_smoother_2_free(u->thread_info.smoother);
+#else
pa_smoother_free(u->thread_info.smoother);
+#endif
pa_xfree(u);
}
=====================================
src/modules/module-esound-sink.c
=====================================
@@ -60,7 +60,13 @@
#include <pulsecore/authkey.h>
#include <pulsecore/thread-mq.h>
#include <pulsecore/thread.h>
+
+#ifdef USE_SMOOTHER_2
+#include <pulsecore/time-smoother_2.h>
+#else
#include <pulsecore/time-smoother.h>
+#endif
+
#include <pulsecore/socket-util.h>
#include <pulsecore/rtpoll.h>
#include <pulsecore/poll.h>
@@ -110,7 +116,12 @@ struct userdata {
esd_format_t format;
int32_t rate;
+#ifdef USE_SMOOTHER_2
+ pa_smoother_2 *smoother;
+#else
pa_smoother *smoother;
+#endif
+
int fd;
int64_t offset;
@@ -142,12 +153,16 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
switch (code) {
case PA_SINK_MESSAGE_GET_LATENCY: {
+#ifdef USE_SMOOTHER_2
+ *((int64_t*) data) = pa_smoother_2_get_delay(u->smoother, pa_rtclock_now(), (uint64_t)u->offset + u->memchunk.length);
+#else
pa_usec_t w, r;
r = pa_smoother_get(u->smoother, pa_rtclock_now());
w = pa_bytes_to_usec((uint64_t) u->offset + u->memchunk.length, &u->sink->sample_spec);
*((int64_t*) data) = (int64_t)w - r;
+#endif
return 0;
}
@@ -185,15 +200,22 @@ static int sink_set_state_in_io_thread_cb(pa_sink *s, pa_sink_state_t new_state,
case PA_SINK_SUSPENDED:
pa_assert(PA_SINK_IS_OPENED(s->thread_info.state));
+#ifdef USE_SMOOTHER_2
+ pa_smoother_2_pause(u->smoother, pa_rtclock_now());
+#else
pa_smoother_pause(u->smoother, pa_rtclock_now());
+#endif
break;
case PA_SINK_IDLE:
case PA_SINK_RUNNING:
if (s->thread_info.state == PA_SINK_SUSPENDED)
+#ifdef USE_SMOOTHER_2
+ pa_smoother_2_resume(u->smoother, pa_rtclock_now());
+#else
pa_smoother_resume(u->smoother, pa_rtclock_now(), true);
-
+#endif
break;
case PA_SINK_UNLINKED:
@@ -215,7 +237,11 @@ static void thread_func(void *userdata) {
pa_thread_mq_install(&u->thread_mq);
+#ifdef USE_SMOOTHER_2
+ pa_smoother_2_reset(u->smoother, pa_rtclock_now());
+#else
pa_smoother_set_time_offset(u->smoother, pa_rtclock_now());
+#endif
for (;;) {
int ret;
@@ -229,7 +255,11 @@ static void thread_func(void *userdata) {
/* Render some data and write it to the fifo */
if (PA_SINK_IS_OPENED(u->sink->thread_info.state) && pollfd->revents) {
+#ifdef USE_SMOOTHER_2
+ size_t bytes;
+#else
pa_usec_t usec;
+#endif
int64_t n;
for (;;) {
@@ -298,6 +328,16 @@ static void thread_func(void *userdata) {
}
#endif
+#ifdef USE_SMOOTHER_2
+ bytes = pa_usec_to_bytes(u->latency, &u->sink->sample_spec);
+
+ if ((uint64_t)n > bytes)
+ bytes = n - bytes;
+ else
+ bytes = 0;
+
+ pa_smoother_2_put(u->smoother, pa_rtclock_now(), bytes);
+#else
usec = pa_bytes_to_usec((uint64_t) n, &u->sink->sample_spec);
if (usec > u->latency)
@@ -306,6 +346,7 @@ static void thread_func(void *userdata) {
usec = 0;
pa_smoother_put(u->smoother, pa_rtclock_now(), usec);
+#endif
}
/* Hmm, nothing to do. Let's sleep */
@@ -559,6 +600,9 @@ int pa__init(pa_module*m) {
u->module = m;
m->userdata = u;
u->fd = -1;
+#ifdef USE_SMOOTHER_2
+ u->smoother = pa_smoother_2_new(5*PA_USEC_PER_SEC, pa_rtclock_now(), pa_frame_size(&ss), ss.rate);
+#else
u->smoother = pa_smoother_new(
PA_USEC_PER_SEC,
PA_USEC_PER_SEC*2,
@@ -567,6 +611,7 @@ int pa__init(pa_module*m) {
10,
0,
false);
+#endif
pa_memchunk_reset(&u->memchunk);
u->offset = 0;
@@ -723,7 +768,11 @@ void pa__done(pa_module*m) {
pa_xfree(u->write_data);
if (u->smoother)
+#ifdef USE_SMOOTHER_2
+ pa_smoother_2_free(u->smoother);
+#else
pa_smoother_free(u->smoother);
+#endif
if (u->fd >= 0)
pa_close(u->fd);
=====================================
src/modules/module-solaris.c
=====================================
@@ -60,7 +60,12 @@
#include <pulsecore/thread-mq.h>
#include <pulsecore/rtpoll.h>
#include <pulsecore/thread.h>
+
+#ifdef USE_SMOOTHER_2
+#include <pulsecore/time-smoother_2.h>
+#else
#include <pulsecore/time-smoother.h>
+#endif
PA_MODULE_AUTHOR("Pierre Ossman");
PA_MODULE_DESCRIPTION("Solaris Sink/Source");
@@ -110,7 +115,11 @@ struct userdata {
int32_t minimum_request;
+#ifdef USE_SMOOTHER_2
+ pa_smoother_2 *smoother;
+#else
pa_smoother *smoother;
+#endif
};
static const char* const valid_modargs[] = {
@@ -164,7 +173,11 @@ static uint64_t get_playback_buffered_bytes(struct userdata *u) {
u->prev_playback_samples = info.play.samples;
played_bytes = (((uint64_t)u->play_samples_msw << 32) + info.play.samples) * u->frame_size;
+#ifdef USE_SMOOTHER_2
+ pa_smoother_2_put(u->smoother, pa_rtclock_now(), played_bytes);
+#else
pa_smoother_put(u->smoother, pa_rtclock_now(), pa_bytes_to_usec(played_bytes, &u->sink->sample_spec));
+#endif
if (u->written_bytes > played_bytes)
return u->written_bytes - played_bytes;
@@ -413,7 +426,11 @@ static int sink_set_state_in_io_thread_cb(pa_sink *s, pa_sink_state_t new_state,
pa_assert(PA_SINK_IS_OPENED(s->thread_info.state));
+#ifdef USE_SMOOTHER_2
+ pa_smoother_2_pause(u->smoother, pa_rtclock_now());
+#else
pa_smoother_pause(u->smoother, pa_rtclock_now());
+#endif
if (!u->source || u->source_suspended)
suspend(u);
@@ -425,7 +442,11 @@ static int sink_set_state_in_io_thread_cb(pa_sink *s, pa_sink_state_t new_state,
case PA_SINK_RUNNING:
if (s->thread_info.state == PA_SINK_SUSPENDED) {
+#ifdef USE_SMOOTHER_2
+ pa_smoother_2_resume(u->smoother, pa_rtclock_now());
+#else
pa_smoother_resume(u->smoother, pa_rtclock_now(), true);
+#endif
if (!u->source || u->source_suspended) {
bool mute;
@@ -654,7 +675,11 @@ static void thread_func(void *userdata) {
pa_thread_mq_install(&u->thread_mq);
+#ifdef USE_SMOOTHER_2
+ pa_smoother_2_reset(u->smoother, pa_rtclock_now());
+#else
pa_smoother_set_time_offset(u->smoother, pa_rtclock_now());
+#endif
for (;;) {
/* Render some data and write it to the dsp */
@@ -680,7 +705,11 @@ static void thread_func(void *userdata) {
if (ioctl(u->fd, AUDIO_SETINFO, &info) < 0)
pa_log("AUDIO_SETINFO: %s", pa_cstrerror(errno));
+#ifdef USE_SMOOTHER_2
+ pa_smoother_2_reset(u->smoother, pa_rtclock_now());
+#else
pa_smoother_reset(u->smoother, pa_rtclock_now(), true);
+#endif
}
for (;;) {
@@ -736,7 +765,11 @@ static void thread_func(void *userdata) {
}
ysleep_interval = pa_bytes_to_usec(buffered_bytes / 2, &u->sink->sample_spec);
+#ifdef USE_SMOOTHER_2
+ xsleep_interval = pa_smoother_2_translate(u->smoother, ysleep_interval);
+#else
xsleep_interval = pa_smoother_translate(u->smoother, xtime0, ysleep_interval);
+#endif
pa_rtpoll_set_timer_absolute(u->rtpoll, xtime0 + PA_MIN(xsleep_interval, ysleep_interval));
} else
pa_rtpoll_set_timer_disabled(u->rtpoll);
@@ -884,8 +917,10 @@ int pa__init(pa_module *m) {
u = pa_xnew0(struct userdata, 1);
+#ifndef USE_SMOOTHER_2
if (!(u->smoother = pa_smoother_new(PA_USEC_PER_SEC, PA_USEC_PER_SEC * 2, true, true, 10, pa_rtclock_now(), true)))
goto fail;
+#endif
/*
* For a process (or several processes) to use the same audio device for both
@@ -901,6 +936,10 @@ int pa__init(pa_module *m) {
}
u->frame_size = pa_frame_size(&ss);
+#ifdef USE_SMOOTHER_2
+ u->smoother = pa_smoother_2_new(5*PA_USEC_PER_SEC, pa_rtclock_now(), u->frame_size, ss.rate);
+#endif
+
u->minimum_request = pa_usec_to_bytes(PA_USEC_PER_SEC / MAX_RENDER_HZ, &ss);
buffer_length_msec = 100;
@@ -1142,7 +1181,11 @@ void pa__done(pa_module *m) {
close(u->fd);
if (u->smoother)
+#ifdef USE_SMOOTHER_2
+ pa_smoother_2_free(u->smoother);
+#else
pa_smoother_free(u->smoother);
+#endif
pa_xfree(u->device_name);
=====================================
src/modules/module-tunnel.c
=====================================
@@ -48,7 +48,13 @@
#include <pulsecore/pstream.h>
#include <pulsecore/pstream-util.h>
#include <pulsecore/socket-client.h>
+
+#ifdef USE_SMOOTHER_2
+#include <pulsecore/time-smoother_2.h>
+#else
#include <pulsecore/time-smoother.h>
+#endif
+
#include <pulsecore/thread.h>
#include <pulsecore/thread-mq.h>
#include <pulsecore/core-rtclock.h>
@@ -231,7 +237,11 @@ struct userdata {
pa_time_event *time_event;
+#ifdef USE_SMOOTHER_2
+ pa_smoother_2 *smoother;
+#else
pa_smoother *smoother;
+#endif
char *device_description;
char *server_fqdn;
@@ -424,9 +434,15 @@ static void check_smoother_status(struct userdata *u, bool past) {
x += u->thread_transport_usec;
if (u->remote_suspended || u->remote_corked)
+#ifdef USE_SMOOTHER_2
+ pa_smoother_2_pause(u->smoother, x);
+ else
+ pa_smoother_2_resume(u->smoother, x);
+#else
pa_smoother_pause(u->smoother, x);
else
pa_smoother_resume(u->smoother, x, true);
+#endif
}
/* Called from IO thread context */
@@ -514,13 +530,18 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
}
case PA_SINK_MESSAGE_GET_LATENCY: {
- pa_usec_t yl, yr;
int64_t *usec = data;
+#ifdef USE_SMOOTHER_2
+ *usec = pa_smoother_2_get_delay(u->smoother, pa_rtclock_now(), u->counter);
+#else
+ pa_usec_t yl, yr;
+
yl = pa_bytes_to_usec((uint64_t) u->counter, &u->sink->sample_spec);
yr = pa_smoother_get(u->smoother, pa_rtclock_now());
*usec = (int64_t)yl - yr;
+#endif
return 0;
}
@@ -547,6 +568,21 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
return 0;
case SINK_MESSAGE_UPDATE_LATENCY: {
+#ifdef USE_SMOOTHER_2
+ int64_t bytes;
+
+ if (offset < 0)
+ bytes = - pa_usec_to_bytes(- offset, &u->sink->sample_spec);
+ else
+ bytes = pa_usec_to_bytes(offset, &u->sink->sample_spec);
+
+ if (u->counter > bytes)
+ bytes = u->counter - bytes;
+ else
+ bytes = 0;
+
+ pa_smoother_2_put(u->smoother, pa_rtclock_now(), bytes);
+#else
pa_usec_t y;
y = pa_bytes_to_usec((uint64_t) u->counter, &u->sink->sample_spec);
@@ -557,6 +593,7 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
y = 0;
pa_smoother_put(u->smoother, pa_rtclock_now(), y);
+#endif
/* We can access this freely here, since the main thread is waiting for us */
u->thread_transport_usec = u->transport_usec;
@@ -632,13 +669,18 @@ static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t off
}
case PA_SOURCE_MESSAGE_GET_LATENCY: {
- pa_usec_t yr, yl;
int64_t *usec = data;
+#ifdef USE_SMOOTHER_2
+ *usec = - pa_smoother_2_get_delay(u->smoother, pa_rtclock_now(), u->counter);
+#else
+ pa_usec_t yr, yl;
+
yl = pa_bytes_to_usec((uint64_t) u->counter, &PA_SOURCE(o)->sample_spec);
yr = pa_smoother_get(u->smoother, pa_rtclock_now());
*usec = (int64_t)yr - yl;
+#endif
return 0;
}
@@ -673,12 +715,25 @@ static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t off
return 0;
case SOURCE_MESSAGE_UPDATE_LATENCY: {
+#ifdef USE_SMOOTHER_2
+ int64_t bytes;
+
+ if (offset < 0)
+ bytes = - pa_usec_to_bytes(- offset, &u->source->sample_spec);
+ else
+ bytes = pa_usec_to_bytes(offset, &u->source->sample_spec);
+
+ bytes += u->counter;
+
+ pa_smoother_2_put(u->smoother, pa_rtclock_now(), bytes);
+#else
pa_usec_t y;
y = pa_bytes_to_usec((uint64_t) u->counter, &u->source->sample_spec);
- y += (pa_usec_t) offset;
+ y += offset;
pa_smoother_put(u->smoother, pa_rtclock_now(), y);
+#endif
/* We can access this freely here, since the main thread is waiting for us */
u->thread_transport_usec = u->transport_usec;
@@ -1991,6 +2046,7 @@ int pa__init(pa_module*m) {
u->source_name = pa_xstrdup(pa_modargs_get_value(ma, "source", NULL));;
u->source = NULL;
#endif
+#ifndef USE_SMOOTHER_2
u->smoother = pa_smoother_new(
PA_USEC_PER_SEC,
PA_USEC_PER_SEC*2,
@@ -1999,6 +2055,7 @@ int pa__init(pa_module*m) {
10,
pa_rtclock_now(),
false);
+#endif
u->ctag = 1;
u->device_index = u->channel = PA_INVALID_INDEX;
u->time_event = NULL;
@@ -2147,6 +2204,11 @@ int pa__init(pa_module*m) {
goto fail;
}
+#ifdef USE_SMOOTHER_2
+ /* Smoother window must be larger than time between updates. */
+ u->smoother = pa_smoother_2_new(LATENCY_INTERVAL + 5*PA_USEC_PER_SEC, pa_rtclock_now(), pa_frame_size(&ss), ss.rate);
+#endif
+
for (;;) {
server_list = pa_strlist_pop(server_list, &u->server_name);
@@ -2365,7 +2427,11 @@ void pa__done(pa_module*m) {
pa_auth_cookie_unref(u->auth_cookie);
if (u->smoother)
+#ifdef USE_SMOOTHER_2
+ pa_smoother_2_free(u->smoother);
+#else
pa_smoother_free(u->smoother);
+#endif
if (u->time_event)
u->core->mainloop->time_free(u->time_event);
=====================================
src/modules/raop/raop-sink.c
=====================================
@@ -60,7 +60,12 @@
#include <pulsecore/poll.h>
#include <pulsecore/rtpoll.h>
#include <pulsecore/core-rtclock.h>
+
+#ifdef USE_SMOOTHER_2
+#include <pulsecore/time-smoother_2.h>
+#else
#include <pulsecore/time-smoother.h>
+#endif
#include "raop-sink.h"
#include "raop-client.h"
@@ -96,7 +101,11 @@ struct userdata {
pa_usec_t delay;
pa_usec_t start;
+#ifdef USE_SMOOTHER_2
+ pa_smoother_2 *smoother;
+#else
pa_smoother *smoother;
+#endif
uint64_t write_count;
uint32_t latency;
@@ -124,16 +133,22 @@ static void raop_state_cb(pa_raop_state_t state, void *userdata) {
}
static int64_t sink_get_latency(const struct userdata *u) {
+#ifndef USE_SMOOTHER_2
pa_usec_t now;
+#endif
int64_t latency;
pa_assert(u);
pa_assert(u->smoother);
+#ifdef USE_SMOOTHER_2
+ latency = pa_smoother_2_get_delay(u->smoother, pa_rtclock_now(), u->write_count);
+#else
now = pa_rtclock_now();
now = pa_smoother_get(u->smoother, now);
latency = pa_bytes_to_usec(u->write_count, &u->sink->sample_spec) - (int64_t) now;
+#endif
/* RAOP default latency */
latency += u->latency * PA_USEC_PER_MSEC;
@@ -182,7 +197,11 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
if (u->autoreconnect && u->sink->state == PA_SINK_RUNNING) {
pa_usec_t now;
now = pa_rtclock_now();
+#ifdef USE_SMOOTHER_2
+ pa_smoother_2_reset(u->smoother, now);
+#else
pa_smoother_reset(u->smoother, now, false);
+#endif
if (!pa_raop_client_is_alive(u->raop)) {
/* Connecting will trigger a RECORD and start steaming */
@@ -312,7 +331,11 @@ static int sink_set_state_in_io_thread_cb(pa_sink *s, pa_sink_state_t new_state,
pa_log_debug("RAOP: RUNNING");
now = pa_rtclock_now();
+#ifdef USE_SMOOTHER_2
+ pa_smoother_2_reset(u->smoother, now);
+#else
pa_smoother_reset(u->smoother, now, false);
+#endif
/* If autonull is enabled, I/O thread is always eating chunks since
* it is emulating a null sink */
@@ -409,16 +432,23 @@ static void thread_func(void *userdata) {
pa_log_debug("Thread starting up");
pa_thread_mq_install(&u->thread_mq);
+#ifdef USE_SMOOTHER_2
+ pa_smoother_2_reset(u->smoother, pa_rtclock_now());
+#else
pa_smoother_set_time_offset(u->smoother, pa_rtclock_now());
+#endif
for (;;) {
struct pollfd *pollfd = NULL;
unsigned int i, nbfds = 0;
- pa_usec_t now, estimated;
+ pa_usec_t now;
uint64_t position;
size_t index;
int ret;
bool canstream, sendstream, on_timeout;
+#ifndef USE_SMOOTHER_2
+ pa_usec_t estimated;
+#endif
/* Polling (audio data + control socket + timing socket). */
if ((ret = pa_rtpoll_run(u->rtpoll)) < 0)
@@ -590,8 +620,12 @@ static void thread_func(void *userdata) {
position = u->write_count - pa_usec_to_bytes(u->delay, &u->sink->sample_spec);
now = pa_rtclock_now();
+#ifdef USE_SMOOTHER_2
+ pa_smoother_2_put(u->smoother, now, position);
+#else
estimated = pa_bytes_to_usec(position, &u->sink->sample_spec);
pa_smoother_put(u->smoother, now, estimated);
+#endif
if ((u->autonull && !canstream) || (u->oob && canstream && on_timeout)) {
/* Sleep until next packet transmission */
@@ -761,6 +795,9 @@ pa_sink* pa_raop_sink_new(pa_module *m, pa_modargs *ma, const char *driver) {
pa_memchunk_reset(&u->memchunk);
u->delay = 0;
+#ifdef USE_SMOOTHER_2
+ u->smoother = pa_smoother_2_new(5*PA_USEC_PER_SEC, pa_rtclock_now(), pa_frame_size(&ss), ss.rate);
+#else
u->smoother = pa_smoother_new(
PA_USEC_PER_SEC,
PA_USEC_PER_SEC*2,
@@ -769,6 +806,7 @@ pa_sink* pa_raop_sink_new(pa_module *m, pa_modargs *ma, const char *driver) {
10,
0,
false);
+#endif
u->write_count = 0;
if (pa_streq(protocol, "TCP")) {
@@ -946,7 +984,11 @@ static void userdata_free(struct userdata *u) {
u->raop = NULL;
if (u->smoother)
+#ifdef USE_SMOOTHER_2
+ pa_smoother_2_free(u->smoother);
+#else
pa_smoother_free(u->smoother);
+#endif
u->smoother = NULL;
if (u->card)
=====================================
src/pulse/internal.h
=====================================
@@ -40,7 +40,13 @@
#include <pulsecore/memblockq.h>
#include <pulsecore/hashmap.h>
#include <pulsecore/refcnt.h>
+
+#ifdef USE_SMOOTHER_2
+#include <pulsecore/time-smoother_2.h>
+#else
#include <pulsecore/time-smoother.h>
+#endif
+
#ifdef HAVE_DBUS
#include <pulsecore/dbus-util.h>
#endif
@@ -208,7 +214,11 @@ struct pa_stream {
pa_time_event *auto_timing_update_event;
pa_usec_t auto_timing_interval_usec;
+#ifdef USE_SMOOTHER_2
+ pa_smoother_2 *smoother;
+#else
pa_smoother *smoother;
+#endif
/* Callbacks */
pa_stream_notify_cb_t state_callback;
=====================================
src/pulse/stream.c
=====================================
@@ -48,9 +48,11 @@
#define AUTO_TIMING_INTERVAL_START_USEC (10*PA_USEC_PER_MSEC)
#define AUTO_TIMING_INTERVAL_END_USEC (1500*PA_USEC_PER_MSEC)
-#define SMOOTHER_ADJUST_TIME (1000*PA_USEC_PER_MSEC)
#define SMOOTHER_HISTORY_TIME (5000*PA_USEC_PER_MSEC)
+#ifndef USE_SMOOTHER_2
+#define SMOOTHER_ADJUST_TIME (1000*PA_USEC_PER_MSEC)
#define SMOOTHER_MIN_HISTORY (4)
+#endif
pa_stream *pa_stream_new(pa_context *c, const char *name, const pa_sample_spec *ss, const pa_channel_map *map) {
return pa_stream_new_with_proplist(c, name, ss, map, NULL);
@@ -303,7 +305,11 @@ static void stream_free(pa_stream *s) {
pa_proplist_free(s->proplist);
if (s->smoother)
+#ifdef USE_SMOOTHER_2
+ pa_smoother_2_free(s->smoother);
+#else
pa_smoother_free(s->smoother);
+#endif
for (i = 0; i < s->n_formats; i++)
pa_format_info_free(s->req_formats[i]);
@@ -463,7 +469,11 @@ static void check_smoother_status(pa_stream *s, bool aposteriori, bool force_sta
}
if (s->suspended || s->corked || force_stop)
+#ifdef USE_SMOOTHER_2
+ pa_smoother_2_pause(s->smoother, x);
+#else
pa_smoother_pause(s->smoother, x);
+#endif
else if (force_start || s->buffer_attr.prebuf == 0) {
if (!s->timing_info_valid &&
@@ -482,7 +492,11 @@ static void check_smoother_status(pa_stream *s, bool aposteriori, bool force_sta
return;
}
+#ifdef USE_SMOOTHER_2
+ pa_smoother_2_resume(s->smoother, x);
+#else
pa_smoother_resume(s->smoother, x, true);
+#endif
}
/* Please note that we have no idea if playback actually started
@@ -1120,6 +1134,11 @@ void pa_create_stream_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag,
s->sample_spec = ss;
}
+#ifdef USE_SMOOTHER_2
+ if (s->flags & PA_STREAM_INTERPOLATE_TIMING)
+ pa_smoother_2_set_sample_spec(s->smoother, pa_rtclock_now(), &s->sample_spec);
+#endif
+
if (s->context->version >= 13 && s->direction != PA_STREAM_UPLOAD) {
pa_usec_t usec;
@@ -1254,6 +1273,9 @@ static int create_stream(
x = pa_rtclock_now();
pa_assert(!s->smoother);
+#ifdef USE_SMOOTHER_2
+ s->smoother = pa_smoother_2_new(SMOOTHER_HISTORY_TIME, x, 0, 0);
+#else
s->smoother = pa_smoother_new(
SMOOTHER_ADJUST_TIME,
SMOOTHER_HISTORY_TIME,
@@ -1262,6 +1284,7 @@ static int create_stream(
SMOOTHER_MIN_HISTORY,
x,
true);
+#endif
}
if (!dev)
@@ -1792,6 +1815,12 @@ static pa_usec_t calc_time(const pa_stream *s, bool ignore_transport) {
return usec;
}
+#ifdef USE_SMOOTHER_2
+static inline size_t calc_bytes(pa_stream *s, bool ignore_transport) {
+ return pa_usec_to_bytes(calc_time(s, ignore_transport), &s->sample_spec);
+}
+#endif
+
static void stream_get_timing_info_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
pa_operation *o = userdata;
struct timeval local, remote, now;
@@ -1950,15 +1979,27 @@ static void stream_get_timing_info_callback(pa_pdispatch *pd, uint32_t command,
}
if (!i->playing)
+#ifdef USE_SMOOTHER_2
+ pa_smoother_2_pause(o->stream->smoother, x);
+#else
pa_smoother_pause(o->stream->smoother, x);
+#endif
/* Update the smoother */
if ((o->stream->direction == PA_STREAM_PLAYBACK && !i->read_index_corrupt) ||
(o->stream->direction == PA_STREAM_RECORD && !i->write_index_corrupt))
+#ifdef USE_SMOOTHER_2
+ pa_smoother_2_put(o->stream->smoother, u, calc_bytes(o->stream, true));
+#else
pa_smoother_put(o->stream->smoother, u, calc_time(o->stream, true));
+#endif
if (i->playing)
+#ifdef USE_SMOOTHER_2
+ pa_smoother_2_resume(o->stream->smoother, x);
+#else
pa_smoother_resume(o->stream->smoother, x, true);
+#endif
}
}
@@ -2467,7 +2508,12 @@ int pa_stream_get_time(pa_stream *s, pa_usec_t *r_usec) {
PA_CHECK_VALIDITY(s->context, s->direction != PA_STREAM_RECORD || !s->timing_info.write_index_corrupt, PA_ERR_NODATA);
if (s->smoother)
+#ifdef USE_SMOOTHER_2
+ usec = pa_smoother_2_get(s->smoother, pa_rtclock_now());
+#else
usec = pa_smoother_get(s->smoother, pa_rtclock_now());
+#endif
+
else
usec = calc_time(s, false);
@@ -2788,6 +2834,10 @@ static void stream_update_sample_rate_callback(pa_pdispatch *pd, uint32_t comman
}
o->stream->sample_spec.rate = PA_PTR_TO_UINT(o->private);
+#ifdef USE_SMOOTHER_2
+ if (o->stream->smoother)
+ pa_smoother_2_set_rate(o->stream->smoother, pa_rtclock_now(), o->stream->sample_spec.rate);
+#endif
pa_assert(pa_sample_spec_valid(&o->stream->sample_spec));
if (o->callback) {
=====================================
src/pulsecore/time-smoother_2.c
=====================================
@@ -0,0 +1,408 @@
+/***
+ This file is part of PulseAudio.
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of the
+ License, or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+/* The code in this file is based on the theoretical background found at
+ * https://www.freedesktop.org/software/pulseaudio/misc/rate_estimator.odt.
+ * The theory has never been reviewed, so it may be inaccurate in places. */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pulsecore/macro.h>
+#include <pulse/sample.h>
+#include <pulse/xmalloc.h>
+#include <pulse/timeval.h>
+
+#include "time-smoother_2.h"
+
+struct pa_smoother_2 {
+ /* Values set when the smoother is created */
+ pa_usec_t smoother_window_time;
+ uint32_t rate;
+ uint32_t frame_size;
+
+ /* USB hack parameters */
+ bool usb_hack;
+ bool enable_usb_hack;
+ uint32_t hack_threshold;
+
+ /* Smoother state */
+ bool init;
+ bool paused;
+
+ /* Current byte count start value */
+ double start_pos;
+ /* System time corresponding to start_pos */
+ pa_usec_t start_time;
+ /* Conversion factor between time domains */
+ double time_factor;
+
+ /* Used if the smoother is paused while still in init state */
+ pa_usec_t fixup_time;
+
+ /* Time offset for USB devices */
+ int64_t time_offset;
+
+ /* Various time stamps */
+ pa_usec_t resume_time;
+ pa_usec_t pause_time;
+ pa_usec_t smoother_start_time;
+ pa_usec_t last_time;
+
+ /* Variables used for Kalman filter */
+ double time_variance;
+ double time_factor_variance;
+ double kalman_variance;
+
+ /* Variables used for low pass filter */
+ double drift_filter;
+ double drift_filter_1;
+};
+
+/* Create new smoother */
+pa_smoother_2* pa_smoother_2_new(pa_usec_t window, pa_usec_t time_stamp, uint32_t frame_size, uint32_t rate) {
+ pa_smoother_2 *s;
+
+ pa_assert(window > 0);
+
+ s = pa_xnew(pa_smoother_2, 1);
+ s->enable_usb_hack = false;
+ s->usb_hack = false;
+ s->hack_threshold = 0;
+ s->smoother_window_time = window;
+ s->rate = rate;
+ s->frame_size = frame_size;
+
+ pa_smoother_2_reset(s, time_stamp);
+
+ return s;
+}
+
+/* Free the smoother */
+void pa_smoother_2_free(pa_smoother_2* s) {
+
+ pa_assert(s);
+
+ pa_xfree(s);
+}
+
+void pa_smoother_2_set_rate(pa_smoother_2 *s, pa_usec_t time_stamp, uint32_t rate) {
+
+ pa_assert(s);
+ pa_assert(rate > 0);
+
+ /* If the rate has changed, data in the smoother will be invalid,
+ * therefore also reset the smoother */
+ if (rate != s->rate) {
+ s->rate = rate;
+ pa_smoother_2_reset(s, time_stamp);
+ }
+}
+
+void pa_smoother_2_set_sample_spec(pa_smoother_2 *s, pa_usec_t time_stamp, pa_sample_spec *spec) {
+ size_t frame_size;
+
+ pa_assert(s);
+ pa_assert(pa_sample_spec_valid(spec));
+
+ /* If the sample spec has changed, data in the smoother will be invalid,
+ * therefore also reset the smoother */
+ frame_size = pa_frame_size(spec);
+ if (frame_size != s->frame_size || spec->rate != s->rate) {
+ s->frame_size = frame_size;
+ s->rate = spec->rate;
+ pa_smoother_2_reset(s, time_stamp);
+ }
+}
+
+/* Add a new data point and re-calculate time conversion factor */
+void pa_smoother_2_put(pa_smoother_2 *s, pa_usec_t time_stamp, int64_t byte_count) {
+ double byte_difference, iteration_time;
+ double time_delta_system, time_delta_card, drift, filter_constant, filter_constant_1;
+ double temp, filtered_time_delta_card, expected_time_delta_card;
+
+ pa_assert(s);
+
+ /* Smoother is paused, nothing to do */
+ if (s->paused)
+ return;
+
+ /* Initial setup or resume */
+ if PA_UNLIKELY((s->init)) {
+ s->resume_time = time_stamp;
+
+ /* We have no data yet, nothing to do */
+ if (byte_count <= 0)
+ return;
+
+ /* Now we are playing/recording.
+ * Get fresh time stamps and save the start count */
+ s->start_pos = (double)byte_count;
+ s->last_time = time_stamp;
+ s->start_time = time_stamp;
+ s->smoother_start_time = time_stamp;
+
+ s->usb_hack = s->enable_usb_hack;
+ s->init = false;
+ return;
+ }
+
+ /* Duration of last iteration */
+ iteration_time = (double)time_stamp - s->last_time;
+
+ /* Don't go backwards in time */
+ if (iteration_time <= 0)
+ return;
+
+ /* Wait at least 100 ms before starting calculations, otherwise the
+ * impact of the offset error will slow down convergence */
+ if (time_stamp < s->smoother_start_time + 100 * PA_USEC_PER_MSEC)
+ return;
+
+ /* Time difference in system time domain */
+ time_delta_system = time_stamp - s->start_time;
+
+ /* Number of bytes played since start_time */
+ byte_difference = (double)byte_count - s->start_pos;
+
+ /* Time difference in soundcard time domain. Don't use
+ * pa_bytes_to_usec() here because byte_difference need not
+ * be on a sample boundary */
+ time_delta_card = byte_difference / s->frame_size / s->rate * PA_USEC_PER_SEC;
+ filtered_time_delta_card = time_delta_card;
+
+ /* Prediction of measurement */
+ expected_time_delta_card = time_delta_system * s->time_factor;
+
+ /* Filtered variance of card time measurements */
+ s->time_variance = 0.9 * s->time_variance + 0.1 * (time_delta_card - expected_time_delta_card) * (time_delta_card - expected_time_delta_card);
+
+ /* Kalman filter, will only be used when the time factor has converged good enough,
+ * the value of 100 corresponds to a change rate of approximately 10e-6 per second. */
+ if (s->time_factor_variance < 100) {
+ filtered_time_delta_card = (time_delta_card * s->kalman_variance + expected_time_delta_card * s->time_variance) / (s->kalman_variance + s->time_variance);
+ s->kalman_variance = s->kalman_variance * s->time_variance / (s->kalman_variance + s->time_variance) + s->time_variance / 4 + 500;
+ }
+
+ /* This is a horrible hack which is necessary because USB sinks seem to fix up
+ * the reported delay by some millisecondsconds shortly after startup. This is
+ * an artifact, the real latency does not change on the reported jump. If the
+ * change is not caught or if the hack is triggered inadvertently, it will lead to
+ * prolonged convergence time and decreased stability of the reported latency.
+ * Since the fix up will occur within the first seconds, it is disabled later to
+ * avoid false triggers. When run as batch device, the threshold for the hack must
+ * be lower (1000) than for timer based scheduling (2000). */
+ if (s->usb_hack && time_stamp - s->smoother_start_time < 5 * PA_USEC_PER_SEC) {
+ if ((time_delta_system - filtered_time_delta_card / s->time_factor) > (double)s->hack_threshold) {
+ /* Recalculate initial conditions */
+ temp = time_stamp - time_delta_card - s->start_time;
+ s->start_time += temp;
+ s->smoother_start_time += temp;
+ s->time_offset = -temp;
+
+ /* Reset time factor variance */
+ s->time_factor_variance = 10000;
+
+ pa_log_debug("USB Hack, start time corrected by %0.2f usec", temp);
+ s->usb_hack = false;
+ return;
+ }
+ }
+
+ /* Parameter for lowpass filters with time constants of smoother_window_time
+ * and smoother_window_time/8 */
+ temp = (double)s->smoother_window_time / 6.2831853;
+ filter_constant = iteration_time / (iteration_time + temp / 8.0);
+ filter_constant_1 = iteration_time / (iteration_time + temp);
+
+ /* Temporarily save the current time factor */
+ temp = s->time_factor;
+
+ /* Calculate geometric series */
+ drift = (s->drift_filter_1 + 1.0) * (1.5 - filtered_time_delta_card / time_delta_system);
+
+ /* 2nd order lowpass */
+ s->drift_filter = (1 - filter_constant) * s->drift_filter + filter_constant * drift;
+ s->drift_filter_1 = (1 - filter_constant) * s->drift_filter_1 + filter_constant * s->drift_filter;
+
+ /* Calculate time conversion factor, filter again */
+ s->time_factor = (1 - filter_constant_1) * s->time_factor + filter_constant_1 * (s->drift_filter_1 + 3) / (s->drift_filter_1 + 1) / 2;
+
+ /* Filtered variance of time factor derivative, used as measure for the convergence of the time factor */
+ temp = (s->time_factor - temp) / iteration_time * 10000000000000;
+ s->time_factor_variance = (1 - filter_constant_1) * s->time_factor_variance + filter_constant_1 * temp * temp;
+
+ /* Calculate new start time and corresponding sample count after window time */
+ if (time_stamp > s->smoother_start_time + s->smoother_window_time) {
+ s->start_pos += ((double)byte_count - s->start_pos) / (time_stamp - s->start_time) * iteration_time;
+ s->start_time += (pa_usec_t)iteration_time;
+ }
+
+ /* Save current system time */
+ s->last_time = time_stamp;
+}
+
+/* Calculate the current latency. For a source, the sign must be inverted */
+int64_t pa_smoother_2_get_delay(pa_smoother_2 *s, pa_usec_t time_stamp, size_t byte_count) {
+ int64_t now, delay;
+
+ pa_assert(s);
+
+ /* If we do not have a valid frame size and rate, just return 0 */
+ if (!s->frame_size || !s->rate)
+ return 0;
+
+ /* Smoother is paused or has been resumed but no new data has been received */
+ if (s->paused || s->init) {
+ delay = (int64_t)((double)byte_count * PA_USEC_PER_SEC / s->frame_size / s->rate);
+ return delay - pa_smoother_2_get(s, time_stamp);
+ }
+
+ /* Convert system time difference to soundcard time difference */
+ now = (time_stamp - s->start_time - s->time_offset) * s->time_factor;
+
+ /* Don't use pa_bytes_to_usec(), u->start_pos needs not be on a sample boundary */
+ return (int64_t)(((double)byte_count - s->start_pos) / s->frame_size / s->rate * PA_USEC_PER_SEC) - now;
+}
+
+/* Convert system time to sound card time */
+pa_usec_t pa_smoother_2_get(pa_smoother_2 *s, pa_usec_t time_stamp) {
+ pa_usec_t current_time;
+
+ pa_assert(s);
+
+ /* If we do not have a valid frame size and rate, just return 0 */
+ if (!s->frame_size || !s->rate)
+ return 0;
+
+ /* Sound card time at start_time */
+ current_time = (pa_usec_t)(s->start_pos / s->frame_size / s->rate * PA_USEC_PER_SEC);
+
+ /* If the smoother has not started, just return system time since resume */
+ if (!s->start_time) {
+ if (time_stamp >= s->resume_time)
+ current_time = time_stamp - s->resume_time;
+ else
+ current_time = 0;
+
+ /* If we are paused return the sound card time at pause_time */
+ } else if (s->paused)
+ current_time += (s->pause_time - s->start_time - s->time_offset - s->fixup_time) * s->time_factor;
+
+ /* If we are initializing, add the time since resume to the card time at pause_time */
+ else if (s->init) {
+ current_time += (s->pause_time - s->start_time - s->time_offset - s->fixup_time) * s->time_factor;
+ current_time += (time_stamp - s->resume_time) * s->time_factor;
+
+ /* Smoother is running, calculate current sound card time */
+ } else
+ current_time += (time_stamp - s->start_time - s->time_offset) * s->time_factor;
+
+ return current_time;
+}
+
+/* Convert a time interval from sound card time to system time */
+pa_usec_t pa_smoother_2_translate(pa_smoother_2 *s, pa_usec_t time_difference) {
+
+ pa_assert(s);
+
+ /* If not started yet, return the time difference */
+ if (!s->start_time)
+ return time_difference;
+
+ return (pa_usec_t)(time_difference / s->time_factor);
+}
+
+/* Enable USB hack */
+void pa_smoother_2_usb_hack_enable(pa_smoother_2 *s, bool enable, pa_usec_t offset) {
+
+ pa_assert(s);
+
+ s->enable_usb_hack = enable;
+ s->hack_threshold = offset;
+}
+
+/* Reset the smoother */
+void pa_smoother_2_reset(pa_smoother_2 *s, pa_usec_t time_stamp) {
+
+ pa_assert(s);
+
+ /* Reset variables for time estimation */
+ s->drift_filter = 1.0;
+ s->drift_filter_1 = 1.0;
+ s->time_factor = 1.0;
+ s->start_pos = 0;
+ s->init = true;
+ s->time_offset = 0;
+ s->time_factor_variance = 10000.0;
+ s->kalman_variance = 10000000.0;
+ s->time_variance = 100000.0;
+ s->start_time = 0;
+ s->last_time = 0;
+ s->smoother_start_time = 0;
+ s->usb_hack = false;
+ s->pause_time = time_stamp;
+ s->fixup_time = 0;
+ s->resume_time = time_stamp;
+ s->paused = false;
+
+ /* Set smoother to paused if rate or frame size are invalid */
+ if (!s->frame_size || !s->rate)
+ s->paused = true;
+}
+
+/* Pause the smoother */
+void pa_smoother_2_pause(pa_smoother_2 *s, pa_usec_t time_stamp) {
+
+ pa_assert(s);
+
+ /* Smoother is already paused, nothing to do */
+ if (s->paused)
+ return;
+
+ /* If we are in init state, add the pause time to the fixup time */
+ if (s->init)
+ s->fixup_time += s->resume_time - s->pause_time;
+ else
+ s->fixup_time = 0;
+
+ s->smoother_start_time = 0;
+ s->resume_time = time_stamp;
+ s->pause_time = time_stamp;
+ s->time_factor_variance = 10000.0;
+ s->kalman_variance = 10000000.0;
+ s->time_variance = 100000.0;
+ s->init = true;
+ s->paused = true;
+}
+
+/* Resume the smoother */
+void pa_smoother_2_resume(pa_smoother_2 *s, pa_usec_t time_stamp) {
+
+ pa_assert(s);
+
+ if (!s->paused)
+ return;
+
+ /* Keep smoother paused if rate or frame size is not set */
+ if (!s->frame_size || !s->rate)
+ return;
+
+ s->resume_time = time_stamp;
+ s->paused = false;
+}
=====================================
src/pulsecore/time-smoother_2.h
=====================================
@@ -0,0 +1,53 @@
+#ifndef foopulsetimesmoother2hfoo
+#define foopulsetimesmoother2hfoo
+
+/***
+ This file is part of PulseAudio.
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of the
+ License, or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <pulse/sample.h>
+
+typedef struct pa_smoother_2 pa_smoother_2;
+
+/* Create new smoother */
+pa_smoother_2* pa_smoother_2_new(pa_usec_t window, pa_usec_t time_stamp, uint32_t frame_size, uint32_t rate);
+/* Free the smoother */
+void pa_smoother_2_free(pa_smoother_2* s);
+/* Reset the smoother */
+void pa_smoother_2_reset(pa_smoother_2 *s, pa_usec_t time_stamp);
+/* Pause the smoother */
+void pa_smoother_2_pause(pa_smoother_2 *s, pa_usec_t time_stamp);
+/* Resume the smoother */
+void pa_smoother_2_resume(pa_smoother_2 *s, pa_usec_t time_stamp);
+
+/* Add a new data point and re-calculate time conversion factor */
+void pa_smoother_2_put(pa_smoother_2 *s, pa_usec_t time_stamp, int64_t byte_count);
+
+/* Calculate the current latency. For a source, the sign of the result must be inverted */
+int64_t pa_smoother_2_get_delay(pa_smoother_2 *s, pa_usec_t time_stamp, size_t byte_count);
+/* Convert system time since start to sound card time */
+pa_usec_t pa_smoother_2_get(pa_smoother_2 *s, pa_usec_t time_stamp);
+/* Convert a time interval from sound card time to system time */
+pa_usec_t pa_smoother_2_translate(pa_smoother_2 *s, pa_usec_t time_difference);
+
+/* Enable USB hack, only used for alsa sinks */
+void pa_smoother_2_usb_hack_enable(pa_smoother_2 *s, bool enable, pa_usec_t offset);
+/* Set sample rate */
+void pa_smoother_2_set_rate(pa_smoother_2 *s, pa_usec_t time_stamp, uint32_t rate);
+/* Set rate and frame size */
+void pa_smoother_2_set_sample_spec(pa_smoother_2 *s, pa_usec_t time_stamp, pa_sample_spec *spec);
+
+#endif
View it on GitLab: https://gitlab.freedesktop.org/pulseaudio/pulseaudio/-/compare/851c377d6b4cbcf4cadd142445f87207d080c770...0497821afc0ab7ae17d5fe5c365d240eec459495
--
View it on GitLab: https://gitlab.freedesktop.org/pulseaudio/pulseaudio/-/compare/851c377d6b4cbcf4cadd142445f87207d080c770...0497821afc0ab7ae17d5fe5c365d240eec459495
You're receiving this email because of your account on gitlab.freedesktop.org.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.freedesktop.org/archives/pulseaudio-commits/attachments/20211103/e7d10e50/attachment-0001.htm>
More information about the pulseaudio-commits
mailing list