Browse Source

Fixed wave sound playback by using a different wave reader library (thanks Boolsheet!)

Resolves issue #25.
Alex Szpakowski 11 years ago
parent
commit
e5adc4e5fd

+ 49 - 1
platform/macosx/love-framework.xcodeproj/project.pbxproj

@@ -313,6 +313,16 @@
 		FAF272B816E3D46400CC193A /* threads.h in Headers */ = {isa = PBXBuildFile; fileRef = FAF272B416E3D46400CC193A /* threads.h */; };
 		FAF4376F17F4AC530074F9E2 /* Mesh.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FAF4376D17F4AC530074F9E2 /* Mesh.cpp */; };
 		FAF4377017F4AC530074F9E2 /* Mesh.h in Headers */ = {isa = PBXBuildFile; fileRef = FAF4376E17F4AC530074F9E2 /* Mesh.h */; };
+		FAF6704C18184FF800DBDEEA /* wuff.c in Sources */ = {isa = PBXBuildFile; fileRef = FAF6704418184FF800DBDEEA /* wuff.c */; };
+		FAF6704D18184FF800DBDEEA /* wuff.h in Headers */ = {isa = PBXBuildFile; fileRef = FAF6704518184FF800DBDEEA /* wuff.h */; };
+		FAF6704E18184FF800DBDEEA /* wuff_config.h in Headers */ = {isa = PBXBuildFile; fileRef = FAF6704618184FF800DBDEEA /* wuff_config.h */; };
+		FAF6704F18184FF800DBDEEA /* wuff_convert.c in Sources */ = {isa = PBXBuildFile; fileRef = FAF6704718184FF800DBDEEA /* wuff_convert.c */; };
+		FAF6705018184FF800DBDEEA /* wuff_convert.h in Headers */ = {isa = PBXBuildFile; fileRef = FAF6704818184FF800DBDEEA /* wuff_convert.h */; };
+		FAF6705118184FF800DBDEEA /* wuff_internal.c in Sources */ = {isa = PBXBuildFile; fileRef = FAF6704918184FF800DBDEEA /* wuff_internal.c */; };
+		FAF6705218184FF800DBDEEA /* wuff_internal.h in Headers */ = {isa = PBXBuildFile; fileRef = FAF6704A18184FF800DBDEEA /* wuff_internal.h */; };
+		FAF6705318184FF800DBDEEA /* wuff_memory.c in Sources */ = {isa = PBXBuildFile; fileRef = FAF6704B18184FF800DBDEEA /* wuff_memory.c */; };
+		FAF670561818501300DBDEEA /* WaveDecoder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FAF670541818501300DBDEEA /* WaveDecoder.cpp */; };
+		FAF670571818501300DBDEEA /* WaveDecoder.h in Headers */ = {isa = PBXBuildFile; fileRef = FAF670551818501300DBDEEA /* WaveDecoder.h */; };
 /* End PBXBuildFile section */
 
 /* Begin PBXFileReference section */
@@ -855,6 +865,16 @@
 		FAF272B416E3D46400CC193A /* threads.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = threads.h; sourceTree = "<group>"; };
 		FAF4376D17F4AC530074F9E2 /* Mesh.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Mesh.cpp; sourceTree = "<group>"; };
 		FAF4376E17F4AC530074F9E2 /* Mesh.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Mesh.h; sourceTree = "<group>"; };
+		FAF6704418184FF800DBDEEA /* wuff.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = wuff.c; sourceTree = "<group>"; };
+		FAF6704518184FF800DBDEEA /* wuff.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = wuff.h; sourceTree = "<group>"; };
+		FAF6704618184FF800DBDEEA /* wuff_config.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = wuff_config.h; sourceTree = "<group>"; };
+		FAF6704718184FF800DBDEEA /* wuff_convert.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = wuff_convert.c; sourceTree = "<group>"; };
+		FAF6704818184FF800DBDEEA /* wuff_convert.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = wuff_convert.h; sourceTree = "<group>"; };
+		FAF6704918184FF800DBDEEA /* wuff_internal.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = wuff_internal.c; sourceTree = "<group>"; };
+		FAF6704A18184FF800DBDEEA /* wuff_internal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = wuff_internal.h; sourceTree = "<group>"; };
+		FAF6704B18184FF800DBDEEA /* wuff_memory.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = wuff_memory.c; sourceTree = "<group>"; };
+		FAF670541818501300DBDEEA /* WaveDecoder.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = WaveDecoder.cpp; sourceTree = "<group>"; };
+		FAF670551818501300DBDEEA /* WaveDecoder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WaveDecoder.h; sourceTree = "<group>"; };
 /* End PBXFileReference section */
 
 /* Begin PBXFrameworksBuildPhase section */
@@ -1340,6 +1360,8 @@
 				77234CEE2EFE633537975850 /* Sound.h */,
 				727648E06CD863A2582F798F /* VorbisDecoder.cpp */,
 				4A80315175C5625804AA4A56 /* VorbisDecoder.h */,
+				FAF670541818501300DBDEEA /* WaveDecoder.cpp */,
+				FAF670551818501300DBDEEA /* WaveDecoder.h */,
 			);
 			path = lullaby;
 			sourceTree = "<group>";
@@ -1353,6 +1375,7 @@
 				3AED1DE005A53DDB07902760 /* luasocket */,
 				FA0354691731F3A700284828 /* noise1234 */,
 				36C14C81334735EC54E33637 /* utf8 */,
+				FAF6704318184FF800DBDEEA /* Wuff */,
 			);
 			name = libraries;
 			path = ../../src/libraries;
@@ -1362,11 +1385,11 @@
 			isa = PBXGroup;
 			children = (
 				43A258C229C75C15238E520C /* Decoder.h */,
+				47217E6729AC72E74FF651E3 /* lullaby */,
 				30ED4BB03C5F11254AF12E98 /* Sound.cpp */,
 				23985AB32E7B463A2CB87E2C /* Sound.h */,
 				0C5C6C6E47851D1308411DE6 /* SoundData.cpp */,
 				785D31764A1D6CDE21BC6404 /* SoundData.h */,
-				47217E6729AC72E74FF651E3 /* lullaby */,
 				5CF629B94C7802D446D61C45 /* wrap_Decoder.cpp */,
 				782A153A1E6314CB583250E0 /* wrap_Decoder.h */,
 				385902BD584E7D73154E4EBB /* wrap_Sound.cpp */,
@@ -1896,6 +1919,21 @@
 			path = sdl;
 			sourceTree = "<group>";
 		};
+		FAF6704318184FF800DBDEEA /* Wuff */ = {
+			isa = PBXGroup;
+			children = (
+				FAF6704418184FF800DBDEEA /* wuff.c */,
+				FAF6704518184FF800DBDEEA /* wuff.h */,
+				FAF6704618184FF800DBDEEA /* wuff_config.h */,
+				FAF6704718184FF800DBDEEA /* wuff_convert.c */,
+				FAF6704818184FF800DBDEEA /* wuff_convert.h */,
+				FAF6704918184FF800DBDEEA /* wuff_internal.c */,
+				FAF6704A18184FF800DBDEEA /* wuff_internal.h */,
+				FAF6704B18184FF800DBDEEA /* wuff_memory.c */,
+			);
+			path = Wuff;
+			sourceTree = "<group>";
+		};
 /* End PBXGroup section */
 
 /* Begin PBXHeadersBuildPhase section */
@@ -1908,6 +1946,7 @@
 				FAF272A716E3D44400CC193A /* LuaThread.h in Headers */,
 				FAF4377017F4AC530074F9E2 /* Mesh.h in Headers */,
 				FAF272A916E3D44400CC193A /* ThreadModule.h in Headers */,
+				FAF6705018184FF800DBDEEA /* wuff_convert.h in Headers */,
 				FAF272AB16E3D44400CC193A /* wrap_Channel.h in Headers */,
 				FAF272AD16E3D44400CC193A /* wrap_LuaThread.h in Headers */,
 				FAF272AF16E3D44400CC193A /* wrap_ThreadModule.h in Headers */,
@@ -1919,6 +1958,7 @@
 				FAE010DD170DDE99006F29D0 /* ddsparse.h in Headers */,
 				FAE010E1170DE25E006F29D0 /* ddsHandler.h in Headers */,
 				FAE010E5170DF75C006F29D0 /* wrap_CompressedData.h in Headers */,
+				FAF6704E18184FF800DBDEEA /* wuff_config.h in Headers */,
 				FA0CDE3D1710F9A50056E8D7 /* FormatHandler.h in Headers */,
 				FA7AA59317F6AC1F00704BE2 /* wrap_Mesh.h in Headers */,
 				FAEC808B1710FEA60057279A /* ImageData.h in Headers */,
@@ -1934,6 +1974,7 @@
 				FA3D9E1616E6D41100CA6630 /* wrap_Cursor.h in Headers */,
 				FA9FC0B1173D6E3E005027FF /* wrap_Window.h in Headers */,
 				FAB007901740C12D00A9664D /* Joystick.h in Headers */,
+				FAF6705218184FF800DBDEEA /* wuff_internal.h in Headers */,
 				FAB007941740C28900A9664D /* Joystick.h in Headers */,
 				FAB007981740C87D00A9664D /* wrap_Joystick.h in Headers */,
 				FAC5710117402D1100D147E4 /* BezierCurve.h in Headers */,
@@ -1946,11 +1987,13 @@
 				FA5FDC831788D548002F0ED2 /* types.h in Headers */,
 				FA5FDC841788D548002F0ED2 /* unix.h in Headers */,
 				FA5FDC851788D548002F0ED2 /* utility.h in Headers */,
+				FAF6704D18184FF800DBDEEA /* wuff.h in Headers */,
 				FA5FDC861788D548002F0ED2 /* win32.h in Headers */,
 				FA5FDC8F1788D548002F0ED2 /* lua-enet.h in Headers */,
 				FA7175AA178E8418001FE7FE /* lua.h in Headers */,
 				FA34E7CE174B513F00E04D3F /* System.h in Headers */,
 				FA34E7D3174B515D00E04D3F /* System.h in Headers */,
+				FAF670571818501300DBDEEA /* WaveDecoder.h in Headers */,
 				FA1EF7C51799FEB200FF380C /* wrap_System.h in Headers */,
 				FA435EA417B36E9C004C3F22 /* Polyline.h in Headers */,
 			);
@@ -2022,6 +2065,7 @@
 				FA08F5B316C752FC00F007B5 /* Audio.cpp in Sources */,
 				FA08F5B416C7530100F007B5 /* Audio.cpp in Sources */,
 				FA08F5B516C7530100F007B5 /* Source.cpp in Sources */,
+				FAF6705118184FF800DBDEEA /* wuff_internal.c in Sources */,
 				FA08F5B616C7530A00F007B5 /* Audio.cpp in Sources */,
 				FA08F5B716C7530A00F007B5 /* Pool.cpp in Sources */,
 				FA08F5B816C7530A00F007B5 /* Source.cpp in Sources */,
@@ -2032,6 +2076,7 @@
 				FA08F5BE16C7532A00F007B5 /* Memoizer.cpp in Sources */,
 				FA08F5BF16C7532A00F007B5 /* Module.cpp in Sources */,
 				FA08F5C016C7532A00F007B5 /* Object.cpp in Sources */,
+				FAF670561818501300DBDEEA /* WaveDecoder.cpp in Sources */,
 				FA08F5C116C7532A00F007B5 /* Reference.cpp in Sources */,
 				FA08F5C216C7532A00F007B5 /* runtime.cpp in Sources */,
 				FA08F5C316C7532A00F007B5 /* utf8.cpp in Sources */,
@@ -2077,6 +2122,7 @@
 				FA08F5EB16C7538F00F007B5 /* b2Joint.cpp in Sources */,
 				FA08F5EC16C7538F00F007B5 /* b2MouseJoint.cpp in Sources */,
 				FA08F5ED16C7538F00F007B5 /* b2PrismaticJoint.cpp in Sources */,
