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