-/*******************************************************************************
-
- Copyright (c) 2009, Charles McGarvey
- All rights reserved.
-
- Redistribution and use in source and binary forms, with or without
- modification, are permitted provided that the following conditions are met:
-
- * Redistributions of source code must retain the above copyright notice,
- this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above copyright notice,
- this list of conditions and the following disclaimer in the documentation
- and/or other materials provided with the distribution.
-
- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
- FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
- SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
- CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-*******************************************************************************/
+/*] Copyright (c) 2009-2010, Charles McGarvey [**************************
+**] All rights reserved.
+*
+* vi:ts=4 sw=4 tw=75
+*
+* Distributable under the terms and conditions of the 2-clause BSD license;
+* see the file COPYING for a complete text of the license.
+*
+**************************************************************************/
#include <cstdio>
#include <deque>
+#include <list>
#include <string>
-#include <vector>
+
+#include <boost/algorithm/string.hpp>
#include <AL/al.h>
+#include <AL/alc.h>
#include <vorbis/codec.h>
#include <vorbis/vorbisfile.h>
-#include "Exception.hh"
-#include "Library.hh"
+#include "Error.hh"
+#include "Manager.hh"
#include "Log.hh"
#include "Sound.hh"
#include "Timer.hh"
class Buffer;
typedef boost::shared_ptr<Buffer> BufferP;
- class Buffer : public Library<Buffer>
+ class Buffer : public Manager<Buffer>
{
public:
- Buffer(const std::string& name) :
- Library<Buffer>(name),
+ Buffer() :
mBuffer(-1)
{
mOggStream.datasource = 0;
- openFile();
}
~Buffer()
}
- void openFile()
+ void init(const std::string& name)
{
if (mOggStream.datasource)
{
mOggStream.datasource = 0;
}
- std::string filePath = Sound::getPath(getName());
- int result = ov_fopen((char*)filePath.c_str(), &mOggStream);
+ std::string path(name);
+ if (!Sound::getPath(path))
+ {
+ Error(Error::RESOURCE_NOT_FOUND, path).raise();
+ }
- if (result < 0)
+ if (ov_fopen((char*)path.c_str(), &mOggStream) < 0)
{
- logWarning("error while loading sound %s",
- getName().c_str());
- throw Exception(ErrorCode::UNKNOWN_AUDIO_FORMAT);
+ Error(Error::UNKNOWN_AUDIO_FORMAT, path).raise();
}
vorbis_info* vorbisInfo = ov_info(&mOggStream, -1);
mFormat = getAudioFormat(vorbisInfo);
mFreq = vorbisInfo->rate;
-
- logDebug(" channels: %d", vorbisInfo->channels);
- logDebug(" frequency: %d", vorbisInfo->rate);
}
void loadAll(ALuint source)
{
- if (!mOggStream.datasource) openFile();
+ if (!mOggStream.datasource) init(getName());
if (!mOggStream.datasource) return;
char data[BUFFER_SIZE];
}
if (size == 0)
{
- logWarning("decoded no bytes from %s", getName().c_str());
- //throw Exception("file_not_found");
+ logWarning << "decoded no bytes from "
+ << getName() << std::endl;
return;
}
void rewind()
{
- if (!mOggStream.datasource) openFile();
+ if (!mOggStream.datasource) init(getName());
else ov_raw_seek(&mOggStream, 0);
}
void init()
{
- ALfloat zero[] = {0.0f, 0.0f, 0.0f};
-
+ retainBackend();
+
+ mIsLoaded = false;
+ mIsPlaying = false;
+ mIsLooping = false;
+
alGenSources(1, &mSource);
+ ALfloat zero[] = {0.0f, 0.0f, 0.0f};
alSourcef(mSource, AL_PITCH, 1.0f);
alSourcef(mSource, AL_GAIN, 1.0f);
alSourcefv(mSource, AL_POSITION, zero);
alSourcefv(mSource, AL_VELOCITY, zero);
- mIsPlaying = false;
- mIsLooping = false;
+ alSourcei(mSource, AL_LOOPING, mIsLooping);
}
~Impl()
alDeleteSources(1, &mSource);
- while (!mBufferObjects.empty())
+ while (!mBuffers.empty())
{
- alDeleteBuffers(1, &mBufferObjects.back());
- mBufferObjects.pop_back();
+ alDeleteBuffers(1, &mBuffers.back());
+ mBuffers.pop_back();
}
+
+ releaseBackend();
}
{
if (mQueue.empty()) return;
- ALenum type;
- alGetSourcei(mSource, AL_SOURCE_TYPE, &type);
-
- if (type != AL_STATIC)
- {
- mQueue.front()->loadAll(mSource);
- }
+ if (!mIsLoaded) mQueue.front()->loadAll(mSource);
- alSourcei(mSource, AL_LOOPING, mIsLooping);
alSourcePlay(mSource);
- mIsPlaying = true;
+ mIsLoaded = true;
}
- void stream()
+ void playStream()
{
- stop();
+ if (mQueue.empty()) return;
- alSourcei(mSource, AL_BUFFER, AL_NONE);
- mQueue.front()->rewind();
- beginStream();
+ if (!mIsPlaying)
+ {
+ alSourcei(mSource, AL_LOOPING, false);
+ bufferStream();
+ }
+
+ if (!mStreamTimer.isValid())
+ {
+ mStreamTimer.init(boost::bind(&Impl::streamUpdate, this, _1, _2),
+ 1.0, Timer::REPEAT);
+ }
- alSourcei(mSource, AL_LOOPING, AL_FALSE);
alSourcePlay(mSource);
mIsPlaying = true;
-
- mStreamTimer.init(boost::bind(&Impl::streamUpdate, this, _1, _2), 1.0,
- Timer::REPEAT);
}
- void beginStream()
+ void bufferStream()
{
ALuint buffer;
- for (int i = mBufferObjects.size(); i < 8; ++i)
+ for (int i = mBuffers.size(); i <= 8; ++i)
{
alGenBuffers(1, &buffer);
- mBufferObjects.push_back(buffer);
- }
- for (int i = 0; i < 8; ++i)
- {
- buffer = mBufferObjects[i];
- mQueue.front()->stream(buffer);
- alSourceQueueBuffers(mSource, 1, &buffer);
+
+ if (mQueue.front()->stream(buffer))
+ {
+ alSourceQueueBuffers(mSource, 1, &buffer);
+ mBuffers.push_back(buffer);
+ }
+ else
+ {
+ alDeleteBuffers(1, &buffer);
+ break;
+ }
}
}
mQueue.front()->stream(bufferObj);
alSourceQueueBuffers(mSource, 1, &bufferObj);
logInfo("loading new buffer");
+
+ // queue up any unused buffers
+ bufferStream();
}
else if (mIsLooping)
{
alSourceQueueBuffers(mSource, 1, &bufferObj);
logInfo("looping same buffer");
}
+ else
+ {
+ // nothing more to play, stopping...
+ mIsPlaying = false;
+ std::remove(mBuffers.begin(), mBuffers.end(),
+ bufferObj);
+ }
}
}
ALenum state;
alGetSourcei(mSource, AL_SOURCE_STATE, &state);
- // restart playing if we're stopped but supposed to be playing... this
- // means we didn't queue enough and the audio skipped :-(
+ // restart playing if we're stopped but supposed to be playing...
+ // this means we didn't queue enough and the audio skipped :-(
if (mIsPlaying && state != AL_PLAYING)
{
alSourcePlay(mSource);
mStreamTimer.invalidate();
}
- void resume()
- {
- alSourcePlay(mSource);
- mIsPlaying = true;
-
- ALenum type;
- alGetSourcei(mSource, AL_SOURCE_TYPE, &type);
-
- if (type == AL_STREAMING)
- {
- mStreamTimer.init(boost::bind(&Impl::streamUpdate, this, _1, _2),
- 1.0, Timer::REPEAT);
- }
- }
-
void setSample(const std::string& name)
{
- bool playing = isPlaying();
- ALenum type;
- alGetSourcei(mSource, AL_SOURCE_TYPE, &type);
-
stop();
+ alSourcei(mSource, AL_BUFFER, AL_NONE);
+
mQueue.clear();
+ mIsLoaded = false;
- //alSourcei(mSource, AL_BUFFER, AL_NONE);
enqueue(name);
- if (playing)
+ while (!mBuffers.empty())
{
- if (type == AL_STREAMING) stream();
- else play();
+ alDeleteBuffers(1, &mBuffers.back());
+ mBuffers.pop_back();
}
}
{
// don't let the music die!
update();
- // TODO - might be nice to also allow using threads for streaming rather
- // than a timer, probably as a compile-time option
+ // TODO - might be nice to also allow using threads for streaming
+ // rather than a timer, probably as a compile-time option
+ }
+
+ static void retainBackend()
+ {
+ if (gRetainCount++ == 0)
+ {
+ gAlDevice = alcOpenDevice(0);
+ gAlContext = alcCreateContext(gAlDevice, 0);
+ if (!gAlDevice || !gAlContext)
+ {
+ const char* error = alcGetString(gAlDevice,
+ alcGetError(gAlDevice));
+ logError << "audio subsystem initialization failure: "
+ << error << std::endl;
+ }
+ else
+ {
+ alcMakeContextCurrent(gAlContext);
+ logInfo << "opened sound device `"
+ << alcGetString(gAlDevice,
+ ALC_DEFAULT_DEVICE_SPECIFIER)
+ << "'" << std::endl;
+ }
+ }
+ }
+
+ static void releaseBackend()
+ {
+ if (--gRetainCount == 0)
+ {
+ alcMakeContextCurrent(0);
+ alcDestroyContext(gAlContext);
+ alcCloseDevice(gAlDevice);
+ }
}
ALuint mSource;
- std::vector<ALuint> mBufferObjects;
+ std::list<ALuint> mBuffers;
+ bool mIsLoaded;
bool mIsPlaying;
bool mIsLooping;
std::deque<BufferP> mQueue;
Timer mStreamTimer;
+
+ static unsigned gRetainCount;
+ static ALCdevice* gAlDevice;
+ static ALCcontext* gAlContext;
};
+unsigned Sound::Impl::gRetainCount = 0;
+ALCdevice* Sound::Impl::gAlDevice = 0;
+ALCcontext* Sound::Impl::gAlContext = 0;
+
Sound::Sound() :
// pass through
mImpl(new Sound::Impl(name)) {}
-void Sound::play()
+void Sound::setSample(const std::string& name)
{
// pass through
- mImpl->play();
+ mImpl->setSample(name);
}
-void Sound::stream()
+
+void Sound::play()
{
// pass through
- mImpl->stream();
+ mImpl->play();
}
-
void Sound::stop()
{
// pass through
mImpl->pause();
}
-void Sound::resume()
-{
- // pass through
- mImpl->resume();
-}
void Sound::toggle()
{
- if (mImpl->mIsPlaying) pause();
- else resume();
-}
-
-
-void Sound::setSample(const std::string& name)
-{
- // pass through
- mImpl->setSample(name);
+ if (isPlaying()) pause();
+ else play();
}
-void Sound::enqueue(const std::string& name)
-{
- // pass through
- mImpl->enqueue(name);
-}
-
-
bool Sound::isPlaying() const
{
// pass through
return mImpl->isPlaying();
}
+
void Sound::setPosition(const Vector3& position)
{
- float p[3] = {position[0], position[1], position[2]};
- alSourcefv(mImpl->mSource, AL_POSITION, p);
+ float vec[3] = {position[0], position[1], position[2]};
+ alSourcefv(mImpl->mSource, AL_POSITION, vec);
}
void Sound::setVelocity(const Vector3& velocity)
{
- float v[3] = {velocity[0], velocity[1], velocity[2]};
- alSourcefv(mImpl->mSource, AL_VELOCITY, v);
+ float vec[3] = {velocity[0], velocity[1], velocity[2]};
+ alSourcefv(mImpl->mSource, AL_VELOCITY, vec);
}
void Sound::setGain(Scalar gain)
void Sound::setListenerPosition(const Vector3& position)
{
- alListener3f(AL_POSITION, float(position[0]), float(position[1]),
- float(position[2]));
+ float vec[] = {position[0], position[1], position[2]};
+ alListenerfv(AL_POSITION, vec);
}
void Sound::setListenerVelocity(const Vector3& velocity)
{
- alListener3f(AL_VELOCITY, float(velocity[0]), float(velocity[1]),
- float(velocity[2]));
+ float vec[] = {velocity[0], velocity[1], velocity[2]};
+ alListenerfv(AL_VELOCITY, vec);
}
-void Sound::setListenerOrientation(const Vector3& forward, const Vector3& up)
+void Sound::setListenerOrientation(const Vector3& forward,
+ const Vector3& up)
{
float vec[6];
vec[0] = float(forward[0]);
}
-std::string Sound::getPath(const std::string& name)
+bool Sound::getPath(std::string& name)
{
- std::string path = Resource::getPath("sounds/" + name + ".ogg");
- return path;
+ return Resource::getPath(name, "sounds/", "ogg");
}
-} // namespace Mf
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+
+void SoundStream::enqueue(const std::string& name)
+{
+ // pass through
+ mImpl->enqueue(name);
+}
-/** vim: set ts=4 sw=4 tw=80: *************************************************/
+
+void SoundStream::play()
+{
+ // pass through
+ mImpl->playStream();
+}
+
+
+} // namespace Mf