+				FAF6705318184FF800DBDEEA /* wuff_memory.c in Sources */,
 				FA08F5EE16C7538F00F007B5 /* b2PulleyJoint.cpp in Sources */,
 				FA08F5EF16C7538F00F007B5 /* b2RevoluteJoint.cpp in Sources */,
 				FA08F5F016C7538F00F007B5 /* b2RopeJoint.cpp in Sources */,
@@ -2086,6 +2132,7 @@
 				FA08F5F416C753A400F007B5 /* luasocket.cpp in Sources */,
 				FA08F5F516C753B800F007B5 /* auxiliar.c in Sources */,
 				FA08F5F616C753B800F007B5 /* buffer.c in Sources */,
+				FAF6704F18184FF800DBDEEA /* wuff_convert.c in Sources */,
 				FA08F5F716C753B800F007B5 /* except.c in Sources */,
 				FA08F5F816C753B800F007B5 /* inet.c in Sources */,
 				FA08F5F916C753B800F007B5 /* io.c in Sources */,
@@ -2175,6 +2222,7 @@
 				FA08F65016C7546400F007B5 /* RopeJoint.cpp in Sources */,
 				FA08F65116C7546400F007B5 /* Shape.cpp in Sources */,
 				FA08F65216C7547300F007B5 /* WeldJoint.cpp in Sources */,
+				FAF6704C18184FF800DBDEEA /* wuff.c in Sources */,
 				FA08F65316C7547300F007B5 /* WheelJoint.cpp in Sources */,
 				FA08F65416C7547300F007B5 /* World.cpp in Sources */,
 				FA08F65516C7547300F007B5 /* wrap_Body.cpp in Sources */,

+ 203 - 0
src/libraries/Wuff/wuff.c

@@ -0,0 +1,203 @@
+#include <string.h>
+
+#include "wuff_config.h"
+#include "wuff.h"
+#include "wuff_internal.h"
+
+
+wuff_sint32 wuff_open(struct wuff_handle ** handle_pointer, struct wuff_callback * callback, void * userdata)
+{
+	struct wuff_handle * handle;
+	wuff_sint32 wuff_status;
+
+	if (handle_pointer == NULL || callback == NULL)
+		return WUFF_INVALID_PARAM;
+
+	handle = wuff_alloc(sizeof(struct wuff_handle));
+	if (handle == NULL)
+		return WUFF_MEMALLOC_ERROR;
+
+	memset(handle, 0, sizeof(struct wuff_handle));
+	handle->buffer.data = NULL;
+	handle->callback = callback;
+	handle->userdata = userdata;
+
+	wuff_status = wuff_setup(handle);
+	if (wuff_status < 0)
+	{
+		wuff_cleanup(handle);
+		return wuff_status;
+	}
+
+	*handle_pointer = handle;
+
+	return WUFF_SUCCESS;
+}
+
+wuff_sint32 wuff_close(struct wuff_handle * handle)
+{
+	wuff_sint32 wuff_status;
+
+	if (handle == NULL)
+		return WUFF_INVALID_PARAM;
+
+	wuff_status = wuff_cleanup(handle);
+	WUFF_STATUS_BAIL()
+
+	return WUFF_SUCCESS;
+}
+
+wuff_sint32 wuff_seek(struct wuff_handle * handle, wuff_uint64 offset)
+{
+	wuff_sint32 wuff_status;
+	wuff_uint64 seek_offset;
+
+	if (handle == NULL)
+		return WUFF_INVALID_PARAM;
+
+	/* Clamp offset to stream length. */
+	offset = offset <= handle->stream.length ? offset : handle->stream.length;
+	seek_offset = offset * handle->stream.header.block_size;
+	wuff_status = handle->callback->seek(handle->userdata, handle->stream.data.offset + seek_offset);
+	WUFF_STATUS_BAIL()
+
+	handle->stream.position = offset;
+	handle->output.block_offset = 0;
+
+	/* A new position requires an empty buffer. */
+	wuff_status = wuff_buffer_clear(handle);
+	WUFF_STATUS_BAIL()
+
+	return	WUFF_SUCCESS;
+}
+
+wuff_sint32 wuff_tell(struct wuff_handle * handle, wuff_uint64 * offset)
+{
+	if (handle == NULL)
+		return WUFF_INVALID_PARAM;
+
+	*offset = handle->stream.position;
+
+	return	WUFF_SUCCESS;
+}
+
+wuff_sint32 wuff_stream_info(struct wuff_handle * handle, struct wuff_info * info)
+{
+	if (handle == NULL || info == NULL)
+		return WUFF_INVALID_PARAM;
+
+	info->format = handle->stream.format;
+	info->channels = handle->stream.header.channels;
+	info->sample_rate = handle->stream.header.sample_rate;
+	info->bits_per_sample = handle->stream.header.bits_per_sample;
+	info->length = handle->stream.length;
+	/* Think about adding channel mapping and perhaps other things. */
+
+	return WUFF_SUCCESS;
+}
+
+wuff_sint32 wuff_format(struct wuff_handle * handle, wuff_uint16 format)
+{
+	wuff_sint32 wuff_status;
+
+	if (handle == NULL)
+		return WUFF_INVALID_PARAM;
+	else if (format >= WUFF_FORMAT_MAX)
+		return WUFF_FORMAT_UNSUPPORTED;
+
+	/* A format change resets the position to the start of the block. */
+	wuff_status = wuff_seek(handle, handle->stream.position);
+	WUFF_STATUS_BAIL()
+
+	wuff_status = wuff_set_output_format(handle, format);
+	WUFF_STATUS_BAIL()
+
+	return WUFF_SUCCESS;
+}
+
+wuff_sint32 wuff_read(struct wuff_handle * handle, wuff_uint8 * out_buffer, size_t * out_size)
+{
+	size_t current_offset;
+	size_t r_samples, num_samples;
+	wuff_uint8 head_offset, head, tail, sample_size;
+	wuff_uint8 * in_buffer;
+	wuff_sint32 wuff_status;
+
+	if (handle == NULL || out_buffer == NULL || out_size == NULL)
+		return WUFF_INVALID_PARAM;
+
+	if (*out_size == 0)
+		return WUFF_SUCCESS;
+
+	sample_size = (wuff_uint8)handle->output.bytes_per_sample;
+
+	/* Calculating the number of samples that fit into the application buffer. */
+	/* The first and last sample may be truncated. */
+	current_offset = handle->output.block_offset;
+	head_offset = current_offset % sample_size;
+	head = head_offset == 0 ? 0 : sample_size - head_offset;
+	num_samples = wuff_calculate_samples(*out_size, sample_size, &head, &tail);
+
+	/* Requesting the number of samples from the buffer. */
+	/* Calculate the new sample count if necessary and write the output. */
+	r_samples = num_samples;
+	wuff_status = wuff_buffer_request(handle, &in_buffer, &r_samples);
+	WUFF_STATUS_BAIL()
+	else if (r_samples == 0)
+	{
+		/* Possible EOF. */
+		*out_size = 0;
+	}
+	else
+	{
+		if (r_samples == 1 && head != 0)
+		{
+			/* Only the first truncated sample fits. */
+			/* I really hope nobody will use small buffers like this. */
+			num_samples = 0;
+			tail = 0;
+		}
+		else
+		{
+			/* At this point the first (possibly truncated) sample will be fully written. */
+			/* Subtract the first and last sample from the count if they're truncated. */
+			if (r_samples < num_samples)
+				tail = 0;
+			num_samples = r_samples - !!head - !!tail;
+		}
+
+		handle->output.function(out_buffer, in_buffer, num_samples, head_offset, head, tail);
+
+		/* Report the number of bytes written. */
+		*out_size = num_samples * sample_size + head + tail;
+
+		/* Adjust the block offset and sample position. */
+		current_offset += *out_size;
+		if (current_offset >= handle->output.block_size)
+		{
+			handle->stream.position += current_offset / handle->output.block_size;
+			handle->output.block_offset = current_offset % handle->output.block_size;
+		}
+		else
+		{
+			handle->output.block_offset = current_offset;
+		}
+
+		/* Release the fully processed samples from the buffer. */
+		wuff_status = wuff_buffer_release(handle, head_offset + head == sample_size ? num_samples + 1 : num_samples);
+		WUFF_STATUS_BAIL()
+	}
+
+	return WUFF_SUCCESS;
+}
+
+void wuff_version(struct wuff_version * version)
+{
+	if (version == NULL)
+		return;
+
+	version->major = WUFF_VERSION_MAJOR;
+	version->minor = WUFF_VERSION_MINOR;
+	version->build = WUFF_VERSION_BUILD;
+	version->revision = WUFF_VERSION_REVISION;
+}

+ 274 - 0
src/libraries/Wuff/wuff.h

