Files
lime/project/src/audio/format/WAV.cpp
Joshua Granick 2f5a602771 Cleanup
2015-07-07 12:42:40 -07:00

213 lines
4.8 KiB
C++

#include <audio/format/WAV.h>
#include <system/System.h>
namespace lime {
template<typename T>
inline const char* readStruct (T& dest, const char*& ptr) {
const char* ret;
memcpy (&dest, ptr, sizeof (T));
ptr += sizeof (WAVE_Data);
ret = ptr;
ptr += dest.subChunkSize;
return ret;
}
const char* find_chunk (const char* start, const char* end, const char* chunkID) {
WAVE_Data chunk;
const char* ptr = start;
while (ptr < (end - sizeof(WAVE_Data))) {
memcpy (&chunk, ptr, sizeof (WAVE_Data));
if (chunk.subChunkID[0] == chunkID[0] && chunk.subChunkID[1] == chunkID[1] && chunk.subChunkID[2] == chunkID[2] && chunk.subChunkID[3] == chunkID[3]) {
return ptr;
}
ptr += sizeof (WAVE_Data) + chunk.subChunkSize;
}
return 0;
}
bool WAV::Decode (Resource *resource, AudioBuffer *audioBuffer) {
WAVE_Format wave_format;
RIFF_Header riff_header;
WAVE_Data wave_data;
unsigned char* data;
FILE_HANDLE *file = NULL;
if (resource->path) {
file = lime::fopen (resource->path, "rb");
if (!file) {
return false;
}
int result = lime::fread (&riff_header, sizeof (RIFF_Header), 1, file);
if ((riff_header.chunkID[0] != 'R' || riff_header.chunkID[1] != 'I' || riff_header.chunkID[2] != 'F' || riff_header.chunkID[3] != 'F') || (riff_header.format[0] != 'W' || riff_header.format[1] != 'A' || riff_header.format[2] != 'V' || riff_header.format[3] != 'E')) {
lime::fclose (file);
return false;
}
long int currentHead = 0;
bool foundFormat = false;
while (!foundFormat) {
currentHead = lime::ftell (file);
result = lime::fread (&wave_format, sizeof (WAVE_Format), 1, file);
if (result != 1) {
LOG_SOUND ("Invalid Wave Format!\n");
lime::fclose (file);
return false;
}
if (wave_format.subChunkID[0] != 'f' || wave_format.subChunkID[1] != 'm' || wave_format.subChunkID[2] != 't' || wave_format.subChunkID[3] != ' ') {
lime::fseek (file, wave_format.subChunkSize, SEEK_CUR);
} else {
foundFormat = true;
}
}
bool foundData = false;
while (!foundData) {
currentHead = lime::ftell (file);
result = lime::fread (&wave_data, sizeof (WAVE_Data), 1, file);
if (result != 1) {
LOG_SOUND ("Invalid Wav Data Header!\n");
lime::fclose (file);
return false;
}
if (wave_data.subChunkID[0] != 'd' || wave_data.subChunkID[1] != 'a' || wave_data.subChunkID[2] != 't' || wave_data.subChunkID[3] != 'a') {
lime::fseek (file, currentHead + sizeof (WAVE_Data) + wave_data.subChunkSize, SEEK_SET);
} else {
foundData = true;
}
}
audioBuffer->data->Resize (wave_data.subChunkSize);
if (!lime::fread (audioBuffer->data->Data (), wave_data.subChunkSize, 1, file)) {
LOG_SOUND ("error loading WAVE data into struct!\n");
lime::fclose (file);
return false;
}
lime::fclose (file);
} else {
const char* start = (const char*)resource->data->Data ();
const char* end = start + resource->data->Length ();
const char* ptr = start;
memcpy (&riff_header, ptr, sizeof (RIFF_Header));
ptr += sizeof (RIFF_Header);
if ((riff_header.chunkID[0] != 'R' || riff_header.chunkID[1] != 'I' || riff_header.chunkID[2] != 'F' || riff_header.chunkID[3] != 'F') || (riff_header.format[0] != 'W' || riff_header.format[1] != 'A' || riff_header.format[2] != 'V' || riff_header.format[3] != 'E')) {
return false;
}
ptr = find_chunk (ptr, end, "fmt ");
if (!ptr) {
return false;
}
readStruct (wave_format, ptr);
if (wave_format.subChunkID[0] != 'f' || wave_format.subChunkID[1] != 'm' || wave_format.subChunkID[2] != 't' || wave_format.subChunkID[3] != ' ') {
LOG_SOUND ("Invalid Wave Format!\n");
return false;
}
ptr = find_chunk (ptr, end, "data");
if (!ptr) {
return false;
}
const char* base = readStruct (wave_data, ptr);
if (wave_data.subChunkID[0] != 'd' || wave_data.subChunkID[1] != 'a' || wave_data.subChunkID[2] != 't' || wave_data.subChunkID[3] != 'a') {
LOG_SOUND ("Invalid Wav Data Header!\n");
return false;
}
audioBuffer->data->Resize (wave_data.subChunkSize);
size_t size = wave_data.subChunkSize;
if (size > (end - base)) {
return false;
}
unsigned char* bytes = audioBuffer->data->Data ();
memcpy (bytes, base, size);
}
audioBuffer->sampleRate = (int)wave_format.sampleRate;
audioBuffer->channels = wave_format.numChannels;
audioBuffer->bitsPerSample = wave_format.bitsPerSample;
return true;
}
}