static int set_normalized_volume(snd_mixer_elem_t *elem, snd_mixer_selem_channel_id_t channel, double volume, int dir, enum ctl_dir ctl_dir) { long min, max, value; double min_norm; int err; err = get_dB_range[ctl_dir](elem, &min, &max); if (err < 0 || min >= max) { err = get_raw_range[ctl_dir](elem, &min, &max); if (err < 0) return err; value = lrint_dir(volume * (max - min), dir) + min; return set_raw[ctl_dir](elem, channel, value); } if (use_linear_dB_scale(min, max)) { value = lrint_dir(volume * (max - min), dir) + min; return set_dB[ctl_dir](elem, channel, value, dir); } if (min != SND_CTL_TLV_DB_GAIN_MUTE) { min_norm = exp10((min - max) / 6000.0); volume = volume * (1 - min_norm) + min_norm; } value = lrint_dir(6000.0 * log10(volume), dir) + max; return set_dB[ctl_dir](elem, channel, value, dir); }
/** * Adjusts the current volume and sends a notification (if enabled). * * @param vol new volume value * @param dir select direction (-1 = accurate or first bellow, 0 = accurate, * 1 = accurate or first above) * @param notify whether to send notification * @return 0 on success otherwise negative error code */ int setvol(int vol, int dir, gboolean notify) { long min = 0, max = 0, value; int cur_perc = getvol(); double dvol = 0.01 * vol; int err = snd_mixer_selem_get_playback_dB_range(elem, &min, &max); if (err < 0 || min >= max || !normalize_vol()) { err = snd_mixer_selem_get_playback_volume_range(elem, &min, &max); value = lrint_dir(dvol * (max - min), dir) + min; snd_mixer_selem_set_playback_volume_all(elem, value); if (enable_noti && notify && cur_perc != getvol()) do_notify_volume(getvol(), FALSE); // intentionally set twice return snd_mixer_selem_set_playback_volume_all(elem, value); } if (use_linear_dB_scale(min, max)) { value = lrint_dir(dvol * (max - min), dir) + min; return snd_mixer_selem_set_playback_dB_all(elem, value, dir); } if (min != SND_CTL_TLV_DB_GAIN_MUTE) { double min_norm = exp10((min - max) / 6000.0); dvol = dvol * (1 - min_norm) + min_norm; } value = lrint_dir(6000.0 * log10(dvol), dir) + max; snd_mixer_selem_set_playback_dB_all(elem, value, dir); if (enable_noti && notify && cur_perc != getvol()) do_notify_volume(getvol(), FALSE); // intentionally set twice return snd_mixer_selem_set_playback_dB_all(elem, value, dir); }