From d1a8a4481e81b9c3716e02af8fa245740acfe97b Mon Sep 17 00:00:00 2001
From: Max Kellermann <max@duempel.org>
Date: Fri, 19 Sep 2014 21:40:22 +0200
Subject: [PATCH] decoder/sndfile: support float and 16 bit samples

Support these PCM formats natively, instead of letting libsndfile
convert everything to 32 bit.
---
 NEWS                                         |  2 ++
 src/decoder/plugins/SndfileDecoderPlugin.cxx | 36 +++++++++++++++++---
 2 files changed, 34 insertions(+), 4 deletions(-)

diff --git a/NEWS b/NEWS
index 0a68d3c80..305cc8841 100644
--- a/NEWS
+++ b/NEWS
@@ -50,6 +50,8 @@ ver 0.19 (not yet released)
   - dsf: fix noise at end of malformed file
   - sndfile: support scanning remote files
   - sndfile: support tags "comment", "album", "track", "genre"
+  - sndfile: native floating point playback
+  - sndfile: optimized 16 bit playback
   - mp4v2: support playback of MP4 files.
 * encoder:
   - shine: new encoder plugin
diff --git a/src/decoder/plugins/SndfileDecoderPlugin.cxx b/src/decoder/plugins/SndfileDecoderPlugin.cxx
index 388c2d594..74567cc82 100644
--- a/src/decoder/plugins/SndfileDecoderPlugin.cxx
+++ b/src/decoder/plugins/SndfileDecoderPlugin.cxx
@@ -150,14 +150,41 @@ gcc_pure
 static SampleFormat
 sndfile_sample_format(const SF_INFO &info)
 {
-	(void)info;
-	return SampleFormat::S32;
+	fprintf(stderr, "SNDFILE format=%d\n", info.format & SF_FORMAT_SUBMASK);
+
+	switch (info.format & SF_FORMAT_SUBMASK) {
+	case SF_FORMAT_PCM_S8:
+	case SF_FORMAT_PCM_U8:
+	case SF_FORMAT_PCM_16:
+		return SampleFormat::S16;
+
+	case SF_FORMAT_FLOAT:
+	case SF_FORMAT_DOUBLE:
+		return SampleFormat::FLOAT;
+
+	default:
+		return SampleFormat::S32;
+	}
 }
 
 static sf_count_t
-sndfile_read_frames(SNDFILE *sf, void *buffer, sf_count_t n_frames)
+sndfile_read_frames(SNDFILE *sf, SampleFormat format,
+		    void *buffer, sf_count_t n_frames)
 {
-	return sf_readf_int(sf, (int *)buffer, n_frames);
+	switch (format) {
+	case SampleFormat::S16:
+		return sf_readf_short(sf, (short *)buffer, n_frames);
+
+	case SampleFormat::S32:
+		return sf_readf_int(sf, (int *)buffer, n_frames);
+
+	case SampleFormat::FLOAT:
+		return sf_readf_float(sf, (float *)buffer, n_frames);
+
+	default:
+		assert(false);
+		gcc_unreachable();
+	}
 }
 
 static void
@@ -198,6 +225,7 @@ sndfile_stream_decode(Decoder &decoder, InputStream &is)
 	do {
 		sf_count_t num_frames =
 			sndfile_read_frames(sf,
+					    audio_format.format,
 					    buffer, read_frames);
 		if (num_frames <= 0)
 			break;