From 7a57398b29d3f54be6e629b5990530538be8fe03 Mon Sep 17 00:00:00 2001 From: borine <32966433+borine@users.noreply.github.com> Date: Mon, 11 Nov 2024 15:45:55 +0000 Subject: [PATCH] aplay: reduce ALSA buffering It seems that chromium on the iPhone cannot adjust A/V sync when the audio latency exceeds about 400ms. So to keep the default delay when using bluealsa-aplay below that critical value, change the default ALSA hw params to lower values which are still compatible with the majority of sound cards. --- .github/spellcheck-wordlist.txt | 1 + doc/bluealsa-aplay.1.rst | 14 +++++++------- utils/aplay/alsa-pcm.c | 5 +++-- utils/aplay/aplay.c | 7 +++++-- 4 files changed, 16 insertions(+), 11 deletions(-) diff --git a/.github/spellcheck-wordlist.txt b/.github/spellcheck-wordlist.txt index abcd4a342..1670ce4ed 100644 --- a/.github/spellcheck-wordlist.txt +++ b/.github/spellcheck-wordlist.txt @@ -67,6 +67,7 @@ ATRAC BlueALSA BlueZ Fraunhofer +iPhone IWYU LDAC LHDC diff --git a/doc/bluealsa-aplay.1.rst b/doc/bluealsa-aplay.1.rst index 44a94c910..9cc01f780 100644 --- a/doc/bluealsa-aplay.1.rst +++ b/doc/bluealsa-aplay.1.rst @@ -89,7 +89,7 @@ OPTIONS --pcm-buffer-time=INT Set the playback PCM buffer duration time to *INT* microseconds. - The default is 500000. It is recommended to choose a buffer time that is + The default is 200000. It is recommended to choose a buffer time that is an exact multiple of the period time to avoid potential issues with some ALSA plugins (see --pcm-period-time option below). ALSA may choose the nearest available alternative if the requested value is @@ -101,7 +101,7 @@ OPTIONS --pcm-period-time=INT Set the playback PCM period duration time to *INT* microseconds. - The default is 100000. + The default is 50000. ALSA may choose the nearest available alternative if the requested value is not supported. @@ -232,12 +232,12 @@ Instead it will choose its own values, which can lead to rounding errors in the period size calculation when used with the ALSA `rate` plugin. To avoid this, it is recommended to explicitly define the hardware period size and buffer size for dmix in your ALSA configuration. For example, suppose we want a period time -of 100000 µs and a buffer holding 5 periods with an Intel 'PCH' card: +of 50000 µs and a buffer holding 4 periods with an Intel 'PCH' card: :: - defaults.dmix.PCH.period_time 100000 - defaults.dmix.PCH.periods 5 + defaults.dmix.PCH.period_time 50000 + defaults.dmix.PCH.periods 4 Alternatively we can define a PCM with the required setting: @@ -250,8 +250,8 @@ Alternatively we can define a PCM with the required setting: ipc_key 12345 slave { pcm "hw:0,0" - period_time 100000 - periods 5 + period_time 50000 + periods 4 } } } diff --git a/utils/aplay/alsa-pcm.c b/utils/aplay/alsa-pcm.c index f47546839..d70d57ae3 100644 --- a/utils/aplay/alsa-pcm.c +++ b/utils/aplay/alsa-pcm.c @@ -89,8 +89,9 @@ static int alsa_pcm_set_sw_params(snd_pcm_t *pcm, snd_pcm_uframes_t buffer_size, goto fail; } - /* start the transfer when the buffer is full (or almost full) */ - snd_pcm_uframes_t threshold = (buffer_size / period_size) * period_size; + /* start the transfer when the buffer is half full - this allows spare + * capacity to accommodate bursts and short breaks in the bluetooth stream. */ + snd_pcm_uframes_t threshold = buffer_size / 2; if ((err = snd_pcm_sw_params_set_start_threshold(pcm, params, threshold)) != 0) { snprintf(buf, sizeof(buf), "Set start threshold: %s: %lu", snd_strerror(err), threshold); goto fail; diff --git a/utils/aplay/aplay.c b/utils/aplay/aplay.c index 67fc5c7da..fa7846942 100644 --- a/utils/aplay/aplay.c +++ b/utils/aplay/aplay.c @@ -86,8 +86,11 @@ static bool ba_profile_a2dp = true; static bool ba_addr_any = false; static bdaddr_t *ba_addrs = NULL; static size_t ba_addrs_count = 0; -static unsigned int pcm_buffer_time = 500000; -static unsigned int pcm_period_time = 100000; +/* Many devices (e.g. the iPhone) cannot handle very high audio latency. To keep + * the overall latency below 400ms we choose ALSA hw parameters such that the + * ALSA latency is below 200ms. */ +static unsigned int pcm_buffer_time = 200000; +static unsigned int pcm_period_time = 50000; /* local PCM muted state for software mute */ static bool pcm_muted = false;