From b0ce5515232052010e6ac8fc64dac5014218c0b2 Mon Sep 17 00:00:00 2001
From: Max Kellermann <max@musicpd.org>
Date: Sat, 22 Apr 2017 08:47:49 +0200
Subject: [PATCH] decoder/pcm: support audio/L24

Closes #31
---
 NEWS                                     |  2 ++
 src/decoder/plugins/PcmDecoderPlugin.cxx | 23 ++++++++++++++++++++++-
 2 files changed, 24 insertions(+), 1 deletion(-)

diff --git a/NEWS b/NEWS
index ab7465171..2e86cc75e 100644
--- a/NEWS
+++ b/NEWS
@@ -4,6 +4,8 @@ ver 0.21 (not yet released)
   - "find" and "search" can sort
 * tags
   - new tag "OriginalDate"
+* decoder
+  - pcm: support audio/L24 (RFC 3190)
 * output
   - alsa: non-blocking mode
 
diff --git a/src/decoder/plugins/PcmDecoderPlugin.cxx b/src/decoder/plugins/PcmDecoderPlugin.cxx
index 421fc3a81..22e34c193 100644
--- a/src/decoder/plugins/PcmDecoderPlugin.cxx
+++ b/src/decoder/plugins/PcmDecoderPlugin.cxx
@@ -21,6 +21,7 @@
 #include "PcmDecoderPlugin.hxx"
 #include "../DecoderAPI.hxx"
 #include "CheckAudioFormat.hxx"
+#include "pcm/PcmPack.hxx"
 #include "input/InputStream.hxx"
 #include "system/ByteOrder.hxx"
 #include "util/Domain.hxx"
@@ -67,13 +68,18 @@ pcm_stream_decode(DecoderClient &client, InputStream &is)
 
 	const bool l16 = mime != nullptr &&
 		GetMimeTypeBase(mime) == "audio/L16";
+	const bool l24 = mime != nullptr &&
+		GetMimeTypeBase(mime) == "audio/L24";
 	const bool is_float = mime != nullptr &&
 		GetMimeTypeBase(mime) == "audio/x-mpd-float";
-	if (l16 || is_float) {
+	if (l16 || l24 || is_float) {
 		audio_format.sample_rate = 0;
 		audio_format.channels = 1;
 	}
 
+	if (l24)
+		audio_format.format = SampleFormat::S24_P32;
+
 	const bool reverse_endian = (l16 && IsLittleEndian()) ||
 		(mime != nullptr &&
 		 strcmp(mime, "audio/x-mpd-cdda-pcm-reverse") == 0);
@@ -149,6 +155,10 @@ pcm_stream_decode(DecoderClient &client, InputStream &is)
 
 	StaticFifoBuffer<uint8_t, 4096> buffer;
 
+	/* a buffer for pcm_unpack_24be() large enough to hold the
+	   results for a full source buffer */
+	int32_t unpack_buffer[buffer.GetCapacity() / 3];
+
 	DecoderCommand cmd;
 	do {
 		if (!FillBuffer(client, is, buffer))
@@ -166,6 +176,14 @@ pcm_stream_decode(DecoderClient &client, InputStream &is)
 			reverse_bytes_16((uint16_t *)r.data,
 					 (uint16_t *)r.data,
 					 (uint16_t *)(r.data + r.size));
+		else if (l24) {
+			/* convert big-endian packed 24 bit
+			   (audio/L24) to native-endian 24 bit (in 32
+			   bit integers) */
+			pcm_unpack_24be(unpack_buffer, r.begin(), r.end());
+			r.data = (uint8_t *)&unpack_buffer[0];
+			r.size = (r.size / 3) * 4;
+		}
 
 		cmd = !r.IsEmpty()
 			? client.SubmitData(is, r.data, r.size, 0)
@@ -192,6 +210,9 @@ static const char *const pcm_mime_types[] = {
 	/* RFC 2586 */
 	"audio/L16",
 
+	/* RFC 3190 */
+	"audio/L24",
+
 	/* MPD-specific: float32 native-endian */
 	"audio/x-mpd-float",