@@ -0,0 +1,274 @@
+/*
+ *	Wuff - A very basic WAVE reader
+ */
+
+#ifndef WUFF_H
+#define WUFF_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+#define WUFF_VERSION_MAJOR         0
+#define WUFF_VERSION_MINOR         0
+#define WUFF_VERSION_BUILD         0
+#define WUFF_VERSION_REVISION      2
+
+
+#ifndef WUFF_API_OVERRIDE
+	#if defined(_WIN32) && defined(WUFF_DYNAMIC_LIB)
+		#define WUFF_EXPORT __declspec(dllexport)
+		#define WUFF_IMPORT __declspec(dllimport)
+	#else
+		#define WUFF_EXPORT
+		#define WUFF_IMPORT
+	#endif
+#endif
+
+#ifdef WUFF_BUILDING_CORE
+	#define WUFF_API WUFF_EXPORT
+#else
+	#define WUFF_API WUFF_IMPORT
+#endif
+
+
+#ifdef WUFF_FORCE_STDINT_H
+	#include <stdint.h>
+typedef uint8_t             wuff_uint8;
+typedef int8_t              wuff_sint8;
+typedef uint16_t            wuff_uint16;
+typedef int16_t             wuff_sint16;
+typedef uint32_t            wuff_uint32;
+typedef int32_t             wuff_sint32;
+	#ifdef WUFF_NO_64BIT_TYPE
+typedef uint32_t            wuff_uint64;
+typedef int32_t             wuff_sint64;
+	#else
+typedef uint64_t            wuff_uint64;
+typedef int64_t             wuff_sint64;
+	#endif
+#elif _MSC_VER
+typedef unsigned __int8     wuff_uint8;
+typedef signed __int8       wuff_sint8;
+typedef unsigned __int16    wuff_uint16;
+typedef signed __int16      wuff_sint16;
+typedef unsigned __int32    wuff_uint32;
+typedef signed __int32      wuff_sint32;
+typedef unsigned __int64    wuff_uint64;
+typedef signed __int64      wuff_sint64;
+#else
+typedef unsigned char       wuff_uint8;
+typedef signed char         wuff_sint8;
+typedef unsigned short      wuff_uint16;
+typedef signed short        wuff_sint16;
+typedef unsigned int        wuff_uint32;
+typedef signed int          wuff_sint32;
+	#ifdef WUFF_NO_64BIT_TYPE
+typedef unsigned long       wuff_uint64;
+typedef signed long         wuff_sint64;
+	#else
+typedef unsigned long long  wuff_uint64;
+typedef signed long long    wuff_sint64;
+	#endif
+#endif
+
+/** @file */
+/**
+ * Opaque structure used to identify the open Wuff streams.
+ */
+struct wuff_handle;
+
+/**
+ * Callbacks that control the delivery of the data of the WAVE file.
+ *
+ * The return values of the functions indicate their status. A zero or positive
+ * value means success and a negative value failure. The macros WUFF_SUCCESS and
+ * WUFF_ERROR, or a value equal or below WUFF_USER_ERROR can be used. The error
+ * value will be returned by the function called by the application.
+ */
+struct wuff_callback
+{
+	/**
+	 * The read callback requests the linking application to write at least
+	 * 'size' bytes into the memory where 'buffer' is pointing to. The value
+	 * pointed to by 'size' must be update to the actual number of bytes
+	 * written. Zero will be interepreted as the end-of-file.
+	 *
+	 * @param userdata The userdata set with wuff_open.
+	 * @param buffer A pointer to the memory where the data can be written to.
+	 * @param size A pointer to the size of the buffer and the bytes written.
+	 */
+	wuff_sint32 (* read)(void * userdata, wuff_uint8 * buffer, size_t * size);
+
+	/**
+	 * The seek callback requests the linking application to seek to a new byte
+	 * offset in the WAVE data. The next call to the read callback must then
+	 * write data starting from this position. The offset is always relative
+	 * to the beginning of the WAVE data.
+	 *
+	 * @param userdata The userdata set with wuff_open.
+	 * @param offset The new offset.
+	 */
+	wuff_sint32 (* seek)(void * userdata, wuff_uint64 offset);
+
+	/**
+	 * The tell callback requests the linking application to write the current
+	 * byte position to the integer pointed to by 'offset'.
+	 *
+	 * @param userdata The userdata set with wuff_open.
+	 * @param offset A pointer to an integer where the current position can be written to.
+	 */
+	wuff_sint32 (* tell)(void * userdata, wuff_uint64 * offset);
+};
+
+
+/**
+ * Stream information structure.
+ */
+struct wuff_info
+{
+	wuff_uint16 format;           /**< The format of the stream.
+	                               *   See "Wuff raw sample formats" below. */
+	wuff_uint16 channels;         /**< The number of channels in the stream. */
+	wuff_uint32 sample_rate;      /**< The sample rate in hertz. */
+	wuff_uint16 bits_per_sample;  /**< The number of bits per sample. */
+	wuff_uint64 length;           /**< The length of the stream in samples. */
+};
+
+
+/**
+ * Version information structure.
+ */
+struct wuff_version
+{
+	wuff_uint16 major;
+	wuff_uint16 minor;
+	wuff_uint16 build;
+	wuff_uint16 revision;
+};
+
+
+/**
+ * Opens a new Wuff stream. This will read from the callbacks immediately, make
+ * sure they're ready. It will check if the WAVE file is supported.
+ *
+ * @param handle A pointer to pointer of a wuff_handle that will be
+ * initialized if the function succeeds.
+ * @param callback The callbacks for the data of the WAVE file.
+ * @param userdata A void pointer that will be passed to the callbacks.
+ * @return Returns a negative value if an error occured.
+ */
+WUFF_API wuff_sint32 wuff_open(struct wuff_handle ** handle, struct wuff_callback * callback, void * userdata);
+
+/**
+ * Closes a Wuff stream.
+ *
+ * @param handle The Wuff stream handle.
+ * @return Returns a negative value if an error occured.
+ */
+WUFF_API wuff_sint32 wuff_close(struct wuff_handle * handle);
+
+/**
+ * Fills the wuff_info struct with information about the stream.
+ *
+ * @param handle The Wuff stream handle.
+ * @param info A pointer to a wuff_info struct.
+ * @return Returns a negative value if an error occured.
+ */
+WUFF_API wuff_sint32 wuff_stream_info(struct wuff_handle * handle, struct wuff_info * info);
+
+/**
+ * Sets the output format of the decoder. A new format resets the decoder output
+ * to the beginning of the current block (the sample of the first channel).
+ *
+ * @param handle The Wuff stream handle.
+ * @param format The new output format.
+ * @return Returns a negative value if an error occured.
+ */
+WUFF_API wuff_sint32 wuff_format(struct wuff_handle * handle, wuff_uint16 format);
+
+/**
+ * Decodes samples to the passed memory location. The size_t pointer points to
+ * the maximum number of bytes that can be written to the buffer. This count
+ * will be adjusted to the number of bytes written to the buffer.
+ *
+ * @param handle The Wuff stream handle.
+ * @param buffer The buffer to write to.
+ * @param size The maximum number of bytes to write to the buffer.
+ * @return Returns a negative value if an error occured.
+ */
+WUFF_API wuff_sint32 wuff_read(struct wuff_handle * handle, wuff_uint8 * buffer, size_t * size);
+
+/**
+ * Seeks to a sample location.
+ * The next call to wuff_read will return samples starting from this position.
+ *
+ * @param handle The Wuff stream handle.
+ * @param offset The sample offset to seek to.
+ * @return Returns a negative value if an error occured.
+ */
+WUFF_API wuff_sint32 wuff_seek(struct wuff_handle * handle, wuff_uint64 offset);
+
+/**
+ * Sets the current position.
+ *
+ * @param handle The Wuff stream handle.
+ * @param offset A pointer to a integer that will receive the sample offset.
+ * @return Returns a negative value if an error occured.
+ */
+WUFF_API wuff_sint32 wuff_tell(struct wuff_handle * handle, wuff_uint64 * offset);
+
+/**
+ * Copies the Wuff version of the binary into the struct.
+ * For compile-time version information use the WUFF_VERSION_MAJOR,
+ * WUFF_VERSION_MINOR, WUFF_VERSION_BUILD, and WUFF_VERSION_REVISION macros.
+ *
+ * @param version A pointer to a wuff_version struct that will receive the
+ * version information.
+ */
+WUFF_API void wuff_version(struct wuff_version * version);
+
+
+/* Wuff raw sample formats. */
+#define WUFF_FORMAT_PCM_U8                             0
+#define WUFF_FORMAT_PCM_S16                            1
+#define WUFF_FORMAT_PCM_S24                            2
+#define WUFF_FORMAT_PCM_S32                            3
+#define WUFF_FORMAT_IEEE_FLOAT_32                      4
+#define WUFF_FORMAT_IEEE_FLOAT_64                      5
+#define WUFF_FORMAT_MAX                                6
+
+
+/* Success and error return values for all functions. */
+#define WUFF_STREAM_EOF                              100
+
+#define WUFF_SUCCESS                                   0
+
+#define WUFF_ERROR                                    -1
+#define WUFF_INVALID_PARAM                            -2
+#define WUFF_MEMALLOC_ERROR                           -3
+
+#define WUFF_STREAM_NOT_RIFF                        -100
+#define WUFF_STREAM_NOT_WAVE                        -101
+#define WUFF_STREAM_INVALID                         -102
+#define WUFF_STREAM_ZERO_CHANNELS                   -103
+#define WUFF_STREAM_ZERO_SAMPLE_RATE                -104
+#define WUFF_STREAM_ZERO_BITS_PER_SAMPLE            -105
+#define WUFF_STREAM_FORMAT_CHUNK_MISSING            -106
+#define WUFF_STREAM_DATA_CHUNK_MISSING              -107
+#define WUFF_STREAM_CHUNK_NOT_FOUND                 -108
+
+#define WUFF_FORMAT_UNSUPPORTED                     -200
+
+#define WUFF_BUFFER_INVALID_SIZE                    -300
+#define WUFF_BUFFER_INVALID_STREAM_POSITION         -301
+
+#define WUFF_USER_ERROR                           -10000
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* WUFF_H */

+ 50 - 0
src/libraries/Wuff/wuff_config.h

@@ -0,0 +1,50 @@
+#ifndef WUFF_CONFIG_H
+#define WUFF_CONFIG_H
+
+/* Defines that the internal code is being built. */
+/* The wuff.h header uses this to change export and import macros. */
+#define WUFF_BUILDING_CORE
+
+#ifndef WUFF_INLINE_OVERRIDE
+	#ifdef __cplusplus
+		#define WUFF_INLINE inline
+	#else
+		#ifdef _MSC_VER
+			#define WUFF_INLINE __inline
+		#elif __GNUC__
+			#define WUFF_INLINE __inline__
+		#else
+			#define WUFF_INLINE
+		#endif
+	#endif
+#endif
+
+
+#ifndef WUFF_GCC_VISIBILITY_OVERRIDE
+	#if __GNUC__ >= 4
+		#define WUFF_INTERN_API __attribute__((visibility("hidden")))
+	#else
+		#define WUFF_INTERN_API
+    #endif
+#endif
+
+
+#ifdef WUFF_MEMALLOC_OVERRIDE
+	#ifdef __cplusplus
+extern "C" {
+	#endif
+
+	/* Define your own memory allocator. */
+	void * wuff_alloc(size_t size);
+	void wuff_free(void * mem);
+
+	#ifdef __cplusplus
+}
+	#endif
+#else
+WUFF_INTERN_API void * wuff_alloc(size_t size);
+WUFF_INTERN_API void wuff_free(void * mem);
+#endif
+
+
+#endif /* WUFF_CONFIG_H */

+ 827 - 0
src/libraries/Wuff/wuff_convert.c

