120 lines
3.3 KiB
C++
120 lines
3.3 KiB
C++
/* -----------------------------------------------------------------------------
|
|
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 <IO::Handle> 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 <SampleWav> 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 <uint> ();
|
|
|
|
// 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();
|
|
}
|
|
//------------------------------------------------------------------------------
|