From 31268ad7cd76cf4ceef4a9c6e2fb3ba7d0eea4be Mon Sep 17 00:00:00 2001
From: Max Kellermann <max@musicpd.org>
Date: Fri, 16 Oct 2020 18:18:15 +0200
Subject: [PATCH] decoder/opus: fix track/album ReplayGain fallback

Fixes regression by commit 23d5a2b8620cea69958d087fc7e13fe1e5adb83d -
that commit always pretended that any Opus file has both track and
album gain, and thus disabled the fallback to the other if one is not
set.

This patch changes the logic to only submit ReplayGain if at least one
value is set, and apply the offset only to that value.  If none is
available, then the new check in HandleAudio() will submit only the
output gain.

Closes https://github.com/MusicPlayerDaemon/MPD/issues/977
---
 NEWS                                      |  1 +
 src/decoder/plugins/OpusDecoderPlugin.cxx | 13 ++++++++++---
 src/decoder/plugins/OpusTags.cxx          |  4 ++--
 3 files changed, 13 insertions(+), 5 deletions(-)

diff --git a/NEWS b/NEWS
index dae8a13eb..07e13ff13 100644
--- a/NEWS
+++ b/NEWS
@@ -1,6 +1,7 @@
 ver 0.22.1 (not yet released)
 * decoder
   - opus: apply the OpusHead output gain even if there is no EBU R128 tag
+  - opus: fix track/album ReplayGain fallback
 * output
   - alsa: don't deadlock when the ALSA driver is buggy
   - jack, pulse: reduce the delay when stopping or pausing playback
diff --git a/src/decoder/plugins/OpusDecoderPlugin.cxx b/src/decoder/plugins/OpusDecoderPlugin.cxx
index e3022d8fc..0343e201c 100644
--- a/src/decoder/plugins/OpusDecoderPlugin.cxx
+++ b/src/decoder/plugins/OpusDecoderPlugin.cxx
@@ -268,7 +268,6 @@ MPDOpusDecoder::HandleTags(const ogg_packet &packet)
 {
 	ReplayGainInfo rgi;
 	rgi.Clear();
-	rgi.track.gain = rgi.album.gain = EbuR128ToReplayGain(output_gain);
 
 	TagBuilder tag_builder;
 	AddTagHandler h(tag_builder);
@@ -276,8 +275,16 @@ MPDOpusDecoder::HandleTags(const ogg_packet &packet)
 	if (!ScanOpusTags(packet.packet, packet.bytes, &rgi, h))
 		return;
 
-	client.SubmitReplayGain(&rgi);
-	submitted_replay_gain = true;
+	if (rgi.IsDefined()) {
+		/* submit all valid EBU R128 values with output_gain
+		   applied */
+		if (rgi.track.IsDefined())
+			rgi.track.gain += EbuR128ToReplayGain(output_gain);
+		if (rgi.album.IsDefined())
+			rgi.album.gain += EbuR128ToReplayGain(output_gain);
+		client.SubmitReplayGain(&rgi);
+		submitted_replay_gain = true;
+	}
 
 	if (!tag_builder.empty()) {
 		Tag tag = tag_builder.Commit();
diff --git a/src/decoder/plugins/OpusTags.cxx b/src/decoder/plugins/OpusTags.cxx
index c78586065..ee1d0cb22 100644
--- a/src/decoder/plugins/OpusTags.cxx
+++ b/src/decoder/plugins/OpusTags.cxx
@@ -61,7 +61,7 @@ ScanOneOpusTag(StringView name, StringView value,
 		const char *endptr;
 		const auto l = ParseInt64(value, &endptr, 10);
 		if (endptr > value.begin() && endptr == value.end())
-			rgi->track.gain += float(l) / 256.0f;
+			rgi->track.gain = float(l) / 256.0f;
 	} else if (rgi != nullptr &&
 		   name.EqualsIgnoreCase("R128_ALBUM_GAIN")) {
 		/* R128_ALBUM_GAIN is a Q7.8 fixed point number in
@@ -70,7 +70,7 @@ ScanOneOpusTag(StringView name, StringView value,
 		const char *endptr;
 		const auto l = ParseInt64(value, &endptr, 10);
 		if (endptr > value.begin() && endptr == value.end())
-			rgi->album.gain += float(l) / 256.0f;
+			rgi->album.gain = float(l) / 256.0f;
 	}
 
 	handler.OnPair(name, value);