implement basic sound engine using OpenAL

This commit is contained in:
Eduardo Bart
2012-04-13 16:54:08 -03:00
parent 9b4115a7e5
commit c4525059ce
25 changed files with 1236 additions and 75 deletions

View File

@@ -0,0 +1,44 @@
/*
* Copyright (c) 2010-2012 OTClient <https://github.com/edubart/otclient>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef FRAMEWORK_SOUND_DECLARATIONS_H
#define FRAMEWORK_SOUND_DECLARATIONS_H
#include <framework/global.h>
#include <AL/al.h>
#include <AL/alc.h>
class SoundManager;
class SoundSource;
class SoundBuffer;
class SoundFile;
class StreamSoundSource;
class OggSoundFile;
typedef std::shared_ptr<SoundSource> SoundSourcePtr;
typedef std::shared_ptr<SoundFile> SoundFilePtr;
typedef std::shared_ptr<SoundBuffer> SoundBufferPtr;
typedef std::shared_ptr<StreamSoundSource> StreamSoundSourcePtr;
typedef std::shared_ptr<OggSoundFile> OggSoundFilePtr;
#endif

View File

@@ -0,0 +1,116 @@
/*
* Copyright (c) 2010-2012 OTClient <https://github.com/edubart/otclient>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "oggsoundfile.h"
OggSoundFile::OggSoundFile(const FileStreamPtr& fileStream) : SoundFile(fileStream)
{
memset(&m_vorbisFile, 0, sizeof(m_vorbisFile));
}
OggSoundFile::~OggSoundFile()
{
ov_clear(&m_vorbisFile);
}
bool OggSoundFile::prepareOgg()
{
ov_callbacks callbacks = { cb_read, cb_seek, cb_close, cb_tell };
ov_open_callbacks(m_file.get(), &m_vorbisFile, 0, 0, callbacks);
vorbis_info* vi = ov_info(&m_vorbisFile, -1);
if(!vi) {
logError("ogg file not supported: ", m_file->name());
return false;
}
m_channels = vi->channels;
m_rate = vi->rate;
m_bps = 16;
m_size = ov_pcm_total(&m_vorbisFile, -1) * 2;
return true;
}
int OggSoundFile::read(void *buffer, int bufferSize)
{
char* bytesBuffer = reinterpret_cast<char*>(buffer);
int section = 0;
size_t totalBytesRead = 0;
while(bufferSize > 0) {
size_t bytesToRead = bufferSize;
long bytesRead = ov_read(&m_vorbisFile, bytesBuffer, bytesToRead, 0, 2, 1, &section);
if(bytesRead == 0)
break;
bufferSize -= bytesRead;
bytesBuffer += bytesRead;
totalBytesRead += bytesRead;
}
return totalBytesRead;
}
void OggSoundFile::reset()
{
ov_pcm_seek(&m_vorbisFile, 0);
}
size_t OggSoundFile::cb_read(void* ptr, size_t size, size_t nmemb, void* source)
{
FileStream *file = static_cast<FileStream*>(source);
return file->read(ptr, size, nmemb);
}
int OggSoundFile::cb_seek(void* source, ogg_int64_t offset, int whence)
{
FileStream *file = static_cast<FileStream*>(source);
switch(whence) {
case SEEK_SET:
if(file->seek(offset))
return 0;
break;
case SEEK_CUR:
if(file->seek(file->tell() + offset))
return 0;
break;
case SEEK_END:
if(file->seek(file->size() + offset))
return 0;
break;
}
return -1;
}
int OggSoundFile::cb_close(void* source)
{
FileStream *file = static_cast<FileStream*>(source);
file->close();
return 0;
}
long OggSoundFile::cb_tell(void* source)
{
FileStream *file = static_cast<FileStream*>(source);
return file->tell();
}

View File

@@ -0,0 +1,50 @@
/*
* Copyright (c) 2010-2012 OTClient <https://github.com/edubart/otclient>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef OGGSOUNDFILE_H
#define OGGSOUNDFILE_H
#include "soundfile.h"
#include <vorbis/vorbisfile.h>
class OggSoundFile : public SoundFile
{
public:
OggSoundFile(const FileStreamPtr& fileStream);
virtual ~OggSoundFile();
bool prepareOgg();
int read(void *buffer, int bufferSize);
void reset();
private:
static size_t cb_read(void* ptr, size_t size, size_t nmemb, void* source);
static int cb_seek(void* source, ogg_int64_t offset, int whence);
static int cb_close(void* source);
static long cb_tell(void* source);
OggVorbis_File m_vorbisFile;
};
#endif

View File

@@ -0,0 +1,64 @@
/*
* Copyright (c) 2010-2012 OTClient <https://github.com/edubart/otclient>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "soundbuffer.h"
#include "soundfile.h"
#include <framework/util/databuffer.h>
SoundBuffer::SoundBuffer()
{
m_bufferId = 0;
alGenBuffers(1, &m_bufferId);
assert(alGetError() == AL_NO_ERROR);
}
SoundBuffer::~SoundBuffer()
{
alDeleteBuffers(1, &m_bufferId);
assert(alGetError() == AL_NO_ERROR);
}
bool SoundBuffer::loadSoundFile(const SoundFilePtr& soundFile)
{
ALenum format = soundFile->getSampleFormat();
if(format == AL_UNDETERMINED) {
logError("unable to determine sample format for '", soundFile->getName(), "'");
return false;
}
DataBuffer<char> samples(soundFile->getSize());
int read = soundFile->read(&samples[0], soundFile->getSize());
if(read <= 0) {
logError("unable to fill audio buffer data for '", soundFile->getName(), "'");
return false;
}
alBufferData(m_bufferId, format, &samples[0], soundFile->getSize(), soundFile->getRate());
ALenum err = alGetError();
if(err != AL_NO_ERROR) {
logError("unable to fill audio buffer data for '", soundFile->getName(), "': ", alGetString(err));
return false;
}
return true;
}

View File

@@ -0,0 +1,42 @@
/*
* Copyright (c) 2010-2012 OTClient <https://github.com/edubart/otclient>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef SOUNDBUFFER_H
#define SOUNDBUFFER_H
#include "declarations.h"
class SoundBuffer
{
public:
SoundBuffer();
~SoundBuffer();
bool loadSoundFile(const SoundFilePtr& soundFile);
int getBufferId() { return m_bufferId; }
private:
ALuint m_bufferId;
};
#endif

View File

@@ -0,0 +1,69 @@
/*
* Copyright (c) 2010-2012 OTClient <https://github.com/edubart/otclient>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "soundfile.h"
#include "oggsoundfile.h"
#include <framework/core/resourcemanager.h>
SoundFile::SoundFile(const FileStreamPtr& fileStream)
{
m_file = fileStream;
}
SoundFilePtr SoundFile::loadSoundFile(const std::string& filename)
{
FileStreamPtr file = g_resources.openFile(filename);
if(!file) {
logTraceError("unable to open ", filename);
return nullptr;
}
char magic[4];
file->read(magic, 4);
file->seek(0);
SoundFilePtr soundFile;
if(strncmp(magic, "OggS", 4) == 0) {
OggSoundFilePtr oggSoundFile = OggSoundFilePtr(new OggSoundFile(file));
if(oggSoundFile->prepareOgg())
soundFile = oggSoundFile;
} else
logError("unknown sound file format ", filename);
return soundFile;
}
ALenum SoundFile::getSampleFormat()
{
if(m_channels == 2) {
if(m_bps == 16)
return AL_FORMAT_STEREO16;
else if(m_bps == 8)
return AL_FORMAT_STEREO8;
} else if(m_channels == 1) {
if(m_bps == 16)
return AL_FORMAT_MONO16;
else if(m_bps == 8)
return AL_FORMAT_MONO8;
}
return AL_UNDETERMINED;
}

View File

@@ -0,0 +1,55 @@
/*
* Copyright (c) 2010-2012 OTClient <https://github.com/edubart/otclient>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef SOUNDFILE_H
#define SOUNDFILE_H
#include "declarations.h"
#include <framework/core/filestream.h>
class SoundFile
{
public:
SoundFile(const FileStreamPtr& fileStream);
virtual ~SoundFile() { }
static SoundFilePtr loadSoundFile(const std::string& filename);
virtual int read(void *buffer, int bufferSize) = 0;
virtual void reset() = 0;
ALenum getSampleFormat();
int getChannels() { return m_channels; }
int getRate() { return m_rate; }
int getBps() { return m_bps; }
int getSize() { return m_size; }
std::string getName() { return m_file ? m_file->name() : std::string(); }
protected:
FileStreamPtr m_file;
int m_channels;
int m_rate;
int m_bps;
int m_size;
};
#endif

View File

@@ -0,0 +1,245 @@
/*
* Copyright (c) 2010-2012 OTClient <https://github.com/edubart/otclient>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "soundmanager.h"
#include "soundsource.h"
#include "soundbuffer.h"
#include "soundfile.h"
#include "streamsoundsource.h"
#include <framework/core/clock.h>
#include <framework/core/eventdispatcher.h>
SoundManager g_sounds;
void SoundManager::init()
{
m_run = false;
m_device = alcOpenDevice(NULL);
if(!m_device) {
logError("unable to open audio device");
return;
}
m_context = alcCreateContext(m_device, NULL);
if(!m_context) {
logError("unable to create audio context: ", alcGetString(m_device, alcGetError(m_device)));
return;
}
alcMakeContextCurrent(m_context);
m_thread = std::thread(std::bind(&SoundManager::audioThread, &g_sounds));
while(!m_run)
g_clock.sleep(1);
m_musicEnabled = true;
m_soundEnabled = true;
/*
g_eventDispatcher.scheduleEvent([this] {
play("/test.ogg");
}, 10);
*/
}
void SoundManager::terminate()
{
m_run = false;
m_thread.join();
m_sources.clear();
m_buffers.clear();
m_musicSource = nullptr;
m_currentMusic = "";
m_musicEnabled = false;
m_soundEnabled = false;
alcMakeContextCurrent(NULL);
if(m_context) {
alcDestroyContext(m_context);
m_context = nullptr;
}
if(m_device) {
alcCloseDevice(m_device);
m_device = nullptr;
}
}
void SoundManager::audioThread()
{
m_run = true;
while(m_run) {
//TODO: use condition variable
g_clock.sleep(30);
update();
}
}
void SoundManager::update()
{
std::lock_guard<std::recursive_mutex> lock(m_mutex);
static ticks_t lastUpdate = 0;
ticks_t now = g_clock.asyncTicks();
if(now - lastUpdate < 300)
return;
lastUpdate = now;
for(auto it = m_sources.begin(); it != m_sources.end();) {
SoundSourcePtr source = *it;
source->update();
if(!source->isPlaying())
it = m_sources.erase(it);
else
++it;
}
if(m_musicSource) {
m_musicSource->update();
if(!m_musicSource->isPlaying())
m_musicSource = nullptr;
}
if(m_context) {
alcProcessContext(m_context);
}
}
void SoundManager::preload(const std::string& filename)
{
std::lock_guard<std::recursive_mutex> lock(m_mutex);
auto it = m_buffers.find(filename);
if(it != m_buffers.end())
return;
SoundFilePtr soundFile = SoundFile::loadSoundFile(filename);
// only keep small files
if(soundFile->getSize() > MAX_CACHE_SIZE)
return;
SoundBufferPtr buffer = SoundBufferPtr(new SoundBuffer);
if(buffer->loadSoundFile(soundFile))
m_buffers[filename] = buffer;
}
void SoundManager::enableSound(bool enable)
{
std::lock_guard<std::recursive_mutex> lock(m_mutex);
if(!isAudioEnabled())
return;
}
void SoundManager::play(const std::string& filename)
{
std::lock_guard<std::recursive_mutex> lock(m_mutex);
if(!m_soundEnabled)
return;
SoundSourcePtr soundSource = createSoundSource(filename);
if(!soundSource) {
logError("unable to play '", filename, "'");
return;
}
soundSource->setRelative(true);
soundSource->play();
m_sources.push_back(soundSource);
}
void SoundManager::enableMusic(bool enable)
{
std::lock_guard<std::recursive_mutex> lock(m_mutex);
if(!isAudioEnabled())
return;
m_musicEnabled = enable;
if(enable)
playMusic(m_currentMusic);
else
m_musicSource = nullptr;
}
void SoundManager::playMusic(const std::string& filename, bool fade)
{
std::lock_guard<std::recursive_mutex> lock(m_mutex);
if(m_currentMusic == filename && m_musicSource)
return;
m_currentMusic = filename;
if(!m_musicEnabled)
return;
if(filename.empty()) {
m_musicSource = nullptr;
return;
}
}
void SoundManager::stopMusic(float fadetime)
{
std::lock_guard<std::recursive_mutex> lock(m_mutex);
}
SoundSourcePtr SoundManager::createSoundSource(const std::string& filename)
{
SoundSourcePtr soundSource;
auto it = m_buffers.find(filename);
if(it != m_buffers.end()) {
soundSource = SoundSourcePtr(new SoundSource);
soundSource->setBuffer(it->second);
} else {
SoundFilePtr soundFile = SoundFile::loadSoundFile(filename);
if(!soundFile)
return nullptr;
if(soundFile->getSize() <= MAX_CACHE_SIZE) {
soundSource = SoundSourcePtr(new SoundSource);
SoundBufferPtr buffer = SoundBufferPtr(new SoundBuffer);
buffer->loadSoundFile(soundFile);
soundSource->setBuffer(buffer);
m_buffers[filename] = buffer;
logWarning("uncached sound '", filename, "' requested to be played");
} else {
StreamSoundSourcePtr streamSoundSource(new StreamSoundSource);
streamSoundSource->setSoundFile(soundFile);
soundSource = streamSoundSource;
}
}
return soundSource;
}

