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:
parent
351b39e0c5
commit
04eb911a51
2
NEWS
2
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
|
||||
|
|
|
@ -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 = {
|
||||
|
|
Loading…
Reference in New Issue