@@ -0,0 +1,827 @@
+#include <string.h>
+
+#include "wuff_config.h"
+#include "wuff.h"
+#include "wuff_convert.h"
+
+/*
+ * int8 functions.
+ */
+
+WUFF_CONV_FUNC(wuff_int8_to_int8)
+{
+	(void)offset;
+	memcpy(dst, src, samples + head + tail);
+}
+
+WUFF_CONV_FUNC(wuff_int8_to_int16)
+{
+	wuff_sint16 i16;
+	size_t i;
+
+	if (head != 0)
+	{
+		i16 = (src[0] - 128) << 8;
+		memcpy(dst, (wuff_uint8 *)&i16 + offset, head);
+		src += 1;
+		dst += head;
+	}
+
+	for (i = 0; i < samples; i++)
+	{
+		i16 = (src[i] - 128) << 8;
+		memcpy(dst + i * 2, &i16, 2);
+	}
+
+	if (tail != 0)
+	{
+		i16 = (src[samples] - 128) << 8;
+		memcpy(dst + samples * 2, &i16, tail);
+	}
+}
+
+WUFF_CONV_FUNC(wuff_int8_to_int24)
+{
+	wuff_sint32 i24;
+	size_t i;
+
+	if (head != 0)
+	{
+		i24 = (src[0] - 128) << 24;
+		memcpy(dst, (wuff_uint8 *)&i24 + 1 + offset, head);
+		src += 1;
+		dst += head;
+	}
+
+	for (i = 0; i < samples; i++)
+	{
+		i24 = (src[i] - 128) << 24;
+		memcpy(dst + i * 3, (wuff_uint8 *)&i24 + 1, 3);
+	}
+
+	if (tail != 0)
+	{
+		i24 = (src[samples] - 128) << 24;
+		memcpy(dst + samples * 3, (wuff_uint8 *)&i24 + 1, tail);
+	}
+}
+
+WUFF_CONV_FUNC(wuff_int8_to_int32)
+{
+	wuff_sint32 i32;
+	size_t i;
+
+	if (head != 0)
+	{
+		i32 = (src[0] - 128) << 24;
+		memcpy(dst, (wuff_uint8 *)&i32 + offset, head);
+		src += 1;
+		dst += head;
+	}
+
+	for (i = 0; i < samples; i++)
+	{
+		i32 = (src[i] - 128) << 24;
+		memcpy(dst + i * 4, &i32, 4);
+	}
+
+	if (tail != 0)
+	{
+		i32 = (src[samples] - 128) << 24;
+		memcpy(dst + samples * 4, &i32, tail);
+	}
+}
+
+WUFF_CONV_FUNC(wuff_int8_to_float32)
+{
+	float f32;
+	size_t i;
+
+	if (head != 0)
+	{
+		f32 = (float)(src[0] - 128) / 128.0f;
+		memcpy(dst, (wuff_uint8 *)&f32 + offset, head);
+		src += 1;
+		dst += head;
+	}
+
+	for (i = 0; i < samples; i++)
+	{
+		f32 = (float)(src[i] - 128) / 128.0f;
+		memcpy(dst + i * 4, &f32, 4);
+	}
+
+	if (tail != 0)
+	{
+		f32 = (float)(src[samples] - 128) / 128.0f;
+		memcpy(dst + samples * 4, &f32, tail);
+	}
+}
+
+WUFF_CONV_FUNC(wuff_int8_to_float64)
+{
+	double f64;
+	size_t i;
+
+	if (head != 0)
+	{
+		f64 = (double)(src[0] - 128) / 128.0;
+		memcpy(dst, (wuff_uint8 *)&f64 + offset, head);
+		src += 1;
+		dst += head;
+	}
+
+	for (i = 0; i < samples; i++)
+	{
+		f64 = (double)(src[i] - 128) / 128.0;
+		memcpy(dst + i * 8, &f64, 8);
+	}
+
+	if (tail != 0)
+	{
+		f64 = (double)(src[samples] - 128) / 128.0;
+		memcpy(dst + samples * 8, &f64, tail);
+	}
+}
+
+/*
+ * int16 functions.
+ */
+
+WUFF_CONV_FUNC(wuff_int16_to_int8)
+{
+	wuff_sint16 i16;
+	size_t i;
+	(void)offset; (void)head; (void)tail;
+
+	for (i = 0; i < samples; i++)
+	{
+		memcpy(&i16, src + i * 2, 2);
+		dst[i] = (i16 >> 8) + 128;
+	}
+}
+
+WUFF_CONV_FUNC(wuff_int16_to_int16)
+{
+	memcpy(dst, src + offset, samples * 2 + head + tail);
+}
+
+WUFF_CONV_FUNC(wuff_int16_to_int24)
+{
+	wuff_sint16 i16;
+	wuff_sint32 i24;
+	size_t i;
+
+	if (head != 0)
+	{
+		memcpy(&i16, src, 2);
+		i24 = i16 << 16;
+		memcpy(dst, (wuff_uint8 *)&i24 + 1 + offset, head);
+		src += 2;
+		dst += head;
+	}
+
+	for (i = 0; i < samples; i++)
+	{
+		memcpy(&i16, src + i * 2, 2);
+		i24 = i16 << 16;
+		memcpy(dst + i * 3, (wuff_uint8 *)&i24 + 1, 3);
+	}
+
+	if (tail != 0)
+	{
+		memcpy(&i16, src + samples * 2, 2);
+		i24 = i16 << 16;
+		memcpy(dst + samples * 3, (wuff_uint8 *)&i24 + 1, tail);
+	}
+}
+
+WUFF_CONV_FUNC(wuff_int16_to_int32)
+{
+	wuff_sint16 i16;
+	wuff_sint32 i32;
+	size_t i;
+
+	if (head != 0)
+	{
+		memcpy(&i16, src, 2);
+		i32 = i16 << 16;
+		memcpy(dst, (wuff_uint8 *)&i32 + offset, head);
+		src += 2;
+		dst += head;
+	}
+
+	for (i = 0; i < samples; i++)
+	{
+		memcpy(&i16, src + i * 2, 2);
+		i32 = i16 << 16;
+		memcpy(dst + i * 4, &i32, 4);
+	}
+
+	if (tail != 0)
+	{
+		memcpy(&i16, src + samples * 2, 2);
+		i32 = i16 << 16;
+		memcpy(dst + samples * 4, &i32, tail);
+	}
+}
+
+WUFF_CONV_FUNC(wuff_int16_to_float32)
+{
+	wuff_sint16 i16;
+	float f32;
+	size_t i;
+
+	if (head != 0)
+	{
+		memcpy(&i16, src, 2);
+		f32 = (float)i16 / 32768.0f;
+		memcpy(dst, (wuff_uint8 *)&f32 + offset, head);
+		src += 2;
+		dst += head;
+	}
+
+	for (i = 0; i < samples; i++)
+	{
+		memcpy(&i16, src + i * 2, 2);
+		f32 = (float)i16 / 32768.0f;
+		memcpy(dst + i * 4, &f32, 4);
+	}
+
+	if (tail != 0)
+	{
+		memcpy(&i16, src + samples * 2, 2);
+		f32 = (float)i16 / 32768.0f;
+		memcpy(dst + samples * 4, &f32, tail);
+	}
+}
+
+WUFF_CONV_FUNC(wuff_int16_to_float64)
+{
+	wuff_sint16 i16;
+	double f64;
+	size_t i;
+
+	if (head != 0)
+	{
+		memcpy(&i16, src, 2);
+		f64 = (double)i16  / 32768.0;
+		memcpy(dst, (wuff_uint8 *)&f64 + offset, head);
+		src += 2;
+		dst += head;
+	}
+
+	for (i = 0; i < samples; i++)
+	{
+		memcpy(&i16, src + i * 2, 2);
+		f64 = (double)i16  / 32768.0;
+		memcpy(dst + i * 8, &f64, 8);
+	}
+
+	if (tail != 0)
+	{
+		memcpy(&i16, src + samples * 2, 2);
+		f64 = (double)i16 / 32768.0;
+		memcpy(dst + samples * 8, &f64, tail);
+	}
+}
+
+/*
+ * int24 functions.
+ */
+
+WUFF_CONV_FUNC(wuff_int24_to_int8)
+{
+	wuff_sint32 i24 = 0;
+	size_t i;
+	(void)offset; (void)head; (void)tail;
+
+	for (i = 0; i < samples; i++)
+	{
+		memcpy((wuff_uint8 *)&i24 + 1, src + i * 3, 3);
+		dst[i] = (wuff_uint8)((i24 >> 16) + 128);
+	}
+}
+
+WUFF_CONV_FUNC(wuff_int24_to_int16)
+{
+	size_t i;
+
+	if (head != 0)
+	{
+		memcpy(dst, src + 1 + offset, head);
+		src += 3;
+		dst += head;
+	}
+
+	for (i = 0; i < samples; i++)
+	{
+		memcpy(dst + i * 2, src + 1 + i * 3, 2);
+	}
+
+	if (tail != 0)
+	{
+		memcpy(dst + samples * 2, src + 1 + samples * 3, tail);
+	}
+}
+
+WUFF_CONV_FUNC(wuff_int24_to_int24)
+{
+	memcpy(dst, src + offset, samples * 3 + head + tail);
+}
+
+WUFF_CONV_FUNC(wuff_int24_to_int32)
+{
+	wuff_sint32 i32 = 0;
+	size_t i;
+
+	if (head != 0)
+	{
+		memcpy((wuff_uint8 *)&i32 + 1, src, 3);
+		memcpy(dst, (wuff_uint8 *)&i32 + offset, head);
+		src += 3;
+		dst += head;
+	}
+
+	for (i = 0; i < samples; i++)
+	{
+		memcpy((wuff_uint8 *)&i32 + 1, src + i * 3, 3);
+		memcpy(dst + i * 4, &i32, 4);
+	}
+
+	if (tail != 0)
+	{
+		memcpy((wuff_uint8 *)&i32 + 1, src + samples * 3, 3);
+		memcpy(dst + samples * 4, &i32, tail);
+	}
+}
+
+WUFF_CONV_FUNC(wuff_int24_to_float32)
+{
+	wuff_sint32 i24 = 0;
+	float f32;
+	size_t i;
+
+	if (head != 0)
+	{
+		memcpy((wuff_uint8 *)&i24 + 1, src, 3);
+		f32 = (float)((double)i24 / 2147483648.0);
+		memcpy(dst, (wuff_uint8 *)&f32 + offset, head);
+		src += 3;
+		dst += head;
+	}
+
+	for (i = 0; i < samples; i++)
+	{
+		memcpy((wuff_uint8 *)&i24 + 1, src + i * 3, 3);
+		f32 = (float)((double)i24 / 2147483648.0);
+		memcpy(dst + i * 4, &f32, 4);
+	}
+
+	if (tail != 0)
+	{
+		memcpy((wuff_uint8 *)&i24 + 1, src + samples * 3, 3);
+		f32 = (float)((double)i24 / 2147483648.0);
+		memcpy(dst + samples * 4, &f32, tail);
+	}
+}
+
+WUFF_CONV_FUNC(wuff_int24_to_float64)
+{
+	wuff_sint32 i24 = 0;
+	double f64;
+	size_t i;
+
+	if (head != 0)
+	{
+		memcpy((wuff_uint8 *)&i24 + 1, src, 3);
+		f64 = (double)i24 / 2147483648.0;
+		memcpy(dst, (wuff_uint8 *)&f64 + offset, head);
+		src += 3;
+		dst += head;
+	}
+
+	for (i = 0; i < samples; i++)
+	{
+		memcpy((wuff_uint8 *)&i24 + 1, src + i * 3, 3);
+		f64 = (double)i24 / 2147483648.0;
+		memcpy(dst + i * 8, &f64, 8);
+	}
+
+	if (tail != 0)
+	{
+		memcpy((wuff_uint8 *)&i24 + 1, src + samples * 3, 3);
+		f64 = (double)i24 / 2147483648.0;
+		memcpy(dst + samples * 8, &f64, tail);
+	}
+}
+
+/*
+ * int32 functions.
+ */
+
+WUFF_CONV_FUNC(wuff_int32_to_int8)
+{
+	wuff_sint32 i32 = 0;
+	size_t i;
+	(void)offset; (void)head; (void)tail;
+
+	for (i = 0; i < samples; i++)
+	{
+		memcpy(&i32, src + i * 4, 4);
+		dst[i] = (i32 >> 24) + 128;
+	}
+}
+
+WUFF_CONV_FUNC(wuff_int32_to_int16)
+{
+	size_t i;
+
+	if (head != 0)
+	{
+		memcpy(dst, src + 2 + offset, head);
+		src += 4;
+		dst += head;
+	}
+
+	for (i = 0; i < samples; i++)
+	{
+		memcpy(dst + i * 2, src + 2 + i * 4, 2);
+	}
+
+	if (tail != 0)
+	{
+		memcpy(dst + samples * 2, src + 2 + samples * 4, tail);
+	}
+}
+
+WUFF_CONV_FUNC(wuff_int32_to_int24)
+{
+	size_t i;
+
+	if (head != 0)
+	{
+		memcpy(dst, src + 1 + offset, head);
+		src += 4;
+		dst += head;
+	}
+
+	for (i = 0; i < samples; i++)
+	{
+		memcpy(dst + i * 3, src + 1 + i * 4, 3);
+	}
+
+	if (tail != 0)
+	{
+		memcpy(dst + samples * 3, src + 1 + samples * 4, tail);
+	}
+}
+
+WUFF_CONV_FUNC(wuff_int32_to_int32)
+{
+	memcpy(dst, src + offset, samples * 4 + head + tail);
+}
+
+WUFF_CONV_FUNC(wuff_int32_to_float32)
+{
+	wuff_sint32 i32;
+	float f32;
+	size_t i;
+
+	if (head != 0)
+	{
+		memcpy(&i32, src, 4);
+		f32 = (float)((double)i32 / 2147483648.0);
+		memcpy(dst, (wuff_uint8 *)&f32 + offset, head);
+		src += 4;
+		dst += head;
+	}
+
+	for (i = 0; i < samples; i++)
+	{
+		memcpy(&i32, src + i * 4, 4);
+		f32 = (float)((double)i32 / 2147483648.0);
+		memcpy(dst + i * 4, &f32, 4);
+	}
+
+	if (tail != 0)
+	{
+		memcpy(&i32, src + samples * 4, 4);
+		f32 = (float)((double)i32 / 2147483648.0);
+		memcpy(dst + samples * 4, &f32, tail);
+	}
+}
+
+WUFF_CONV_FUNC(wuff_int32_to_float64)
+{
+	wuff_sint32 i32;
+	double f64;
+	size_t i;
+
+	if (head != 0)
+	{
+		memcpy(&i32, src, 4);
+		f64 = (double)i32 / 2147483648.0;
+		memcpy(dst, (wuff_uint8 *)&f64 + offset, head);
+		src += 4;
+		dst += head;
+	}
+
+	for (i = 0; i < samples; i++)
+	{
+		memcpy(&i32, src + i * 4, 4);
+		f64 = (double)i32 / 2147483648.0;
+		memcpy(dst + i * 8, &f64, 8);
+	}
+
+	if (tail != 0)
+	{
+		memcpy(&i32, src + samples * 4, 4);
+		f64 = (double)i32 / 2147483648.0;
+		memcpy(dst + samples * 8, &f64, tail);
+	}
+}
+
+/*
+ * float32 functions.
+ */
+
+WUFF_CONV_FUNC(wuff_float32_to_int8)
+{
+	float f32;
+	size_t i;
+	(void)offset; (void)head; (void)tail;
+
+	for (i = 0; i < samples; i++)
+	{
+		memcpy(&f32, src + i * 4, 4);
+		dst[i] = (wuff_uint8)((f32 * 127.5f) + 128.0f);
+	}
+}
+
+WUFF_CONV_FUNC(wuff_float32_to_int16)
+{
+	float f32;
+	wuff_sint16 i16;
+	size_t i;
+
+	if (head != 0)
+	{
+		memcpy(&f32, src, 4);
+		i16 = (wuff_sint16)(f32 * 32767.5f);
+		memcpy(dst, (wuff_uint8 *)&i16 + offset, head);
+		src += 4;
+		dst += head;
+	}
+
+	for (i = 0; i < samples; i++)
+	{
+		memcpy(&f32, src + i * 4, 4);
+		i16 = (wuff_sint16)(f32 * 32767.5f);
+		memcpy(dst + i * 2, &i16, 2);
+	}
+
+	if (tail != 0)
+	{
+		memcpy(&f32, src + i * 4, 4);
+		i16 = (wuff_sint16)(f32 * 32767.5f);
+		memcpy(dst + i * 2, &i16, tail);
+	}
+}
+
+WUFF_CONV_FUNC(wuff_float32_to_int24)
+{
+	float f32;
+	wuff_sint32 i24;
+	size_t i;
+
+	if (head != 0)
+	{
+		memcpy(&f32, src, 4);
+		i24 = (wuff_sint32)((double)f32 * 2147483647.5);
+		memcpy(dst, (wuff_uint8 *)&i24 + 1 + offset, head);
+		src += 4;
+		dst += head;
+	}
+
+	for (i = 0; i < samples; i++)
+	{
+		memcpy(&f32, src + i * 4, 4);
+		i24 = (wuff_sint32)((double)f32 * 2147483647.5);
+		memcpy(dst + i * 3, (wuff_uint8 *)&i24 + 1, 3);
+	}
+
+	if (tail != 0)
+	{
+		memcpy(&f32, src + samples * 4, 4);
+		i24 = (wuff_sint32)((double)f32 * 2147483647.5);
+		memcpy(dst + samples * 3, (wuff_uint8 *)&i24 + 1, tail);
+	}
+}
+
+WUFF_CONV_FUNC(wuff_float32_to_int32)
+{
+	float f32;
+	wuff_sint32 i32;
+	size_t i;
+
+	if (head != 0)
+	{
+		memcpy(&f32, src, 4);
+		i32 = (wuff_sint32)((double)f32 * 2147483647.5);
+		memcpy(dst, (wuff_uint8 *)&i32 + offset, head);
+		src += 4;
+		dst += head;
+	}
+
+	for (i = 0; i < samples; i++)
+	{
+		memcpy(&f32, src + i * 4, 4);
+		i32 = (wuff_sint32)((double)f32 * 2147483647.5);
+		memcpy(dst + i * 4, &i32, 4);
+	}
+
+	if (tail != 0)
+	{
+		memcpy(&f32, src + samples * 4, 4);
+		i32 = (wuff_sint32)((double)f32 * 2147483647.5);
+		memcpy(dst + samples * 4, &i32, tail);
+	}
+}
+
+WUFF_CONV_FUNC(wuff_float32_to_float32)
+{
+	memcpy(dst, src + offset, samples * 4 + head + tail);
+}
+
+WUFF_CONV_FUNC(wuff_float32_to_float64)
+{
+	float f32;
+	double f64;
+	size_t i;
+
+	if (head != 0)
+	{
+		memcpy(&f32, src, 4);
+		f64 = f32;
+		memcpy(dst, (wuff_uint8 *)&f64 + offset, head);
+		src += 4;
+		dst += head;
+	}
+
+	for (i = 0; i < samples; i++)
+	{
+		memcpy(&f32, src + i * 4, 4);
+		f64 = f32;
+		memcpy(dst + i * 8, &f64, 8);
+	}
+
+	if (tail != 0)
+	{
+		memcpy(&f32, src + samples * 4, 4);
+		f64 = f32;
+		memcpy(dst + samples * 8, &f64, tail);
+	}
+}
+
+/*
+ * float64 functions.
+ */
+
+WUFF_CONV_FUNC(wuff_float64_to_int8)
+{
+	double f64;
+	size_t i;
+	(void)offset; (void)head; (void)tail;
+
+	for (i = 0; i < samples; i++)
+	{
+		memcpy(&f64, src + i * 8, 8);
+		dst[i] = (wuff_uint8)((f64 * 127.5) + 128.0);
+	}
+}
+
+WUFF_CONV_FUNC(wuff_float64_to_int16)
+{
+	double f64;
+	wuff_sint16 i16;
+	size_t i;
+
+	if (head != 0)
+	{
+		memcpy(&f64, src, 8);
+		i16 = (wuff_sint16)(f64 * 32767.5);
+		memcpy(dst, (wuff_uint8 *)&i16 + offset, head);
+		src += 8;
+		dst += head;
+	}
+
+	for (i = 0; i < samples; i++)
+	{
+		memcpy(&f64, src + i * 8, 8);
+		i16 = (wuff_sint16)(f64 * 32767.5);
+		memcpy(dst + i * 2, &i16, 2);
+	}
+
+	if (tail != 0)
+	{
+		memcpy(&f64, src + i * 8, 8);
+		i16 = (wuff_sint16)(f64 * 32767.5);
+		memcpy(dst + i * 2, &i16, tail);
+	}
+}
+
+WUFF_CONV_FUNC(wuff_float64_to_int24)
+{
+	double f64;
+	wuff_sint32 i24;
+	size_t i;
+
+	if (head != 0)
+	{
+		memcpy(&f64, src, 8);
+		i24 = (wuff_sint32)(f64 * 2147483647.5);
+		memcpy(dst, (wuff_uint8 *)&i24 + 1 + offset, head);
+		src += 8;
+		dst += head;
+	}
+
+	for (i = 0; i < samples; i++)
+	{
+		memcpy(&f64, src + i * 8, 8);
+		i24 = (wuff_sint32)(f64 * 2147483647.5);
+		memcpy(dst + i * 3, (wuff_uint8 *)&i24 + 1, 3);
+	}
+
+	if (tail != 0)
+	{
+		memcpy(&f64, src + samples * 8, 8);
+		i24 = (wuff_sint32)(f64 * 2147483647.5);
+		memcpy(dst + samples * 3, (wuff_uint8 *)&i24 + 1, tail);
+	}
+}
+
+WUFF_CONV_FUNC(wuff_float64_to_int32)
+{
+	double f64;
+	wuff_sint32 i32;
+	size_t i;
+
+	if (head != 0)
+	{
+		memcpy(&f64, src, 8);
+		i32 = (wuff_sint32)(f64 * 2147483647.5);
+		memcpy(dst, (wuff_uint8 *)&i32 + offset, head);
+		src += 8;
+		dst += head;
+	}
+
+	for (i = 0; i < samples; i++)
+	{
+		memcpy(&f64, src + i * 8, 8);
+		i32 = (wuff_sint32)(f64 * 2147483647.5);
+		memcpy(dst + i * 4, &i32, 4);
+	}
+
+	if (tail != 0)
+	{
+		memcpy(&f64, src + samples * 8, 8);
+		i32 = (wuff_sint32)(f64 * 2147483647.5);
+		memcpy(dst + samples * 4, &i32, tail);
+	}
+}
+
+WUFF_CONV_FUNC(wuff_float64_to_float32)
+{
+	double f64;
+	float f32;
+	size_t i;
+
+	if (head != 0)
+	{
+		memcpy(&f64, src, 8);
+		f32 = (float)f64;
+		memcpy(dst, (wuff_uint8 *)&f32 + offset, head);
+		src += 8;
+		dst += head;
+	}
+
+	for (i = 0; i < samples; i++)
+	{
+		memcpy(&f64, src + i * 8, 8);
+		f32 = (float)f64;
+		memcpy(dst + i * 4, &f32, 4);
+	}
+
+	if (tail != 0)
+	{
+		memcpy(&f64, src + samples * 8, 8);
+		f32 = (float)f64;
+		memcpy(dst + samples * 4, &f32, tail);
+	}
+}
+
+WUFF_CONV_FUNC(wuff_float64_to_float64)
+{
+	memcpy(dst, src + offset, samples * 8 + head + tail);
+}

