.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;
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;
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;
} 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);
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);