diff --git a/doc/bluealsa-aplay.1.rst b/doc/bluealsa-aplay.1.rst index 9cc01f780..95b680100 100644 --- a/doc/bluealsa-aplay.1.rst +++ b/doc/bluealsa-aplay.1.rst @@ -6,7 +6,7 @@ bluealsa-aplay a simple BlueALSA player ------------------------ -:Date: August 2024 +:Date: February 2025 :Manual section: 1 :Manual group: General Commands Manual :Version: $VERSION$ @@ -58,7 +58,8 @@ OPTIONS (i.e., all messages are logged). -v, --verbose - Make the output more verbose. + Make the output more verbose. This option may be given multiple times to + increase the verbosity. -l, --list-devices List connected Bluetooth audio devices. @@ -89,35 +90,40 @@ OPTIONS --pcm-buffer-time=INT Set the playback PCM buffer duration time to *INT* microseconds. - 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 - not supported. + The default is four times the period time. 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). For + reliable performance the buffer time should be at least 3 times the period + time. - If you experience underruns on the ALSA device then a larger buffer may - help. However, a larger buffer will also increase the latency. For reliable - performance the buffer time should be at least 3 times the period time. + ALSA may choose the nearest available alternative if the requested value is + not supported; and some ALSA devices may ignore the requested value + completely (e.g. **dmix**, see dmix_ in the **NOTES** section below). --pcm-period-time=INT - Set the playback PCM period duration time to *INT* microseconds. - The default is 50000. + Set the playback PCM period duration time to *INT* microseconds. The + default is 50000 for A2DP and 20000 for SCO profiles. ALSA may choose the nearest available alternative if the requested value is - not supported. + not supported; and some ALSA devices may ignore the requested value + completely (e.g. **dmix**, see dmix_ in the **NOTES** section below). + + If you experience underruns on the ALSA device or overruns on the Bluetooth + stream then a larger period time may help. However, a larger period time + will also increase the latency. The ALSA **rate** plugin, which may be invoked by **plug**, does not always produce the exact required effective sample rate because of rounding errors in the conversion between period time and period size. This can have a significant impact on synchronization "drift", especially with small period sizes, and can also result in stream underruns (if the effective rate is - too fast) or dropped A2DP frames in the **bluealsad(8)** server (if the - effective rate is too slow). This effect is avoided if the selected period - time results in an exact integer number of frames for both the source rate - (Bluetooth) and sink rate (hardware card). For example, in the case of - Bluetooth stream sampled at 44100Hz playing to a hardware device that - supports only 48000Hz, choosing a period time that is a multiple of 10000 - microseconds will result in zero rounding error. (10000 µs at 44100Hz is - 441 frames, and at 48000Hz is 480 frames). + too fast) or dropped frames (if the effective rate is too slow). This + effect is avoided if the selected period time results in an exact integer + number of frames for both the source rate (Bluetooth) and sink rate + (hardware card). For example, in the case of Bluetooth stream sampled at + 44100 Hz playing to a hardware device that supports only 48000 Hz, choosing + a period time that is a multiple of 10000 microseconds will result in zero + rounding error. (10000 µs at 44100 Hz is 441 frames, and at 48000 Hz is 480 + frames). See also dmix_ in the **NOTES** section below for more information on rate calculation rounding errors. @@ -226,13 +232,14 @@ control. dmix ---- -The ALSA `dmix` plugin will ignore the period and buffer times selected by the -application (because it has to allow connections from multiple applications). -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 50000 µs and a buffer holding 4 periods with an Intel 'PCH' card: +The ALSA **dmix** plugin will ignore the period and buffer times selected by +the application (because it has to allow connections from multiple +applications). 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 50000 µs and a buffer holding 4 +periods with an Intel 'PCH' card: :: @@ -260,7 +267,7 @@ EXAMPLES ======== The simplest usage of **bluealsa-aplay** is to run it with no arguments. It -will play audio from all connected Bluetooth devices to the ``default`` ALSA +will play audio from all connected Bluetooth devices to the **default** ALSA playback PCM. :: diff --git a/utils/aplay/aplay.c b/utils/aplay/aplay.c index 3b51ad633..7c071c861 100644 --- a/utils/aplay/aplay.c +++ b/utils/aplay/aplay.c @@ -48,6 +48,14 @@ #include "dbus.h" #include "delay-report.h" +/* Many devices cannot synchronize A/V with very high audio latency. To keep + * the overall latency below 400ms we choose default ALSA parameters such that + * the ALSA latency for A2DP is below 200ms. For SCO we choose to prioritize + * much lower latency over audio quality. */ +#define DEFAULT_PERIOD_TIME_A2DP 50000 +#define DEFAULT_PERIOD_TIME_SCO 20000 +#define DEFAULT_PERIODS 4 + enum volume_type { VOL_TYPE_AUTO, VOL_TYPE_MIXER, @@ -85,11 +93,8 @@ 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; -/* 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; +static unsigned int pcm_buffer_time = 0; +static unsigned int pcm_period_time = 0; /* local PCM muted state for software mute */ static bool pcm_muted = false; @@ -1116,6 +1121,16 @@ int main(int argc, char *argv[]) { if (volume_type == VOL_TYPE_NONE || volume_type == VOL_TYPE_SOFTWARE) mixer_device = NULL; + if (pcm_buffer_time == 0) { + if (pcm_period_time == 0) + pcm_period_time = ba_profile_a2dp ? + DEFAULT_PERIOD_TIME_A2DP : DEFAULT_PERIOD_TIME_SCO; + pcm_buffer_time = pcm_period_time * DEFAULT_PERIODS; + } + else if (pcm_period_time == 0) { + pcm_period_time = pcm_buffer_time / DEFAULT_PERIODS; + } + if (verbose >= 1) { char *ba_str = malloc(19 * ba_addrs_count + 1); @@ -1137,16 +1152,16 @@ int main(int argc, char *argv[]) { " ALSA PCM device: %s\n" " ALSA PCM buffer time: %u us\n" " ALSA PCM period time: %u us\n" - " Volume control type: %s\n" " ALSA mixer device: %s\n" " ALSA mixer element: %s\n" + " Volume control type: %s\n" " Bluetooth device(s): %s\n" " Profile: %s", dbus_ba_service, pcm_device, pcm_buffer_time, pcm_period_time, - volume_type_str, mixer_device_str, mixer_element_str, + volume_type_str, ba_addr_any ? "ANY" : &ba_str[2], ba_profile_a2dp ? "A2DP" : "SCO");