+ 48 - 0
src/libraries/Wuff/wuff_convert.h

@@ -0,0 +1,48 @@
+#ifndef WUFF_CONVERT_H
+#define WUFF_CONVERT_H
+
+#define WUFF_CONV_FUNC(name) WUFF_INTERN_API void name(wuff_uint8 * dst, wuff_uint8 * src, size_t samples, wuff_uint8 offset, wuff_uint8 head, wuff_uint8 tail)
+
+WUFF_CONV_FUNC(wuff_int8_to_int8);
+WUFF_CONV_FUNC(wuff_int8_to_int16);
+WUFF_CONV_FUNC(wuff_int8_to_int24);
+WUFF_CONV_FUNC(wuff_int8_to_int32);
+WUFF_CONV_FUNC(wuff_int8_to_float32);
+WUFF_CONV_FUNC(wuff_int8_to_float64);
+
+WUFF_CONV_FUNC(wuff_int16_to_int8);
+WUFF_CONV_FUNC(wuff_int16_to_int16);
+WUFF_CONV_FUNC(wuff_int16_to_int24);
+WUFF_CONV_FUNC(wuff_int16_to_int32);
+WUFF_CONV_FUNC(wuff_int16_to_float32);
+WUFF_CONV_FUNC(wuff_int16_to_float64);
+
+WUFF_CONV_FUNC(wuff_int24_to_int8);
+WUFF_CONV_FUNC(wuff_int24_to_int16);
+WUFF_CONV_FUNC(wuff_int24_to_int24);
+WUFF_CONV_FUNC(wuff_int24_to_int32);
+WUFF_CONV_FUNC(wuff_int24_to_float32);
+WUFF_CONV_FUNC(wuff_int24_to_float64);
+
+WUFF_CONV_FUNC(wuff_int32_to_int8);
+WUFF_CONV_FUNC(wuff_int32_to_int16);
+WUFF_CONV_FUNC(wuff_int32_to_int24);
+WUFF_CONV_FUNC(wuff_int32_to_int32);
+WUFF_CONV_FUNC(wuff_int32_to_float32);
+WUFF_CONV_FUNC(wuff_int32_to_float64);
+
+WUFF_CONV_FUNC(wuff_float32_to_int8);
+WUFF_CONV_FUNC(wuff_float32_to_int16);
+WUFF_CONV_FUNC(wuff_float32_to_int24);
+WUFF_CONV_FUNC(wuff_float32_to_int32);
+WUFF_CONV_FUNC(wuff_float32_to_float32);
+WUFF_CONV_FUNC(wuff_float32_to_float64);
+
+WUFF_CONV_FUNC(wuff_float64_to_int8);
+WUFF_CONV_FUNC(wuff_float64_to_int16);
+WUFF_CONV_FUNC(wuff_float64_to_int24);
+WUFF_CONV_FUNC(wuff_float64_to_int32);
+WUFF_CONV_FUNC(wuff_float64_to_float32);
+WUFF_CONV_FUNC(wuff_float64_to_float64);
+
+#endif /* WUFF_CONVERT_H */

+ 540 - 0
src/libraries/Wuff/wuff_internal.c

