Skip to content

Commit

Permalink
aplay: Refactor code for PCM delay reporting
Browse files Browse the repository at this point in the history
  • Loading branch information
borine authored and arkq committed Feb 6, 2025
1 parent ab281a2 commit fe5cc21
Show file tree
Hide file tree
Showing 4 changed files with 155 additions and 48 deletions.
3 changes: 2 additions & 1 deletion utils/aplay/Makefile.am
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# BlueALSA - Makefile.am
# Copyright (c) 2016-2024 Arkadiusz Bokowy
# Copyright (c) 2016-2025 Arkadiusz Bokowy

if ENABLE_APLAY

Expand All @@ -16,6 +16,7 @@ bluealsa_aplay_SOURCES = \
alsa-mixer.c \
alsa-pcm.c \
dbus.c \
delay-report.c \
aplay.c

bluealsa_aplay_CFLAGS = \
Expand Down
55 changes: 8 additions & 47 deletions utils/aplay/aplay.c
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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"
Expand Down Expand Up @@ -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;
Expand Down
91 changes: 91 additions & 0 deletions utils/aplay/delay-report.c
Original file line number Diff line number Diff line change
@@ -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 <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/time.h>

#include <alsa/asoundlib.h>
#include <dbus/dbus.h>

#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);
}
54 changes: 54 additions & 0 deletions utils/aplay/delay-report.h
Original file line number Diff line number Diff line change
@@ -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 <stdbool.h>
#include <sys/time.h>

#include <alsa/asoundlib.h>
#include <dbus/dbus.h>

#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

0 comments on commit fe5cc21

Please sign in to comment.