diff options
author | Max Kellermann <max@musicpd.org> | 2021-08-06 18:15:52 +0200 |
---|---|---|
committer | Max Kellermann <max@musicpd.org> | 2021-08-06 18:16:37 +0200 |
commit | 04eb911a5110a2a61ea7fcb5995e77bdcc21bbc4 (patch) | |
tree | 6810f9b8730e31265de06688f9bed68711cb9e45 | |
parent | 351b39e0c5b70380074464522264e5106bf476da (diff) |
mixer/alsa: use cached values to work around rounding errors
This replaces 967af603270246f1c97e11b8bad6a0eb65c81318 with a more
effective workaround.
Closes https://github.com/MusicPlayerDaemon/MPD/issues/822
-rw-r--r-- | NEWS | 2 | ||||
-rw-r--r-- | src/mixer/plugins/AlsaMixerPlugin.cxx | 45 |
2 files changed, 43 insertions, 4 deletions
@@ -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 diff --git a/src/mixer/plugins/AlsaMixerPlugin.cxx b/src/mixer/plugins/AlsaMixerPlugin.cxx index 27243cfb4..8328e11a4 100644 --- a/src/mixer/plugins/AlsaMixerPlugin.cxx +++ b/src/mixer/plugins/AlsaMixerPlugin.cxx @@ -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 = { |