Skip to content

Commit

Permalink
Account for playback volume dB max value
Browse files Browse the repository at this point in the history
Fixes #714
  • Loading branch information
arkq committed Jun 4, 2024
1 parent adf9334 commit 726581e
Show file tree
Hide file tree
Showing 2 changed files with 54 additions and 20 deletions.
1 change: 1 addition & 0 deletions NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ unreleased
==========

- optional support for Android 13 A2DP Opus codec
- bluealsa-aplay: fix volume synchronization on Raspberry Pi

bluez-alsa v4.2.0 (2024-05-11)
==========
Expand Down
73 changes: 53 additions & 20 deletions utils/aplay/aplay.c
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ struct io_worker {
/* mixer for volume control */
snd_mixer_t *snd_mixer;
snd_mixer_elem_t *snd_mixer_elem;
long mixer_volume_db_max_value;
bool mixer_has_mute_switch;
/* if true, playback is active */
atomic_bool active;
Expand Down Expand Up @@ -327,6 +328,45 @@ static int pause_device_player(const struct ba_pcm *ba_pcm) {
return ret;
}

/**
* Open ALSA mixer element for volume control. */
static int io_worker_mixer_open(
struct io_worker *worker,
const char *dev_name,
const char *elem_name,
unsigned int elem_idx) {

if (dev_name == NULL)
return 0;

debug("Opening ALSA mixer: name=%s elem=%s index=%u",
dev_name, elem_name, elem_idx);

snd_mixer_elem_t *elem;
long vmin_db, vmax_db = 0;
bool has_mute_switch;
char *tmp;
int err;

if (alsa_mixer_open(&worker->snd_mixer, &elem,
dev_name, elem_name, elem_idx, &tmp) != 0) {
warn("Couldn't open ALSA mixer: %s", tmp);
free(tmp);
return -1;
}

has_mute_switch = snd_mixer_selem_has_playback_switch(elem);

if ((err = snd_mixer_selem_get_playback_dB_range(elem, &vmin_db, &vmax_db)) != 0)
warn("Couldn't get ALSA mixer playback dB range: %s", snd_strerror(err));

worker->snd_mixer_elem = elem;
worker->mixer_has_mute_switch = has_mute_switch;
worker->mixer_volume_db_max_value = vmax_db;

return 0;
}

/**
* Update BlueALSA PCM volume according to ALSA mixer element. */
static int io_worker_mixer_volume_sync_ba_pcm(
Expand All @@ -343,22 +383,24 @@ static int io_worker_mixer_volume_sync_ba_pcm(

long ch_volume_db;
int ch_switch = 1;

int err;
if ((err = snd_mixer_selem_get_playback_dB(elem, 0, &ch_volume_db)) != 0) {

if ((err = snd_mixer_selem_get_playback_dB(elem, ch, &ch_volume_db)) != 0) {
error("Couldn't get ALSA mixer playback dB level: %s", snd_strerror(err));
return -1;
}

/* mute switch is an optional feature for a mixer element */
if ((worker->mixer_has_mute_switch = snd_mixer_selem_has_playback_switch(elem))) {
if ((err = snd_mixer_selem_get_playback_switch(elem, 0, &ch_switch)) != 0) {
error("Couldn't get ALSA mixer playback switch: %s", snd_strerror(err));
return -1;
}
/* Mute switch is an optional feature for a mixer element. */
if (worker->mixer_has_mute_switch &&
(err = snd_mixer_selem_get_playback_switch(elem, ch, &ch_switch)) != 0) {
error("Couldn't get ALSA mixer playback switch: %s", snd_strerror(err));
return -1;
}

volume_db_sum += ch_volume_db;
/* Normalize volume level so it will not exceed 0.00 dB. */
volume_db_sum -= worker->mixer_volume_db_max_value;

if (ch_switch == 1)
muted = false;

Expand Down Expand Up @@ -427,6 +469,7 @@ static int io_worker_mixer_volume_sync_snd_mixer_elem(

/* convert loudness to dB using decibel formula */
long db = 10 * log2(1.0 * volume / vmax) * 100;
db += worker->mixer_volume_db_max_value;

int err;
if ((err = snd_mixer_selem_set_playback_dB_all(elem, db, 0)) != 0) {
Expand All @@ -444,7 +487,7 @@ static int io_worker_mixer_volume_sync_snd_mixer_elem(
return 0;
}

int io_worker_mixer_elem_callback(snd_mixer_elem_t *elem, unsigned int mask) {
static int io_worker_mixer_elem_callback(snd_mixer_elem_t *elem, unsigned int mask) {
struct io_worker *worker = snd_mixer_elem_get_callback_private(elem);
if (mask & SND_CTL_EVENT_MASK_VALUE)
io_worker_mixer_volume_sync_ba_pcm(worker, &worker->ba_pcm);
Expand Down Expand Up @@ -704,16 +747,7 @@ static void *io_worker_routine(struct io_worker *w) {
snd_pcm_get_params(w->snd_pcm, &buffer_frames, &period_frames);
pcm_max_read_len = period_frames * w->ba_pcm.channels * pcm_format_size;

if (mixer_device != NULL) {
debug("Opening ALSA mixer: name=%s elem=%s index=%u",
mixer_device, mixer_elem_name, mixer_elem_index);
if (alsa_mixer_open(&w->snd_mixer, &w->snd_mixer_elem,
mixer_device, mixer_elem_name, mixer_elem_index, &tmp) != 0) {
warn("Couldn't open ALSA mixer: %s", tmp);
free(tmp);
}
}

io_worker_mixer_open(w, mixer_device, mixer_elem_name, mixer_elem_index);
io_worker_mixer_volume_sync_setup(w);

/* reset retry counters */
Expand Down Expand Up @@ -868,7 +902,6 @@ static struct io_worker *supervise_io_worker_start(const struct ba_pcm *ba_pcm)
worker->snd_pcm = NULL;
worker->snd_mixer = NULL;
worker->snd_mixer_elem = NULL;
worker->mixer_has_mute_switch = false;
worker->active = false;

pthread_rwlock_unlock(&workers_lock);
Expand Down

0 comments on commit 726581e

Please sign in to comment.