mixer/alsa: use cached values to work around rounding errors

This replaces 967af60327 with a more
effective workaround.

Closes https://github.com/MusicPlayerDaemon/MPD/issues/822
This commit is contained in:
Max Kellermann 2021-08-06 18:15:52 +02:00
parent 351b39e0c5
commit 04eb911a51
2 changed files with 43 additions and 4 deletions

2
NEWS
View File

@ -13,6 +13,8 @@ ver 0.22.10 (not yet released)
* output
- httpd: fix missing tag after seeking into a new song
- oss: fix channel order of multi-channel files
* mixer
- alsa: fix yet more rounding errors
ver 0.22.9 (2021/06/23)
* database

View File

@ -80,6 +80,24 @@ class AlsaMixer final : public Mixer {
AlsaMixerMonitor *monitor;
/**
* These fields are our workaround for rounding errors when
* the resolution of a mixer knob isn't fine enough to
* represent all 101 possible values (0..100).
*
* "desired_volume" is the percent value passed to
* SetVolume(), and "resulting_volume" is the volume which was
* actually set, and would be returned by the next
* GetPercentVolume() call.
*
* When GetVolume() is called, we compare the
* "resulting_volume" with the value returned by
* GetPercentVolume(), and if it's the same, we're still on
* the same value that was previously set (but may have been
* rounded down or up).
*/
int desired_volume, resulting_volume;
public:
AlsaMixer(EventLoop &_event_loop, MixerListener &_listener)
:Mixer(alsa_mixer_plugin, _listener),
@ -167,6 +185,17 @@ AlsaMixer::ElemCallback(snd_mixer_elem_t *elem, unsigned mask) noexcept
if (mask & SND_CTL_EVENT_MASK_VALUE) {
int volume = mixer.GetPercentVolume();
if (mixer.resulting_volume >= 0 &&
volume == mixer.resulting_volume)
/* still the same volume (this might be a
callback caused by SetVolume()) - switch to
desired_volume */
volume = mixer.desired_volume;
else
/* flush */
mixer.desired_volume = mixer.resulting_volume = -1;
mixer.listener.OnMixerVolumeChanged(mixer, volume);
}
@ -253,6 +282,8 @@ AlsaMixer::Setup()
void
AlsaMixer::Open()
{
desired_volume = resulting_volume = -1;
int err;
err = snd_mixer_open(&handle, 0);
@ -291,7 +322,12 @@ AlsaMixer::GetVolume()
throw FormatRuntimeError("snd_mixer_handle_events() failed: %s",
snd_strerror(err));
return GetPercentVolume();
int volume = GetPercentVolume();
if (resulting_volume >= 0 && volume == resulting_volume)
/* we're still on the value passed to SetVolume() */
volume = desired_volume;
return volume;
}
void
@ -299,12 +335,13 @@ AlsaMixer::SetVolume(unsigned volume)
{
assert(handle != nullptr);
double cur = GetNormalizedVolume();
int delta = volume - NormalizedToPercent(cur);
int err = set_normalized_playback_volume(elem, cur + 0.01*delta, delta);
int err = set_normalized_playback_volume(elem, 0.01*volume, 1);
if (err < 0)
throw FormatRuntimeError("failed to set ALSA volume: %s",
snd_strerror(err));
desired_volume = volume;
resulting_volume = GetPercentVolume();
}
const MixerPlugin alsa_mixer_plugin = {