audio: improve read/write timing for streams

Change-Id: Ic4231c4b44369a9438c6c94622b90862047e9a32
Reference: https://github.com/LineageOS/android_hardware_libhardware/commit/0caeee8ac429dd8098e97b2cd8ad3751031a5b67
diff --git a/audio/audio_hw.c b/audio/audio_hw.c
index 423ce06..2bd2eca 100644
--- a/audio/audio_hw.c
+++ b/audio/audio_hw.c
@@ -228,7 +228,6 @@
     [USECASE_VOICE_CALL] = "voice-call",
 };
 
-
 #define STRING_TO_ENUM(string) { #string, string }
 
 static unsigned int audio_device_ref_count;
@@ -2556,6 +2555,9 @@
     }
     pthread_mutex_unlock(&out->lock);
     ALOGV("%s: exit", __func__);
+
+    // out->last_write_time_us = 0; unnecessary as a stale write time has same effect
+
     return 0;
 }
 
@@ -2917,8 +2919,28 @@
                 ALOGE("%s: error %zd - %s", __func__, ret, pcm_get_error(pcm_device->pcm));
         }
         out_standby(&out->stream.common);
-        usleep(bytes * 1000000 / audio_stream_out_frame_size(stream) /
-               out_get_sample_rate(&out->stream.common));
+        struct timespec t = { .tv_sec = 0, .tv_nsec = 0 };
+        clock_gettime(CLOCK_MONOTONIC, &t);
+        const int64_t now = (t.tv_sec * 1000000000LL + t.tv_nsec) / 1000;
+        const int64_t elapsed_time_since_last_write = now - out->last_write_time_us;
+        int64_t sleep_time = bytes * 1000000LL / audio_stream_out_frame_size(stream) /
+                   out_get_sample_rate(&stream->common) - elapsed_time_since_last_write;
+        if (sleep_time > 0) {
+            usleep(sleep_time);
+        } else {
+            // we don't sleep when we exit standby (this is typical for a real alsa buffer).
+            sleep_time = 0;
+        }
+        out->last_write_time_us = now + sleep_time;
+        // last_write_time_us is an approximation of when the (simulated) alsa
+        // buffer is believed completely full. The usleep above waits for more space
+        // in the buffer, but by the end of the sleep the buffer is considered
+        // topped-off.
+        //
+        // On the subsequent out_write(), we measure the elapsed time spent in
+        // the mixer. This is subtracted from the sleep estimate based on frames,
+        // thereby accounting for drain in the alsa buffer during mixing.
+        // This is a crude approximation; we don't handle underruns precisely.
     }
 
 #ifdef PREPROCESSING_ENABLED
@@ -3171,6 +3193,9 @@
 
         in->standby = 1;
     }
+
+    in->last_read_time_us = 0;
+
     return 0;
 }
 
@@ -3388,8 +3413,28 @@
     if (read_and_process_successful == false) {
         in_standby(&in->stream.common);
         ALOGV("%s: read failed - sleeping for buffer duration", __func__);
-        usleep(bytes * 1000000 / audio_stream_in_frame_size(stream) /
-               in->requested_rate);
+        struct timespec t = { .tv_sec = 0, .tv_nsec = 0 };
+        clock_gettime(CLOCK_MONOTONIC, &t);
+        const int64_t now = (t.tv_sec * 1000000000LL + t.tv_nsec) / 1000;
+
+        // we do a full sleep when exiting standby.
+        const bool standby = in->last_read_time_us == 0;
+        const int64_t elapsed_time_since_last_read = standby ?
+                0 : now - in->last_read_time_us;
+        int64_t sleep_time = bytes * 1000000LL / audio_stream_in_frame_size(stream) /
+                in_get_sample_rate(&stream->common) - elapsed_time_since_last_read;
+        if (sleep_time > 0) {
+            usleep(sleep_time);
+        } else {
+            sleep_time = 0;
+        }
+        in->last_read_time_us = now + sleep_time;
+        // last_read_time_us is an approximation of when the (simulated) alsa
+        // buffer is drained by the read, and is empty.
+        //
+        // On the subsequent in_read(), we measure the elapsed time spent in
+        // the recording thread. This is subtracted from the sleep estimate based on frames,
+        // thereby accounting for fill in the alsa buffer during the interim.
     }
     return bytes;
 }
diff --git a/audio/audio_hw.h b/audio/audio_hw.h
index 851b44d..6e58a11 100644
--- a/audio/audio_hw.h
+++ b/audio/audio_hw.h
@@ -306,6 +306,8 @@
 #endif
 
     bool                         is_fastmixer_affinity_set;
+
+    int64_t                      last_write_time_us;
 };
 
 struct stream_in {
@@ -359,6 +361,8 @@
 
     struct audio_device*                dev;
     bool                                is_fastcapture_affinity_set;
+
+    int64_t                             last_read_time_us;
 };
 
 struct mixer_card {