diff --git a/src/source.c b/src/source.c index f792244..eff79c9 100644 --- a/src/source.c +++ b/src/source.c @@ -9,16 +9,52 @@ #include #include #include "source.h" +#include "filesystem.h" #include "lib/dmt/dmt.h" -#include "wavefile.h" +#include "wav.h" char const* source_init(source_t *self, char const* filename) { - return wavefile_load(self, filename); + int fileSize; + void *waveData = filesystem_read(filename, &fileSize); + + char const *err = NULL; + wav_t wav; + int res = wav_read(&wav, waveData, fileSize); + + if(res != WAV_ESUCCESS) { + err = wav_strerror(res); + goto fail; + } + + if(wav.bitdepth != 16) { + err = "Invalid Audio Format (only 16 Bit supported)"; + goto fail; + } + + if(wav.samplerate != 22050) { + err = "Invalid Audio Format (only 22050Hz supported)"; + goto fail; + } + + if(wav.channels != 1) { + err = "Invalid Audio Format (only mono supported)"; + goto fail; + } + + self->sampleCount = wav.length; + self->data = waveData; + self->samples = (int16_t*)wav.data; + + return NULL; + +fail: + filesystem_free(waveData); + return err; } void source_deinit(source_t *self) { - dmt_free(self->samples); + filesystem_free(self->data); } diff --git a/src/source.h b/src/source.h index 95df48e..6176013 100644 --- a/src/source.h +++ b/src/source.h @@ -13,6 +13,7 @@ typedef struct { int sampleCount; int16_t *samples; + void *data; } source_t; char const* source_init(source_t *self, char const* filename); diff --git a/src/wav.c b/src/wav.c new file mode 100644 index 0000000..030d856 --- /dev/null +++ b/src/wav.c @@ -0,0 +1,84 @@ +/** + * Copyright (c) 2015 rxi + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the MIT license. See LICENSE for details. + */ + + +#include +#include +#include +#include "wav.h" + +typedef unsigned short Uint16; +typedef unsigned int Uint32; + + +static const char *findSubChunk( + const char *data, size_t len, const char *id, size_t *size +) { + /* TODO : Error handling on malformed wav file */ + size_t idLen = strlen(id); + const char *p = data + 12; +next: + *size = *((Uint32*) (p + 4)); + if (memcmp(p, id, idLen)) { + p += 8 + *size; + if (p > data + len) return NULL; + goto next; + } + return p + 8; +} + + +int wav_read(wav_t *w, const void *data, size_t len) { + int bitdepth, channels, samplerate, format; + size_t sz; + const char *p = data; + memset(w, 0, sizeof(*w)); + /* Check header */ + if (memcmp(p, "RIFF", 4) || memcmp(p + 8, "WAVE", 4)) { + return WAV_EBADHEADER; + } + /* Find fmt subchunk */ + p = findSubChunk(data, len, "fmt", &sz); + if (!p) return WAV_ENOFMT; + /* Load fmt info */ + format = *((Uint16*) (p)); + channels = *((Uint16*) (p + 2)); + samplerate = *((Uint32*) (p + 4)); + bitdepth = *((Uint16*) (p + 14)); + if (format != 1) { + return WAV_ENOSUPPORT; + } + if (channels == 0 || samplerate == 0 || bitdepth == 0) { + return WAV_EBADFMT; + } + /* Find data subchunk */ + p = findSubChunk(data, len, "data", &sz); + if (!p) return WAV_ENODATA; + /* Init wav_t struct */ + w->data = p; + w->samplerate = samplerate; + w->channels = channels; + w->length = (sz / (bitdepth / 8)) / channels; + w->bitdepth = bitdepth; + /* Done! */ + return WAV_ESUCCESS; +} + + +const char *wav_strerror(int err) { + switch (err) { + case WAV_ESUCCESS : return "success"; + case WAV_EFAILURE : return "failure"; + case WAV_EBADHEADER : return "bad header data"; + case WAV_EBADFMT : return "bad fmt data"; + case WAV_ENOFMT : return "missing 'fmt' subchunk"; + case WAV_ENODATA : return "missing 'data' subchunk"; + case WAV_ENOSUPPORT : return "unsupported format; " + "expected uncompressed PCM"; + } + return "unknown error"; +} diff --git a/src/wavefile.c b/src/wavefile.c deleted file mode 100644 index 8fe4168..0000000 --- a/src/wavefile.c +++ /dev/null @@ -1,102 +0,0 @@ -/** - * Copyright (c) 2017 Florian Kesseler - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the MIT license. See LICENSE for details. - */ - -#include -#include "filesystem.h" -#include "wavefile.h" -#include "source.h" -#include "lib/dmt/dmt.h" - -typedef struct { - uint32_t chunkId; - uint32_t chunkSize; - uint32_t format; - uint32_t subchunk1Id; - uint32_t subchunk1Size; - uint16_t audioFormat; - uint16_t numChannels; - uint32_t sampleRate; - uint32_t byteRate; - uint16_t blockAlign; - uint16_t bitsPerSample; - uint32_t subchunk2Id; - uint32_t subchunk2Size; -} waveheader_t; - -#define RIFF_CHUNK_ID 0x46464952 -#define WAVE_FORMAT 0x45564157 -#define FORMAT_CHUNK_ID 0x20746d66 -#define DATA_CHUNK_ID 0x61746164 -#define FORMAT_CHUNK_SIZE 16 -#define AUDIO_FORMAT_PCM 1 - - -char const* wavefile_load(source_t *source, char const *filename) { - int fileSize; - char const *err = 0; - uint8_t *waveData = filesystem_read(filename, &fileSize); - - if(waveData == NULL) { - err = "Could not read WAV file"; - goto fail; - } - - waveheader_t const* header = (waveheader_t const*)waveData; - - if(header->chunkId != RIFF_CHUNK_ID - || header->format != WAVE_FORMAT - || header->subchunk1Id != FORMAT_CHUNK_ID - || header->subchunk2Id != DATA_CHUNK_ID) { - err = "Invalid WAV header"; - goto fail; - } - - if(header->subchunk1Size != FORMAT_CHUNK_SIZE - || header->audioFormat != AUDIO_FORMAT_PCM) { - err = "Invalid Audio Format (only PCM supported)"; - goto fail; - } - - if(header->numChannels != 1) { - err = "Invalid Audio Format (only 1 channel supported)"; - goto fail; - } - - if(header->sampleRate != 22050) { - err = "Invalid Audio Format (only 22050Hz supported)"; - goto fail; - } - - if(header->bitsPerSample != 16) { - err = "Invalid Audio Format (only 16 Bit supported)"; - goto fail; - } - - if(header->byteRate != header->sampleRate * header->numChannels * header->bitsPerSample / 8) { - err = "Byte rate doesn't match header info"; - goto fail; - } - - if(header->blockAlign != header->numChannels * header->bitsPerSample / 8) { - err = "Block alignment doesn't match header info"; - goto fail; - } - - if(fileSize < sizeof(waveheader_t) + header->subchunk2Size) { - err = "Short read while reading samples"; - goto fail; - } - - source->sampleCount = header->subchunk2Size / 2; - source->samples = (int16_t*)dmt_malloc(header->subchunk2Size); - - memcpy(source->samples, ((uint8_t*)waveData) + sizeof(waveheader_t), header->subchunk2Size); - -fail: - filesystem_free(waveData); - return err; -} diff --git a/src/wavefile.h b/src/wavefile.h deleted file mode 100644 index 24a2044..0000000 --- a/src/wavefile.h +++ /dev/null @@ -1,15 +0,0 @@ -/** - * Copyright (c) 2017 Florian Kesseler - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the MIT license. See LICENSE for details. - */ - -#ifndef WAVEFILE_H -#define WAVEFILE_H - -#include "source.h" - -char const* wavefile_load(source_t *source, char const *filename); - -#endif