Commit 04eb911a authored by Max Kellermann's avatar Max Kellermann

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

This replaces 967af603 with a more effective workaround. Closes https://github.com/MusicPlayerDaemon/MPD/issues/822
parent 351b39e0
...@@ -13,6 +13,8 @@ ver 0.22.10 (not yet released) ...@@ -13,6 +13,8 @@ ver 0.22.10 (not yet released)
* output * output
- httpd: fix missing tag after seeking into a new song - httpd: fix missing tag after seeking into a new song
- oss: fix channel order of multi-channel files - oss: fix channel order of multi-channel files
* mixer
- alsa: fix yet more rounding errors
ver 0.22.9 (2021/06/23) ver 0.22.9 (2021/06/23)
* database * database
......
...@@ -80,6 +80,24 @@ class AlsaMixer final : public Mixer { ...@@ -80,6 +80,24 @@ class AlsaMixer final : public Mixer {
AlsaMixerMonitor *monitor; 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: public:
AlsaMixer(EventLoop &_event_loop, MixerListener &_listener) AlsaMixer(EventLoop &_event_loop, MixerListener &_listener)
:Mixer(alsa_mixer_plugin, _listener), :Mixer(alsa_mixer_plugin, _listener),
...@@ -167,6 +185,17 @@ AlsaMixer::ElemCallback(snd_mixer_elem_t *elem, unsigned mask) noexcept ...@@ -167,6 +185,17 @@ AlsaMixer::ElemCallback(snd_mixer_elem_t *elem, unsigned mask) noexcept
if (mask & SND_CTL_EVENT_MASK_VALUE) { if (mask & SND_CTL_EVENT_MASK_VALUE) {
int volume = mixer.GetPercentVolume(); 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); mixer.listener.OnMixerVolumeChanged(mixer, volume);
} }
...@@ -253,6 +282,8 @@ AlsaMixer::Setup() ...@@ -253,6 +282,8 @@ AlsaMixer::Setup()
void void
AlsaMixer::Open() AlsaMixer::Open()
{ {
desired_volume = resulting_volume = -1;
int err; int err;
err = snd_mixer_open(&handle, 0); err = snd_mixer_open(&handle, 0);
...@@ -291,7 +322,12 @@ AlsaMixer::GetVolume() ...@@ -291,7 +322,12 @@ AlsaMixer::GetVolume()
throw FormatRuntimeError("snd_mixer_handle_events() failed: %s", throw FormatRuntimeError("snd_mixer_handle_events() failed: %s",
snd_strerror(err)); 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 void
...@@ -299,12 +335,13 @@ AlsaMixer::SetVolume(unsigned volume) ...@@ -299,12 +335,13 @@ AlsaMixer::SetVolume(unsigned volume)
{ {
assert(handle != nullptr); assert(handle != nullptr);
double cur = GetNormalizedVolume(); int err = set_normalized_playback_volume(elem, 0.01*volume, 1);
int delta = volume - NormalizedToPercent(cur);
int err = set_normalized_playback_volume(elem, cur + 0.01*delta, delta);
if (err < 0) if (err < 0)
throw FormatRuntimeError("failed to set ALSA volume: %s", throw FormatRuntimeError("failed to set ALSA volume: %s",
snd_strerror(err)); snd_strerror(err));
desired_volume = volume;
resulting_volume = GetPercentVolume();
} }
const MixerPlugin alsa_mixer_plugin = { const MixerPlugin alsa_mixer_plugin = {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment