From e5adc4e5fdbed9540acc75f6b66e0fabd216525b Mon Sep 17 00:00:00 2001 From: Alex Szpakowski Date: Wed, 23 Oct 2013 16:02:06 -0300 Subject: [PATCH] Fixed wave sound playback by using a different wave reader library (thanks Boolsheet!) Resolves issue #25. --- .../love-framework.xcodeproj/project.pbxproj | 50 +- src/libraries/Wuff/wuff.c | 203 +++++ src/libraries/Wuff/wuff.h | 274 ++++++ src/libraries/Wuff/wuff_config.h | 50 ++ src/libraries/Wuff/wuff_convert.c | 827 ++++++++++++++++++ src/libraries/Wuff/wuff_convert.h | 48 + src/libraries/Wuff/wuff_internal.c | 540 ++++++++++++ src/libraries/Wuff/wuff_internal.h | 171 ++++ src/libraries/Wuff/wuff_memory.c | 17 + src/modules/sound/lullaby/ModPlugDecoder.cpp | 4 +- src/modules/sound/lullaby/Sound.cpp | 3 + src/modules/sound/lullaby/WaveDecoder.cpp | 194 ++++ src/modules/sound/lullaby/WaveDecoder.h | 76 ++ 13 files changed, 2454 insertions(+), 3 deletions(-) create mode 100644 src/libraries/Wuff/wuff.c create mode 100644 src/libraries/Wuff/wuff.h create mode 100644 src/libraries/Wuff/wuff_config.h create mode 100644 src/libraries/Wuff/wuff_convert.c create mode 100644 src/libraries/Wuff/wuff_convert.h create mode 100644 src/libraries/Wuff/wuff_internal.c create mode 100644 src/libraries/Wuff/wuff_internal.h create mode 100644 src/libraries/Wuff/wuff_memory.c create mode 100644 src/modules/sound/lullaby/WaveDecoder.cpp create mode 100644 src/modules/sound/lullaby/WaveDecoder.h diff --git a/platform/macosx/love-framework.xcodeproj/project.pbxproj b/platform/macosx/love-framework.xcodeproj/project.pbxproj index 1c3ea84cd..3d684c3d3 100644 --- a/platform/macosx/love-framework.xcodeproj/project.pbxproj +++ b/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 = ""; }; FAF4376D17F4AC530074F9E2 /* Mesh.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Mesh.cpp; sourceTree = ""; }; FAF4376E17F4AC530074F9E2 /* Mesh.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Mesh.h; sourceTree = ""; }; + FAF6704418184FF800DBDEEA /* wuff.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = wuff.c; sourceTree = ""; }; + FAF6704518184FF800DBDEEA /* wuff.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = wuff.h; sourceTree = ""; }; + FAF6704618184FF800DBDEEA /* wuff_config.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = wuff_config.h; sourceTree = ""; }; + FAF6704718184FF800DBDEEA /* wuff_convert.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = wuff_convert.c; sourceTree = ""; }; + FAF6704818184FF800DBDEEA /* wuff_convert.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = wuff_convert.h; sourceTree = ""; }; + FAF6704918184FF800DBDEEA /* wuff_internal.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = wuff_internal.c; sourceTree = ""; }; + FAF6704A18184FF800DBDEEA /* wuff_internal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = wuff_internal.h; sourceTree = ""; }; + FAF6704B18184FF800DBDEEA /* wuff_memory.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = wuff_memory.c; sourceTree = ""; }; + FAF670541818501300DBDEEA /* WaveDecoder.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = WaveDecoder.cpp; sourceTree = ""; }; + FAF670551818501300DBDEEA /* WaveDecoder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WaveDecoder.h; sourceTree = ""; }; /* 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 = ""; @@ -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 = ""; }; + 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 = ""; + }; /* 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 */, diff --git a/src/libraries/Wuff/wuff.c b/src/libraries/Wuff/wuff.c new file mode 100644 index 000000000..f35973330 --- /dev/null +++ b/src/libraries/Wuff/wuff.c @@ -0,0 +1,203 @@ +#include + +#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; +} diff --git a/src/libraries/Wuff/wuff.h b/src/libraries/Wuff/wuff.h new file mode 100644 index 000000000..c7cc10927 --- /dev/null +++ b/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 +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 */ diff --git a/src/libraries/Wuff/wuff_config.h b/src/libraries/Wuff/wuff_config.h new file mode 100644 index 000000000..ed17005f4 --- /dev/null +++ b/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 */ diff --git a/src/libraries/Wuff/wuff_convert.c b/src/libraries/Wuff/wuff_convert.c new file mode 100644 index 000000000..9e60ff5d3 --- /dev/null +++ b/src/libraries/Wuff/wuff_convert.c @@ -0,0 +1,827 @@ +#include + +#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); +} diff --git a/src/libraries/Wuff/wuff_convert.h b/src/libraries/Wuff/wuff_convert.h new file mode 100644 index 000000000..280973d25 --- /dev/null +++ b/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 */ diff --git a/src/libraries/Wuff/wuff_internal.c b/src/libraries/Wuff/wuff_internal.c new file mode 100644 index 000000000..e4813329d --- /dev/null +++ b/src/libraries/Wuff/wuff_internal.c @@ -0,0 +1,540 @@ +#include +#include + +#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; +} diff --git a/src/libraries/Wuff/wuff_internal.h b/src/libraries/Wuff/wuff_internal.h new file mode 100644 index 000000000..2679cdbcb --- /dev/null +++ b/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 */ diff --git a/src/libraries/Wuff/wuff_memory.c b/src/libraries/Wuff/wuff_memory.c new file mode 100644 index 000000000..eaffa4e62 --- /dev/null +++ b/src/libraries/Wuff/wuff_memory.c @@ -0,0 +1,17 @@ +#include + +#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 diff --git a/src/modules/sound/lullaby/ModPlugDecoder.cpp b/src/modules/sound/lullaby/ModPlugDecoder.cpp index ab16cbf56..7d3bd7583 100644 --- a/src/modules/sound/lullaby/ModPlugDecoder.cpp +++ b/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++) diff --git a/src/modules/sound/lullaby/Sound.cpp b/src/modules/sound/lullaby/Sound.cpp index 10f4c9469..15604c5ba 100644 --- a/src/modules/sound/lullaby/Sound.cpp +++ b/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);*/ diff --git a/src/modules/sound/lullaby/WaveDecoder.cpp b/src/modules/sound/lullaby/WaveDecoder.cpp new file mode 100644 index 000000000..d38f4098e --- /dev/null +++ b/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 +#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 diff --git a/src/modules/sound/lullaby/WaveDecoder.h b/src/modules/sound/lullaby/WaveDecoder.h new file mode 100644 index 000000000..e8ac57e40 --- /dev/null +++ b/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