mirror of
https://github.com/owntone/owntone-server.git
synced 2024-12-29 08:33:23 -05:00
[alsa] use alsamixer volume adjustment algorithm
This commit is contained in:
parent
fc5df02d29
commit
bfe99e56f7
@ -2,6 +2,12 @@
|
|||||||
* Copyright (C) 2015-2019 Espen Jürgensen <espenjurgensen@gmail.com>
|
* Copyright (C) 2015-2019 Espen Jürgensen <espenjurgensen@gmail.com>
|
||||||
* Copyright (C) 2010 Julien BLACHE <jb@jblache.org>
|
* Copyright (C) 2010 Julien BLACHE <jb@jblache.org>
|
||||||
*
|
*
|
||||||
|
* Copyright (c) 2010 Clemens Ladisch <clemens@ladisch.de>
|
||||||
|
* from alsa-utils/alsamixer/volume_mapping.c
|
||||||
|
* use_linear_dB_scale()
|
||||||
|
* lrint_dir()
|
||||||
|
* set_normalized_volume()
|
||||||
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* This program is free software; you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
* the Free Software Foundation; either version 2 of the License, or
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
@ -38,6 +44,10 @@
|
|||||||
#include "player.h"
|
#include "player.h"
|
||||||
#include "outputs.h"
|
#include "outputs.h"
|
||||||
|
|
||||||
|
|
||||||
|
// For setting volume, treat everything below this as linear scale
|
||||||
|
#define MAX_LINEAR_DB_SCALE 24
|
||||||
|
|
||||||
// We measure latency each second, and after a number of measurements determined
|
// We measure latency each second, and after a number of measurements determined
|
||||||
// by adjust_period_seconds we try to determine drift and latency. If both are
|
// by adjust_period_seconds we try to determine drift and latency. If both are
|
||||||
// below the two thresholds set by the below, we don't do anything. Otherwise we
|
// below the two thresholds set by the below, we don't do anything. Otherwise we
|
||||||
@ -288,35 +298,94 @@ bps2format(int bits_per_sample)
|
|||||||
return SND_PCM_FORMAT_UNKNOWN;
|
return SND_PCM_FORMAT_UNKNOWN;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* from alsa-utils/alsamixer/volume_mapping.c
|
||||||
|
*
|
||||||
|
* The mapping is designed so that the position in the interval is proportional
|
||||||
|
* to the volume as a human ear would perceive it (i.e., the position is the
|
||||||
|
* cubic root of the linear sample multiplication factor). For controls with
|
||||||
|
* a small range (24 dB or less), the mapping is linear in the dB values so
|
||||||
|
* that each step has the same size visually. Only for controls without dB
|
||||||
|
* information, a linear mapping of the hardware volume register values is used
|
||||||
|
* (this is the same algorithm as used in the old alsamixer).
|
||||||
|
*
|
||||||
|
* When setting the volume, 'dir' is the rounding direction:
|
||||||
|
* -1/0/1 = down/nearest/up.
|
||||||
|
*/
|
||||||
|
static inline bool
|
||||||
|
use_linear_dB_scale(long dBmin, long dBmax)
|
||||||
|
{
|
||||||
|
return dBmax - dBmin <= MAX_LINEAR_DB_SCALE * 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
static long lrint_dir(double x, int dir)
|
||||||
|
{
|
||||||
|
if (dir > 0)
|
||||||
|
return lrint(ceil(x));
|
||||||
|
else if (dir < 0)
|
||||||
|
return lrint(floor(x));
|
||||||
|
else
|
||||||
|
return lrint(x);
|
||||||
|
}
|
||||||
|
|
||||||
|
// from alsamixer/volume-mapping.c, sets volume in line with human perception
|
||||||
|
static int
|
||||||
|
set_normalized_volume(snd_mixer_elem_t *elem, double volume, int dir)
|
||||||
|
{
|
||||||
|
long min, max, value;
|
||||||
|
double min_norm;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
err = snd_mixer_selem_get_playback_dB_range(elem, &min, &max);
|
||||||
|
if (err < 0 || min >= max)
|
||||||
|
{
|
||||||
|
err = snd_mixer_selem_get_playback_volume_range(elem, &min, &max);
|
||||||
|
if (err < 0)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
value = lrint_dir(volume * (max - min), dir) + min;
|
||||||
|
return snd_mixer_selem_set_playback_volume_all(elem, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Corner case from mpd - log10() expects non-zero
|
||||||
|
if (volume <= 0)
|
||||||
|
return snd_mixer_selem_set_playback_dB_all(elem, min, dir);
|
||||||
|
else if (volume >= 1)
|
||||||
|
return snd_mixer_selem_set_playback_dB_all(elem, max, dir);
|
||||||
|
|
||||||
|
if (use_linear_dB_scale(min, max))
|
||||||
|
{
|
||||||
|
value = lrint_dir(volume * (max - min), dir) + min;
|
||||||
|
return snd_mixer_selem_set_playback_dB_all(elem, value, dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (min != SND_CTL_TLV_DB_GAIN_MUTE)
|
||||||
|
{
|
||||||
|
min_norm = pow(10, (min - max) / 6000.0);
|
||||||
|
volume = volume * (1 - min_norm) + min_norm;
|
||||||
|
}
|
||||||
|
value = lrint_dir(6000.0 * log10(volume), dir) + max;
|
||||||
|
return snd_mixer_selem_set_playback_dB_all(elem, value, dir);
|
||||||
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
volume_set(struct alsa_mixer *mixer, int volume)
|
volume_set(struct alsa_mixer *mixer, int volume)
|
||||||
{
|
{
|
||||||
int pcm_vol;
|
int ret;
|
||||||
|
|
||||||
snd_mixer_handle_events(mixer->hdl);
|
snd_mixer_handle_events(mixer->hdl);
|
||||||
|
|
||||||
if (!snd_mixer_selem_is_active(mixer->vol_elem))
|
if (!snd_mixer_selem_is_active(mixer->vol_elem))
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
switch (volume)
|
DPRINTF(E_DBG, L_LAUDIO, "Setting ALSA volume to %d\n", volume);
|
||||||
|
|
||||||
|
ret = set_normalized_volume(mixer->vol_elem, volume >= 0 && volume <= 100 ? volume/100.0 : 0.75, 0);
|
||||||
|
if (ret < 0)
|
||||||
{
|
{
|
||||||
case 0:
|
DPRINTF(E_LOG, L_LAUDIO, "Failed to set ALSA volume to %d\n: %s", volume, snd_strerror(ret));
|
||||||
pcm_vol = mixer->vol_min;
|
return -1;
|
||||||
break;
|
|
||||||
|
|
||||||
case 100:
|
|
||||||
pcm_vol = mixer->vol_max;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
pcm_vol = mixer->vol_min + (volume * (mixer->vol_max - mixer->vol_min)) / 100;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DPRINTF(E_DBG, L_LAUDIO, "Setting ALSA volume to %d (%d)\n", pcm_vol, volume);
|
|
||||||
|
|
||||||
snd_mixer_selem_set_playback_volume_all(mixer->vol_elem, pcm_vol);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user