#include <AL/al.h>
#include <AL/alc.h>
#include <vorbis/vorbisfile.h>
#include <stdexcept>
#include <boost/noncopyable.hpp>
class Sound : boost::noncopyable {
public:
Sound(ALuint buffer) : source_(0), buffer_(buffer)
{
alGenSources(1, &source_);
alListener3f(AL_POSITION, 0.0f, 0.0f, 0.0f);
alSource3f(source_, AL_POSITION, 0.0f, 0.0f, 0.0f);
alSourcei(source_, AL_BUFFER, buffer);
alSourcePlay(source_);
}
~Sound()
{
alSourceStop(source_);
alSourceUnqueueBuffers(source_, 1, &buffer_);
alDeleteSources(1, &source_);
}
bool IsPlaying()
{
ALint state;
alGetSourcei(source_, AL_SOURCE_STATE, &state);
return state == AL_PLAYING;
}
bool Stopped()
{
ALint state;
alGetSourcei(source_, AL_SOURCE_STATE, &state);
return state == AL_STOPPED;
}
void SetPitch(float p)
{
alSourcef(source_, AL_PITCH, p);
}
private:
ALuint source_;
ALuint buffer_;
};
class Audio : boost::noncopyable {
public:
Audio() : device_(0), context_(0)
{
device_ = alcOpenDevice(0);
if(!device_)
{
throw std::runtime_error("Could not open audio device.");
}
context_ = alcCreateContext(device_, 0);
if(context_)
{
alcMakeContextCurrent(context_);
}
else
{
throw std::runtime_error("Could not create audio context.");
}
}
~Audio()
{
alcMakeContextCurrent(0);
alcDestroyContext(context_);
alcCloseDevice(device_);
}
void Play(boost::shared_ptr<Sound> sound)
{
std::vector<boost::shared_ptr<Sound> >::iterator end = sounds_.end();
for(std::vector<boost::shared_ptr<Sound> >::iterator it = sounds_.begin(); it != end; ++it)
{
if((*it)->Stopped())
{
it = sounds_.erase(it);
}
}
sounds_.push_back(boost::shared_ptr<Sound>(sound));
}
void Stop(boost::shared_ptr<Sound> sound)
{
std::vector<boost::shared_ptr<Sound> >::iterator i;
if((i = std::find(sounds_.begin(), sounds_.end(), sound)) != sounds_.end()) // sound hasn't been loaded yet?
{
sounds_.erase(i);
}
}
private:
std::vector<boost::shared_ptr<Sound> > sounds_;
ALCdevice* device_;
ALCcontext* context_;
};
Audio& GetAudio()
{
static Audio audio_;
return audio_;
}
class SoundFile : boost::noncopyable {
public:
SoundFile(const std::string& filename) : sound_((Sound*)0)
{
Debug("Decoding "); Debug(filename); Debug(" ... ");
// based on http://www.gamedev.net/reference/articles/article2031.asp
FILE* f = fopen(filename.c_str(), "rb");
if(!f)
{
throw std::runtime_error("Could not open OGG file.");
}
OggVorbis_File oggFile;
if(ov_open(f, &oggFile, 0, 0) != 0)
{
throw std::runtime_error("Could not open OGG file.");
}
vorbis_info* pInfo;
pInfo = ov_info(&oggFile, -1);
ALenum format;
if(pInfo->channels == 1)
{
format = AL_FORMAT_MONO16;
}
else
{
format = AL_FORMAT_STEREO16;
}
ALsizei freq = pInfo->rate;
std::vector<char> bufferData;
const int bufferSize = 32768;
char array[bufferSize]; // 32 KB buffers
const int endian = 0; // 0 for Little-Endian, 1 for Big-Endian
int bitStream;
int bytes;
do
{
bytes = ov_read(&oggFile, array, bufferSize, endian, 2, 1, &bitStream);
if (bytes < 0)
{
ov_clear(&oggFile);
throw std::runtime_error("Error decoding OGG file.");
}
bufferData.insert(bufferData.end(), array, array + bytes);
}
while(bytes > 0);
ov_clear(&oggFile);
Debug("OK\n");
alGenBuffers(1, &buffer_);
alBufferData(buffer_, format, &bufferData[0], static_cast<ALsizei>(bufferData.size()), freq);
}
~SoundFile()
{
alDeleteBuffers(1, &buffer_);
}
void Play()
{
sound_.reset(new Sound(buffer_));
GetAudio().Play(sound_);
}
void Stop()
{
if(sound_)
{
GetAudio().Stop(sound_);
sound_.reset((Sound*)0);
}
}
bool IsPlaying()
{
if(sound_)
{
return sound_->IsPlaying();
}
return false;
}
void SetPitch(float p)
{
if(sound_)
{
sound_->SetPitch(p);
}
}
private:
boost::shared_ptr<Sound> sound_;
ALuint buffer_;
};