View File

@@ -0,0 +1,75 @@
/*
* Copyright (c) 2010-2012 OTClient <https://github.com/edubart/otclient>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef SOUNDMANAGER_H
#define SOUNDMANAGER_H
#include "declarations.h"
class SoundManager
{
enum {
MAX_CACHE_SIZE = 10000000
};
public:
void init();
void terminate();
void audioThread();
void update();
void preload(const std::string& filename);
void enableSound(bool enable);
void play(const std::string& filename);
void enableMusic(bool enable);
void playMusic(const std::string& filename, bool fade = false);
void stopMusic(float fadetime = 0);
bool isMusicEnabled() { return m_musicEnabled; }
bool isSoundEnabled() { return m_soundEnabled; }
bool isAudioEnabled() { return m_device && m_context; }
std::string getCurrentMusic() { return m_currentMusic; }
private:
SoundSourcePtr createSoundSource(const std::string& filename);
ALuint loadFileIntoBuffer(const SoundFilePtr& soundFile);
std::map<std::string, SoundBufferPtr> m_buffers;
std::vector<SoundSourcePtr> m_sources;
StreamSoundSourcePtr m_musicSource;
ALCdevice *m_device;
ALCcontext *m_context;
std::thread m_thread;
std::atomic<bool> m_run;
std::recursive_mutex m_mutex;
Boolean<false> m_musicEnabled;
Boolean<false> m_soundEnabled;
std::string m_currentMusic;
};
extern SoundManager g_sounds;
#endif

View File

@@ -0,0 +1,95 @@
/*
* Copyright (c) 2010-2012 OTClient <https://github.com/edubart/otclient>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "soundsource.h"
#include "soundbuffer.h"
SoundSource::SoundSource()
{
m_sourceId = 0;
alGenSources(1, &m_sourceId);
assert(alGetError() == AL_NO_ERROR);
setReferenceDistance(128);
}
SoundSource::~SoundSource()
{
stop();
alDeleteSources(1, &m_sourceId);
assert(alGetError() == AL_NO_ERROR);
}
void SoundSource::play()
{
alSourcePlay(m_sourceId);
assert(alGetError() == AL_NO_ERROR);
}
void SoundSource::stop()
{
alSourceStop(m_sourceId);
assert(alGetError() == AL_NO_ERROR);
if(m_buffer) {
alSourcei(m_sourceId, AL_BUFFER, AL_NONE);
assert(alGetError() == AL_NO_ERROR);
m_buffer = nullptr;
}
}
bool SoundSource::isPlaying()
{
ALint state = AL_PLAYING;
alGetSourcei(m_sourceId, AL_SOURCE_STATE, &state);
return state != AL_STOPPED;
}
void SoundSource::setBuffer(const SoundBufferPtr& buffer)
{
alSourcei(m_sourceId, AL_BUFFER, buffer->getBufferId());
assert(alGetError() == AL_NO_ERROR);
m_buffer = buffer;
}
void SoundSource::setLooping(bool looping)
{
alSourcei(m_sourceId, AL_LOOPING, looping ? AL_TRUE : AL_FALSE);
}
void SoundSource::setRelative(bool relative)
{
alSourcei(m_sourceId, AL_SOURCE_RELATIVE, relative ? AL_TRUE : AL_FALSE);
}
void SoundSource::setReferenceDistance(float distance)
{
alSourcef(m_sourceId, AL_REFERENCE_DISTANCE, distance);
}
void SoundSource::setGain(float gain)
{
alSourcef(m_sourceId, AL_GAIN, gain);
}
void SoundSource::setPitch(float pitch)
{
alSourcef(m_sourceId, AL_PITCH, pitch);
}

View File

@@ -0,0 +1,56 @@
/*
* Copyright (c) 2010-2012 OTClient <https://github.com/edubart/otclient>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef SOUNDSOURCE_H
#define SOUNDSOURCE_H
#include "declarations.h"
class SoundSource
{
public:
SoundSource();
virtual ~SoundSource();
void play();
void stop();
bool isPlaying();
void setBuffer(const SoundBufferPtr& buffer);
virtual void setLooping(bool looping);
void setRelative(bool relative);
void setReferenceDistance(float distance);
void setGain(float gain);
void setPitch(float pitch);
// TODO: velocity, position
protected:
virtual void update() { }
friend class SoundManager;
ALuint m_sourceId;
SoundBufferPtr m_buffer;
};
#endif

View File

@@ -0,0 +1,144 @@
/*
* Copyright (c) 2010-2012 OTClient <https://github.com/edubart/otclient>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "streamsoundsource.h"
#include "soundbuffer.h"
#include "soundfile.h"
#include <framework/util/databuffer.h>
#include <framework/core/clock.h>
StreamSoundSource::StreamSoundSource()
{
for(auto& buffer : m_buffers)
buffer = SoundBufferPtr(new SoundBuffer);
m_fadeState = NoFading;
}
StreamSoundSource::~StreamSoundSource()
{
stop();
ALint queued;
alGetSourcei(m_sourceId, AL_BUFFERS_QUEUED, &queued);
for(int i = 0; i < queued; ++i) {
ALuint buffer;
alSourceUnqueueBuffers(m_sourceId, 1, &buffer);
}
m_buffers.fill(nullptr);
}
void StreamSoundSource::setSoundFile(const SoundFilePtr& soundFile)
{
m_soundFile = soundFile;
ALint queued;
alGetSourcei(m_sourceId, AL_BUFFERS_QUEUED, &queued);
for(int i = 0; i < STREAM_FRAGMENTS - queued; ++i) {
if(!fillBufferAndQueue(m_buffers[i]->getBufferId()))
break;
}
}
void StreamSoundSource::update()
{
ALint processed = 0;
alGetSourcei(m_sourceId, AL_BUFFERS_PROCESSED, &processed);
for(ALint i = 0; i < processed; ++i) {
ALuint buffer;
alSourceUnqueueBuffers(m_sourceId, 1, &buffer);
//SoundManager::check_al_error("Couldn't unqueue audio buffer: ");
if(!fillBufferAndQueue(buffer))
break;
}
if(!isPlaying()) {
if(processed == 0 || !m_looping)
return;
// we might have to restart the source if we had a buffer underrun
//log_info << "Restarting audio source because of buffer underrun" << std::endl;
//play();
}
float realTime = g_clock.asyncTime();
if(m_fadeState == FadingOn) {
float time = realTime - m_fadeStartTime;
if(time >= m_fadeTime) {
setGain(1.0);
m_fadeState = NoFading;
} else {
setGain(time / m_fadeTime);
}
} else if(m_fadeState == FadingOff) {
float time = realTime - m_fadeStartTime;
if(time >= m_fadeTime) {
stop();
m_fadeState = NoFading;
} else {
setGain((m_fadeTime - time) / m_fadeTime);
}
}
}
bool StreamSoundSource::fillBufferAndQueue(ALuint buffer)
{
// fill buffer
DataBuffer<char> bufferData(STREAM_FRAGMENT_SIZE);
int bytesRead = 0;
do {
bytesRead += m_soundFile->read(&bufferData[bytesRead], STREAM_FRAGMENT_SIZE - bytesRead);
// end of sound file
if(bytesRead < STREAM_FRAGMENT_SIZE) {
if(m_looping)
m_soundFile->reset();
else
break;
}
} while(bytesRead < STREAM_FRAGMENT_SIZE);
if(bytesRead > 0) {
ALenum format = m_soundFile->getSampleFormat();
alBufferData(buffer, format, &bufferData[0], bytesRead, m_soundFile->getRate());
ALenum err = alGetError();
if(err != AL_NO_ERROR)
logError("unable to refill audio buffer for '", m_soundFile->getName(), "': ", alGetString(err));
alSourceQueueBuffers(m_sourceId, 1, &buffer);
err = alGetError();
if(err != AL_NO_ERROR)
logError("unable to queue audio buffer for '", m_soundFile->getName(), "': ", alGetString(err));
}
// return false if there aren't more buffers to fill
return bytesRead >= STREAM_FRAGMENT_SIZE;
}
void StreamSoundSource::setFading(FadeState state, float fadeTime)
{
m_fadeState = state;
m_fadeTime = fadeTime;
m_fadeStartTime = g_clock.asyncTime();
}

View File

@@ -0,0 +1,60 @@
/*
* Copyright (c) 2010-2012 OTClient <https://github.com/edubart/otclient>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef STREAMSOUNDSOURCE_H
#define STREAMSOUNDSOURCE_H
#include "soundsource.h"
class StreamSoundSource : public SoundSource
{
enum {
STREAM_BUFFER_SIZE = 1024 * 500,
STREAM_FRAGMENTS = 5,
STREAM_FRAGMENT_SIZE = STREAM_BUFFER_SIZE / STREAM_FRAGMENTS
};
public:
enum FadeState { NoFading, FadingOn, FadingOff };
StreamSoundSource();
virtual ~StreamSoundSource();
void setSoundFile(const SoundFilePtr& soundFile);
void setFading(FadeState state, float fadetime);
FadeState getFadeState() { return m_fadeState; }
void update();
private:
bool fillBufferAndQueue(ALuint buffer);
SoundFilePtr m_soundFile;
std::array<SoundBufferPtr,STREAM_FRAGMENTS> m_buffers;
FadeState m_fadeState;
float m_fadeStartTime;
float m_fadeTime;
Boolean<false> m_looping;
};
#endif