mirror of
https://github.com/love2d/love.git
synced 2026-08-16 00:02:12 +02:00
Fixed wave sound playback by using a different wave reader library (thanks Boolsheet!)
Resolves issue #25.
This commit is contained in:
@@ -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;
|
||||
}
|
||||
@@ -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 */
|
||||
@@ -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 */
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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 */
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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 */
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user