Files
lovedos/src/wavefile.c
2017-01-22 20:46:17 +01:00

103 lines
2.5 KiB
C

/**
* Copyright (c) 2017 rnlf
*
* 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 <string.h>
#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;
}