You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

263 lines
7.2 KiB

/*
* 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 "combinedsoundsource.h"
#include <framework/core/clock.h>
#include <framework/core/eventdispatcher.h>
#include <framework/core/resourcemanager.h>
SoundManager g_sounds;
void SoundManager::init()
{
m_device = alcOpenDevice(NULL);
if(!m_device) {
g_logger.error("unable to open audio device");
return;
}
m_context = alcCreateContext(m_device, NULL);
if(!m_context) {
g_logger.error(stdext::format("unable to create audio context: %s", alcGetString(m_device, alcGetError(m_device))));
return;
}
alcMakeContextCurrent(m_context);
m_musicEnabled = true;
m_soundEnabled = true;
}
void SoundManager::terminate()
{
m_sources.clear();
m_buffers.clear();
m_musicSource = nullptr;
m_currentMusic = "";
m_musicEnabled = false;
m_soundEnabled = false;
alcMakeContextCurrent(nullptr);
if(m_context) {
alcDestroyContext(m_context);
m_context = nullptr;
}
if(m_device) {
alcCloseDevice(m_device);
m_device = nullptr;
}
}
void SoundManager::poll()
{
static ticks_t lastUpdate = 0;
ticks_t now = g_clock.millis();
if(now - lastUpdate < POLL_DELAY)
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(std::string filename)
{
filename = g_resources.resolvePath(filename);
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->fillBuffer(soundFile))
m_buffers[filename] = buffer;
}
void SoundManager::enableSound(bool enable)
{
if(!isAudioEnabled())
return;
}
void SoundManager::play(std::string filename)
{
if(!m_soundEnabled || filename.empty())
return;
filename = g_resources.resolvePath(filename);
SoundSourcePtr soundSource = createSoundSource(filename);
if(!soundSource) {
g_logger.error(stdext::format("unable to play '%s'", filename));
return;
}
soundSource->setRelative(true);
soundSource->play();
m_sources.push_back(soundSource);
}
void SoundManager::enableMusic(bool enable)
{
if(!isAudioEnabled())
return;
m_musicEnabled = enable;
if(enable && !m_currentMusic.empty())
playMusic(m_currentMusic, 3.0f);
else
m_musicSource = nullptr;
}
void SoundManager::playMusic(std::string filename, float fadetime)
{
if(filename.empty())
return;
filename = g_resources.resolvePath(filename);
if(m_currentMusic == filename && m_musicSource)
return;
if(!m_musicEnabled)
return;
if(filename.empty()) {
m_musicSource = nullptr;
return;
}
m_musicSource = createSoundSource(filename);
if(!m_musicSource) {
g_logger.error(stdext::format("unable to play '%s'", filename));
return;
}
m_musicSource->setRelative(true);
if(fadetime > 0) {
m_musicSource->setGain(0);
m_musicSource->setFading(StreamSoundSource::FadingOn, fadetime);
}
m_musicSource->play();
}
void SoundManager::stopMusic(float fadetime)
{
if(m_musicSource) {
if(fadetime > 0)
m_musicSource->setFading(StreamSoundSource::FadingOff, 3.0f);
else
m_musicSource->stop();
}
}
SoundSourcePtr SoundManager::createSoundSource(const std::string& filename)
{
SoundSourcePtr source;
auto it = m_buffers.find(filename);
if(it != m_buffers.end()) {
source = SoundSourcePtr(new SoundSource);
source->setBuffer(it->second);
} else {
SoundFilePtr soundFile = SoundFile::loadSoundFile(filename);
if(!soundFile)
return nullptr;
if(soundFile->getSize() <= MAX_CACHE_SIZE) {
source = SoundSourcePtr(new SoundSource);
SoundBufferPtr buffer = SoundBufferPtr(new SoundBuffer);
buffer->fillBuffer(soundFile);
source->setBuffer(buffer);
m_buffers[filename] = buffer;
g_logger.warning(stdext::format("uncached sound '%s' requested to be played", filename));
} else {
StreamSoundSourcePtr streamSource(new StreamSoundSource);
streamSource->setSoundFile(soundFile);
source = streamSource;
#if defined __linux && !defined OPENGL_ES
// due to OpenAL implementation bug, stereo buffers are always downmixed to mono on linux systems
// this is hack to work around the issue
// solution taken from http://opensource.creative.com/pipermail/openal/2007-April/010355.html
if(soundFile->getSampleFormat() == AL_FORMAT_STEREO16) {
CombinedSoundSourcePtr combinedSource(new CombinedSoundSource);
streamSource->downMix(StreamSoundSource::DownMixLeft);
streamSource->setRelative(true);
streamSource->setPosition(Point(-128, 0));
combinedSource->addSource(streamSource);
streamSource = StreamSoundSourcePtr(new StreamSoundSource);
streamSource->setSoundFile(SoundFile::loadSoundFile(filename));
streamSource->downMix(StreamSoundSource::DownMixRight);
streamSource->setRelative(true);
streamSource->setPosition(Point(128,0));
combinedSource->addSource(streamSource);
source = combinedSource;
}
#endif
}
}
return source;
}