/* ----------------------------------------------------------------------------- GSFramework Copyright 2001-2013 Emmanuel Julien. All Rights Reserved. ----------------------------------------------------------------------------- */ #include "audio/sample_wav_factory.h" #include "audio/sample_wav.h" #include "filesystem/filesystem.h" #include "filesystem/io_handle.h" #include "memory/endian.h" #include "platform.h" #include "log/log.h" using namespace GS; //------------------------------------------------------------------------------ ISample *SampleWavFactory::Load(const char *path) { AutoPtr h(Platform::Get().io->Open(path)); if (h.IsNull()) return NULL; // Verify format. char header[4]; h->Read(header, 4); if (memcmp(header, "RIFF", 4)) return NULL; h->Seek(4); h->Read(header, 4); if (memcmp(header, "WAVE", 4)) return NULL; // Create sample. AutoPtr sample(new SampleWav); if (sample.IsNull()) return NULL; // Parse format. bool has_format = false, has_data = false; struct Format { short wFormatTag; unsigned short wChannels; unsigned long dwSamplesPerSec; unsigned long dwAvgBytesPerSec; unsigned short wBlockAlign; unsigned short wBitsPerSample; }; Format format; Memory::Set(&format, 0, sizeof(Format)); forever { // Chunk + size. if (h->Read(header, 4) != 4) break; uint chunk_size = h->Read (); // WAV format tag. if (!memcmp(header, "fmt ", 4)) { uint cs = chunk_size; if (cs > sizeof(Format)) { __LOG_W__ << "Unexpected WAV 'format' chunk size. Found " << cs << ", expected " << (int)sizeof(Format) << ".\n"; cs = sizeof(Format); } if (h->Read(&format, cs) != cs) __ERR__(__LOG_E__ << "Mangled WAV 'format' chunk in '" << path << "'.\n", NULL) Endian::ToHost(&format.wFormatTag, 2, Endian::Intel); Endian::ToHost(&format.wChannels, 2, Endian::Intel); Endian::ToHost(&format.dwSamplesPerSec, 4, Endian::Intel); Endian::ToHost(&format.dwAvgBytesPerSec, 4, Endian::Intel); Endian::ToHost(&format.wBlockAlign, 2, Endian::Intel); Endian::ToHost(&format.wBitsPerSample, 2, Endian::Intel); has_format = true; // Finish skipping tag. if (cs != chunk_size) h->Seek(chunk_size - cs); } // WAV data tag. else if (!memcmp(header, "data", 4)) { if (has_format) { char *pcm = sample->AllocAs(chunk_size / (format.wBitsPerSample / 8) / format.wChannels, SampleFormat(SampleFormat::Format_PCM, format.wChannels, format.dwSamplesPerSec, (uchar)format.wBitsPerSample)); if (!pcm) __ERR__(__LOG_E__ << "failed to allocate WAV data chunk for '" << path << "'.\n", NULL) if (h->Read(pcm, chunk_size) != chunk_size) __ERR__(__LOG_E__ << "mangled WAV 'data' chunk in '" << path << "'.\n", NULL) } else __ERR__(__LOG_E__ << "WAV data with no format in '" << path << "'.\n", NULL) has_data = true; } else h->Seek(chunk_size); } if (!has_format || !has_data) return NULL; SampleFormat sample_format; if (!sample->GetSampleFormat(sample_format)) return NULL; __LOG__ << "Sample format: " << sample_format.frequency / 1000 << "KHz@" << sample_format.resolution << "bit, " << sample_format.channels << " channel(s).\n"; return sample.Detach(); } //------------------------------------------------------------------------------