@@ -0,0 +1,540 @@
+#include <stdlib.h>
+#include <string.h>
+
+#include "wuff_config.h"
+#include "wuff.h"
+#include "wuff_internal.h"
+#include "wuff_convert.h"
+
+
+wuff_sint32 wuff_setup(struct wuff_handle * handle)
+{
+	wuff_sint32 wuff_status;
+
+	if (handle == NULL)
+		return WUFF_INVALID_PARAM;
+
+	wuff_status = wuff_init_stream(handle);
+	WUFF_STATUS_BAIL()
+
+	/* Allocating the buffer for the handle requires information from the stream. */
+	wuff_status = wuff_buffer_alloc(handle);
+	WUFF_STATUS_BAIL()
+
+	/* The output format defaults to the stream format. */
+	wuff_status = wuff_format(handle, handle->stream.format);
+	WUFF_STATUS_BAIL()
+
+	return WUFF_SUCCESS;
+}
+
+wuff_sint32 wuff_cleanup(struct wuff_handle * handle)
+{
+	if (handle == NULL)
+		return WUFF_INVALID_PARAM;
+
+	if (handle->buffer.data != NULL)
+		wuff_free(handle->buffer.data);
+	wuff_free(handle);
+
+	return WUFF_SUCCESS;
+}
+
+wuff_sint32 wuff_set_output_format(struct wuff_handle * handle, wuff_uint16 format)
+{
+	wuff_uint16 bits;
+	wuff_uint16 stream_format;
+
+	if (handle == NULL)
+		return WUFF_INVALID_PARAM;
+	else if (format >= WUFF_FORMAT_MAX)
+		return WUFF_FORMAT_UNSUPPORTED;
+
+	stream_format = handle->stream.format;
+
+	switch (format)
+	{
+	case WUFF_FORMAT_PCM_U8:
+		bits = 8;
+		switch (stream_format)
+		{
+		case WUFF_FORMAT_PCM_U8:
+			handle->output.function = wuff_int8_to_int8;
+			break;
+		case WUFF_FORMAT_PCM_S16:
+			handle->output.function = wuff_int16_to_int8;
+			break;
+		case WUFF_FORMAT_PCM_S24:
+			handle->output.function = wuff_int24_to_int8;
+			break;
+		case WUFF_FORMAT_PCM_S32:
+			handle->output.function = wuff_int32_to_int8;
+			break;
+		case WUFF_FORMAT_IEEE_FLOAT_32:
+			handle->output.function = wuff_float32_to_int8;
+			break;
+		case WUFF_FORMAT_IEEE_FLOAT_64:
+			handle->output.function = wuff_float64_to_int8;
+			break;
+		}
+		break;
+	case WUFF_FORMAT_PCM_S16:
+		bits = 16;
+		switch (stream_format)
+		{
+		case WUFF_FORMAT_PCM_U8:
+			handle->output.function = wuff_int8_to_int16;
+			break;
+		case WUFF_FORMAT_PCM_S16:
+			handle->output.function = wuff_int16_to_int16;
+			break;
+		case WUFF_FORMAT_PCM_S24:
+			handle->output.function = wuff_int24_to_int16;
+			break;
+		case WUFF_FORMAT_PCM_S32:
+			handle->output.function = wuff_int32_to_int16;
+			break;
+		case WUFF_FORMAT_IEEE_FLOAT_32:
+			handle->output.function = wuff_float32_to_int16;
+			break;
+		case WUFF_FORMAT_IEEE_FLOAT_64:
+			handle->output.function = wuff_float64_to_int16;
+			break;
+		}
+		break;
+	case WUFF_FORMAT_PCM_S24:
+		bits = 24;
+		switch (stream_format)
+		{
+		case WUFF_FORMAT_PCM_U8:
+			handle->output.function = wuff_int8_to_int24;
+			break;
+		case WUFF_FORMAT_PCM_S16:
+			handle->output.function = wuff_int16_to_int24;
+			break;
+		case WUFF_FORMAT_PCM_S24:
+			handle->output.function = wuff_int24_to_int24;
+			break;
+		case WUFF_FORMAT_PCM_S32:
+			handle->output.function = wuff_int32_to_int24;
+			break;
+		case WUFF_FORMAT_IEEE_FLOAT_32:
+			handle->output.function = wuff_float32_to_int24;
+			break;
+		case WUFF_FORMAT_IEEE_FLOAT_64:
+			handle->output.function = wuff_float64_to_int24;
+			break;
+		}
+		break;
+	case WUFF_FORMAT_PCM_S32:
+		bits = 32;
+		switch (stream_format)
+		{
+		case WUFF_FORMAT_PCM_U8:
+			handle->output.function = wuff_int8_to_int32;
+			break;
+		case WUFF_FORMAT_PCM_S16:
+			handle->output.function = wuff_int16_to_int32;
+			break;
+		case WUFF_FORMAT_PCM_S24:
+			handle->output.function = wuff_int24_to_int32;
+			break;
+		case WUFF_FORMAT_PCM_S32:
+			handle->output.function = wuff_int32_to_int32;
+			break;
+		case WUFF_FORMAT_IEEE_FLOAT_32:
+			handle->output.function = wuff_float32_to_int32;
+			break;
+		case WUFF_FORMAT_IEEE_FLOAT_64:
+			handle->output.function = wuff_float64_to_int32;
+			break;
+		}
+		break;
+	case WUFF_FORMAT_IEEE_FLOAT_32:
+		bits = 32;
+		switch (stream_format)
+		{
+		case WUFF_FORMAT_PCM_U8:
+			handle->output.function = wuff_int8_to_float32;
+			break;
+		case WUFF_FORMAT_PCM_S16:
+			handle->output.function = wuff_int16_to_float32;
+			break;
+		case WUFF_FORMAT_PCM_S24:
+			handle->output.function = wuff_int24_to_float32;
+			break;
+		case WUFF_FORMAT_PCM_S32:
+			handle->output.function = wuff_int32_to_float32;
+			break;
+		case WUFF_FORMAT_IEEE_FLOAT_32:
+			handle->output.function = wuff_float32_to_float32;
+			break;
+		case WUFF_FORMAT_IEEE_FLOAT_64:
+			handle->output.function = wuff_float64_to_float32;
+			break;
+		}
+		break;
+	case WUFF_FORMAT_IEEE_FLOAT_64:
+		bits = 64;
+		switch (stream_format)
+		{
+		case WUFF_FORMAT_PCM_U8:
+			handle->output.function = wuff_int8_to_float64;
+			break;
+		case WUFF_FORMAT_PCM_S16:
+			handle->output.function = wuff_int16_to_float64;
+			break;
+		case WUFF_FORMAT_PCM_S24:
+			handle->output.function = wuff_int24_to_float64;
+			break;
+		case WUFF_FORMAT_PCM_S32:
+			handle->output.function = wuff_int32_to_float64;
+			break;
+		case WUFF_FORMAT_IEEE_FLOAT_32:
+			handle->output.function = wuff_float32_to_float64;
+			break;
+		case WUFF_FORMAT_IEEE_FLOAT_64:
+			handle->output.function = wuff_float64_to_float64;
+			break;
+		}
+		break;
+	default:
+		return WUFF_FORMAT_UNSUPPORTED;
+	}
+
+	handle->output.format = format;
+	handle->output.bytes_per_sample = bits / 8;
+	handle->output.block_size = handle->stream.header.channels * (bits / 8);
+
+	return WUFF_SUCCESS;
+}
+
+wuff_sint32 wuff_check_bits(wuff_uint16 bits, wuff_uint16 * format)
+{
+	if (*format == WUFF_FORMAT_PCM)
+	{
+		switch (bits)
+		{
+		case 8:
+			*format = WUFF_FORMAT_PCM_U8;
+			break;
+		case 16:
+			*format = WUFF_FORMAT_PCM_S16;
+			break;
+		case 24:
+			*format = WUFF_FORMAT_PCM_S24;
+			break;
+		case 32:
+			*format = WUFF_FORMAT_PCM_S32;
+			break;
+		default:
+			return WUFF_FORMAT_UNSUPPORTED;
+		}
+	}
+	else if (*format == WUFF_FORMAT_IEEE_FLOAT)
+	{
+		switch (bits)
+		{
+		case 32:
+			*format = WUFF_FORMAT_IEEE_FLOAT_32;
+			break;
+		case 64:
+			*format = WUFF_FORMAT_IEEE_FLOAT_64;
+			break;
+		default:
+			return WUFF_FORMAT_UNSUPPORTED;
+		}
+	}
+	else
+	{
+		return WUFF_FORMAT_UNSUPPORTED;
+	}
+
+	return WUFF_SUCCESS;
+}
+
+size_t wuff_calculate_samples(size_t target_size, wuff_uint8 sample_size, wuff_uint8 * head, wuff_uint8 * tail)
+{
+	size_t samples = 0;
+
+	if (*head != 0)
+	{
+		if (target_size <= *head)
+		{
+			*head = (wuff_uint8)target_size;
+			*tail = 0;
+			return 1;
+		}
+		target_size -= *head;
+		++samples;
+	}
+
+	samples = target_size / sample_size;
+	*tail = target_size % sample_size;
+	if (*tail != 0)
+		++samples;
+	return samples;
+}
+
+wuff_sint32 wuff_init_stream(struct wuff_handle * handle)
+{
+	/* Allocate some space on the stack. */
+	/* No need to do dynamic allocation for simple header probing. */
+	wuff_uint8 buffer[WUFF_HEADER_FETCH_SIZE];
+	size_t buffer_size = WUFF_HEADER_FETCH_SIZE;
+	wuff_uint64 search_offset;
+	struct wuff_chunk_header chunk;
+	wuff_sint32 wuff_status;
+
+	wuff_status = handle->callback->read(handle->userdata, buffer, &buffer_size);
+	WUFF_STATUS_BAIL()
+	else if (buffer_size < WUFF_STREAM_MIN_SIZE)
+		return WUFF_STREAM_NOT_RIFF;
+
+	/* Check for RIFF signature. */
+	wuff_copy_chunk_header_data(&chunk, buffer);
+	if (chunk.id != WUFF_RIFF_CHUNK_ID)
+		return WUFF_STREAM_NOT_RIFF;
+	handle->stream.size = chunk.size;
+
+	/* Check for WAVE format. */
+	wuff_copy_chunk_header_data(&chunk, buffer + 8);
+	if (chunk.id != WUFF_WAVE_CHUNK_ID)
+		return WUFF_STREAM_NOT_WAVE;
+
+	/* Search fmt chunk. */
+	wuff_copy_chunk_header_data(&chunk, buffer + 12);
+	search_offset = 12;
+	if (chunk.id != WUFF_FORMAT_CHUNK_ID)
+	{
+		chunk.id = 0;
+		/* The fmt chunk must appear before the data chunk. */
+		wuff_status = wuff_search_chunk(handle, &chunk, &search_offset, WUFF_FORMAT_CHUNK_ID, WUFF_DATA_CHUNK_ID);
+		if (wuff_status == WUFF_STREAM_CHUNK_NOT_FOUND)
+			return WUFF_STREAM_FORMAT_CHUNK_MISSING;
+		else WUFF_STATUS_BAIL()
+
+		/* In case the fmt chunk is not the first chunk, align it on the stack buffer as if it were. */
+		buffer_size = WUFF_HEADER_FETCH_SIZE - 20;
+		wuff_status = handle->callback->read(handle->userdata, buffer + 20, &buffer_size);
+		WUFF_STATUS_BAIL()
+		/* EOF bail. */
+		else if (buffer_size < WUFF_HEADER_MIN_SIZE)
+			return WUFF_STREAM_INVALID;
+	}
+
+	/* Extract header information. */
+	handle->stream.header.size = chunk.size;
+	handle->stream.header.offset = search_offset + 8;
+	handle->stream.header.format = wuff_get_uint16(buffer + 20);
+	handle->stream.header.channels = wuff_get_uint16(buffer + 22);
+	handle->stream.header.sample_rate = wuff_get_uint32(buffer + 24);
+	handle->stream.header.bits_per_sample = wuff_get_uint16(buffer + 34);
+	handle->stream.header.bytes_per_sample = handle->stream.header.bits_per_sample / 8;
+	handle->stream.header.block_size = handle->stream.header.channels * handle->stream.header.bytes_per_sample;
+
+	/* Bail on invalid streams. */
+	if (handle->stream.header.channels == 0)
+		return WUFF_STREAM_ZERO_CHANNELS;
+	else if (handle->stream.header.sample_rate == 0)
+		return WUFF_STREAM_ZERO_SAMPLE_RATE;
+	else if (handle->stream.header.bits_per_sample == 0)
+		return WUFF_STREAM_ZERO_BITS_PER_SAMPLE;
+
+	/* Grab the format from the extended header. */
+	if (handle->stream.header.size > WUFF_HEADER_MIN_SIZE && wuff_get_uint16(buffer + 36) == 22)
+	{
+		if (handle->stream.header.format == WUFF_FORMAT_EXTENSIBLE)
+			handle->stream.header.format = wuff_get_uint16(buffer + 44);
+	}
+
+	/* The check if this format is actually supported. */
+	handle->stream.format = handle->stream.header.format;
+	wuff_status = wuff_check_bits(handle->stream.header.bits_per_sample, &handle->stream.format);
+	WUFF_STATUS_BAIL()
+
+	/* The search for the data chunk begins. */
+	wuff_copy_chunk_header_data(&chunk, buffer + 20 + handle->stream.header.size);
+	search_offset = handle->stream.header.offset + handle->stream.header.size;
+	wuff_status = wuff_search_chunk(handle, &chunk, &search_offset, WUFF_DATA_CHUNK_ID, 0);
+	if (wuff_status == WUFF_STREAM_CHUNK_NOT_FOUND)
+		return WUFF_STREAM_DATA_CHUNK_MISSING;
+	else WUFF_STATUS_BAIL()
+
+	handle->stream.data.size = chunk.size;
+	handle->stream.data.offset = search_offset + 8;
+	handle->stream.length = handle->stream.data.size / handle->stream.header.channels / handle->stream.header.bytes_per_sample;
+	handle->stream.position = 0;
+
+	return WUFF_SUCCESS;
+}
+
+wuff_sint32 wuff_search_chunk(struct wuff_handle * handle, struct wuff_chunk_header * chunk, wuff_uint64 * offset, wuff_uint32 id, wuff_uint32 stop_id)
+{
+	wuff_uint8 buffer[8];
+	wuff_uint64 search_offset;
+	size_t buffer_size;
+	wuff_sint32 wuff_status = 0;
+
+	if (chunk->id != 0 && chunk->id == id)
+		return WUFF_SUCCESS;
+
+	/* Copy the current file position. */
+	search_offset = *offset;
+
+	while (wuff_status >= 0)
+	{
+		search_offset += 8 + chunk->size;
+		/* FIXME: Non-compliant RIFFs may not pad to WORD alignment. What now? */
+		if (search_offset & 1)
+			search_offset++;
+
+		wuff_status = handle->callback->seek(handle->userdata, search_offset);
+		WUFF_STATUS_BAIL()
+		/*else if (wuff_status == WUFF_CALLBACK_EOF)
+			return WUFF_STREAM_CHUNK_NOT_FOUND;*/
+
+		buffer_size = 8;
+		wuff_status = handle->callback->read(handle->userdata, buffer, &buffer_size);
+		WUFF_STATUS_BAIL()
+
+		wuff_copy_chunk_header_data(chunk, buffer);
+		/* Bail if we're at the EOF or the stop id. */
+		if (buffer_size < 8 || (stop_id != 0 && chunk->id == stop_id))
+			return WUFF_STREAM_CHUNK_NOT_FOUND;
+		else if (chunk->id == id)
+			break;
+	}
+
+	/* Report chunk offset. */
+	*offset = search_offset;
+
+	return WUFF_SUCCESS;
+}
+
+wuff_sint32 wuff_buffer_alloc(struct wuff_handle * handle)
+{
+	wuff_sint32 wuff_status;
+
+	if (handle == NULL)
+		return WUFF_INVALID_PARAM;
+
+	/* Try to allocate a buffer for 0.25 seconds, but clamp at some minimum and maximum value. */
+	handle->buffer.size = handle->stream.header.sample_rate * handle->stream.header.block_size / 4;
+	if (handle->buffer.size < WUFF_BUFFER_MIN_SIZE)
+		handle->buffer.size = WUFF_BUFFER_MIN_SIZE;
+	else if (handle->buffer.size > WUFF_BUFFER_MAX_SIZE)
+		handle->buffer.size = WUFF_BUFFER_MAX_SIZE;
+
+	handle->buffer.data = wuff_alloc(handle->buffer.size);
+	if (handle->buffer.data == NULL)
+		return WUFF_MEMALLOC_ERROR;
+
+	/* Just in case, let's null the offsets. */
+	wuff_status = wuff_buffer_clear(handle);
+	WUFF_STATUS_BAIL()
+
+	return WUFF_SUCCESS;
+}
+
+wuff_sint32 wuff_buffer_clear(struct wuff_handle * handle)
+{
+	wuff_uint64 position;
+	wuff_sint32 wuff_status;
+
+	if (handle == NULL)
+		return WUFF_INVALID_PARAM;
+
+	wuff_status = handle->callback->tell(handle->userdata, &position);
+	WUFF_STATUS_BAIL()
+
+	if (position < handle->stream.data.offset || position > handle->stream.data.offset + handle->stream.data.size)
+		return WUFF_BUFFER_INVALID_STREAM_POSITION;
+
+	handle->buffer.bytes_left = handle->stream.data.size - (position - handle->stream.data.offset);
+	handle->buffer.offset = 0;
+	handle->buffer.end = 0;
+
+	return WUFF_SUCCESS;
+}
+
+wuff_sint32 wuff_buffer_fill(struct wuff_handle * handle)
+{
+	size_t bytes_in_buffer;
+	size_t bytes_to_read;
+	wuff_sint32 wuff_status;
+
+	if (handle == NULL)
+		return WUFF_INVALID_PARAM;
+
+	/* Check if there are bytes in the buffer and move them to the start of the buffer. */
+	/* Probably not the most efficient way. Think on it some more! */
+	bytes_in_buffer = handle->buffer.end - handle->buffer.offset;
+
+	if (bytes_in_buffer == handle->buffer.size)
+		return WUFF_SUCCESS;
+	else if (bytes_in_buffer > 0)
+		memmove(handle->buffer.data, handle->buffer.data + handle->buffer.offset, bytes_in_buffer);
+
+	bytes_to_read = handle->buffer.size - bytes_in_buffer;
+	if (bytes_to_read > handle->buffer.bytes_left)
+		bytes_to_read = (size_t)handle->buffer.bytes_left;
+
+	wuff_status = handle->callback->read(handle->userdata, handle->buffer.data + bytes_in_buffer, &bytes_to_read);
+	WUFF_STATUS_BAIL()
+
+	handle->buffer.offset = 0;
+	handle->buffer.end = bytes_in_buffer + bytes_to_read;
+	handle->buffer.bytes_left -= bytes_to_read;
+
+	return WUFF_SUCCESS;
+}
+
+wuff_sint32 wuff_buffer_release(struct wuff_handle * handle, size_t samples)
+{
+	size_t size;
+
+	if (handle == NULL)
+		return WUFF_INVALID_PARAM;
+
+	size = samples * handle->stream.header.bytes_per_sample;
+
+	/* Check for an attempt to release more samples than the buffer could hold. */
+	/* "This should never happen." Let's throw an error anyway in case.*/
+	if (size > handle->buffer.end - handle->buffer.offset)
+		return WUFF_BUFFER_INVALID_SIZE;
+
+	handle->buffer.offset += size;
+
+	return WUFF_SUCCESS;
+}
+
+wuff_sint32 wuff_buffer_request(struct wuff_handle * handle, wuff_uint8 ** buffer, size_t * samples)
+{
+	size_t request_samples = *samples;
+	size_t buffer_samples, size;
+	size_t bps = handle->stream.header.bytes_per_sample;
+	wuff_sint32 wuff_status;
+
+	if (handle == NULL || buffer == NULL || samples == NULL)
+		return WUFF_INVALID_PARAM;
+
+	/* Fill the buffer some more if the requested size is bigger than the current data in the buffer. */
+	size = request_samples * bps;
+	if (size > handle->buffer.end - handle->buffer.offset)
+	{
+		wuff_status = wuff_buffer_fill(handle);
+		WUFF_STATUS_BAIL()
+	}
+
+	buffer_samples = (handle->buffer.end - handle->buffer.offset) / bps;
+
+	/* Report sample count change. */
+	if (buffer_samples < request_samples)
+		*samples = buffer_samples;
+
+	/* Report sample buffer start. */
+	*buffer = handle->buffer.data + handle->buffer.offset;
+
+	return WUFF_SUCCESS;
+}

