diff --git a/src/decoder/plugins/DsdiffDecoderPlugin.cxx b/src/decoder/plugins/DsdiffDecoderPlugin.cxx
index e325185cd..72c0917fd 100644
--- a/src/decoder/plugins/DsdiffDecoderPlugin.cxx
+++ b/src/decoder/plugins/DsdiffDecoderPlugin.cxx
@@ -16,7 +16,7 @@
 #include "input/InputStream.hxx"
 #include "pcm/CheckAudioFormat.hxx"
 #include "util/BitReverse.hxx"
-#include "util/ByteOrder.hxx"
+#include "util/PackedBigEndian.hxx"
 #include "tag/Handler.hxx"
 #include "DsdLib.hxx"
 
diff --git a/src/decoder/plugins/DsfDecoderPlugin.cxx b/src/decoder/plugins/DsfDecoderPlugin.cxx
index 6c83fcefe..d99af62df 100644
--- a/src/decoder/plugins/DsfDecoderPlugin.cxx
+++ b/src/decoder/plugins/DsfDecoderPlugin.cxx
@@ -17,7 +17,7 @@
 #include "input/InputStream.hxx"
 #include "pcm/CheckAudioFormat.hxx"
 #include "util/BitReverse.hxx"
-#include "util/ByteOrder.hxx"
+#include "util/PackedLittleEndian.hxx"
 #include "DsdLib.hxx"
 #include "tag/Handler.hxx"
 
diff --git a/src/output/plugins/snapcast/Client.cxx b/src/output/plugins/snapcast/Client.cxx
index 1b9d72822..a3ed85659 100644
--- a/src/output/plugins/snapcast/Client.cxx
+++ b/src/output/plugins/snapcast/Client.cxx
@@ -9,6 +9,8 @@
 #include "event/Loop.hxx"
 #include "net/SocketError.hxx"
 #include "net/UniqueSocketDescriptor.hxx"
+#include "util/PackedBigEndian.hxx"
+#include "util/PackedLittleEndian.hxx"
 #include "util/SpanCast.hxx"
 #include "Log.hxx"
 
diff --git a/src/output/plugins/snapcast/Protocol.hxx b/src/output/plugins/snapcast/Protocol.hxx
index fd6cc3b54..3e662cfe7 100644
--- a/src/output/plugins/snapcast/Protocol.hxx
+++ b/src/output/plugins/snapcast/Protocol.hxx
@@ -1,10 +1,9 @@
 // SPDX-License-Identifier: GPL-2.0-or-later
 // Copyright The Music Player Daemon Project
 
-#ifndef MPD_OUTPUT_SNAPCAST_PROTOCOL_HXX
-#define MPD_OUTPUT_SNAPCAST_PROTOCOL_HXX
+#pragma once
 
-#include "util/ByteOrder.hxx"
+#include "util/PackedLittleEndian.hxx"
 
 // see https://github.com/badaix/snapcast/blob/master/doc/binary_protocol.md
 
@@ -55,5 +54,3 @@ struct SnapcastWireChunk {
 struct SnapcastTime {
 	SnapcastTimestamp latency;
 };
-
-#endif
diff --git a/src/tag/ApeLoader.cxx b/src/tag/ApeLoader.cxx
index 3b7037161..71725c07d 100644
--- a/src/tag/ApeLoader.cxx
+++ b/src/tag/ApeLoader.cxx
@@ -2,8 +2,8 @@
 // Copyright The Music Player Daemon Project
 
 #include "ApeLoader.hxx"
-#include "util/ByteOrder.hxx"
 #include "input/InputStream.hxx"
+#include "util/PackedLittleEndian.hxx"
 
 #include <cassert>
 #include <cstdint>
diff --git a/src/tag/Id3Picture.cxx b/src/tag/Id3Picture.cxx
index 86b95d38d..ad9bd3415 100644
--- a/src/tag/Id3Picture.cxx
+++ b/src/tag/Id3Picture.cxx
@@ -3,7 +3,7 @@
 
 #include "Id3Picture.hxx"
 #include "Handler.hxx"
-#include "util/ByteOrder.hxx"
+#include "util/PackedBigEndian.hxx"
 
 #include <cstdint>
 #include <string>
diff --git a/src/util/ByteOrder.hxx b/src/util/ByteOrder.hxx
index 868079a9f..78bcc21df 100644
--- a/src/util/ByteOrder.hxx
+++ b/src/util/ByteOrder.hxx
@@ -224,299 +224,3 @@ FromLE16S(uint16_t value) noexcept
 	/* assuming two's complement representation */
 	return static_cast<int16_t>(FromLE16(value));
 }
