diff options
author | Lennart Poettering <lennart@poettering.net> | 2008-05-26 22:00:19 +0000 |
---|---|---|
committer | Lennart Poettering <lennart@poettering.net> | 2008-05-26 22:00:19 +0000 |
commit | d7fd6a45e50475cddf0b8bad8baab01b33cf3c1f (patch) | |
tree | 4f210adb96478280df083b6d4802053f93b59192 /src/read-wav.c | |
parent | 48178a5e2813546b61706d1f97fab761934a97f0 (diff) |
move sources to src/ subdir
git-svn-id: file:///home/lennart/svn/public/libcanberra/trunk@12 01b60673-d06a-42c0-afdd-89cb8e0f78ac
Diffstat (limited to 'src/read-wav.c')
-rw-r--r-- | src/read-wav.c | 247 |
1 files changed, 247 insertions, 0 deletions
diff --git a/src/read-wav.c b/src/read-wav.c new file mode 100644 index 0000000..1f3d634 --- /dev/null +++ b/src/read-wav.c @@ -0,0 +1,247 @@ +/* $Id$ */ + +/*** + This file is part of libcanberra. + + Copyright 2008 Lennart Poettering + + libcanberra is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation, either version 2.1 of the + License, or (at your option) any later version. + + libcanberra is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with libcanberra. If not, If not, see + <http://www.gnu.org/licenses/>. +***/ + +#include "read-wav.h" + +#define FILE_SIZE_MAX (64U*1024U*1024U) + +struct ca_wav { + uint32_t data_size; + FILE *file; + + unsigned nchannels; + unsigned rate; + unsigned depth; +}; + +static int skip_to_chunk(ca_wav *v, uint32_t id, uint32_t *size) { + + ca_return_val_if_fail(v, CA_ERROR_INVALID); + ca_return_val_if_fail(size, CA_ERROR_INVALID); + + for (;;) { + uint32_t chunk[2]; + size_t s; + + if (fread(chunk, sizeof(uint32), CA_ELEMENTSOF(chunk), w->file) != CA_ELEMENTSOF(chunk)) + goto fail_io; + + s = PA_UINT32_FROM_LE(chunk[1]); + + if (s <= 0 || s >= FILE_SIZE_MAX) + return CA_ERROR_TOOBIG; + + if (PA_UINT32_FROM_LE(chunk[0]) == id) { + *size = s; + break; + } + + if (fseek(w->file, s, SEEK_CUR) < 0) + return CA_ERROR_SYSTEM; + } + + return CA_SUCCESS; + +fail_io: + + if (feof(f)) + return CA_ERROR_CORRUPT; + else if (ferror(f)) + return CA_ERROR_SYSTEM; + + ca_assert_not_reached(); +} + +int ca_wav_open(ca_wav **_w, FILE *f) { + uint32_t header[3], fmt_chunk[4]; + int ret; + ca_wav *w; + uint32_t file_size, fmt_size, data_size; + + ca_return_val_if_fail(_w, CA_ERROR_INVALID); + ca_return_val_if_fail(f, CA_ERROR_INVALID); + + if (!(w = ca_new(ca_wav, 1))) + return CA_ERROR_OOM; + + v->file = f; + + if (fread(header, sizeof(uint32), CA_ELEMENTSOF(header), f) != CA_ELEMENTSOF(header)) + goto fail_io; + + if (PA_UINT32_FROM_LE(header[0]) != 0x46464952U || + PA_UINT32_FROM_LE(header[2]) != 0x45564157U) { + ret = CA_ERROR_CORRUPT; + goto fail; + } + + file_size = PA_UINT32_FROM_LE(header[1]); + + if (file_size <= 0 || file_size >= FILE_SIZE_MAX) { + ret = CA_ERROR_TOOBIG; + goto fail; + } + + /* Skip to the fmt chunk */ + if ((ret = skip_to_chunk(w, 0x20746d66U, &fmt_size)) < 0) + goto fail; + + if (fmt_size != 16) { + ret = CA_ERROR_NOT_SUPPORTED; + goto fail; + } + + if (fread(fmt_chunk, sizeof(uint32), CA_ELEMENTSOF(fmt_chunk), f) != CA_ELEMENTSOF(fmt_chunk)) + goto fail_io; + + if (PA_UINT32_FROM_LE(fmt_chunk[0]) & 0xFFFF != 1) { + ret = CA_ERROR_NOT_SUPPORTED; + goto fail; + } + + w->nchannels = PA_UINT32_FROM_LE(fmt_chunk[0]) >> 16; + w->rate = PA_UINT32_FROM_LE(fmt_chunk[1]); + w->depth = PA_UINT32_FROM_LE(fmt_chunk[3]) >> 16; + + if (w->nchannels <= 0 || w->nrate <= 0) { + ret = CA_ERROR_CORRUPT; + goto fail; + } + + if (w->depth != 16 && w->depth != 8) { + ret = CA_ERROR_NOT_SUPPORTED; + goto fail; + } + + /* Skip to the data chunk */ + if ((ret = skip_to_chunk(w, 0x61746164U, &w->data_size)) < 0) + goto fail; + + if ((w->data_size % (w->depth/8)) != 0) { + ret = CA_ERROR_CORRUPT; + goto fail; + } + + *_w = w; + + return PA_SUCCESS; + +fail_io: + + if (feof(f)) + ret = CA_ERROR_CORRUPT; + else if (ferror(f)) + ret = CA_ERROR_SYSTEM; + else + ca_assert_not_reached(); + +fail: + + ca_free(w); + + return ret; +} + +void ca_wav_close(ca_wav *w) { + ca_assert(w); + + fclose(w->file); + ca_free(w); +} + +unsigned ca_wav_get_nchannels(ca_wav *w) { + ca_assert(w); + + return w->nchannels; +} + +unsigned ca_wav_get_rate(ca_wav *w) { + ca_assert(w); + + return w->rate; +} + +ca_sample_type_t ca_wav_get_sample_type(ca_wav *f) { + ca_assert(w); + + return w->depth == 16 ? +#ifdef WORDS_BIGENDIAN + CA_SAMPLE_S16RE +#else + CA_SAMPLE_S16NE +#endif + : CA_SAMPLE_U8; +} + +int ca_wav_read_s16le(ca_wav *w, int16_t *d, unsigned *n) { + unsigned remaining; + + ca_return_val_if_fail(w, CA_ERROR_INVALID); + ca_return_val_if_fail(w->depth == 16, CA_ERROR_INVALID); + ca_return_val_if_fail(d, CA_ERROR_INVALID); + ca_return_val_if_fail(n, CA_ERROR_INVALID); + ca_return_val_if_fail(*n > 0, CA_ERROR_INVALID); + + remaining = w->data_size / sizeof(int16_t); + + if (*n > remaining) + *n = remaining; + + if (*n > 0) { + *n = fread(d, sizeof(int16_t), *n, w->file); + + if (*n <= 0 && ferror(w->file)) + return CA_ERROR_SYSTEM; + + ca_assert(w->data_size >= *n * sizeof(int16_t)); + w->data_size -= *n * sizeof(int16_t); + } + + return CA_SUCCESS; +} + +int ca_wav_read_u(ca_wav *w, uint8_t *d, unsigned *n) { + unsigned remaining; + + ca_return_val_if_fail(w, CA_ERROR_INVALID); + ca_return_val_if_fail(w->depth == 8, CA_ERROR_INVALID); + ca_return_val_if_fail(d, CA_ERROR_INVALID); + ca_return_val_if_fail(n, CA_ERROR_INVALID); + ca_return_val_if_fail(*n > 0, CA_ERROR_INVALID); + + remaining = w->data_size / sizeof(uint8_t); + + if (*n > remaining) + *n = remaining; + + if (*n > 0) { + *n = fread(d, sizeof(uint8_t), *n, w->file); + + if (*n <= 0 && ferror(w->file)) + return CA_ERROR_SYSTEM; + + ca_assert(w->data_size >= *n * sizeof(int16_t)); + w->data_size -= *n * sizeof(uint8_t); + } + + return CA_SUCCESS; +} |