+ 171 - 0
src/libraries/Wuff/wuff_internal.h

@@ -0,0 +1,171 @@
+#ifndef WUFF_INTERNAL_H
+#define WUFF_INTERNAL_H
+
+#define WUFF_BUFFER_MIN_SIZE		4096
+#define WUFF_BUFFER_MAX_SIZE		2097152
+#define WUFF_STREAM_MIN_SIZE		36
+#define WUFF_HEADER_MIN_SIZE		16
+#define WUFF_HEADER_FETCH_SIZE		80
+
+#define WUFF_FORMAT_PCM				1
+#define WUFF_FORMAT_IEEE_FLOAT		3
+#define WUFF_FORMAT_EXTENSIBLE		0xFFFE
+
+
+#define WUFF_RIFF_CHUNK_ID   wuff_get_chunk_id("RIFF")
+#define WUFF_WAVE_CHUNK_ID   wuff_get_chunk_id("WAVE")
+#define WUFF_FORMAT_CHUNK_ID wuff_get_chunk_id("fmt ")
+#define WUFF_DATA_CHUNK_ID   wuff_get_chunk_id("data")
+
+#define WUFF_STATUS_BAIL() if (wuff_status < 0) return wuff_status;
+
+
+static WUFF_INLINE wuff_uint32 wuff_get_uint32(wuff_uint8 * data)
+{
+	return data[0] + (data[1] << 8) + (data[2] << 16) + (data[3] << 24);
+}
+
+static WUFF_INLINE wuff_uint16 wuff_get_uint16(wuff_uint8 * data)
+{
+	return data[0] + (data[1] << 8);
+}
+
+struct wuff_chunk_header
+{
+	wuff_uint32 id;
+	wuff_uint32 size;
+};
+
+static WUFF_INLINE wuff_uint32 wuff_get_chunk_id(const char txt[5])
+{
+	const wuff_uint8 * id = (wuff_uint8*) txt;
+	wuff_uint32 int_id;
+	wuff_uint8 * id_bytes = (wuff_uint8 *)&int_id;
+	id_bytes[0] = id[0];
+	id_bytes[1] = id[1];
+	id_bytes[2] = id[2];
+	id_bytes[3] = id[3];
+
+	return int_id;
+}
+
+static WUFF_INLINE void wuff_copy_chunk_header_data(struct wuff_chunk_header * chunk, wuff_uint8 * data)
+{
+	wuff_uint8 * id = (wuff_uint8 *)&chunk->id;
+	id[0] = data[0];
+	id[1] = data[1];
+	id[2] = data[2];
+	id[3] = data[3];
+
+	chunk->size = wuff_get_uint32(data + 4);
+}
+
+struct wuff_stream_header
+{
+	wuff_uint64 size;
+	wuff_uint64 offset;
+
+	wuff_uint16 format;
+	wuff_uint16 channels;
+	wuff_uint32 sample_rate;
+	wuff_uint16 bits_per_sample;
+	wuff_uint16 bytes_per_sample;
+	size_t block_size;
+};
+
+struct wuff_stream_data
+{
+	wuff_uint64 size;
+	wuff_uint64 offset;
+};
+
+struct wuff_buffer
+{
+	wuff_uint8 * data;
+	wuff_uint64 bytes_left;
+	size_t size;
+	size_t offset;
+	size_t end;
+};
+
+struct wuff_output
+{
+	wuff_uint16 format;
+	size_t bytes_per_sample;
+	size_t block_size;
+	size_t block_offset;
+	void (* function)(wuff_uint8 *, wuff_uint8 *, size_t, wuff_uint8, wuff_uint8, wuff_uint8);
+};
+
+struct wuff_stream
+{
+	wuff_uint64 size;
+	wuff_uint64 length;
+	wuff_uint16 format;
+
+	wuff_uint64 position;
+
+	struct wuff_stream_header header;
+	struct wuff_stream_data data;
+};
+
+struct wuff_handle
+{
+	struct wuff_stream stream;
+	struct wuff_buffer buffer;
+	struct wuff_output output;
+	struct wuff_callback * callback;
+	void * userdata;
+};
+
+
+/* Initializes the stream, allocates the buffer, and sets the output format. */
+/* Expects a nulled wuff_handle and the callbacks set and ready. */
+WUFF_INTERN_API wuff_sint32 wuff_setup(struct wuff_handle * handle);
+
+/* Cleans the stream up, frees the buffer and the wuff_handle. */
+WUFF_INTERN_API wuff_sint32 wuff_cleanup(struct wuff_handle * handle);
+
+/* Called by wuff_setup. Initializes the stream by reading the data from the */
+/* callbacks, searching for headers and stream information. */
+WUFF_INTERN_API wuff_sint32 wuff_init_stream(struct wuff_handle * handle);
+
+/* Searches for a specific chunk id and stops before another if it's not 0. */
+/* If the id in wuff_chunk_header is not 0, it will be checked too and if */
+/* they match, then the function will return immediately. */
+/* Expects offset to point to the file position of a chunk and */
+/* wuff_chunk_header to have the size of this chunk. */
+WUFF_INTERN_API wuff_sint32 wuff_search_chunk(struct wuff_handle * handle, struct wuff_chunk_header * chunk, wuff_uint64 * offset, wuff_uint32 id, wuff_uint32 stop_id);
+
+/* Sets the output struct of the stream to the new format. */
+WUFF_INTERN_API wuff_sint32 wuff_set_output_format(struct wuff_handle * handle, wuff_uint16);
+
+/* Checks if the number of bits per samples is supported and writes the */
+/* output identifier to the 16-bit integer. */
+WUFF_INTERN_API wuff_sint32 wuff_check_bits(wuff_uint16 bits, wuff_uint16 * format);
+
+/* Calculates the number of samples that have to be requested from the buffer */
+/* by also taking the truncated samples at the start and end into account. */
+/* The return value is the number of samples needed. */
+WUFF_INTERN_API size_t wuff_calculate_samples(size_t target_size, wuff_uint8 sample_size, wuff_uint8 * head, wuff_uint8 * tail);
+
+
+/* Allocates the buffer for the input stream. */
+/* Expects the stream to be initialized, as format information is needed. */
+WUFF_INTERN_API wuff_sint32 wuff_buffer_alloc(struct wuff_handle * handle);
+
+/* Fills the buffer with new data. */
+WUFF_INTERN_API wuff_sint32 wuff_buffer_fill(struct wuff_handle * handle);
+
+/* Marks all bytes in the buffer as free. */
+WUFF_INTERN_API wuff_sint32 wuff_buffer_clear(struct wuff_handle * handle);
+
+/* Requests samples and a pointer to them. */
+/* The number of samples may be lower than requested. */
+WUFF_INTERN_API wuff_sint32 wuff_buffer_request(struct wuff_handle * handle, wuff_uint8 ** buffer, size_t * samples);
+
+/* Releases the number of samples from the buffer. */
+WUFF_INTERN_API wuff_sint32 wuff_buffer_release(struct wuff_handle * handle, size_t samples);
+
+
+#endif /* WUFF_INTERNAL_H */