-
-/**
- * A packed big-endian 16 bit integer.
- */
-class PackedBE16 {
-	uint8_t hi, lo;
-
-public:
-	PackedBE16() = default;
-
-	constexpr PackedBE16(uint16_t src) noexcept
-		:hi(uint8_t(src >> 8)),
-		 lo(uint8_t(src)) {}
-
-	/**
-	 * Construct an instance from an integer which is already
-	 * big-endian.
-	 */
-	static constexpr auto FromBE(uint16_t src) noexcept {
-		union {
-			uint16_t in;
-			PackedBE16 out;
-		} u{src};
-		return u.out;
-	}
-
-	constexpr operator uint16_t() const noexcept {
-		return (uint16_t(hi) << 8) | uint16_t(lo);
-	}
-
-	/**
-	 * Reads the raw, big-endian value.
-	 */
-	constexpr uint16_t raw() const noexcept {
-		uint16_t x = *this;
-		if (IsLittleEndian())
-			x = ByteSwap16(x);
-		return x;
-	}
-};
-
-static_assert(sizeof(PackedBE16) == sizeof(uint16_t), "Wrong size");
-static_assert(alignof(PackedBE16) == 1, "Wrong alignment");
-
-/**
- * A packed big-endian 32 bit integer.
- */
-class PackedBE32 {
-	uint8_t a, b, c, d;
-
-public:
-	PackedBE32() = default;
-
-	constexpr PackedBE32(uint32_t src) noexcept
-		:a(uint8_t(src >> 24)),
-		 b(uint8_t(src >> 16)),
-		 c(uint8_t(src >> 8)),
-		 d(uint8_t(src)) {}
-
-	/**
-	 * Construct an instance from an integer which is already
-	 * big-endian.
-	 */
-	static constexpr auto FromBE(uint32_t src) noexcept {
-		union {
-			uint32_t in;
-			PackedBE32 out;
-		} u{src};
-		return u.out;
-	}
-
-	constexpr operator uint32_t() const noexcept {
-		return (uint32_t(a) << 24) | (uint32_t(b) << 16) |
-			(uint32_t(c) << 8) | uint32_t(d);
-	}
-
-	/**
-	 * Reads the raw, big-endian value.
-	 */
-	constexpr uint32_t raw() const noexcept {
-		uint32_t x = *this;
-		if (IsLittleEndian())
-			x = ByteSwap32(x);
-		return x;
-	}
-};
-
-static_assert(sizeof(PackedBE32) == sizeof(uint32_t), "Wrong size");
-static_assert(alignof(PackedBE32) == 1, "Wrong alignment");
-
-/**
- * A packed big-endian 64 bit integer.
- */
-class PackedBE64 {
-	uint8_t a, b, c, d, e, f, g, h;
-
-public:
-	PackedBE64() = default;
-
-	constexpr PackedBE64(uint64_t src) noexcept
-		:a(uint8_t(src >> 56)),
-		 b(uint8_t(src >> 48)),
-		 c(uint8_t(src >> 40)),
-		 d(uint8_t(src >> 32)),
-		 e(uint8_t(src >> 24)),
-		 f(uint8_t(src >> 16)),
-		 g(uint8_t(src >> 8)),
-		 h(uint8_t(src)) {}
-
-	/**
-	 * Construct an instance from an integer which is already
-	 * big-endian.
-	 */
-	static constexpr auto FromBE(uint64_t src) noexcept {
-		union {
-			uint64_t in;
-			PackedBE64 out;
-		} u{src};
-		return u.out;
-	}
-
-	constexpr operator uint64_t() const noexcept {
-		return (uint64_t(a) << 56) | (uint64_t(b) << 48) |
-			(uint64_t(c) << 40) | (uint64_t(d) << 32) |
-			(uint64_t(e) << 24) | (uint64_t(f) << 16) |
-			(uint64_t(g) << 8) | uint64_t(h);
-	}
-
-	/**
-	 * Reads the raw, big-endian value.
-	 */
-	constexpr uint64_t raw() const noexcept {
-		uint64_t x = *this;
-		if (IsLittleEndian())
-			x = ByteSwap64(x);
-		return x;
-	}
-};
-
-static_assert(sizeof(PackedBE64) == sizeof(uint64_t), "Wrong size");
-static_assert(alignof(PackedBE64) == 1, "Wrong alignment");
-
-/**
- * A packed little-endian 16 bit integer.
- */
-class PackedLE16 {
-	uint8_t lo, hi;
-
-public:
-	PackedLE16() = default;
-
-	constexpr PackedLE16(uint16_t src) noexcept
-		:lo(uint8_t(src)),
-		 hi(uint8_t(src >> 8)) {}
-
-	/**
-	 * Construct an instance from an integer which is already
-	 * little-endian.
-	 */
-	static constexpr auto FromLE(uint16_t src) noexcept {
-		union {
-			uint16_t in;
-			PackedLE16 out;
-		} u{src};
-		return u.out;
-	}
-
-	constexpr operator uint16_t() const noexcept {
-		return (uint16_t(hi) << 8) | uint16_t(lo);
-	}
-
-	PackedLE16 &operator=(uint16_t new_value) noexcept {
-		lo = uint8_t(new_value);
-		hi = uint8_t(new_value >> 8);
-		return *this;
-	}
-
-	/**
-	 * Reads the raw, little-endian value.
-	 */
-	constexpr uint16_t raw() const noexcept {
-		uint16_t x = *this;
-		if (IsBigEndian())
-			x = ByteSwap16(x);
-		return x;
-	}
-};
-
-static_assert(sizeof(PackedLE16) == sizeof(uint16_t), "Wrong size");
-static_assert(alignof(PackedLE16) == 1, "Wrong alignment");
-
-/**
- * A packed little-endian 32 bit integer.
- */
-class PackedLE32 {
-	uint8_t a, b, c, d;
-
-public:
-	PackedLE32() = default;
-
-	constexpr PackedLE32(uint32_t src) noexcept
-		:a(uint8_t(src)),
-		 b(uint8_t(src >> 8)),
-		 c(uint8_t(src >> 16)),
-		 d(uint8_t(src >> 24)) {}
-
-	/**
-	 * Construct an instance from an integer which is already
-	 * little-endian.
-	 */
-	static constexpr auto FromLE(uint32_t src) noexcept {
-		union {
-			uint32_t in;
-			PackedLE32 out;
-		} u{src};
-		return u.out;
-	}
-
-	constexpr operator uint32_t() const noexcept {
-		return uint32_t(a) | (uint32_t(b) << 8) |
-			(uint32_t(c) << 16) | (uint32_t(d) << 24);
-	}
-
-	PackedLE32 &operator=(uint32_t new_value) noexcept {
-		a = uint8_t(new_value);
-		b = uint8_t(new_value >> 8);
-		c = uint8_t(new_value >> 16);
-		d = uint8_t(new_value >> 24);
-		return *this;
-	}
-
-	/**
-	 * Reads the raw, little-endian value.
-	 */
-	constexpr uint32_t raw() const noexcept {
-		uint32_t x = *this;
-		if (IsBigEndian())
-			x = ByteSwap32(x);
-		return x;
-	}
-};
-
-static_assert(sizeof(PackedLE32) == sizeof(uint32_t), "Wrong size");
-static_assert(alignof(PackedLE32) == 1, "Wrong alignment");
-
-/**
- * A packed little-endian 64 bit integer.
- */
-class PackedLE64 {
-	uint8_t a, b, c, d, e, f, g, h;
-
-public:
-	PackedLE64() = default;
-
-	constexpr PackedLE64(uint64_t src) noexcept
-		:a(uint8_t(src)),
-		 b(uint8_t(src >> 8)),
-		 c(uint8_t(src >> 16)),
-		 d(uint8_t(src >> 24)),
-		 e(uint8_t(src >> 32)),
-		 f(uint8_t(src >> 40)),
-		 g(uint8_t(src >> 48)),
-		 h(uint8_t(src >> 56)) {}
-
-	/**
-	 * Construct an instance from an integer which is already
-	 * little-endian.
-	 */
-	static constexpr auto FromLE(uint64_t src) noexcept {
-		union {
-			uint64_t in;
-			PackedLE64 out;
-		} u{src};
-		return u.out;
-	}
-
-	constexpr operator uint64_t() const noexcept {
-		return uint64_t(a) | (uint64_t(b) << 8) |
-			(uint64_t(c) << 16) | (uint64_t(d) << 24) |
-			(uint64_t(e) << 32) | (uint64_t(f) << 40) |
-			(uint64_t(g) << 48) | (uint64_t(h) << 56);
-	}
-
-	/**
-	 * Reads the raw, big-endian value.
-	 */
-	constexpr uint64_t raw() const noexcept {
-		uint64_t x = *this;
-		if (IsBigEndian())
-			x = ByteSwap64(x);
-		return x;
-	}
-};
-
-static_assert(sizeof(PackedLE64) == sizeof(uint64_t), "Wrong size");
-static_assert(alignof(PackedLE64) == 1, "Wrong alignment");
diff --git a/src/util/PackedBigEndian.hxx b/src/util/PackedBigEndian.hxx
new file mode 100644
index 000000000..1933d3ded
--- /dev/null
+++ b/src/util/PackedBigEndian.hxx
@@ -0,0 +1,149 @@
+// SPDX-License-Identifier: BSD-2-Clause
+// author: Max Kellermann <max.kellermann@gmail.com>
+
+#pragma once
+
+#include "ByteOrder.hxx"
+
+#include <cstdint>
+
+/**
+ * A packed big-endian 16 bit integer.
+ */
+class PackedBE16 {
+	uint8_t hi, lo;
+
+public:
+	PackedBE16() = default;
+
+	constexpr PackedBE16(uint16_t src) noexcept
+		:hi(uint8_t(src >> 8)),
+		 lo(uint8_t(src)) {}
+
+	/**
+	 * Construct an instance from an integer which is already
+	 * big-endian.
+	 */
+	static constexpr auto FromBE(uint16_t src) noexcept {
+		union {
+			uint16_t in;
+			PackedBE16 out;
+		} u{src};
+		return u.out;
+	}
+
+	constexpr operator uint16_t() const noexcept {
+		return (uint16_t(hi) << 8) | uint16_t(lo);
+	}
+
+	/**
+	 * Reads the raw, big-endian value.
+	 */
+	constexpr uint16_t raw() const noexcept {
+		uint16_t x = *this;
+		if (IsLittleEndian())
+			x = ByteSwap16(x);
+		return x;
+	}
+};
+
+static_assert(sizeof(PackedBE16) == sizeof(uint16_t), "Wrong size");
+static_assert(alignof(PackedBE16) == 1, "Wrong alignment");
+
+/**
+ * A packed big-endian 32 bit integer.
+ */
+class PackedBE32 {
+	uint8_t a, b, c, d;
+
+public:
+	PackedBE32() = default;
+
+	constexpr PackedBE32(uint32_t src) noexcept
+		:a(uint8_t(src >> 24)),
+		 b(uint8_t(src >> 16)),
+		 c(uint8_t(src >> 8)),
+		 d(uint8_t(src)) {}
+
+	/**
+	 * Construct an instance from an integer which is already
+	 * big-endian.
+	 */
+	static constexpr auto FromBE(uint32_t src) noexcept {
+		union {
+			uint32_t in;
+			PackedBE32 out;
+		} u{src};
+		return u.out;
+	}
+
+	constexpr operator uint32_t() const noexcept {
+		return (uint32_t(a) << 24) | (uint32_t(b) << 16) |
+			(uint32_t(c) << 8) | uint32_t(d);
+	}
+
+	/**
+	 * Reads the raw, big-endian value.
+	 */
+	constexpr uint32_t raw() const noexcept {
+		uint32_t x = *this;
+		if (IsLittleEndian())
+			x = ByteSwap32(x);
+		return x;
+	}
+};
+
+static_assert(sizeof(PackedBE32) == sizeof(uint32_t), "Wrong size");
+static_assert(alignof(PackedBE32) == 1, "Wrong alignment");
+
+/**
+ * A packed big-endian 64 bit integer.
+ */
+class PackedBE64 {
+	uint8_t a, b, c, d, e, f, g, h;
+
+public:
+	PackedBE64() = default;
+
+	constexpr PackedBE64(uint64_t src) noexcept
+		:a(uint8_t(src >> 56)),
+		 b(uint8_t(src >> 48)),
+		 c(uint8_t(src >> 40)),
+		 d(uint8_t(src >> 32)),
+		 e(uint8_t(src >> 24)),
+		 f(uint8_t(src >> 16)),
+		 g(uint8_t(src >> 8)),
+		 h(uint8_t(src)) {}
+
+	/**
+	 * Construct an instance from an integer which is already
+	 * big-endian.
+	 */
+	static constexpr auto FromBE(uint64_t src) noexcept {
+		union {
+			uint64_t in;
+			PackedBE64 out;
+		} u{src};
+		return u.out;
+	}
+
+	constexpr operator uint64_t() const noexcept {
+		return (uint64_t(a) << 56) | (uint64_t(b) << 48) |
+			(uint64_t(c) << 40) | (uint64_t(d) << 32) |
+			(uint64_t(e) << 24) | (uint64_t(f) << 16) |
+			(uint64_t(g) << 8) | uint64_t(h);
+	}
+
+	/**
+	 * Reads the raw, big-endian value.
+	 */
+	constexpr uint64_t raw() const noexcept {
+		uint64_t x = *this;
+		if (IsLittleEndian())
+			x = ByteSwap64(x);
+		return x;
+	}
+};
+
+static_assert(sizeof(PackedBE64) == sizeof(uint64_t), "Wrong size");
+static_assert(alignof(PackedBE64) == 1, "Wrong alignment");
diff --git a/src/util/PackedLittleEndian.hxx b/src/util/PackedLittleEndian.hxx
new file mode 100644
index 000000000..8242a453e
--- /dev/null
+++ b/src/util/PackedLittleEndian.hxx
@@ -0,0 +1,163 @@
+// SPDX-License-Identifier: BSD-2-Clause
+// author: Max Kellermann <max.kellermann@gmail.com>
+
+#pragma once
+
+#include "ByteOrder.hxx"
+
+#include <cstdint>
+
+/**
+ * A packed little-endian 16 bit integer.
+ */
+class PackedLE16 {
+	uint8_t lo, hi;
+
+public:
+	PackedLE16() = default;
+
+	constexpr PackedLE16(uint16_t src) noexcept
+		:lo(uint8_t(src)),
+		 hi(uint8_t(src >> 8)) {}
+
+	/**
+	 * Construct an instance from an integer which is already
+	 * little-endian.
+	 */
+	static constexpr auto FromLE(uint16_t src) noexcept {
+		union {
+			uint16_t in;
+			PackedLE16 out;
+		} u{src};
+		return u.out;
+	}
+
+	constexpr operator uint16_t() const noexcept {
+		return (uint16_t(hi) << 8) | uint16_t(lo);
+	}
+
+	PackedLE16 &operator=(uint16_t new_value) noexcept {
+		lo = uint8_t(new_value);
+		hi = uint8_t(new_value >> 8);
+		return *this;
+	}
+
+	/**
+	 * Reads the raw, little-endian value.
+	 */
+	constexpr uint16_t raw() const noexcept {
+		uint16_t x = *this;
+		if (IsBigEndian())
+			x = ByteSwap16(x);
+		return x;
+	}
+};
+
+static_assert(sizeof(PackedLE16) == sizeof(uint16_t), "Wrong size");
+static_assert(alignof(PackedLE16) == 1, "Wrong alignment");
+
+/**
+ * A packed little-endian 32 bit integer.
+ */
+class PackedLE32 {
+	uint8_t a, b, c, d;
+
+public:
+	PackedLE32() = default;
+
+	constexpr PackedLE32(uint32_t src) noexcept
+		:a(uint8_t(src)),
+		 b(uint8_t(src >> 8)),
+		 c(uint8_t(src >> 16)),
+		 d(uint8_t(src >> 24)) {}
+
+	/**
+	 * Construct an instance from an integer which is already
+	 * little-endian.
+	 */
+	static constexpr auto FromLE(uint32_t src) noexcept {
+		union {
+			uint32_t in;
+			PackedLE32 out;
+		} u{src};
+		return u.out;
+	}
+
+	constexpr operator uint32_t() const noexcept {
+		return uint32_t(a) | (uint32_t(b) << 8) |
+			(uint32_t(c) << 16) | (uint32_t(d) << 24);
+	}
+
+	PackedLE32 &operator=(uint32_t new_value) noexcept {
+		a = uint8_t(new_value);
+		b = uint8_t(new_value >> 8);
+		c = uint8_t(new_value >> 16);
+		d = uint8_t(new_value >> 24);
+		return *this;
+	}
+
+	/**
+	 * Reads the raw, little-endian value.
+	 */
+	constexpr uint32_t raw() const noexcept {
+		uint32_t x = *this;
+		if (IsBigEndian())
+			x = ByteSwap32(x);
+		return x;
+	}
+};
+
+static_assert(sizeof(PackedLE32) == sizeof(uint32_t), "Wrong size");
+static_assert(alignof(PackedLE32) == 1, "Wrong alignment");
+
+/**
+ * A packed little-endian 64 bit integer.
+ */
+class PackedLE64 {
+	uint8_t a, b, c, d, e, f, g, h;
+
+public:
+	PackedLE64() = default;
+
+	constexpr PackedLE64(uint64_t src) noexcept
+		:a(uint8_t(src)),
+		 b(uint8_t(src >> 8)),
+		 c(uint8_t(src >> 16)),
+		 d(uint8_t(src >> 24)),
+		 e(uint8_t(src >> 32)),
+		 f(uint8_t(src >> 40)),
+		 g(uint8_t(src >> 48)),
+		 h(uint8_t(src >> 56)) {}
+
+	/**
+	 * Construct an instance from an integer which is already
+	 * little-endian.
+	 */
+	static constexpr auto FromLE(uint64_t src) noexcept {
+		union {
+			uint64_t in;
+			PackedLE64 out;
+		} u{src};
+		return u.out;
+	}
+
+	constexpr operator uint64_t() const noexcept {
+		return uint64_t(a) | (uint64_t(b) << 8) |
+			(uint64_t(c) << 16) | (uint64_t(d) << 24) |
+			(uint64_t(e) << 32) | (uint64_t(f) << 40) |
+			(uint64_t(g) << 48) | (uint64_t(h) << 56);
+	}
+
+	/**
+	 * Reads the raw, big-endian value.
+	 */
+	constexpr uint64_t raw() const noexcept {
+		uint64_t x = *this;
+		if (IsBigEndian())
+			x = ByteSwap64(x);
+		return x;
+	}
+};
+
+static_assert(sizeof(PackedLE64) == sizeof(uint64_t), "Wrong size");
+static_assert(alignof(PackedLE64) == 1, "Wrong alignment");