Hi.

I had this code ready for some time already but I was lacking the time
to post this to the list. We have this code running reliably in one of
our projects. This patch is against Dr. No and adds support for 24 bit
wav files and allows all rates in a reasonable range (8 - 192 kHz) as we
discussed on the list some time ago.

I don't know who's responsible for applying this to the HEAD. Please let
me know if some questions should arise or some adjustments regarding
coding style should become necessary and the maintainer can not perform
them autonomously.

Regards
Uli
--- xmms2-0.7DrNo/src/plugins/wave/wave.c	2010-02-19 18:43:54.000000000 +0100
+++ xmms2/src/plugins/wave/wave.c	2011-02-07 15:39:23.964206845 +0100
@@ -36,6 +36,8 @@
 	guint16 bits_per_sample;
 	guint header_size;
 	guint bytes_total;
+	gint32 read_buffer_size;
+	gchar* read_buffer;
 } xmms_wave_data_t;
 
 typedef enum {
@@ -167,7 +169,7 @@
 
 	data = g_new0 (xmms_wave_data_t, 1);
 	g_return_val_if_fail (data, FALSE);
-
+	data->read_buffer = NULL;
 	xmms_xform_private_data_set (xform, data);
 
 	read = xmms_xform_peek (xform, (gchar *) buf, sizeof (buf), &error);
@@ -199,8 +201,22 @@
 			/* skip over the header */
 			xmms_xform_read (xform, (gchar *) buf, data->header_size, &error);
 
-			sample_fmt = (data->bits_per_sample == 8 ? XMMS_SAMPLE_FORMAT_U8
-			                                         : XMMS_SAMPLE_FORMAT_S16);
+			/* set output sample format */
+			switch (data->bits_per_sample)
+			{
+				case 8:
+					sample_fmt = XMMS_SAMPLE_FORMAT_U8;
+					break;
+				case 16:
+					sample_fmt = XMMS_SAMPLE_FORMAT_S16;
+					break;
+				case 24:
+					sample_fmt = XMMS_SAMPLE_FORMAT_FLOAT;
+					break;
+				case 32:
+					xmms_log_error ("Wave currently supported only up to 24 bits.");
+					return FALSE;
+			}
 
 			xmms_xform_outdata_type_add (xform,
 			                             XMMS_STREAM_TYPE_MIMETYPE,
@@ -229,23 +245,63 @@
 	data = xmms_xform_private_data_get (xform);
 	g_return_val_if_fail (data, -1);
 
-	ret = xmms_xform_read (xform, (gchar *) buf, len, error);
-	if (ret <= 0) {
+	switch(data->bits_per_sample) {
+	case 8:
+		ret = xmms_xform_read (xform, (gchar *) buf, len, error);
 		return ret;
-	}
-
+	case 16:
+		{
+			ret = xmms_xform_read (xform, (gchar *) buf, len, error);
 #if G_BYTE_ORDER == G_BIG_ENDIAN
-	if (data->bits_per_sample == 16) {
-		gint16 *s = (gint16 *) buf;
-		gint i;
-
-		for (i = 0; i < (ret / 2); i++) {
-			s[i] = GINT16_FROM_LE (s[i]);
+			gint16 *s = (gint16 *) buf;
+			gint i;
+			for (i = 0; i < (ret / 2); i++) {
+				s[i] = GINT16_FROM_LE(s[i]);
+			}
+#endif
+			return ret;
 		}
-	}
+	case 24:
+		{
+			gint32 readbuffersize = len/sizeof(gfloat)*3;
+			if ( data->read_buffer == NULL ) {
+				data->read_buffer_size = readbuffersize;
+				data->read_buffer = g_malloc (readbuffersize);
+			} else if ( readbuffersize != data->read_buffer_size) {
+				data->read_buffer_size = readbuffersize;
+				data->read_buffer = g_realloc(data->read_buffer, readbuffersize);
+			}
+			/* read raw 24 bit samples into buffer */
+			ret = xmms_xform_read (xform, (gchar *) data->read_buffer, readbuffersize, error);
+			gint32 nSamples = ret/3;
+			gint32 sample;
+
+			/* convert samples first to signed 32 bit integer and subsequently to float. */
+			volatile gfloat *write = (gfloat*)buf+nSamples-1;
+			volatile gchar *read = data->read_buffer+3*nSamples-3;
+			for ( sample = nSamples; sample > 0; sample--, read -= 3, write -= 1 ) {
+				gchar sample24[sizeof(gint32)] = { 0 };
+				/* wav file is little endian by default. when converting to
+				32 bit we have to sign extend. */
+#if G_BYTE_ORDER == G_BIG_ENDIAN
+				sample24[0] = (read[2] & 0x80) != 0 ? 0xFF : 0x00;
+				sample24[1] = read[2];
+				sample24[2] = read[1];
+				sample24[3] = read[0];
+#else
+				sample24[3] = (read[2] & 0x80) != 0 ? 0xFF : 0x00;
+				sample24[2] = read[2];
+				sample24[1] = read[1];
+				sample24[0] = read[0];
 #endif
+				/* map 24 bit [-2^23 .. 2^23-1] to [+1.0 .. -1.0] float range */
+				*write = *((gint32*)sample24)/8388608.0;
+			}
+			return nSamples*sizeof(gfloat);
+		}
+	}
 
-	return ret;
+	return 0;
 }
 
 static gint64
@@ -306,7 +362,9 @@
 {
 	g_return_if_fail (xform);
 
-	g_free (xmms_xform_private_data_get (xform));
+	xmms_wave_data_t *data = xmms_xform_private_data_get (xform);
+	g_free (data->read_buffer);
+	g_free (data);
 }
 
 static xmms_wave_format_t
@@ -367,8 +425,7 @@
 
 			GET_32 (buf, data->samplerate);
 			XMMS_DBG ("samplerate %i", data->samplerate);
-			if (data->samplerate != 8000 && data->samplerate != 11025 &&
-			    data->samplerate != 22050 && data->samplerate != 44100) {
+			if (data->samplerate < 8000 || data->samplerate > 192000) {
 				xmms_log_error ("Invalid samplerate: %i", data->samplerate);
 				return WAVE_FORMAT_UNDEFINED;
 			}
@@ -378,7 +435,8 @@
 
 			GET_16 (buf, data->bits_per_sample);
 			XMMS_DBG ("bits per sample %i", data->bits_per_sample);
-			if (data->bits_per_sample != 8 && data->bits_per_sample != 16) {
+			if (data->bits_per_sample != 8 && data->bits_per_sample != 16
+					&& data->bits_per_sample != 24) {
 				xmms_log_error ("Unhandled bits per sample: %i",
 				                data->bits_per_sample);
 				return WAVE_FORMAT_UNDEFINED;
--
_______________________________________________
Xmms2-devel mailing list
Xmms2-devel@lists.xmms.se
http://lists.xmms.se/cgi-bin/mailman/listinfo/xmms2-devel

Reply via email to