diff --git a/.github/spellcheck-wordlist.txt b/.github/spellcheck-wordlist.txt index 3dab6c08f..c20297a9a 100644 --- a/.github/spellcheck-wordlist.txt +++ b/.github/spellcheck-wordlist.txt @@ -68,6 +68,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..48c41ac79 100644 --- a/utils/aplay/alsa-pcm.c +++ b/utils/aplay/alsa-pcm.c @@ -89,14 +89,16 @@ 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; } - /* allow the transfer when at least period_size samples can be processed */ + /* Allow the transfer when at least period_size samples can be processed. */ if ((err = snd_pcm_sw_params_set_avail_min(pcm, params, period_size)) != 0) { snprintf(buf, sizeof(buf), "Set avail min: %s: %lu", snd_strerror(err), period_size); goto fail; diff --git a/utils/aplay/aplay.c b/utils/aplay/aplay.c index 67fc5c7da..cdaf348d8 100644 --- a/utils/aplay/aplay.c +++ b/utils/aplay/aplay.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -86,8 +87,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 cannot synchronize A/V with very high audio latency. To keep + * the overall latency below 400ms we choose ALSA 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; @@ -817,11 +821,19 @@ static void *io_worker_routine(struct io_worker *w) { ffb_shift(&buffer, frames * w->ba_pcm.channels); int ret; - if ((ret = snd_pcm_delay(w->snd_pcm, - &pcm_delay_frames[pcm_delay_frames_i++ % ARRAYSIZE(pcm_delay_frames)])) != 0) + snd_pcm_sframes_t delay_frames = 0; + if ((ret = snd_pcm_delay(w->snd_pcm, &delay_frames)) != 0) warn("Couldn't get PCM delay: %s", snd_strerror(ret)); else { + unsigned int buffered = 0; + ioctl(w->ba_pcm_fd, FIONREAD, &buffered); + buffered += ffb_blen_out(&buffer); + delay_frames += buffered / (w->ba_pcm.channels * pcm_format_size); + + pcm_delay_frames[pcm_delay_frames_i % ARRAYSIZE(pcm_delay_frames)] = delay_frames; + pcm_delay_frames_i++; + struct timespec ts_now; /* Rate limit delay updates to 1 update per second. */ struct timespec ts_delay = { .tv_sec = 1 };