Add support for ALSA softvol plugin
The ALSA softvol plugin allows the user to add a new volume control and control
the sound volume by software. This is necessary if the sound card can't control
the volume by hardware.
An example configuration in the `~/.asoundrc` file might look like this:
```
pcm.softvol {
type softvol
slave {
pcm "hw:0"
}
control {
name "Master"
card 0
}
min_dB -57.2
max_dB -6.2
}
ctl.softvol {
type hw
card 0
}
```
The corresponding entries in the shairport-sync would be:
```
alsa =
{
output_device = "softvol";
mixer_control_name = "Master";
}
```
Please check the (ALSA documentation)[http://alsa.opensrc.org/Softvol] and the
(ALSA PCM library reference)[http://www.alsa-project.org/alsa-doc/alsa-lib/pcm_plugins.html]
If shairport-syncs alsa backend does not have success in getting the dB volume
information from the hardware device it tries to get these information from a
software volume control with the name mixer device name as for the hardware
mixer.
This commit is contained in:
+52
-11
@@ -59,9 +59,9 @@ audio_output audio_alsa = {
|
||||
.flush = &flush,
|
||||
.delay = &delay,
|
||||
.play = &play,
|
||||
.mute = NULL, // to be set later on...
|
||||
.volume = NULL, // to be set later on...
|
||||
.parameters = NULL // to be set later on...
|
||||
.mute = &mute,
|
||||
.volume = &volume,
|
||||
.parameters = ¶meters
|
||||
};
|
||||
|
||||
static pthread_mutex_t alsa_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
@@ -70,7 +70,8 @@ static unsigned int desired_sample_rate;
|
||||
|
||||
static snd_pcm_t *alsa_handle = NULL;
|
||||
static snd_pcm_hw_params_t *alsa_params = NULL;
|
||||
|
||||
static snd_ctl_t *ctl = NULL;
|
||||
static snd_ctl_elem_id_t *elem_id = NULL;
|
||||
static snd_mixer_t *alsa_mix_handle = NULL;
|
||||
static snd_mixer_elem_t *alsa_mix_elem = NULL;
|
||||
static snd_mixer_selem_id_t *alsa_mix_sid = NULL;
|
||||
@@ -82,7 +83,7 @@ static char *alsa_mix_dev = NULL;
|
||||
static char *alsa_mix_ctrl = "Master";
|
||||
static int alsa_mix_index = 0;
|
||||
static int hardware_mixer = 0;
|
||||
|
||||
static int has_softvol = 0;
|
||||
|
||||
static int play_number;
|
||||
static int64_t accumulated_delay, accumulated_da_delay;
|
||||
@@ -299,8 +300,29 @@ static int init(int argc, char **argv) {
|
||||
} else {
|
||||
// use the linear scale and do the db conversion ourselves
|
||||
debug(1, "note: the hardware mixer specified -- \"%s\" -- does not have "
|
||||
"a dB volume scale, so it can't be used.",
|
||||
"a dB volume scale, so it can't be used. Trying software "
|
||||
"volume control.",
|
||||
alsa_mix_ctrl);
|
||||
|
||||
if (snd_ctl_open(&ctl, alsa_mix_dev, 0) < 0)
|
||||
die("Cannot open control \"%s\"", alsa_mix_dev);
|
||||
if (snd_ctl_elem_id_malloc(&elem_id) < 0)
|
||||
die("Cannot allocate memory for control \"%s\"", alsa_mix_dev);
|
||||
snd_ctl_elem_id_set_interface(elem_id, SND_CTL_ELEM_IFACE_MIXER);
|
||||
snd_ctl_elem_id_set_name(elem_id, alsa_mix_ctrl);
|
||||
|
||||
if (snd_ctl_get_dB_range(ctl, elem_id, &alsa_mix_mindb,
|
||||
&alsa_mix_maxdb) == 0) {
|
||||
debug(1, "Volume control \"%s\" has dB volume from %f to %f.",
|
||||
alsa_mix_ctrl,
|
||||
(1.0 * alsa_mix_mindb) / 100.0,
|
||||
(1.0 * alsa_mix_maxdb) / 100.0);
|
||||
has_softvol = 1;
|
||||
} else {
|
||||
debug(1, "Cannot get the dB range from the volume control \"%s\"",
|
||||
alsa_mix_ctrl);
|
||||
}
|
||||
|
||||
/*
|
||||
debug(1, "Min and max volumes are %d and
|
||||
%d.",alsa_mix_minv,alsa_mix_maxv);
|
||||
@@ -728,11 +750,30 @@ static void volume(double vol) {
|
||||
debug(2, "Setting volume db to %f.", vol);
|
||||
set_volume = vol;
|
||||
if (hardware_mixer && alsa_mix_handle) {
|
||||
if (snd_mixer_selem_set_playback_dB_all(alsa_mix_elem, vol, 0) != 0) {
|
||||
debug(1, "Can't set playback volume accurately to %f dB.", vol);
|
||||
if (snd_mixer_selem_set_playback_dB_all(alsa_mix_elem, vol, -1) != 0)
|
||||
if (snd_mixer_selem_set_playback_dB_all(alsa_mix_elem, vol, 1) != 0)
|
||||
die("Failed to set playback dB volume");
|
||||
if (has_softvol) {
|
||||
if (ctl && elem_id) {
|
||||
snd_ctl_elem_value_t *value;
|
||||
long raw;
|
||||
|
||||
if (snd_ctl_convert_from_dB(ctl, elem_id, (long) vol, &raw, 0) < 0)
|
||||
debug(1, "Failed converting dB gain to raw volume value for the "
|
||||
"software volume control.");
|
||||
|
||||
snd_ctl_elem_value_alloca(&value);
|
||||
snd_ctl_elem_value_set_id(value, elem_id);
|
||||
snd_ctl_elem_value_set_integer(value, 0, raw);
|
||||
snd_ctl_elem_value_set_integer(value, 1, raw);
|
||||
if (snd_ctl_elem_write(ctl, value) < 0)
|
||||
debug(1, "Failed to set playback dB volume for the software volume "
|
||||
"control.");
|
||||
}
|
||||
} else {
|
||||
if (snd_mixer_selem_set_playback_dB_all(alsa_mix_elem, vol, 0) != 0) {
|
||||
debug(1, "Can't set playback volume accurately to %f dB.", vol);
|
||||
if (snd_mixer_selem_set_playback_dB_all(alsa_mix_elem, vol, -1) != 0)
|
||||
if (snd_mixer_selem_set_playback_dB_all(alsa_mix_elem, vol, 1) != 0)
|
||||
die("Failed to set playback dB volume");
|
||||
}
|
||||
}
|
||||
}
|
||||
pthread_mutex_unlock(&alsa_mutex);
|
||||
|
||||
Reference in New Issue
Block a user