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 {