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

Resolves issue #25.
This commit is contained in:
Alex Szpakowski
2013-10-23 16:02:06 -03:00
parent 259bb8ffdb
commit e5adc4e5fd
13 changed files with 2454 additions and 3 deletions
+203
View File
@@ -0,0 +1,203 @@
#include <string.h>
#include "wuff_config.h"
#include "wuff.h"
#include "wuff_internal.h"
wuff_sint32 wuff_open(struct wuff_handle ** handle_pointer, struct wuff_callback * callback, void * userdata)
{
struct wuff_handle * handle;
wuff_sint32 wuff_status;
if (handle_pointer == NULL || callback == NULL)
return WUFF_INVALID_PARAM;
handle = wuff_alloc(sizeof(struct wuff_handle));
if (handle == NULL)
return WUFF_MEMALLOC_ERROR;
memset(handle, 0, sizeof(struct wuff_handle));
handle->buffer.data = NULL;
handle->callback = callback;
handle->userdata = userdata;
wuff_status = wuff_setup(handle);
if (wuff_status < 0)
{
wuff_cleanup(handle);
return wuff_status;
}
*handle_pointer = handle;
return WUFF_SUCCESS;
}
wuff_sint32 wuff_close(struct wuff_handle * handle)
{
wuff_sint32 wuff_status;
if (handle == NULL)
return WUFF_INVALID_PARAM;
wuff_status = wuff_cleanup(handle);
WUFF_STATUS_BAIL()
return WUFF_SUCCESS;
}
wuff_sint32 wuff_seek(struct wuff_handle * handle, wuff_uint64 offset)
{
wuff_sint32 wuff_status;
wuff_uint64 seek_offset;
if (handle == NULL)
return WUFF_INVALID_PARAM;
/* Clamp offset to stream length. */
offset = offset <= handle->stream.length ? offset : handle->stream.length;
seek_offset = offset * handle->stream.header.block_size;
wuff_status = handle->callback->seek(handle->userdata, handle->stream.data.offset + seek_offset);
WUFF_STATUS_BAIL()
handle->stream.position = offset;
handle->output.block_offset = 0;
/* A new position requires an empty buffer. */
wuff_status = wuff_buffer_clear(handle);
WUFF_STATUS_BAIL()
return WUFF_SUCCESS;
}
wuff_sint32 wuff_tell(struct wuff_handle * handle, wuff_uint64 * offset)
{
if (handle == NULL)
return WUFF_INVALID_PARAM;
*offset = handle->stream.position;
return WUFF_SUCCESS;
}
wuff_sint32 wuff_stream_info(struct wuff_handle * handle, struct wuff_info * info)
{
if (handle == NULL || info == NULL)
return WUFF_INVALID_PARAM;
info->format = handle->stream.format;
info->channels = handle->stream.header.channels;
info->sample_rate = handle->stream.header.sample_rate;
info->bits_per_sample = handle->stream.header.bits_per_sample;
info->length = handle->stream.length;
/* Think about adding channel mapping and perhaps other things. */
return WUFF_SUCCESS;
}
wuff_sint32 wuff_format(struct wuff_handle * handle, wuff_uint16 format)
{
wuff_sint32 wuff_status;
if (handle == NULL)
return WUFF_INVALID_PARAM;
else if (format >= WUFF_FORMAT_MAX)
return WUFF_FORMAT_UNSUPPORTED;
/* A format change resets the position to the start of the block. */
wuff_status = wuff_seek(handle, handle->stream.position);
WUFF_STATUS_BAIL()
wuff_status = wuff_set_output_format(handle, format);
WUFF_STATUS_BAIL()
return WUFF_SUCCESS;
}
wuff_sint32 wuff_read(struct wuff_handle * handle, wuff_uint8 * out_buffer, size_t * out_size)
{
size_t current_offset;
size_t r_samples, num_samples;
wuff_uint8 head_offset, head, tail, sample_size;
wuff_uint8 * in_buffer;
wuff_sint32 wuff_status;
if (handle == NULL || out_buffer == NULL || out_size == NULL)
return WUFF_INVALID_PARAM;
if (*out_size == 0)
return WUFF_SUCCESS;
sample_size = (wuff_uint8)handle->output.bytes_per_sample;
/* Calculating the number of samples that fit into the application buffer. */
/* The first and last sample may be truncated. */
current_offset = handle->output.block_offset;
head_offset = current_offset % sample_size;
head = head_offset == 0 ? 0 : sample_size - head_offset;
num_samples = wuff_calculate_samples(*out_size, sample_size, &head, &tail);
/* Requesting the number of samples from the buffer. */
/* Calculate the new sample count if necessary and write the output. */
r_samples = num_samples;
wuff_status = wuff_buffer_request(handle, &in_buffer, &r_samples);
WUFF_STATUS_BAIL()
else if (r_samples == 0)
{
/* Possible EOF. */
*out_size = 0;
}
else
{
if (r_samples == 1 && head != 0)
{
/* Only the first truncated sample fits. */
/* I really hope nobody will use small buffers like this. */
num_samples = 0;
tail = 0;
}
else
{
/* At this point the first (possibly truncated) sample will be fully written. */
/* Subtract the first and last sample from the count if they're truncated. */
if (r_samples < num_samples)
tail = 0;
num_samples = r_samples - !!head - !!tail;
}
handle->output.function(out_buffer, in_buffer, num_samples, head_offset, head, tail);
/* Report the number of bytes written. */
*out_size = num_samples * sample_size + head + tail;
/* Adjust the block offset and sample position. */
current_offset += *out_size;
if (current_offset >= handle->output.block_size)
{
handle->stream.position += current_offset / handle->output.block_size;
handle->output.block_offset = current_offset % handle->output.block_size;
}
else
{
handle->output.block_offset = current_offset;
}
/* Release the fully processed samples from the buffer. */
wuff_status = wuff_buffer_release(handle, head_offset + head == sample_size ? num_samples + 1 : num_samples);
WUFF_STATUS_BAIL()
}
return WUFF_SUCCESS;
}
void wuff_version(struct wuff_version * version)
{
if (version == NULL)
return;
version->major = WUFF_VERSION_MAJOR;
version->minor = WUFF_VERSION_MINOR;
version->build = WUFF_VERSION_BUILD;
version->revision = WUFF_VERSION_REVISION;
}
+274
View File
@@ -0,0 +1,274 @@
/*
* Wuff - A very basic WAVE reader
*/
#ifndef WUFF_H
#define WUFF_H
#ifdef __cplusplus
extern "C" {
#endif
#define WUFF_VERSION_MAJOR 0
#define WUFF_VERSION_MINOR 0
#define WUFF_VERSION_BUILD 0
#define WUFF_VERSION_REVISION 2
#ifndef WUFF_API_OVERRIDE
#if defined(_WIN32) && defined(WUFF_DYNAMIC_LIB)
#define WUFF_EXPORT __declspec(dllexport)
#define WUFF_IMPORT __declspec(dllimport)
#else
#define WUFF_EXPORT
#define WUFF_IMPORT
#endif
#endif
#ifdef WUFF_BUILDING_CORE
#define WUFF_API WUFF_EXPORT
#else
#define WUFF_API WUFF_IMPORT
#endif
#ifdef WUFF_FORCE_STDINT_H
#include <stdint.h>
typedef uint8_t wuff_uint8;
typedef int8_t wuff_sint8;
typedef uint16_t wuff_uint16;
typedef int16_t wuff_sint16;
typedef uint32_t wuff_uint32;
typedef int32_t wuff_sint32;
#ifdef WUFF_NO_64BIT_TYPE
typedef uint32_t wuff_uint64;
typedef int32_t wuff_sint64;
#else
typedef uint64_t wuff_uint64;
typedef int64_t wuff_sint64;
#endif
#elif _MSC_VER
typedef unsigned __int8 wuff_uint8;
typedef signed __int8 wuff_sint8;
typedef unsigned __int16 wuff_uint16;
typedef signed __int16 wuff_sint16;
typedef unsigned __int32 wuff_uint32;
typedef signed __int32 wuff_sint32;
typedef unsigned __int64 wuff_uint64;
typedef signed __int64 wuff_sint64;
#else
typedef unsigned char wuff_uint8;
typedef signed char wuff_sint8;
typedef unsigned short wuff_uint16;
typedef signed short wuff_sint16;
typedef unsigned int wuff_uint32;
typedef signed int wuff_sint32;
#ifdef WUFF_NO_64BIT_TYPE
typedef unsigned long wuff_uint64;
typedef signed long wuff_sint64;
#else
typedef unsigned long long wuff_uint64;
typedef signed long long wuff_sint64;
#endif
#endif
/** @file */
/**
* Opaque structure used to identify the open Wuff streams.
*/
struct wuff_handle;
/**
* Callbacks that control the delivery of the data of the WAVE file.
*
* The return values of the functions indicate their status. A zero or positive
* value means success and a negative value failure. The macros WUFF_SUCCESS and
* WUFF_ERROR, or a value equal or below WUFF_USER_ERROR can be used. The error
* value will be returned by the function called by the application.
*/
struct wuff_callback
{
/**
* The read callback requests the linking application to write at least
* 'size' bytes into the memory where 'buffer' is pointing to. The value
* pointed to by 'size' must be update to the actual number of bytes
* written. Zero will be interepreted as the end-of-file.
*
* @param userdata The userdata set with wuff_open.
* @param buffer A pointer to the memory where the data can be written to.
* @param size A pointer to the size of the buffer and the bytes written.
*/
wuff_sint32 (* read)(void * userdata, wuff_uint8 * buffer, size_t * size);
/**
* The seek callback requests the linking application to seek to a new byte
* offset in the WAVE data. The next call to the read callback must then
* write data starting from this position. The offset is always relative
* to the beginning of the WAVE data.
*
* @param userdata The userdata set with wuff_open.
* @param offset The new offset.
*/
wuff_sint32 (* seek)(void * userdata, wuff_uint64 offset);
/**
* The tell callback requests the linking application to write the current
* byte position to the integer pointed to by 'offset'.
*
* @param userdata The userdata set with wuff_open.
* @param offset A pointer to an integer where the current position can be written to.
*/
wuff_sint32 (* tell)(void * userdata, wuff_uint64 * offset);
};
/**
* Stream information structure.
*/
struct wuff_info
{
wuff_uint16 format; /**< The format of the stream.
* See "Wuff raw sample formats" below. */
wuff_uint16 channels; /**< The number of channels in the stream. */
wuff_uint32 sample_rate; /**< The sample rate in hertz. */
wuff_uint16 bits_per_sample; /**< The number of bits per sample. */
wuff_uint64 length; /**< The length of the stream in samples. */
};
/**
* Version information structure.
*/
struct wuff_version
{
wuff_uint16 major;
wuff_uint16 minor;
wuff_uint16 build;
wuff_uint16 revision;
};
/**
* Opens a new Wuff stream. This will read from the callbacks immediately, make
* sure they're ready. It will check if the WAVE file is supported.
*
* @param handle A pointer to pointer of a wuff_handle that will be
* initialized if the function succeeds.
* @param callback The callbacks for the data of the WAVE file.
* @param userdata A void pointer that will be passed to the callbacks.
* @return Returns a negative value if an error occured.
*/
WUFF_API wuff_sint32 wuff_open(struct wuff_handle ** handle, struct wuff_callback * callback, void * userdata);
/**
* Closes a Wuff stream.
*
* @param handle The Wuff stream handle.
* @return Returns a negative value if an error occured.
*/
WUFF_API wuff_sint32 wuff_close(struct wuff_handle * handle);
/**
* Fills the wuff_info struct with information about the stream.
*
* @param handle The Wuff stream handle.
* @param info A pointer to a wuff_info struct.
* @return Returns a negative value if an error occured.
*/
WUFF_API wuff_sint32 wuff_stream_info(struct wuff_handle * handle, struct wuff_info * info);
/**
* Sets the output format of the decoder. A new format resets the decoder output
* to the beginning of the current block (the sample of the first channel).
*
* @param handle The Wuff stream handle.
* @param format The new output format.
* @return Returns a negative value if an error occured.
*/
WUFF_API wuff_sint32 wuff_format(struct wuff_handle * handle, wuff_uint16 format);
/**
* Decodes samples to the passed memory location. The size_t pointer points to
* the maximum number of bytes that can be written to the buffer. This count
* will be adjusted to the number of bytes written to the buffer.
*
* @param handle The Wuff stream handle.
* @param buffer The buffer to write to.
* @param size The maximum number of bytes to write to the buffer.
* @return Returns a negative value if an error occured.
*/
WUFF_API wuff_sint32 wuff_read(struct wuff_handle * handle, wuff_uint8 * buffer, size_t * size);
/**
* Seeks to a sample location.
* The next call to wuff_read will return samples starting from this position.
*
* @param handle The Wuff stream handle.
* @param offset The sample offset to seek to.
* @return Returns a negative value if an error occured.
*/
WUFF_API wuff_sint32 wuff_seek(struct wuff_handle * handle, wuff_uint64 offset);
/**
* Sets the current position.
*
* @param handle The Wuff stream handle.
* @param offset A pointer to a integer that will receive the sample offset.
* @return Returns a negative value if an error occured.
*/
WUFF_API wuff_sint32 wuff_tell(struct wuff_handle * handle, wuff_uint64 * offset);
/**
* Copies the Wuff version of the binary into the struct.
* For compile-time version information use the WUFF_VERSION_MAJOR,
* WUFF_VERSION_MINOR, WUFF_VERSION_BUILD, and WUFF_VERSION_REVISION macros.
*
* @param version A pointer to a wuff_version struct that will receive the
* version information.
*/
WUFF_API void wuff_version(struct wuff_version * version);
/* Wuff raw sample formats. */
#define WUFF_FORMAT_PCM_U8 0
#define WUFF_FORMAT_PCM_S16 1
#define WUFF_FORMAT_PCM_S24 2
#define WUFF_FORMAT_PCM_S32 3
#define WUFF_FORMAT_IEEE_FLOAT_32 4
#define WUFF_FORMAT_IEEE_FLOAT_64 5
#define WUFF_FORMAT_MAX 6
/* Success and error return values for all functions. */
#define WUFF_STREAM_EOF 100
#define WUFF_SUCCESS 0
#define WUFF_ERROR -1
#define WUFF_INVALID_PARAM -2
#define WUFF_MEMALLOC_ERROR -3
#define WUFF_STREAM_NOT_RIFF -100
#define WUFF_STREAM_NOT_WAVE -101
#define WUFF_STREAM_INVALID -102
#define WUFF_STREAM_ZERO_CHANNELS -103
#define WUFF_STREAM_ZERO_SAMPLE_RATE -104
#define WUFF_STREAM_ZERO_BITS_PER_SAMPLE -105
#define WUFF_STREAM_FORMAT_CHUNK_MISSING -106
#define WUFF_STREAM_DATA_CHUNK_MISSING -107
#define WUFF_STREAM_CHUNK_NOT_FOUND -108
#define WUFF_FORMAT_UNSUPPORTED -200
#define WUFF_BUFFER_INVALID_SIZE -300
#define WUFF_BUFFER_INVALID_STREAM_POSITION -301
#define WUFF_USER_ERROR -10000
#ifdef __cplusplus
}
#endif
#endif /* WUFF_H */
+50
View File
@@ -0,0 +1,50 @@
#ifndef WUFF_CONFIG_H
#define WUFF_CONFIG_H
/* Defines that the internal code is being built. */
/* The wuff.h header uses this to change export and import macros. */
#define WUFF_BUILDING_CORE
#ifndef WUFF_INLINE_OVERRIDE
#ifdef __cplusplus
#define WUFF_INLINE inline
#else
#ifdef _MSC_VER
#define WUFF_INLINE __inline
#elif __GNUC__
#define WUFF_INLINE __inline__
#else
#define WUFF_INLINE
#endif
#endif
#endif
#ifndef WUFF_GCC_VISIBILITY_OVERRIDE
#if __GNUC__ >= 4
#define WUFF_INTERN_API __attribute__((visibility("hidden")))
#else
#define WUFF_INTERN_API
#endif
#endif
#ifdef WUFF_MEMALLOC_OVERRIDE
#ifdef __cplusplus
extern "C" {
#endif
/* Define your own memory allocator. */
void * wuff_alloc(size_t size);
void wuff_free(void * mem);
#ifdef __cplusplus
}
#endif
#else
WUFF_INTERN_API void * wuff_alloc(size_t size);
WUFF_INTERN_API void wuff_free(void * mem);
#endif
#endif /* WUFF_CONFIG_H */
+827
View File
@@ -0,0 +1,827 @@
#include <string.h>
#include "wuff_config.h"
#include "wuff.h"
#include "wuff_convert.h"
/*
* int8 functions.
*/
WUFF_CONV_FUNC(wuff_int8_to_int8)
{
(void)offset;
memcpy(dst, src, samples + head + tail);
}
WUFF_CONV_FUNC(wuff_int8_to_int16)
{
wuff_sint16 i16;
size_t i;
if (head != 0)
{
i16 = (src[0] - 128) << 8;
memcpy(dst, (wuff_uint8 *)&i16 + offset, head);
src += 1;
dst += head;
}
for (i = 0; i < samples; i++)
{
i16 = (src[i] - 128) << 8;
memcpy(dst + i * 2, &i16, 2);
}
if (tail != 0)
{
i16 = (src[samples] - 128) << 8;
memcpy(dst + samples * 2, &i16, tail);
}
}
WUFF_CONV_FUNC(wuff_int8_to_int24)
{
wuff_sint32 i24;
size_t i;
if (head != 0)
{
i24 = (src[0] - 128) << 24;
memcpy(dst, (wuff_uint8 *)&i24 + 1 + offset, head);
src += 1;
dst += head;
}
for (i = 0; i < samples; i++)
{
i24 = (src[i] - 128) << 24;
memcpy(dst + i * 3, (wuff_uint8 *)&i24 + 1, 3);
}
if (tail != 0)
{
i24 = (src[samples] - 128) << 24;
memcpy(dst + samples * 3, (wuff_uint8 *)&i24 + 1, tail);
}
}
WUFF_CONV_FUNC(wuff_int8_to_int32)
{
wuff_sint32 i32;
size_t i;
if (head != 0)
{
i32 = (src[0] - 128) << 24;
memcpy(dst, (wuff_uint8 *)&i32 + offset, head);
src += 1;
dst += head;
}
for (i = 0; i < samples; i++)
{
i32 = (src[i] - 128) << 24;
memcpy(dst + i * 4, &i32, 4);
}
if (tail != 0)
{
i32 = (src[samples] - 128) << 24;
memcpy(dst + samples * 4, &i32, tail);
}
}
WUFF_CONV_FUNC(wuff_int8_to_float32)
{
float f32;
size_t i;
if (head != 0)
{
f32 = (float)(src[0] - 128) / 128.0f;
memcpy(dst, (wuff_uint8 *)&f32 + offset, head);
src += 1;
dst += head;
}
for (i = 0; i < samples; i++)
{
f32 = (float)(src[i] - 128) / 128.0f;
memcpy(dst + i * 4, &f32, 4);
}
if (tail != 0)
{
f32 = (float)(src[samples] - 128) / 128.0f;
memcpy(dst + samples * 4, &f32, tail);
}
}
WUFF_CONV_FUNC(wuff_int8_to_float64)
{
double f64;
size_t i;
if (head != 0)
{
f64 = (double)(src[0] - 128) / 128.0;
memcpy(dst, (wuff_uint8 *)&f64 + offset, head);
src += 1;
dst += head;
}
for (i = 0; i < samples; i++)
{
f64 = (double)(src[i] - 128) / 128.0;
memcpy(dst + i * 8, &f64, 8);
}
if (tail != 0)
{
f64 = (double)(src[samples] - 128) / 128.0;
memcpy(dst + samples * 8, &f64, tail);
}
}
/*
* int16 functions.
*/
WUFF_CONV_FUNC(wuff_int16_to_int8)
{
wuff_sint16 i16;
size_t i;
(void)offset; (void)head; (void)tail;
for (i = 0; i < samples; i++)
{
memcpy(&i16, src + i * 2, 2);
dst[i] = (i16 >> 8) + 128;
}
}
WUFF_CONV_FUNC(wuff_int16_to_int16)
{
memcpy(dst, src + offset, samples * 2 + head + tail);
}
WUFF_CONV_FUNC(wuff_int16_to_int24)
{
wuff_sint16 i16;
wuff_sint32 i24;
size_t i;
if (head != 0)
{
memcpy(&i16, src, 2);
i24 = i16 << 16;
memcpy(dst, (wuff_uint8 *)&i24 + 1 + offset, head);
src += 2;
dst += head;
}
for (i = 0; i < samples; i++)
{
memcpy(&i16, src + i * 2, 2);
i24 = i16 << 16;
memcpy(dst + i * 3, (wuff_uint8 *)&i24 + 1, 3);
}
if (tail != 0)
{
memcpy(&i16, src + samples * 2, 2);
i24 = i16 << 16;
memcpy(dst + samples * 3, (wuff_uint8 *)&i24 + 1, tail);
}
}
WUFF_CONV_FUNC(wuff_int16_to_int32)
{
wuff_sint16 i16;
wuff_sint32 i32;
size_t i;
if (head != 0)
{
memcpy(&i16, src, 2);
i32 = i16 << 16;
memcpy(dst, (wuff_uint8 *)&i32 + offset, head);
src += 2;
dst += head;
}
for (i = 0; i < samples; i++)
{
memcpy(&i16, src + i * 2, 2);
i32 = i16 << 16;
memcpy(dst + i * 4, &i32, 4);
}
if (tail != 0)
{
memcpy(&i16, src + samples * 2, 2);
i32 = i16 << 16;
memcpy(dst + samples * 4, &i32, tail);
}
}
WUFF_CONV_FUNC(wuff_int16_to_float32)
{
wuff_sint16 i16;
float f32;
size_t i;
if (head != 0)
{
memcpy(&i16, src, 2);
f32 = (float)i16 / 32768.0f;
memcpy(dst, (wuff_uint8 *)&f32 + offset, head);
src += 2;
dst += head;
}
for (i = 0; i < samples; i++)
{
memcpy(&i16, src + i * 2, 2);
f32 = (float)i16 / 32768.0f;
memcpy(dst + i * 4, &f32, 4);
}
if (tail != 0)
{
memcpy(&i16, src + samples * 2, 2);
f32 = (float)i16 / 32768.0f;
memcpy(dst + samples * 4, &f32, tail);
}
}
WUFF_CONV_FUNC(wuff_int16_to_float64)
{
wuff_sint16 i16;
double f64;
size_t i;
if (head != 0)
{
memcpy(&i16, src, 2);
f64 = (double)i16 / 32768.0;
memcpy(dst, (wuff_uint8 *)&f64 + offset, head);
src += 2;
dst += head;
}
for (i = 0; i < samples; i++)
{
memcpy(&i16, src + i * 2, 2);
f64 = (double)i16 / 32768.0;
memcpy(dst + i * 8, &f64, 8);
}
if (tail != 0)
{
memcpy(&i16, src + samples * 2, 2);
f64 = (double)i16 / 32768.0;
memcpy(dst + samples * 8, &f64, tail);
}
}
/*
* int24 functions.
*/
WUFF_CONV_FUNC(wuff_int24_to_int8)
{
wuff_sint32 i24 = 0;
size_t i;
(void)offset; (void)head; (void)tail;
for (i = 0; i < samples; i++)
{
memcpy((wuff_uint8 *)&i24 + 1, src + i * 3, 3);
dst[i] = (wuff_uint8)((i24 >> 16) + 128);
}
}
WUFF_CONV_FUNC(wuff_int24_to_int16)
{
size_t i;
if (head != 0)
{
memcpy(dst, src + 1 + offset, head);
src += 3;
dst += head;
}
for (i = 0; i < samples; i++)
{
memcpy(dst + i * 2, src + 1 + i * 3, 2);
}
if (tail != 0)
{
memcpy(dst + samples * 2, src + 1 + samples * 3, tail);
}
}
WUFF_CONV_FUNC(wuff_int24_to_int24)
{
memcpy(dst, src + offset, samples * 3 + head + tail);
}
WUFF_CONV_FUNC(wuff_int24_to_int32)
{
wuff_sint32 i32 = 0;
size_t i;
if (head != 0)
{
memcpy((wuff_uint8 *)&i32 + 1, src, 3);
memcpy(dst, (wuff_uint8 *)&i32 + offset, head);
src += 3;
dst += head;
}
for (i = 0; i < samples; i++)
{
memcpy((wuff_uint8 *)&i32 + 1, src + i * 3, 3);
memcpy(dst + i * 4, &i32, 4);
}
if (tail != 0)
{
memcpy((wuff_uint8 *)&i32 + 1, src + samples * 3, 3);
memcpy(dst + samples * 4, &i32, tail);
}
}
WUFF_CONV_FUNC(wuff_int24_to_float32)
{
wuff_sint32 i24 = 0;
float f32;
size_t i;
if (head != 0)
{
memcpy((wuff_uint8 *)&i24 + 1, src, 3);
f32 = (float)((double)i24 / 2147483648.0);
memcpy(dst, (wuff_uint8 *)&f32 + offset, head);
src += 3;
dst += head;
}
for (i = 0; i < samples; i++)
{
memcpy((wuff_uint8 *)&i24 + 1, src + i * 3, 3);
f32 = (float)((double)i24 / 2147483648.0);
memcpy(dst + i * 4, &f32, 4);
}
if (tail != 0)
{
memcpy((wuff_uint8 *)&i24 + 1, src + samples * 3, 3);
f32 = (float)((double)i24 / 2147483648.0);
memcpy(dst + samples * 4, &f32, tail);
}
}
WUFF_CONV_FUNC(wuff_int24_to_float64)
{
wuff_sint32 i24 = 0;
double f64;
size_t i;
if (head != 0)
{
memcpy((wuff_uint8 *)&i24 + 1, src, 3);
f64 = (double)i24 / 2147483648.0;
memcpy(dst, (wuff_uint8 *)&f64 + offset, head);
src += 3;
dst += head;
}
for (i = 0; i < samples; i++)
{
memcpy((wuff_uint8 *)&i24 + 1, src + i * 3, 3);
f64 = (double)i24 / 2147483648.0;
memcpy(dst + i * 8, &f64, 8);
}
if (tail != 0)
{
memcpy((wuff_uint8 *)&i24 + 1, src + samples * 3, 3);
f64 = (double)i24 / 2147483648.0;
memcpy(dst + samples * 8, &f64, tail);
}
}
/*
* int32 functions.
*/
WUFF_CONV_FUNC(wuff_int32_to_int8)
{
wuff_sint32 i32 = 0;
size_t i;
(void)offset; (void)head; (void)tail;
for (i = 0; i < samples; i++)
{
memcpy(&i32, src + i * 4, 4);
dst[i] = (i32 >> 24) + 128;
}
}
WUFF_CONV_FUNC(wuff_int32_to_int16)
{
size_t i;
if (head != 0)
{
memcpy(dst, src + 2 + offset, head);
src += 4;
dst += head;
}
for (i = 0; i < samples; i++)
{
memcpy(dst + i * 2, src + 2 + i * 4, 2);
}
if (tail != 0)
{
memcpy(dst + samples * 2, src + 2 + samples * 4, tail);
}
}
WUFF_CONV_FUNC(wuff_int32_to_int24)
{
size_t i;
if (head != 0)
{
memcpy(dst, src + 1 + offset, head);
src += 4;
dst += head;
}
for (i = 0; i < samples; i++)
{
memcpy(dst + i * 3, src + 1 + i * 4, 3);
}
if (tail != 0)
{
memcpy(dst + samples * 3, src + 1 + samples * 4, tail);
}
}
WUFF_CONV_FUNC(wuff_int32_to_int32)
{
memcpy(dst, src + offset, samples * 4 + head + tail);
}
WUFF_CONV_FUNC(wuff_int32_to_float32)
{
wuff_sint32 i32;
float f32;
size_t i;
if (head != 0)
{
memcpy(&i32, src, 4);
f32 = (float)((double)i32 / 2147483648.0);
memcpy(dst, (wuff_uint8 *)&f32 + offset, head);
src += 4;
dst += head;
}
for (i = 0; i < samples; i++)
{
memcpy(&i32, src + i * 4, 4);
f32 = (float)((double)i32 / 2147483648.0);
memcpy(dst + i * 4, &f32, 4);
}
if (tail != 0)
{
memcpy(&i32, src + samples * 4, 4);
f32 = (float)((double)i32 / 2147483648.0);
memcpy(dst + samples * 4, &f32, tail);
}
}
WUFF_CONV_FUNC(wuff_int32_to_float64)
{
wuff_sint32 i32;
double f64;
size_t i;
if (head != 0)
{
memcpy(&i32, src, 4);
f64 = (double)i32 / 2147483648.0;
memcpy(dst, (wuff_uint8 *)&f64 + offset, head);
src += 4;
dst += head;
}
for (i = 0; i < samples; i++)
{
memcpy(&i32, src + i * 4, 4);
f64 = (double)i32 / 2147483648.0;
memcpy(dst + i * 8, &f64, 8);
}
if (tail != 0)
{
memcpy(&i32, src + samples * 4, 4);
f64 = (double)i32 / 2147483648.0;
memcpy(dst + samples * 8, &f64, tail);
}
}
/*
* float32 functions.
*/
WUFF_CONV_FUNC(wuff_float32_to_int8)
{
float f32;
size_t i;
(void)offset; (void)head; (void)tail;
for (i = 0; i < samples; i++)
{
memcpy(&f32, src + i * 4, 4);
dst[i] = (wuff_uint8)((f32 * 127.5f) + 128.0f);
}
}
WUFF_CONV_FUNC(wuff_float32_to_int16)
{
float f32;
wuff_sint16 i16;
size_t i;
if (head != 0)
{
memcpy(&f32, src, 4);
i16 = (wuff_sint16)(f32 * 32767.5f);
memcpy(dst, (wuff_uint8 *)&i16 + offset, head);
src += 4;
dst += head;
}
for (i = 0; i < samples; i++)
{
memcpy(&f32, src + i * 4, 4);
i16 = (wuff_sint16)(f32 * 32767.5f);
memcpy(dst + i * 2, &i16, 2);
}
if (tail != 0)
{
memcpy(&f32, src + i * 4, 4);
i16 = (wuff_sint16)(f32 * 32767.5f);
memcpy(dst + i * 2, &i16, tail);
}
}
WUFF_CONV_FUNC(wuff_float32_to_int24)
{
float f32;
wuff_sint32 i24;
size_t i;
if (head != 0)
{
memcpy(&f32, src, 4);
i24 = (wuff_sint32)((double)f32 * 2147483647.5);
memcpy(dst, (wuff_uint8 *)&i24 + 1 + offset, head);
src += 4;
dst += head;
}
for (i = 0; i < samples; i++)
{
memcpy(&f32, src + i * 4, 4);
i24 = (wuff_sint32)((double)f32 * 2147483647.5);
memcpy(dst + i * 3, (wuff_uint8 *)&i24 + 1, 3);
}
if (tail != 0)
{
memcpy(&f32, src + samples * 4, 4);
i24 = (wuff_sint32)((double)f32 * 2147483647.5);
memcpy(dst + samples * 3, (wuff_uint8 *)&i24 + 1, tail);
}
}
WUFF_CONV_FUNC(wuff_float32_to_int32)
{
float f32;
wuff_sint32 i32;
size_t i;
if (head != 0)
{
memcpy(&f32, src, 4);
i32 = (wuff_sint32)((double)f32 * 2147483647.5);
memcpy(dst, (wuff_uint8 *)&i32 + offset, head);
src += 4;
dst += head;
}
for (i = 0; i < samples; i++)
{
memcpy(&f32, src + i * 4, 4);
i32 = (wuff_sint32)((double)f32 * 2147483647.5);
memcpy(dst + i * 4, &i32, 4);
}
if (tail != 0)
{
memcpy(&f32, src + samples * 4, 4);
i32 = (wuff_sint32)((double)f32 * 2147483647.5);
memcpy(dst + samples * 4, &i32, tail);
}
}
WUFF_CONV_FUNC(wuff_float32_to_float32)
{
memcpy(dst, src + offset, samples * 4 + head + tail);
}
WUFF_CONV_FUNC(wuff_float32_to_float64)
{
float f32;
double f64;
size_t i;
if (head != 0)
{
memcpy(&f32, src, 4);
f64 = f32;
memcpy(dst, (wuff_uint8 *)&f64 + offset, head);
src += 4;
dst += head;
}
for (i = 0; i < samples; i++)
{
memcpy(&f32, src + i * 4, 4);
f64 = f32;
memcpy(dst + i * 8, &f64, 8);
}
if (tail != 0)
{
memcpy(&f32, src + samples * 4, 4);
f64 = f32;
memcpy(dst + samples * 8, &f64, tail);
}
}
/*
* float64 functions.
*/
WUFF_CONV_FUNC(wuff_float64_to_int8)
{
double f64;
size_t i;
(void)offset; (void)head; (void)tail;
for (i = 0; i < samples; i++)
{
memcpy(&f64, src + i * 8, 8);
dst[i] = (wuff_uint8)((f64 * 127.5) + 128.0);
}
}
WUFF_CONV_FUNC(wuff_float64_to_int16)
{
double f64;
wuff_sint16 i16;
size_t i;
if (head != 0)
{
memcpy(&f64, src, 8);
i16 = (wuff_sint16)(f64 * 32767.5);
memcpy(dst, (wuff_uint8 *)&i16 + offset, head);
src += 8;
dst += head;
}
for (i = 0; i < samples; i++)
{
memcpy(&f64, src + i * 8, 8);
i16 = (wuff_sint16)(f64 * 32767.5);
memcpy(dst + i * 2, &i16, 2);
}
if (tail != 0)
{
memcpy(&f64, src + i * 8, 8);
i16 = (wuff_sint16)(f64 * 32767.5);
memcpy(dst + i * 2, &i16, tail);
}
}
WUFF_CONV_FUNC(wuff_float64_to_int24)
{
double f64;
wuff_sint32 i24;
size_t i;
if (head != 0)
{
memcpy(&f64, src, 8);
i24 = (wuff_sint32)(f64 * 2147483647.5);
memcpy(dst, (wuff_uint8 *)&i24 + 1 + offset, head);
src += 8;
dst += head;
}
for (i = 0; i < samples; i++)
{
memcpy(&f64, src + i * 8, 8);
i24 = (wuff_sint32)(f64 * 2147483647.5);
memcpy(dst + i * 3, (wuff_uint8 *)&i24 + 1, 3);
}
if (tail != 0)
{
memcpy(&f64, src + samples * 8, 8);
i24 = (wuff_sint32)(f64 * 2147483647.5);
memcpy(dst + samples * 3, (wuff_uint8 *)&i24 + 1, tail);
}
}
WUFF_CONV_FUNC(wuff_float64_to_int32)
{
double f64;
wuff_sint32 i32;
size_t i;
if (head != 0)
{
memcpy(&f64, src, 8);
i32 = (wuff_sint32)(f64 * 2147483647.5);
memcpy(dst, (wuff_uint8 *)&i32 + offset, head);
src += 8;
dst += head;
}
for (i = 0; i < samples; i++)
{
memcpy(&f64, src + i * 8, 8);
i32 = (wuff_sint32)(f64 * 2147483647.5);
memcpy(dst + i * 4, &i32, 4);
}
if (tail != 0)
{
memcpy(&f64, src + samples * 8, 8);
i32 = (wuff_sint32)(f64 * 2147483647.5);
memcpy(dst + samples * 4, &i32, tail);
}
}
WUFF_CONV_FUNC(wuff_float64_to_float32)
{
double f64;
float f32;
size_t i;
if (head != 0)
{
memcpy(&f64, src, 8);
f32 = (float)f64;
memcpy(dst, (wuff_uint8 *)&f32 + offset, head);
src += 8;
dst += head;
}
for (i = 0; i < samples; i++)
{
memcpy(&f64, src + i * 8, 8);
f32 = (float)f64;
memcpy(dst + i * 4, &f32, 4);
}
if (tail != 0)
{
memcpy(&f64, src + samples * 8, 8);
f32 = (float)f64;
memcpy(dst + samples * 4, &f32, tail);
}
}
WUFF_CONV_FUNC(wuff_float64_to_float64)
{
memcpy(dst, src + offset, samples * 8 + head + tail);
}
+48
View File
@@ -0,0 +1,48 @@
#ifndef WUFF_CONVERT_H
#define WUFF_CONVERT_H
#define WUFF_CONV_FUNC(name) WUFF_INTERN_API void name(wuff_uint8 * dst, wuff_uint8 * src, size_t samples, wuff_uint8 offset, wuff_uint8 head, wuff_uint8 tail)
WUFF_CONV_FUNC(wuff_int8_to_int8);
WUFF_CONV_FUNC(wuff_int8_to_int16);
WUFF_CONV_FUNC(wuff_int8_to_int24);
WUFF_CONV_FUNC(wuff_int8_to_int32);
WUFF_CONV_FUNC(wuff_int8_to_float32);
WUFF_CONV_FUNC(wuff_int8_to_float64);
WUFF_CONV_FUNC(wuff_int16_to_int8);
WUFF_CONV_FUNC(wuff_int16_to_int16);
WUFF_CONV_FUNC(wuff_int16_to_int24);
WUFF_CONV_FUNC(wuff_int16_to_int32);
WUFF_CONV_FUNC(wuff_int16_to_float32);
WUFF_CONV_FUNC(wuff_int16_to_float64);
WUFF_CONV_FUNC(wuff_int24_to_int8);
WUFF_CONV_FUNC(wuff_int24_to_int16);
WUFF_CONV_FUNC(wuff_int24_to_int24);
WUFF_CONV_FUNC(wuff_int24_to_int32);
WUFF_CONV_FUNC(wuff_int24_to_float32);
WUFF_CONV_FUNC(wuff_int24_to_float64);
WUFF_CONV_FUNC(wuff_int32_to_int8);
WUFF_CONV_FUNC(wuff_int32_to_int16);
WUFF_CONV_FUNC(wuff_int32_to_int24);
WUFF_CONV_FUNC(wuff_int32_to_int32);
WUFF_CONV_FUNC(wuff_int32_to_float32);
WUFF_CONV_FUNC(wuff_int32_to_float64);
WUFF_CONV_FUNC(wuff_float32_to_int8);
WUFF_CONV_FUNC(wuff_float32_to_int16);
WUFF_CONV_FUNC(wuff_float32_to_int24);
WUFF_CONV_FUNC(wuff_float32_to_int32);
WUFF_CONV_FUNC(wuff_float32_to_float32);
WUFF_CONV_FUNC(wuff_float32_to_float64);
WUFF_CONV_FUNC(wuff_float64_to_int8);
WUFF_CONV_FUNC(wuff_float64_to_int16);
WUFF_CONV_FUNC(wuff_float64_to_int24);
WUFF_CONV_FUNC(wuff_float64_to_int32);
WUFF_CONV_FUNC(wuff_float64_to_float32);
WUFF_CONV_FUNC(wuff_float64_to_float64);
#endif /* WUFF_CONVERT_H */
+540
View File
@@ -0,0 +1,540 @@
#include <stdlib.h>
#include <string.h>
#include "wuff_config.h"
#include "wuff.h"
#include "wuff_internal.h"
#include "wuff_convert.h"
wuff_sint32 wuff_setup(struct wuff_handle * handle)
{
wuff_sint32 wuff_status;
if (handle == NULL)
return WUFF_INVALID_PARAM;
wuff_status = wuff_init_stream(handle);
WUFF_STATUS_BAIL()
/* Allocating the buffer for the handle requires information from the stream. */
wuff_status = wuff_buffer_alloc(handle);
WUFF_STATUS_BAIL()
/* The output format defaults to the stream format. */
wuff_status = wuff_format(handle, handle->stream.format);
WUFF_STATUS_BAIL()
return WUFF_SUCCESS;
}
wuff_sint32 wuff_cleanup(struct wuff_handle * handle)
{
if (handle == NULL)
return WUFF_INVALID_PARAM;
if (handle->buffer.data != NULL)
wuff_free(handle->buffer.data);
wuff_free(handle);
return WUFF_SUCCESS;
}
wuff_sint32 wuff_set_output_format(struct wuff_handle * handle, wuff_uint16 format)
{
wuff_uint16 bits;
wuff_uint16 stream_format;
if (handle == NULL)
return WUFF_INVALID_PARAM;
else if (format >= WUFF_FORMAT_MAX)
return WUFF_FORMAT_UNSUPPORTED;
stream_format = handle->stream.format;
switch (format)
{
case WUFF_FORMAT_PCM_U8:
bits = 8;
switch (stream_format)
{
case WUFF_FORMAT_PCM_U8:
handle->output.function = wuff_int8_to_int8;
break;
case WUFF_FORMAT_PCM_S16:
handle->output.function = wuff_int16_to_int8;
break;
case WUFF_FORMAT_PCM_S24:
handle->output.function = wuff_int24_to_int8;
break;
case WUFF_FORMAT_PCM_S32:
handle->output.function = wuff_int32_to_int8;
break;
case WUFF_FORMAT_IEEE_FLOAT_32:
handle->output.function = wuff_float32_to_int8;
break;
case WUFF_FORMAT_IEEE_FLOAT_64:
handle->output.function = wuff_float64_to_int8;
break;
}
break;
case WUFF_FORMAT_PCM_S16:
bits = 16;
switch (stream_format)
{
case WUFF_FORMAT_PCM_U8:
handle->output.function = wuff_int8_to_int16;
break;
case WUFF_FORMAT_PCM_S16:
handle->output.function = wuff_int16_to_int16;
break;
case WUFF_FORMAT_PCM_S24:
handle->output.function = wuff_int24_to_int16;
break;
case WUFF_FORMAT_PCM_S32:
handle->output.function = wuff_int32_to_int16;
break;
case WUFF_FORMAT_IEEE_FLOAT_32:
handle->output.function = wuff_float32_to_int16;
break;
case WUFF_FORMAT_IEEE_FLOAT_64:
handle->output.function = wuff_float64_to_int16;
break;
}
break;
case WUFF_FORMAT_PCM_S24:
bits = 24;
switch (stream_format)
{
case WUFF_FORMAT_PCM_U8:
handle->output.function = wuff_int8_to_int24;
break;
case WUFF_FORMAT_PCM_S16:
handle->output.function = wuff_int16_to_int24;
break;
case WUFF_FORMAT_PCM_S24:
handle->output.function = wuff_int24_to_int24;
break;
case WUFF_FORMAT_PCM_S32:
handle->output.function = wuff_int32_to_int24;
break;
case WUFF_FORMAT_IEEE_FLOAT_32:
handle->output.function = wuff_float32_to_int24;
break;
case WUFF_FORMAT_IEEE_FLOAT_64:
handle->output.function = wuff_float64_to_int24;
break;
}
break;
case WUFF_FORMAT_PCM_S32:
bits = 32;
switch (stream_format)
{
case WUFF_FORMAT_PCM_U8:
handle->output.function = wuff_int8_to_int32;
break;
case WUFF_FORMAT_PCM_S16:
handle->output.function = wuff_int16_to_int32;
break;
case WUFF_FORMAT_PCM_S24:
handle->output.function = wuff_int24_to_int32;
break;
case WUFF_FORMAT_PCM_S32:
handle->output.function = wuff_int32_to_int32;
break;
case WUFF_FORMAT_IEEE_FLOAT_32:
handle->output.function = wuff_float32_to_int32;
break;
case WUFF_FORMAT_IEEE_FLOAT_64:
handle->output.function = wuff_float64_to_int32;
break;
}
break;
case WUFF_FORMAT_IEEE_FLOAT_32:
bits = 32;
switch (stream_format)
{
case WUFF_FORMAT_PCM_U8:
handle->output.function = wuff_int8_to_float32;
break;
case WUFF_FORMAT_PCM_S16:
handle->output.function = wuff_int16_to_float32;
break;
case WUFF_FORMAT_PCM_S24:
handle->output.function = wuff_int24_to_float32;
break;
case WUFF_FORMAT_PCM_S32:
handle->output.function = wuff_int32_to_float32;
break;
case WUFF_FORMAT_IEEE_FLOAT_32:
handle->output.function = wuff_float32_to_float32;
break;
case WUFF_FORMAT_IEEE_FLOAT_64:
handle->output.function = wuff_float64_to_float32;
break;
}
break;
case WUFF_FORMAT_IEEE_FLOAT_64:
bits = 64;
switch (stream_format)
{
case WUFF_FORMAT_PCM_U8:
handle->output.function = wuff_int8_to_float64;
break;
case WUFF_FORMAT_PCM_S16:
handle->output.function = wuff_int16_to_float64;
break;
case WUFF_FORMAT_PCM_S24:
handle->output.function = wuff_int24_to_float64;
break;
case WUFF_FORMAT_PCM_S32:
handle->output.function = wuff_int32_to_float64;
break;
case WUFF_FORMAT_IEEE_FLOAT_32:
handle->output.function = wuff_float32_to_float64;
break;
case WUFF_FORMAT_IEEE_FLOAT_64:
handle->output.function = wuff_float64_to_float64;
break;
}
break;
default:
return WUFF_FORMAT_UNSUPPORTED;
}
handle->output.format = format;
handle->output.bytes_per_sample = bits / 8;
handle->output.block_size = handle->stream.header.channels * (bits / 8);
return WUFF_SUCCESS;
}
wuff_sint32 wuff_check_bits(wuff_uint16 bits, wuff_uint16 * format)
{
if (*format == WUFF_FORMAT_PCM)
{
switch (bits)
{
case 8:
*format = WUFF_FORMAT_PCM_U8;
break;
case 16:
*format = WUFF_FORMAT_PCM_S16;
break;
case 24:
*format = WUFF_FORMAT_PCM_S24;
break;
case 32:
*format = WUFF_FORMAT_PCM_S32;
break;
default:
return WUFF_FORMAT_UNSUPPORTED;
}
}
else if (*format == WUFF_FORMAT_IEEE_FLOAT)
{
switch (bits)
{
case 32:
*format = WUFF_FORMAT_IEEE_FLOAT_32;
break;
case 64:
*format = WUFF_FORMAT_IEEE_FLOAT_64;
break;
default:
return WUFF_FORMAT_UNSUPPORTED;
}
}
else
{
return WUFF_FORMAT_UNSUPPORTED;
}
return WUFF_SUCCESS;
}
size_t wuff_calculate_samples(size_t target_size, wuff_uint8 sample_size, wuff_uint8 * head, wuff_uint8 * tail)
{
size_t samples = 0;
if (*head != 0)
{
if (target_size <= *head)
{
*head = (wuff_uint8)target_size;
*tail = 0;
return 1;
}
target_size -= *head;
++samples;
}
samples = target_size / sample_size;
*tail = target_size % sample_size;
if (*tail != 0)
++samples;
return samples;
}
wuff_sint32 wuff_init_stream(struct wuff_handle * handle)
{
/* Allocate some space on the stack. */
/* No need to do dynamic allocation for simple header probing. */
wuff_uint8 buffer[WUFF_HEADER_FETCH_SIZE];
size_t buffer_size = WUFF_HEADER_FETCH_SIZE;
wuff_uint64 search_offset;
struct wuff_chunk_header chunk;
wuff_sint32 wuff_status;
wuff_status = handle->callback->read(handle->userdata, buffer, &buffer_size);
WUFF_STATUS_BAIL()
else if (buffer_size < WUFF_STREAM_MIN_SIZE)
return WUFF_STREAM_NOT_RIFF;
/* Check for RIFF signature. */
wuff_copy_chunk_header_data(&chunk, buffer);
if (chunk.id != WUFF_RIFF_CHUNK_ID)
return WUFF_STREAM_NOT_RIFF;
handle->stream.size = chunk.size;
/* Check for WAVE format. */
wuff_copy_chunk_header_data(&chunk, buffer + 8);
if (chunk.id != WUFF_WAVE_CHUNK_ID)
return WUFF_STREAM_NOT_WAVE;
/* Search fmt chunk. */
wuff_copy_chunk_header_data(&chunk, buffer + 12);
search_offset = 12;
if (chunk.id != WUFF_FORMAT_CHUNK_ID)
{
chunk.id = 0;
/* The fmt chunk must appear before the data chunk. */
wuff_status = wuff_search_chunk(handle, &chunk, &search_offset, WUFF_FORMAT_CHUNK_ID, WUFF_DATA_CHUNK_ID);
if (wuff_status == WUFF_STREAM_CHUNK_NOT_FOUND)
return WUFF_STREAM_FORMAT_CHUNK_MISSING;
else WUFF_STATUS_BAIL()
/* In case the fmt chunk is not the first chunk, align it on the stack buffer as if it were. */
buffer_size = WUFF_HEADER_FETCH_SIZE - 20;
wuff_status = handle->callback->read(handle->userdata, buffer + 20, &buffer_size);
WUFF_STATUS_BAIL()
/* EOF bail. */
else if (buffer_size < WUFF_HEADER_MIN_SIZE)
return WUFF_STREAM_INVALID;
}
/* Extract header information. */
handle->stream.header.size = chunk.size;
handle->stream.header.offset = search_offset + 8;
handle->stream.header.format = wuff_get_uint16(buffer + 20);
handle->stream.header.channels = wuff_get_uint16(buffer + 22);
handle->stream.header.sample_rate = wuff_get_uint32(buffer + 24);
handle->stream.header.bits_per_sample = wuff_get_uint16(buffer + 34);
handle->stream.header.bytes_per_sample = handle->stream.header.bits_per_sample / 8;
handle->stream.header.block_size = handle->stream.header.channels * handle->stream.header.bytes_per_sample;
/* Bail on invalid streams. */
if (handle->stream.header.channels == 0)
return WUFF_STREAM_ZERO_CHANNELS;
else if (handle->stream.header.sample_rate == 0)
return WUFF_STREAM_ZERO_SAMPLE_RATE;
else if (handle->stream.header.bits_per_sample == 0)
return WUFF_STREAM_ZERO_BITS_PER_SAMPLE;
/* Grab the format from the extended header. */
if (handle->stream.header.size > WUFF_HEADER_MIN_SIZE && wuff_get_uint16(buffer + 36) == 22)
{
if (handle->stream.header.format == WUFF_FORMAT_EXTENSIBLE)
handle->stream.header.format = wuff_get_uint16(buffer + 44);
}
/* The check if this format is actually supported. */
handle->stream.format = handle->stream.header.format;
wuff_status = wuff_check_bits(handle->stream.header.bits_per_sample, &handle->stream.format);
WUFF_STATUS_BAIL()
/* The search for the data chunk begins. */
wuff_copy_chunk_header_data(&chunk, buffer + 20 + handle->stream.header.size);
search_offset = handle->stream.header.offset + handle->stream.header.size;
wuff_status = wuff_search_chunk(handle, &chunk, &search_offset, WUFF_DATA_CHUNK_ID, 0);
if (wuff_status == WUFF_STREAM_CHUNK_NOT_FOUND)
return WUFF_STREAM_DATA_CHUNK_MISSING;
else WUFF_STATUS_BAIL()
handle->stream.data.size = chunk.size;
handle->stream.data.offset = search_offset + 8;
handle->stream.length = handle->stream.data.size / handle->stream.header.channels / handle->stream.header.bytes_per_sample;
handle->stream.position = 0;
return WUFF_SUCCESS;
}
wuff_sint32 wuff_search_chunk(struct wuff_handle * handle, struct wuff_chunk_header * chunk, wuff_uint64 * offset, wuff_uint32 id, wuff_uint32 stop_id)
{
wuff_uint8 buffer[8];
wuff_uint64 search_offset;
size_t buffer_size;
wuff_sint32 wuff_status = 0;
if (chunk->id != 0 && chunk->id == id)
return WUFF_SUCCESS;
/* Copy the current file position. */
search_offset = *offset;
while (wuff_status >= 0)
{
search_offset += 8 + chunk->size;
/* FIXME: Non-compliant RIFFs may not pad to WORD alignment. What now? */
if (search_offset & 1)
search_offset++;
wuff_status = handle->callback->seek(handle->userdata, search_offset);
WUFF_STATUS_BAIL()
/*else if (wuff_status == WUFF_CALLBACK_EOF)
return WUFF_STREAM_CHUNK_NOT_FOUND;*/
buffer_size = 8;
wuff_status = handle->callback->read(handle->userdata, buffer, &buffer_size);
WUFF_STATUS_BAIL()
wuff_copy_chunk_header_data(chunk, buffer);
/* Bail if we're at the EOF or the stop id. */
if (buffer_size < 8 || (stop_id != 0 && chunk->id == stop_id))
return WUFF_STREAM_CHUNK_NOT_FOUND;
else if (chunk->id == id)
break;
}
/* Report chunk offset. */
*offset = search_offset;
return WUFF_SUCCESS;
}
wuff_sint32 wuff_buffer_alloc(struct wuff_handle * handle)
{
wuff_sint32 wuff_status;
if (handle == NULL)
return WUFF_INVALID_PARAM;
/* Try to allocate a buffer for 0.25 seconds, but clamp at some minimum and maximum value. */
handle->buffer.size = handle->stream.header.sample_rate * handle->stream.header.block_size / 4;
if (handle->buffer.size < WUFF_BUFFER_MIN_SIZE)
handle->buffer.size = WUFF_BUFFER_MIN_SIZE;
else if (handle->buffer.size > WUFF_BUFFER_MAX_SIZE)
handle->buffer.size = WUFF_BUFFER_MAX_SIZE;
handle->buffer.data = wuff_alloc(handle->buffer.size);
if (handle->buffer.data == NULL)
return WUFF_MEMALLOC_ERROR;
/* Just in case, let's null the offsets. */
wuff_status = wuff_buffer_clear(handle);
WUFF_STATUS_BAIL()
return WUFF_SUCCESS;
}
wuff_sint32 wuff_buffer_clear(struct wuff_handle * handle)
{
wuff_uint64 position;
wuff_sint32 wuff_status;
if (handle == NULL)
return WUFF_INVALID_PARAM;
wuff_status = handle->callback->tell(handle->userdata, &position);
WUFF_STATUS_BAIL()
if (position < handle->stream.data.offset || position > handle->stream.data.offset + handle->stream.data.size)
return WUFF_BUFFER_INVALID_STREAM_POSITION;
handle->buffer.bytes_left = handle->stream.data.size - (position - handle->stream.data.offset);
handle->buffer.offset = 0;
handle->buffer.end = 0;
return WUFF_SUCCESS;
}
wuff_sint32 wuff_buffer_fill(struct wuff_handle * handle)
{
size_t bytes_in_buffer;
size_t bytes_to_read;
wuff_sint32 wuff_status;
if (handle == NULL)
return WUFF_INVALID_PARAM;
/* Check if there are bytes in the buffer and move them to the start of the buffer. */
/* Probably not the most efficient way. Think on it some more! */
bytes_in_buffer = handle->buffer.end - handle->buffer.offset;
if (bytes_in_buffer == handle->buffer.size)
return WUFF_SUCCESS;
else if (bytes_in_buffer > 0)
memmove(handle->buffer.data, handle->buffer.data + handle->buffer.offset, bytes_in_buffer);
bytes_to_read = handle->buffer.size - bytes_in_buffer;
if (bytes_to_read > handle->buffer.bytes_left)
bytes_to_read = (size_t)handle->buffer.bytes_left;
wuff_status = handle->callback->read(handle->userdata, handle->buffer.data + bytes_in_buffer, &bytes_to_read);
WUFF_STATUS_BAIL()
handle->buffer.offset = 0;
handle->buffer.end = bytes_in_buffer + bytes_to_read;
handle->buffer.bytes_left -= bytes_to_read;
return WUFF_SUCCESS;
}
wuff_sint32 wuff_buffer_release(struct wuff_handle * handle, size_t samples)
{
size_t size;
if (handle == NULL)
return WUFF_INVALID_PARAM;
size = samples * handle->stream.header.bytes_per_sample;
/* Check for an attempt to release more samples than the buffer could hold. */
/* "This should never happen." Let's throw an error anyway in case.*/
if (size > handle->buffer.end - handle->buffer.offset)
return WUFF_BUFFER_INVALID_SIZE;
handle->buffer.offset += size;
return WUFF_SUCCESS;
}
wuff_sint32 wuff_buffer_request(struct wuff_handle * handle, wuff_uint8 ** buffer, size_t * samples)
{
size_t request_samples = *samples;
size_t buffer_samples, size;
size_t bps = handle->stream.header.bytes_per_sample;
wuff_sint32 wuff_status;
if (handle == NULL || buffer == NULL || samples == NULL)
return WUFF_INVALID_PARAM;
/* Fill the buffer some more if the requested size is bigger than the current data in the buffer. */
size = request_samples * bps;
if (size > handle->buffer.end - handle->buffer.offset)
{
wuff_status = wuff_buffer_fill(handle);
WUFF_STATUS_BAIL()
}
buffer_samples = (handle->buffer.end - handle->buffer.offset) / bps;
/* Report sample count change. */
if (buffer_samples < request_samples)
*samples = buffer_samples;
/* Report sample buffer start. */
*buffer = handle->buffer.data + handle->buffer.offset;
return WUFF_SUCCESS;
}
+171
View File
@@ -0,0 +1,171 @@
#ifndef WUFF_INTERNAL_H
#define WUFF_INTERNAL_H
#define WUFF_BUFFER_MIN_SIZE 4096
#define WUFF_BUFFER_MAX_SIZE 2097152
#define WUFF_STREAM_MIN_SIZE 36
#define WUFF_HEADER_MIN_SIZE 16
#define WUFF_HEADER_FETCH_SIZE 80
#define WUFF_FORMAT_PCM 1
#define WUFF_FORMAT_IEEE_FLOAT 3
#define WUFF_FORMAT_EXTENSIBLE 0xFFFE
#define WUFF_RIFF_CHUNK_ID wuff_get_chunk_id("RIFF")
#define WUFF_WAVE_CHUNK_ID wuff_get_chunk_id("WAVE")
#define WUFF_FORMAT_CHUNK_ID wuff_get_chunk_id("fmt ")
#define WUFF_DATA_CHUNK_ID wuff_get_chunk_id("data")
#define WUFF_STATUS_BAIL() if (wuff_status < 0) return wuff_status;
static WUFF_INLINE wuff_uint32 wuff_get_uint32(wuff_uint8 * data)
{
return data[0] + (data[1] << 8) + (data[2] << 16) + (data[3] << 24);
}
static WUFF_INLINE wuff_uint16 wuff_get_uint16(wuff_uint8 * data)
{
return data[0] + (data[1] << 8);
}
struct wuff_chunk_header
{
wuff_uint32 id;
wuff_uint32 size;
};
static WUFF_INLINE wuff_uint32 wuff_get_chunk_id(const char txt[5])
{
const wuff_uint8 * id = (wuff_uint8*) txt;
wuff_uint32 int_id;
wuff_uint8 * id_bytes = (wuff_uint8 *)&int_id;
id_bytes[0] = id[0];
id_bytes[1] = id[1];
id_bytes[2] = id[2];
id_bytes[3] = id[3];
return int_id;
}
static WUFF_INLINE void wuff_copy_chunk_header_data(struct wuff_chunk_header * chunk, wuff_uint8 * data)
{
wuff_uint8 * id = (wuff_uint8 *)&chunk->id;
id[0] = data[0];
id[1] = data[1];
id[2] = data[2];
id[3] = data[3];
chunk->size = wuff_get_uint32(data + 4);
}
struct wuff_stream_header
{
wuff_uint64 size;
wuff_uint64 offset;
wuff_uint16 format;
wuff_uint16 channels;
wuff_uint32 sample_rate;
wuff_uint16 bits_per_sample;
wuff_uint16 bytes_per_sample;
size_t block_size;
};
struct wuff_stream_data
{
wuff_uint64 size;
wuff_uint64 offset;
};
struct wuff_buffer
{
wuff_uint8 * data;
wuff_uint64 bytes_left;
size_t size;
size_t offset;
size_t end;
};
struct wuff_output
{
wuff_uint16 format;
size_t bytes_per_sample;
size_t block_size;
size_t block_offset;
void (* function)(wuff_uint8 *, wuff_uint8 *, size_t, wuff_uint8, wuff_uint8, wuff_uint8);
};
struct wuff_stream
{
wuff_uint64 size;
wuff_uint64 length;
wuff_uint16 format;
wuff_uint64 position;
struct wuff_stream_header header;
struct wuff_stream_data data;
};
struct wuff_handle
{
struct wuff_stream stream;
struct wuff_buffer buffer;
struct wuff_output output;
struct wuff_callback * callback;
void * userdata;
};
/* Initializes the stream, allocates the buffer, and sets the output format. */
/* Expects a nulled wuff_handle and the callbacks set and ready. */
WUFF_INTERN_API wuff_sint32 wuff_setup(struct wuff_handle * handle);
/* Cleans the stream up, frees the buffer and the wuff_handle. */
WUFF_INTERN_API wuff_sint32 wuff_cleanup(struct wuff_handle * handle);
/* Called by wuff_setup. Initializes the stream by reading the data from the */
/* callbacks, searching for headers and stream information. */
WUFF_INTERN_API wuff_sint32 wuff_init_stream(struct wuff_handle * handle);
/* Searches for a specific chunk id and stops before another if it's not 0. */
/* If the id in wuff_chunk_header is not 0, it will be checked too and if */
/* they match, then the function will return immediately. */
/* Expects offset to point to the file position of a chunk and */
/* wuff_chunk_header to have the size of this chunk. */
WUFF_INTERN_API wuff_sint32 wuff_search_chunk(struct wuff_handle * handle, struct wuff_chunk_header * chunk, wuff_uint64 * offset, wuff_uint32 id, wuff_uint32 stop_id);
/* Sets the output struct of the stream to the new format. */
WUFF_INTERN_API wuff_sint32 wuff_set_output_format(struct wuff_handle * handle, wuff_uint16);
/* Checks if the number of bits per samples is supported and writes the */
/* output identifier to the 16-bit integer. */
WUFF_INTERN_API wuff_sint32 wuff_check_bits(wuff_uint16 bits, wuff_uint16 * format);
/* Calculates the number of samples that have to be requested from the buffer */
/* by also taking the truncated samples at the start and end into account. */
/* The return value is the number of samples needed. */
WUFF_INTERN_API size_t wuff_calculate_samples(size_t target_size, wuff_uint8 sample_size, wuff_uint8 * head, wuff_uint8 * tail);
/* Allocates the buffer for the input stream. */
/* Expects the stream to be initialized, as format information is needed. */
WUFF_INTERN_API wuff_sint32 wuff_buffer_alloc(struct wuff_handle * handle);
/* Fills the buffer with new data. */
WUFF_INTERN_API wuff_sint32 wuff_buffer_fill(struct wuff_handle * handle);
/* Marks all bytes in the buffer as free. */
WUFF_INTERN_API wuff_sint32 wuff_buffer_clear(struct wuff_handle * handle);
/* Requests samples and a pointer to them. */
/* The number of samples may be lower than requested. */
WUFF_INTERN_API wuff_sint32 wuff_buffer_request(struct wuff_handle * handle, wuff_uint8 ** buffer, size_t * samples);
/* Releases the number of samples from the buffer. */
WUFF_INTERN_API wuff_sint32 wuff_buffer_release(struct wuff_handle * handle, size_t samples);
#endif /* WUFF_INTERNAL_H */
+17
View File
@@ -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