+ 17 - 0
src/libraries/Wuff/wuff_memory.c

@@ -0,0 +1,17 @@
+#include <stdlib.h>
+
+#include "wuff_config.h"
+
+/* Default memory allocators. */
+/* They can be overridden with custom functions at build time. */
+#ifndef WUFF_MEMALLOC_OVERRIDE
+void * wuff_alloc(size_t size)
+{
+	return malloc(size);
+}
+
+void wuff_free(void * mem)
+{
+	free(mem);
+}
+#endif

+ 2 - 2
src/modules/sound/lullaby/ModPlugDecoder.cpp

@@ -83,8 +83,8 @@ bool ModPlugDecoder::accepts(const std::string &ext)
 		"699", "abc", "amf", "ams", "dbm", "dmf",
 		"dsm", "far", "it", "j2b", "mdl", "med",
 		"mid", "mod", "mt2", "mtm", "okt", "pat",
-		"psm", "s3m", "stm", "ult", "umx", "wav",
-		"xm", ""
+		"psm", "s3m", "stm", "ult", "umx",  "xm",
+		""
 	};
 
 	for (int i = 0; !(supported[i].empty()); i++)

+ 3 - 0
src/modules/sound/lullaby/Sound.cpp

@@ -27,6 +27,7 @@
 #include "ModPlugDecoder.h"
 #include "VorbisDecoder.h"
 #include "GmeDecoder.h"
+#include "WaveDecoder.h"
 //#include "FLACDecoder.h"
 
 #ifndef LOVE_NOMPG123
@@ -76,6 +77,8 @@ sound::Decoder *Sound::newDecoder(love::filesystem::FileData *data, int bufferSi
 	else if (GmeDecoder::accepts(ext))
 		decoder = new GmeDecoder(data, ext, bufferSize);
 #endif // LOVE_SUPPORT_GME
+	else if (WaveDecoder::accepts(ext))
+		decoder = new WaveDecoder(data, ext, bufferSize);
 	/*else if (FLACDecoder::accepts(ext))
 		decoder = new FLACDecoder(data, ext, bufferSize);*/
 

+ 194 - 0
src/modules/sound/lullaby/WaveDecoder.cpp

@@ -0,0 +1,194 @@
+/**
+ * Copyright (c) 2006-2012 LOVE Development Team
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty.  In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ *    claim that you wrote the original software. If you use this software
+ *    in a product, an acknowledgment in the product documentation would be
+ *    appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ *    misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ **/
+
+#include "WaveDecoder.h"
+
+#include <string.h>
+#include "common/config.h"
+#include "common/Exception.h"
+
+namespace love
+{
+namespace sound
+{
+namespace lullaby
+{
+
+// Callbacks
+wuff_sint32 read_callback(void *userdata, wuff_uint8 *buffer, size_t *size)
+{
+	WaveFile *input = (WaveFile *) userdata;
+	size_t bytes_left = input->size - input->offset;
+	size_t target_size = *size < bytes_left ? *size : bytes_left;
+	memcpy(buffer, input->data + input->offset, target_size);
+	input->offset += target_size;
+	*size = target_size;
+	return WUFF_SUCCESS;
+}
+
+wuff_sint32 seek_callback(void *userdata, wuff_uint64 offset)
+{
+	WaveFile *input = (WaveFile *)userdata;
+	input->offset = (size_t) (offset < input->size ? offset : input->size);
+	return WUFF_SUCCESS;
+}
+
+wuff_sint32 tell_callback(void *userdata, wuff_uint64 *offset)
+{
+	WaveFile *input = (WaveFile *)userdata;
+	*offset = input->offset;
+	return WUFF_SUCCESS;
+}
+
+wuff_callback WaveDecoderCallbacks = {read_callback, seek_callback, tell_callback};
+
+
+WaveDecoder::WaveDecoder(Data *data, const std::string &ext, int bufferSize)
+	: Decoder(data, ext, bufferSize)
+{
+	dataFile.data = (char *) data->getData();
+	dataFile.size = data->getSize();
+	dataFile.offset = 0;
+
+	int wuff_status = wuff_open(&handle, &WaveDecoderCallbacks, &dataFile);
+	if (wuff_status < 0)
+		throw love::Exception("Could not open WAVE");
+
+	try
+	{
+		wuff_status = wuff_stream_info(handle, &info);
+		if (wuff_status < 0)
+			throw love::Exception("Could not retrieve WAVE stream info");
+
+		if (info.channels > 2)
+			throw love::Exception("Multichannel audio not supported");
+
+		if (info.format != WUFF_FORMAT_PCM_U8 && info.format != WUFF_FORMAT_PCM_S16)
+		{
+			wuff_status = wuff_format(handle, WUFF_FORMAT_PCM_S16);
+			if (wuff_status < 0)
+				throw love::Exception("Could not set output format");
+		}
+	}
+	catch (love::Exception &)
+	{
+		wuff_close(handle);
+		throw;
+	}
+}
+
+WaveDecoder::~WaveDecoder()
+{
+	wuff_close(handle);
+}
+
+bool WaveDecoder::accepts(const std::string &ext)
+{
+	static const std::string supported[] =
+	{
+		"wav", ""
+	};
+
+	for (int i = 0; !(supported[i].empty()); i++)
+	{
+		if (supported[i].compare(ext) == 0)
+			return true;
+	}
+
+	return false;
+}
+
+love::sound::Decoder *WaveDecoder::clone()
+{
+	return new WaveDecoder(data, ext, bufferSize);
+}
+
+int WaveDecoder::decode()
+{
+	size_t size = 0;
+
+	while (size < (size_t) bufferSize)
+	{
+		size_t bytes = bufferSize;
+		int wuff_status = wuff_read(handle, (wuff_uint8 *) buffer, &bytes);
+
+		if (wuff_status < 0)
+			return 0;
+		else if (bytes == 0)
+		{
+			eof = true;
+			break;
+		}
+
+		size += bytes;
+	}
+
+	return size;
+}
+
+bool WaveDecoder::seek(float s)
+{
+	int wuff_status = wuff_seek(handle, (wuff_uint64) (s * info.sample_rate));
+
+	if (wuff_status >= 0)
+	{
+		eof = false;
+		return true;
+	}
+
+	return false;
+}
+
+bool WaveDecoder::rewind()
+{
+	int wuff_status = wuff_seek(handle, 0);
+
+	if (wuff_status >= 0)
+	{
+		eof = false;
+		return true;
+	}
+
+	return false;
+}
+
+bool WaveDecoder::isSeekable()
+{
+	return true;
+}
+
+int WaveDecoder::getChannels() const
+{
+	return info.channels;
+}
+
+int WaveDecoder::getBitDepth() const
+{
+	return info.bits_per_sample == 8 ? 8 : 16;
+}
+
+int WaveDecoder::getSampleRate() const
+{
+	return info.sample_rate;
+}
+
+} // lullaby
+} // sound
+} // love

+ 76 - 0
src/modules/sound/lullaby/WaveDecoder.h

@@ -0,0 +1,76 @@
+/**
+ * Copyright (c) 2006-2012 LOVE Development Team
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty.  In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ *    claim that you wrote the original software. If you use this software
+ *    in a product, an acknowledgment in the product documentation would be
+ *    appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ *    misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ **/
+
+#ifndef LOVE_SOUND_LULLABY_WAVE_DECODER_H
+#define LOVE_SOUND_LULLABY_WAVE_DECODER_H
+
+// LOVE
+#include "common/Data.h"
+#include "Decoder.h"
+
+// vorbis
+#include "libraries/Wuff/wuff.h"
+
+namespace love
+{
+namespace sound
+{
+namespace lullaby
+{
+
+// Struct for handling data
+struct WaveFile
+{
+	char *data;
+	size_t size;
+	size_t offset;
+};
+
+class WaveDecoder : public Decoder
+{
+public:
+
+	WaveDecoder(Data *data, const std::string &ext, int bufferSize);
+	virtual ~WaveDecoder();
+
+	static bool accepts(const std::string &ext);
+
+	love::sound::Decoder *clone();
+	int decode();
+	bool seek(float s);
+	bool rewind();
+	bool isSeekable();
+	int getChannels() const;
+	int getBitDepth() const;
+	int getSampleRate() const;
+
+private:
+
+	WaveFile dataFile;
+	wuff_handle * handle;
+	wuff_info info;
+
+}; // WaveDecoder
+
+} // lullaby
+} // sound
+} // love
+
+#endif // LOVE_SOUND_LULLABY_WAVE_DECODER_H