diff --git a/audio/audio_hw.c b/audio/audio_hw.c index 423ce062..2bd2eca4 100644 --- a/audio/audio_hw.c +++ b/audio/audio_hw.c @@ -228,7 +228,6 @@ static const char * const use_case_table[AUDIO_USECASE_MAX] = { [USECASE_VOICE_CALL] = "voice-call", }; - #define STRING_TO_ENUM(string) { #string, string } static unsigned int audio_device_ref_count; @@ -2556,6 +2555,9 @@ static int out_standby(struct audio_stream *stream) } 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 @@ exit: 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 @@ static int do_in_standby_l(struct stream_in *in) in->standby = 1; } + + in->last_read_time_us = 0; + return 0; } @@ -3388,8 +3413,28 @@ exit: 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 851b44d4..6e58a118 100644 --- a/audio/audio_hw.h +++ b/audio/audio_hw.h @@ -306,6 +306,8 @@ struct stream_out { #endif bool is_fastmixer_affinity_set; + + int64_t last_write_time_us; }; struct stream_in { @@ -359,6 +361,8 @@ struct stream_in { struct audio_device* dev; bool is_fastcapture_affinity_set; + + int64_t last_read_time_us; }; struct mixer_card {