[PATCH 3/4] alsa-sink: Add hack to compensate USB startup behavior

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



USB sinks exhibit a strange behavior at startup. See comment in the code for
more explanation. This hack compensates the issue.

---
 src/modules/alsa/alsa-sink.c | 47 +++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 46 insertions(+), 1 deletion(-)

diff --git a/src/modules/alsa/alsa-sink.c b/src/modules/alsa/alsa-sink.c
index d497200..de51710 100644
--- a/src/modules/alsa/alsa-sink.c
+++ b/src/modules/alsa/alsa-sink.c
@@ -150,6 +150,9 @@ struct userdata {
     double time_factor;
 
     bool first_delayed;
+    bool usb_hack;
+
+    double hack_threshold;
 
     pa_idxset *formats;
 
@@ -850,6 +853,10 @@ static int unix_write(struct userdata *u, pa_usec_t *sleep_usec, bool polled, bo
 }
 
 static void init_time_estimate(struct userdata *u) {
+    const char *id;
+    snd_pcm_info_t* pcm_info;
+
+    snd_pcm_info_alloca(&pcm_info);
 
    /* Reset variables for rate estimation */
     u->drift_filter = 1.0;
@@ -857,6 +864,21 @@ static void init_time_estimate(struct userdata *u) {
     u->time_factor = 1.0;
     u->start_pos = 0;
     u->first_delayed = false;
+    u->usb_hack = false;
+    u->time_offset = 0;
+
+    /* Check if this is an USB device, see alsa-util.c
+     * USB devices unfortunately need some special handling */
+    if (snd_pcm_info(u->pcm_handle, pcm_info) == 0 &&
+        (id = snd_pcm_info_get_id(pcm_info))) {
+        if (pa_streq(id, "USB Audio")) {
+            /* USB device, set hack parameter */
+            u->usb_hack = true;
+            u->hack_threshold = 2000;
+            if (!u->use_tsched)
+                u->hack_threshold = 1000;
+        }
+    }
 
     /* Start time */
     u->start_time = pa_rtclock_now();
@@ -869,6 +891,7 @@ static void update_time_estimate(struct userdata *u) {
     double byte_count, iteration_time;
     int err;
     double time_delta_system, time_delta_card, drift, filter_constant, filter_constant_1;
+    double temp;
     pa_usec_t time_stamp;
     snd_pcm_status_t *status;
 
@@ -935,6 +958,28 @@ static void update_time_estimate(struct userdata *u) {
      * be on a sample boundary */
     time_delta_card = byte_count / u->frame_size / u->sink->sample_spec.rate * PA_USEC_PER_SEC;
 
+    /* This is a horrible hack which is necessary because USB devices 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 than for timer based scheduling. */
+    if (u->usb_hack && time_delta_system < 5 * PA_USEC_PER_SEC) {
+        if (abs(time_delta_system - time_delta_card / u->time_factor) > u->hack_threshold) {
+            /* Recalculate initial conditions */
+            temp = time_stamp - time_delta_card - u->start_time;
+            u->start_time += temp;
+            u->first_start_time += temp;
+            u->time_offset = -temp;
+
+            pa_log_debug("USB Hack, start time corrected by %0.2f usec", temp);
+            u->usb_hack = false;
+            return;
+         }
+    }
+
     /* Parameter for lowpass filter with 2s and 15s time constant */
     filter_constant = iteration_time / (iteration_time + 318310.0);
     filter_constant_1 = iteration_time / (iteration_time + 2387324.0);
@@ -960,7 +1005,7 @@ static pa_usec_t sink_get_latency(struct userdata *u, bool raw) {
     pa_assert(u);
 
     /* Convert system time difference to soundcard time difference */
-    now2 = (pa_rtclock_now() - u->start_time) * u->time_factor;
+    now2 = (pa_rtclock_now() - u->start_time - u->time_offset) * u->time_factor;
 
     /* Don't use pa_bytes_to_usec(), u->start_pos needs not be on a sample boundary */
     delay = (int64_t)(((double)u->write_count - u->start_pos) / u->frame_size / u->sink->sample_spec.rate * PA_USEC_PER_SEC) - now2;
-- 
2.8.1



[Index of Archives]     [Linux Audio Users]     [AMD Graphics]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux