From fe5cc218c65e40f4ce3928b14849be1af0440f74 Mon Sep 17 00:00:00 2001 From: borine <32966433+borine@users.noreply.github.com> Date: Tue, 21 Jan 2025 19:35:35 +0000 Subject: [PATCH] aplay: Refactor code for PCM delay reporting --- utils/aplay/Makefile.am | 3 +- utils/aplay/aplay.c | 55 ++++------------------- utils/aplay/delay-report.c | 91 ++++++++++++++++++++++++++++++++++++++ utils/aplay/delay-report.h | 54 ++++++++++++++++++++++ 4 files changed, 155 insertions(+), 48 deletions(-) create mode 100644 utils/aplay/delay-report.c create mode 100644 utils/aplay/delay-report.h diff --git a/utils/aplay/Makefile.am b/utils/aplay/Makefile.am index b57b71b89..b92c180e8 100644 --- a/utils/aplay/Makefile.am +++ b/utils/aplay/Makefile.am @@ -1,5 +1,5 @@ # BlueALSA - Makefile.am -# Copyright (c) 2016-2024 Arkadiusz Bokowy +# Copyright (c) 2016-2025 Arkadiusz Bokowy if ENABLE_APLAY @@ -16,6 +16,7 @@ bluealsa_aplay_SOURCES = \ alsa-mixer.c \ alsa-pcm.c \ dbus.c \ + delay-report.c \ aplay.c bluealsa_aplay_CFLAGS = \ diff --git a/utils/aplay/aplay.c b/utils/aplay/aplay.c index 45d1caa10..3b51ad633 100644 --- a/utils/aplay/aplay.c +++ b/utils/aplay/aplay.c @@ -46,6 +46,7 @@ #include "alsa-mixer.h" #include "alsa-pcm.h" #include "dbus.h" +#include "delay-report.h" enum volume_type { VOL_TYPE_AUTO, @@ -469,11 +470,8 @@ static void *io_worker_routine(struct io_worker *w) { size_t pcm_open_retry_pcm_samples = 0; size_t pcm_open_retries = 0; - /* The time-stamp for delay update rate limiting. */ - struct timespec pcm_delay_update_ts = { 0 }; - /* Window buffer for calculating delay moving average. */ - snd_pcm_sframes_t pcm_delay_frames[64]; - size_t pcm_delay_frames_i = 0; + struct delay_report dr; + delay_report_init(&dr, &dbus_ctx, &w->ba_pcm); size_t pause_retry_pcm_samples = pcm_1s_samples; size_t pause_retries = 0; @@ -636,7 +634,7 @@ static void *io_worker_routine(struct io_worker *w) { pcm_open_retries = 0; /* Reset moving delay window buffer. */ - memset(pcm_delay_frames, 0, sizeof(pcm_delay_frames)); + delay_report_reset(&dr); if (verbose >= 2) { info("Used configuration for %s:\n" @@ -678,47 +676,10 @@ static void *io_worker_routine(struct io_worker *w) { if (alsa_pcm_write(&w->alsa_pcm, &buffer) < 0) goto close_alsa; - int ret; - snd_pcm_sframes_t delay_frames = 0; - if ((ret = alsa_pcm_delay(&w->alsa_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 }; - - gettimestamp(&ts_now); - timespecadd(&pcm_delay_update_ts, &ts_delay, &ts_delay); - - snd_pcm_sframes_t pcm_delay_frames_avg = 0; - for (size_t i = 0; i < ARRAYSIZE(pcm_delay_frames); i++) - pcm_delay_frames_avg += pcm_delay_frames[i]; - pcm_delay_frames_avg /= ARRAYSIZE(pcm_delay_frames); - - const int delay = pcm_delay_frames_avg * 10000 / w->ba_pcm.rate; - if (difftimespec(&ts_now, &ts_delay, &ts_delay) < 0 && - abs(delay - w->ba_pcm.client_delay) >= 100 /* 10ms */) { - - pcm_delay_update_ts = ts_now; - - w->ba_pcm.client_delay = delay; - if (!ba_dbus_pcm_update(&dbus_ctx, &w->ba_pcm, BLUEALSA_PCM_CLIENT_DELAY, &err)) { - error("Couldn't update BlueALSA PCM client delay: %s", err.message); - dbus_error_free(&err); - goto fail; - } - - } - + if (!delay_report_update(&dr, &w->alsa_pcm, w->ba_pcm_fd, &buffer, &err)) { + error("Couldn't update BlueALSA PCM client delay: %s", err.message); + dbus_error_free(&err); + goto fail; } continue; diff --git a/utils/aplay/delay-report.c b/utils/aplay/delay-report.c new file mode 100644 index 000000000..24dadd5b3 --- /dev/null +++ b/utils/aplay/delay-report.c @@ -0,0 +1,91 @@ +/* + * BlueALSA - delay-report.c + * Copyright (c) 2016-2025 Arkadiusz Bokowy + * + * This file is a part of bluez-alsa. + * + * This project is licensed under the terms of the MIT license. + * + */ + +#include "delay-report.h" + +#include +#include +#include +#include +#include + +#include +#include + +#include "alsa-pcm.h" +#include "shared/dbus-client-pcm.h" +#include "shared/defs.h" +#include "shared/ffb.h" +#include "shared/log.h" +#include "shared/rt.h" + +void delay_report_init( + struct delay_report *dr, + struct ba_dbus_ctx *dbus_ctx, + struct ba_pcm *ba_pcm) { + + memset(dr, 0, sizeof(*dr)); + dr->dbus_ctx = dbus_ctx; + dr->ba_pcm = ba_pcm; + +} + +void delay_report_reset( + struct delay_report *dr) { + memset(dr->values, 0, sizeof(dr->values)); +} + +bool delay_report_update( + struct delay_report *dr, + struct alsa_pcm *pcm, + int ba_pcm_fd, + ffb_t *buffer, + DBusError *err) { + + int ret; + snd_pcm_sframes_t alsa_delay_frames = 0; + /* Get the delay reported by the ALSA driver. */ + if ((ret = alsa_pcm_delay(pcm, &alsa_delay_frames)) != 0) + warn("Couldn't get PCM delay: %s", snd_strerror(ret)); + + unsigned int ba_pcm_buffered = 0; + /* Get the delay due to BlueALSA PCM FIFO buffering. */ + ioctl(ba_pcm_fd, FIONREAD, &ba_pcm_buffered); + snd_pcm_sframes_t ba_pcm_frames = ba_pcm_buffered / pcm->frame_size; + + /* Get the delay due to internal buffering. */ + snd_pcm_sframes_t buffer_frames = ffb_blen_out(buffer) / pcm->frame_size; + + const size_t num_values = ARRAYSIZE(dr->values); + /* Store the delay calculated from all components. */ + dr->values[dr->values_i % num_values] = alsa_delay_frames + ba_pcm_frames + buffer_frames; + dr->values_i++; + + struct timespec ts_now; + /* Rate limit delay updates to 1 update per second. */ + struct timespec ts_delay = { .tv_sec = 1 }; + + gettimestamp(&ts_now); + timespecadd(&dr->update_ts, &ts_delay, &ts_delay); + + snd_pcm_sframes_t delay_frames_avg = 0; + for (size_t i = 0; i < num_values; i++) + delay_frames_avg += dr->values[i]; + delay_frames_avg /= num_values; + + const int delay = delay_frames_avg * 10000 / dr->ba_pcm->rate; + if (difftimespec(&ts_now, &ts_delay, &ts_delay) >= 0 || + abs(delay - dr->ba_pcm->client_delay) < 100 /* 10ms */) + return true; + + dr->update_ts = ts_now; + dr->ba_pcm->client_delay = delay; + return ba_dbus_pcm_update(dr->dbus_ctx, dr->ba_pcm, BLUEALSA_PCM_CLIENT_DELAY, err); +} diff --git a/utils/aplay/delay-report.h b/utils/aplay/delay-report.h new file mode 100644 index 000000000..340f8bf60 --- /dev/null +++ b/utils/aplay/delay-report.h @@ -0,0 +1,54 @@ +/* + * BlueALSA - delay-report.h + * Copyright (c) 2016-2025 Arkadiusz Bokowy + * + * This file is a part of bluez-alsa. + * + * This project is licensed under the terms of the MIT license. + * + */ + +#pragma once +#ifndef BLUEALSA_APLAY_DELAYREPORT_H_ +#define BLUEALSA_APLAY_DELAYREPORT_H_ + +#include +#include + +#include +#include + +#include "alsa-pcm.h" +#include "shared/ffb.h" + +struct delay_report { + + struct ba_dbus_ctx *dbus_ctx; + + struct ba_pcm *ba_pcm; + + /* The time-stamp for delay update rate limiting. */ + struct timespec update_ts; + + /* Window buffer for calculating delay moving average. */ + snd_pcm_sframes_t values[64]; + size_t values_i; + +}; + +void delay_report_init( + struct delay_report *dr, + struct ba_dbus_ctx *dbus_ctx, + struct ba_pcm *ba_pcm); + +void delay_report_reset( + struct delay_report *dr); + +bool delay_report_update( + struct delay_report *dr, + struct alsa_pcm *pcm, + int ba_pcm_fd, + ffb_t *buffer, + DBusError *err); + +#endif