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:
		
							
								
								
									
										2
									
								
								NEWS
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								NEWS
									
									
									
									
									
								
							| @@ -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 { | |||||||
|  |  | ||||||
| 	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 | |||||||
|  |  | ||||||
| 	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() | |||||||
| 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() | |||||||
| 		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) | |||||||
| { | { | ||||||
| 	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 = { | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Max Kellermann
					Max Kellermann