summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMax Kellermann <max@musicpd.org>2021-08-06 18:15:52 +0200
committerMax Kellermann <max@musicpd.org>2021-08-06 18:16:37 +0200
commit04eb911a5110a2a61ea7fcb5995e77bdcc21bbc4 (patch)
tree6810f9b8730e31265de06688f9bed68711cb9e45
parent351b39e0c5b70380074464522264e5106bf476da (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--NEWS2
-rw-r--r--src/mixer/plugins/AlsaMixerPlugin.cxx45
2 files changed, 43 insertions, 4 deletions
diff --git a/NEWS b/NEWS
index 3d6f0a112..f0a8434c2 100644
--- a/NEWS
+++ b/NEWS
@@ -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 = {