#include "SoundFile.h"
#include <vorbis/vorbisfile.h>
typedef struct
{
char szRIFF[4];
unsigned long ulRIFFSize;
char szWAVE[4];
} WAVEFILEHEADER;
typedef struct
{
char szChunkName[4];
unsigned long ulChunkSize;
} RIFFCHUNK;
typedef struct
{
unsigned short usFormatTag;
unsigned short usChannels;
unsigned long ulSamplesPerSec;
unsigned long ulAvgBytesPerSec;
unsigned short usBlockAlign;
unsigned short usBitsPerSample;
unsigned short usSize;
unsigned short usReserved;
unsigned long ulChannelMask;
GUID guidSubFormat;
} WAVEFMT;
size_t read_func(void *ptr, size_t size, size_t nmemb, void *datasource)
{
return ((IDevice*)datasource)->Read((uint8*)ptr, size*nmemb);
}
int seek_func(void *datasource, ogg_int64_t offset, int whence)
{
return ((IDevice*)datasource)->Seek(offset, whence);
}
int close_func(void *datasource)
{
return 0;
}
long tell_func(void* datasource)
{
return ((IDevice*)datasource)->Tell();
}
ov_callbacks g_ovc = {
read_func,
seek_func,
close_func,
tell_func
};
SoundFile::SoundFile()
{
_buffer = 0;
_source = 0;
_data = NULL;
}
SoundFile::~SoundFile()
{
if (0 != _source)
{
alDeleteSources(1, &_source);
_source = 0;
}
if(0 != _buffer)
{
alDeleteBuffers(1, &_buffer);
_buffer = 0;
}
if (_data)
{
free(_data);
_data = 0;
}
}
bool_t SoundFile::Load(IDevice& device)
{
if (!device.IsValid())
return FALSE;
uint32 head;
if (4 != device.Read((uint8*)&head, 4))
return FALSE;
//wBitsPerSample == 8 ? AL_FORMAT_MONO8 : AL_FORMAT_MONO16
ALenum format = AL_FORMAT_MONO16;
ALsizei freq = 0;
device.Seek(0);
// TODO: add big endian, little endian
if (0x46464952 == head) // RIFF
{
uint_t data_off = 0;
uint_t data_size = 0;
WAVEFILEHEADER wfh;
device.Read((uint8*)&wfh, sizeof(WAVEFILEHEADER));
assert(!_strnicmp(wfh.szRIFF, "RIFF", 4) && !_strnicmp(wfh.szWAVE, "WAVE", 4));
RIFFCHUNK rc;
while (sizeof(RIFFCHUNK) == device.Read((uint8*)&rc, sizeof(RIFFCHUNK)))
{
if (!_strnicmp(rc.szChunkName, "fmt ", 4))
{
if (rc.ulChunkSize <= sizeof(WAVEFMT))
{
WAVEFMT wf;
device.Read((uint8*)&wf, rc.ulChunkSize);
assert(wf.usFormatTag == WAVE_FORMAT_PCM );
if (8 == wf.usBitsPerSample)
format = AL_FORMAT_MONO8;
freq = wf.ulSamplesPerSec;
}
else
{
device.Seek(rc.ulChunkSize, SEEK_CUR);
}
}
else if (!_strnicmp(rc.szChunkName, "data", 4))
{
data_size = rc.ulChunkSize;
data_off = device.Tell();
device.Seek(rc.ulChunkSize, SEEK_CUR);
}
else
{
device.Seek( rc.ulChunkSize, SEEK_CUR);
}
if (rc.ulChunkSize & 1)
device.Seek(1, SEEK_CUR);
}
if(_data)
free(_data);
_data = (uint8*)malloc(data_size);
assert(_data);
device.Seek(data_off, SEEK_SET);
device.Read(_data, data_size);
alGenBuffers( 1, &_buffer);
assert( _buffer != AL_INVALID_VALUE && _buffer != AL_OUT_OF_MEMORY );
alBufferData(_buffer, format, _data, data_size, freq);
}
else if(0x5367674f == head) // OggS
{
OggVorbis_File ogg;
if (0 != ov_open_callbacks(&device, &ogg, NULL, 0, g_ovc))
return FALSE;
vorbis_info* info = ov_info(&ogg, -1);
if (info->channels > 1)
format = AL_FORMAT_STEREO16;
int_t len = ov_pcm_total(&ogg, -1) * info->channels * 2;
if(_data)
free(_data);
_data = (uint8*)malloc(len);
int bs = -1;
int_t todo = len;
uint8* bufpt = _data;
while (todo)
{
int read = ov_read(&ogg, (char *) bufpt, todo, 0 /*OGG_ENDIAN*/, 2, 1, &bs);
todo -= read;
bufpt += read;
}
alGenBuffers( 1, &_buffer);
assert( _buffer != AL_INVALID_VALUE && _buffer != AL_OUT_OF_MEMORY );
alBufferData(_buffer, format, _data, len, info->rate);
ov_clear(&ogg);
}
else
return FALSE;
alGenSources(1, &_source);
alSourcei(_source, AL_BUFFER, _buffer);
return TRUE;
}
bool_t SoundFile::Play(int_t type)
{
if(PLAY_LOOP == type)
alSourcei (_source, AL_LOOPING, AL_TRUE);
alSourcePlay(_source);
return TRUE;
}
bool_t SoundFile::Stop()
{
alSourceStop(_source);
return TRUE;
}
bool_t SoundFile::IsPlaying()
{
ALint state = 0;
alGetSourcei(_source, AL_SOURCE_STATE, &state);
return AL_PLAYING == state;
}