diff --git a/NEWS b/NEWS
index b82032c82..fec18ffb4 100644
--- a/NEWS
+++ b/NEWS
@@ -1,4 +1,6 @@
 ver 0.23.14 (not yet released)
+* decoder
+  - mad: fix calculation of LAME peak values
 
 ver 0.23.13 (2023/05/22)
 * input
diff --git a/src/decoder/plugins/MadDecoderPlugin.cxx b/src/decoder/plugins/MadDecoderPlugin.cxx
index 7e34c25b1..d861e2e6e 100644
--- a/src/decoder/plugins/MadDecoderPlugin.cxx
+++ b/src/decoder/plugins/MadDecoderPlugin.cxx
@@ -562,7 +562,21 @@ parse_lame(struct lame *lame, struct mad_bitptr *ptr, int *bitlen) noexcept
 
 	mad_bit_skip(ptr, 16);
 
-	lame->peak = MAD_F(mad_bit_read(ptr, 32) << 5); /* peak */
+	/* The lame peak value is a float multiplied by 2^23 and stored as an
+	 * unsigned integer (it is always positive). MAD's fixed-point format uses
+	 * 28 bits for the fractional part, so shift the 23 bit fraction up before
+	 * converting to a float.
+	 */
+	unsigned long peak_int = mad_bit_read(ptr, 32);
+
+#define LAME_PEAK_FRACBITS 23
+#if MAD_F_FRACBITS > LAME_PEAK_FRACBITS
+	peak_int <<= (MAD_F_FRACBITS - LAME_PEAK_FRACBITS);
+#elif LAME_PEAK_FRACBITS > MAD_F_FRACBITS
+	peak_int >>= (LAME_PEAK_FRACBITS - MAD_F_FRACBITS);
+#endif
+
+	lame->peak = mad_f_todouble(peak_int); /* peak */
 	FmtDebug(mad_domain, "LAME peak found: {}", lame->peak);
 
 	lame->track_gain = 0;