stk-code_catmod/src/audio/sfx_manager.cpp
Deve c06ea3447d Revert "Move loadSfx to the SFXManager thread"
This reverts commit ce426d82cc5988b82b8f35eadfd3e61198df2159.
2017-04-09 09:29:25 +02:00

988 lines
34 KiB
C++

//
// SuperTuxKart - a fun racing game with go-kart
// Copyright (C) 2008-2015 Joerg Henrichs
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 3
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include "audio/dummy_sfx.hpp"
#include "audio/music_manager.hpp"
#include "audio/sfx_openal.hpp"
#include "audio/sfx_buffer.hpp"
#include "config/user_config.hpp"
#include "io/file_manager.hpp"
#include "modes/world.hpp"
#include "race/race_manager.hpp"
#include "utils/vs.hpp"
#include <pthread.h>
#include <stdexcept>
#include <algorithm>
#include <cerrno>
#include <map>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#if HAVE_OGGVORBIS
# ifdef __APPLE__
# include <OpenAL/al.h>
# include <OpenAL/alc.h>
# else
# include <AL/al.h>
# include <AL/alc.h>
# endif
#endif
SFXManager *SFXManager::m_sfx_manager;
// ----------------------------------------------------------------------------
/** Static function to create the singleton sfx manager.
*/
void SFXManager::create()
{
assert(!m_sfx_manager);
m_sfx_manager = new SFXManager();
} // create
// ------------------------------------------------------------------------
/** Static function to delete the singleton sfx manager.
*/
void SFXManager::destroy()
{
assert(m_sfx_manager);
delete m_sfx_manager;
m_sfx_manager = NULL;
} // destroy
// ----------------------------------------------------------------------------
/** Initialises the SFX manager and loads the sfx from a config file.
*/
SFXManager::SFXManager()
{
// The sound manager initialises OpenAL
m_initialized = music_manager->initialized();
m_master_gain = UserConfigParams::m_sfx_volume;
m_last_update_time = -1.0f;
// Init position, since it can be used before positionListener is called.
// No need to use lock here, since the thread will be created later.
m_listener_position.getData() = Vec3(0, 0, 0);
m_listener_front = Vec3(0, 0, 1);
m_listener_up = Vec3(0, 1, 0);
loadSfx();
pthread_cond_init(&m_cond_request, NULL);
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
m_thread_id.setAtomic(new pthread_t());
// The thread is created even if there atm sfx are disabled
// (since the user might enable it later).
int error = pthread_create(m_thread_id.getData(), &attr,
&SFXManager::mainLoop, this);
if (error)
{
m_thread_id.lock();
delete m_thread_id.getData();
m_thread_id.unlock();
m_thread_id.setAtomic(0);
Log::error("SFXManager", "Could not create thread, error=%d.",
errno);
}
pthread_attr_destroy(&attr);
setMasterSFXVolume( UserConfigParams::m_sfx_volume );
m_sfx_commands.lock();
m_sfx_commands.getData().clear();
m_sfx_commands.unlock();
} // SoundManager
//-----------------------------------------------------------------------------
/** Destructor, frees all sound effects.
*/
SFXManager::~SFXManager()
{
m_thread_id.lock();
pthread_join(*m_thread_id.getData(), NULL);
delete m_thread_id.getData();
m_thread_id.unlock();
pthread_cond_destroy(&m_cond_request);
// ---- clear m_all_sfx
// not strictly necessary, but might avoid copy&paste problems
m_all_sfx.lock();
const int sfx_amount = (int) m_all_sfx.getData().size();
for (int n=0; n<sfx_amount; n++)
{
delete m_all_sfx.getData()[n];
}
m_all_sfx.getData().clear();
m_all_sfx.unlock();
m_quick_sounds.lock();
// ---- clear m_quick_sounds
{
std::map<std::string, SFXBase*>::iterator i = m_quick_sounds.getData().begin();
for (; i != m_quick_sounds.getData().end(); i++)
{
SFXBase* snd = (*i).second;
delete snd;
}
}
m_quick_sounds.getData().clear();
m_quick_sounds.unlock();
// ---- clear m_all_sfx_types
{
std::map<std::string, SFXBuffer*>::iterator i = m_all_sfx_types.begin();
for (; i != m_all_sfx_types.end(); i++)
{
SFXBuffer* buffer = (*i).second;
buffer->unload();
delete buffer;
}
m_all_sfx_types.clear();
}
m_all_sfx_types.clear();
} // ~SFXManager
//----------------------------------------------------------------------------
/** Adds a sound effect command to the queue of the sfx manager. Openal
* commands can sometimes cause a 5ms delay, so it is done in a separate
* thread.
* \param command The command to execute.
* \param sfx The sound effect to be started.
*/
void SFXManager::queue(SFXCommands command, SFXBase *sfx)
{
SFXCommand *sfx_command = new SFXCommand(command, sfx);
queueCommand(sfx_command);
} // queue
//----------------------------------------------------------------------------
/** Adds a sound effect command with a single floating point parameter to the
* queue of the sfx manager. Openal commands can sometimes cause a 5ms delay,
* so it is done in a separate thread.
* \param command The command to execute.
* \param sfx The sound effect to be started.
* \param f Floating point parameter for the command.
*/
void SFXManager::queue(SFXCommands command, SFXBase *sfx, float f)
{
SFXCommand *sfx_command = new SFXCommand(command, sfx, f);
queueCommand(sfx_command);
} // queue(float)
//----------------------------------------------------------------------------
/** Adds a sound effect command with a Vec3 parameter to the queue of the sfx
* manager. Openal commands can sometimes cause a 5ms delay, so it is done in
* a separate thread.
* \param command The command to execute.
* \param sfx The sound effect to be started.
* \param p A Vec3 parameter for the command.
*/
void SFXManager::queue(SFXCommands command, SFXBase *sfx, const Vec3 &p)
{
SFXCommand *sfx_command = new SFXCommand(command, sfx, p);
queueCommand(sfx_command);
} // queue (Vec3)
//----------------------------------------------------------------------------
/** Adds a sound effect command with a float and a Vec3 parameter to the queue
* of the sfx manager. Openal commands can sometimes cause a 5ms delay, so it
* is done in a separate thread.
* \param command The command to execute.
* \param sfx The sound effect to be started.
* \param f A float parameter for the command.
* \param p A Vec3 parameter for the command.
*/
void SFXManager::queue(SFXCommands command, SFXBase *sfx, float f,
const Vec3 &p)
{
SFXCommand *sfx_command = new SFXCommand(command, sfx, f, p);
queueCommand(sfx_command);
} // queue(float, Vec3)
//----------------------------------------------------------------------------
/** Queues a command for the music manager.
* \param mi The music for which the command is.
*/
void SFXManager::queue(SFXCommands command, MusicInformation *mi)
{
SFXCommand *sfx_command = new SFXCommand(command, mi);
queueCommand(sfx_command);
} // queue(MusicInformation)
//----------------------------------------------------------------------------
/** Queues a command for the music manager that takes a floating point value
* (e.g. setTemporaryVolume).
* \param mi The music for which the command is.
* \param f The floating point parameter.
*/
void SFXManager::queue(SFXCommands command, MusicInformation *mi, float f)
{
SFXCommand *sfx_command = new SFXCommand(command, mi, f);
queueCommand(sfx_command);
} // queue(MusicInformation)
//----------------------------------------------------------------------------
/** Enqueues a command to the sfx queue threadsafe. Then signal the
* sfx manager to wake up.
* \param command Pointer to the command to queue up.
*/
void SFXManager::queueCommand(SFXCommand *command)
{
m_sfx_commands.lock();
if(World::getWorld() &&
m_sfx_commands.getData().size() > 20*race_manager->getNumberOfKarts()+20 &&
race_manager->getMinorMode() != RaceManager::MINOR_MODE_CUTSCENE)
{
if(command->m_command==SFX_POSITION || command->m_command==SFX_LOOP ||
command->m_command==SFX_SPEED ||
command->m_command==SFX_SPEED_POSITION )
{
delete command;
static int count_messages = 0;
if(count_messages < 5)
{
Log::warn("SFXManager", "Throttling sfx - queue size %d",
m_sfx_commands.getData().size());
count_messages++;
}
m_sfx_commands.unlock();
return;
} // if throttling
}
m_sfx_commands.getData().push_back(command);
m_sfx_commands.unlock();
} // queueCommand
//----------------------------------------------------------------------------
/** Puts a NULL request into the queue, which will trigger the thread to
* exit.
*/
void SFXManager::stopThread()
{
queue(SFX_EXIT);
// Make sure the thread wakes up.
pthread_cond_signal(&m_cond_request);
} // stopThread
//----------------------------------------------------------------------------
/** This loops runs in a different threads, and starts sfx to be played.
* This can sometimes take up to 5 ms, so it needs to be handled in a thread
* in order to avoid rendering delays.
* \param obj A pointer to the SFX singleton.
*/
void* SFXManager::mainLoop(void *obj)
{
VS::setThreadName("SFXManager");
SFXManager *me = (SFXManager*)obj;
me->m_sfx_commands.lock();
// Wait till we have an empty sfx in the queue
while (me->m_sfx_commands.getData().empty() ||
me->m_sfx_commands.getData().front()->m_command!=SFX_EXIT)
{
bool empty = me->m_sfx_commands.getData().empty();
// Wait in cond_wait for a request to arrive. The 'while' is necessary
// since "spurious wakeups from the pthread_cond_wait ... may occur"
// (pthread_cond_wait man page)!
while (empty)
{
pthread_cond_wait(&me->m_cond_request, me->m_sfx_commands.getMutex());
empty = me->m_sfx_commands.getData().empty();
}
SFXCommand *current = me->m_sfx_commands.getData().front();
me->m_sfx_commands.getData().erase(me->m_sfx_commands.getData().begin());
if (current->m_command == SFX_EXIT)
{
delete current;
break;
}
me->m_sfx_commands.unlock();
switch (current->m_command)
{
case SFX_PLAY: current->m_sfx->reallyPlayNow(); break;
case SFX_PLAY_POSITION:
current->m_sfx->reallyPlayNow(current->m_parameter); break;
case SFX_STOP: current->m_sfx->reallyStopNow(); break;
case SFX_PAUSE: current->m_sfx->reallyPauseNow(); break;
case SFX_RESUME: current->m_sfx->reallyResumeNow(); break;
case SFX_SPEED: current->m_sfx->reallySetSpeed(
current->m_parameter.getX()); break;
case SFX_POSITION: current->m_sfx->reallySetPosition(
current->m_parameter); break;
case SFX_SPEED_POSITION: current->m_sfx->reallySetSpeedPosition(
// Extract float from W component
current->m_parameter.getW(),
current->m_parameter); break;
case SFX_VOLUME: current->m_sfx->reallySetVolume(
current->m_parameter.getX()); break;
case SFX_MASTER_VOLUME:
current->m_sfx->reallySetMasterVolumeNow(
current->m_parameter.getX()); break;
case SFX_LOOP: current->m_sfx->reallySetLoop(
current->m_parameter.getX() != 0); break;
case SFX_DELETE: me->deleteSFX(current->m_sfx); break;
case SFX_PAUSE_ALL: me->reallyPauseAllNow(); break;
case SFX_RESUME_ALL: me->reallyResumeAllNow(); break;
case SFX_LISTENER: me->reallyPositionListenerNow(); break;
case SFX_UPDATE: me->reallyUpdateNow(current); break;
case SFX_MUSIC_START:
{
current->m_music_information->setDefaultVolume();
current->m_music_information->startMusic(); break;
}
case SFX_MUSIC_STOP:
current->m_music_information->stopMusic(); break;
case SFX_MUSIC_PAUSE:
current->m_music_information->pauseMusic(); break;
case SFX_MUSIC_RESUME:
current->m_music_information->resumeMusic();
// This might be necessasary if the volume was changed
// in the in-game menu
current->m_music_information->setDefaultVolume(); break;
case SFX_MUSIC_SWITCH_FAST:
current->m_music_information->switchToFastMusic(); break;
case SFX_MUSIC_SET_TMP_VOLUME:
{
MusicInformation *mi = current->m_music_information;
mi->setTemporaryVolume(current->m_parameter.getX()); break;
}
case SFX_MUSIC_WAITING:
current->m_music_information->setMusicWaiting(); break;
case SFX_MUSIC_DEFAULT_VOLUME:
{
current->m_music_information->setDefaultVolume();
break;
}
case SFX_CREATE_SOURCE:
current->m_sfx->init(); break;
default: assert("Not yet supported.");
}
delete current;
current = NULL;
// We access the size without lock, doesn't matter if we
// should get an incorrect value because of concurrent read/writes
if (me->m_sfx_commands.getData().size() == 0)
{
// Wait some time to let other threads run, then queue an
// update event to keep music playing.
double t = StkTime::getRealTime();
StkTime::sleep(1);
t = StkTime::getRealTime() - t;
me->queue(SFX_UPDATE, (SFXBase*)NULL, float(t));
}
me->m_sfx_commands.lock();
} // while
// Signal that the sfx manager can now be deleted.
// We signal this even before cleaning up memory, since there is no
// need to keep the user waiting for STK to exit.
me->setCanBeDeleted();
// Clean up memory to avoid leak detection
while(!me->m_sfx_commands.getData().empty())
{
delete me->m_sfx_commands.getData().front();
me->m_sfx_commands.getData().erase(me->m_sfx_commands.getData().begin());
}
me->m_sfx_commands.unlock();
return NULL;
} // mainLoop
//----------------------------------------------------------------------------
/** Called when sound is globally switched on or off. It either pauses or
* resumes all sound effects.
* \param on If sound is switched on or off.
*/
void SFXManager::toggleSound(const bool on)
{
// When activating SFX, load all buffers
if (on)
{
std::map<std::string, SFXBuffer*>::iterator i = m_all_sfx_types.begin();
for (; i != m_all_sfx_types.end(); i++)
{
SFXBuffer* buffer = (*i).second;
buffer->load();
}
reallyResumeAllNow();
m_all_sfx.lock();
const int sfx_amount = (int)m_all_sfx.getData().size();
for (int n=0; n<sfx_amount; n++)
{
m_all_sfx.getData()[n]->onSoundEnabledBack();
}
m_all_sfx.unlock();
}
else
{
// First stop all sfx that are not looped
const int sfx_amount = (int)m_all_sfx.getData().size();
m_all_sfx.lock();
for (int i=0; i<sfx_amount; i++)
{
if(!m_all_sfx.getData()[i]->isLooped())
{
m_all_sfx.getData()[i]->reallyStopNow();
}
}
m_all_sfx.unlock();
pauseAll();
}
} // toggleSound
//----------------------------------------------------------------------------
/** Returns if sfx can be played. This means sfx are enabled and
* the manager is correctly initialised.
*/
bool SFXManager::sfxAllowed()
{
if(!UserConfigParams::m_sfx || !m_initialized)
return false;
else
return true;
} // sfxAllowed
//----------------------------------------------------------------------------
/** Loads all sounds specified in the sound config file.
*/
void SFXManager::loadSfx()
{
std::string sfx_config_name = file_manager->getAsset(FileManager::SFX, "sfx.xml");
XMLNode* root = file_manager->createXMLTree(sfx_config_name);
if (!root || root->getName()!="sfx-config")
{
Log::fatal("SFXManager", "Could not read sound effects XML file '%s'.",
sfx_config_name.c_str());
}
int i;
const int amount = root->getNumNodes();
for (i=0; i<amount; i++)
{
const XMLNode* node = root->getNode(i);
if (node->getName() == "sfx")
{
loadSingleSfx(node, "", false);
}
else
{
Log::warn("SFXManager", "Unknown node '%s' in sfx XML file '%s'.",
node->getName().c_str(), sfx_config_name.c_str());
throw std::runtime_error("Unknown node in sfx XML file");
}
}// nend for
delete root;
// Now load them in parallel
const int max = m_all_sfx_types.size();
SFXBuffer **array = new SFXBuffer *[max];
i = 0;
for (std::map<std::string, SFXBuffer*>::iterator it = m_all_sfx_types.begin();
it != m_all_sfx_types.end(); it++)
{
SFXBuffer* const buffer = (*it).second;
array[i++] = buffer;
}
for (i = 0; i < max; i++)
{
array[i]->load();
}
delete [] array;
} // loadSfx
// -----------------------------------------------------------------------------
/** Introduces a mechanism by which one can load sound effects beyond the basic
* enumerated types. This will be used when loading custom sound effects for
* individual karts (character voices) so that we can avoid creating an
* enumeration for each effect, for each kart.
* \param sfx_name
* \param sfxFile must be an absolute pathname
* \return whether loading this sound effect was successful
*/
SFXBuffer* SFXManager::addSingleSfx(const std::string &sfx_name,
const std::string &sfx_file,
bool positional,
float rolloff,
float max_width,
float gain,
const bool load)
{
SFXBuffer* buffer = new SFXBuffer(sfx_file, positional, rolloff,
max_width, gain);
m_all_sfx_types[sfx_name] = buffer;
if (!m_initialized)
{
// Keep the buffer even if SFX is disabled, in case
// SFX is enabled back later
return NULL;
}
if (UserConfigParams::logMisc())
Log::debug("SFXManager", "Loading SFX %s", sfx_file.c_str());
if (load && buffer->load()) return buffer;
return NULL;
} // addSingleSFX
//----------------------------------------------------------------------------
/** Loads a single sfx from the XML specification.
* \param node The XML node with the data for this sfx.
*/
SFXBuffer* SFXManager::loadSingleSfx(const XMLNode* node,
const std::string &path,
const bool load)
{
std::string filename;
if (node->get("filename", &filename) == 0)
{
Log::error("SFXManager",
"The 'filename' attribute is mandatory in the SFX XML file!");
return NULL;
}
std::string sfx_name = StringUtils::removeExtension(filename);
if(m_all_sfx_types.find(sfx_name)!=m_all_sfx_types.end())
{
Log::error("SFXManager",
"There is already a sfx named '%s' installed - new one is ignored.",
sfx_name.c_str());
return NULL;
}
// Only use the filename if no full path is specified. This is used
// to load terrain specific sfx.
const std::string full_path = (path == "")
? file_manager->getAsset(FileManager::SFX,filename)
: path+"/"+filename;
SFXBuffer tmpbuffer(full_path, node);
return addSingleSfx(sfx_name, full_path,
tmpbuffer.isPositional(),
tmpbuffer.getRolloff(),
tmpbuffer.getMaxDist(),
tmpbuffer.getGain(),
load);
} // loadSingleSfx
//----------------------------------------------------------------------------
/** Creates a new SFX object. The memory for this object is managed completely
* by the SFXManager. This makes it easy to use different implementations of
* SFX - since createSoundSource can return whatever type is used. To free the memory,
* call deleteSFX().
* \param id Identifier of the sound effect to create.
*/
SFXBase* SFXManager::createSoundSource(SFXBuffer* buffer,
const bool add_to_SFX_list,
const bool owns_buffer)
{
bool positional = false;
if (race_manager->getNumLocalPlayers() < 2)
{
positional = buffer->isPositional();
}
#if HAVE_OGGVORBIS
//assert( alIsBuffer(buffer->getBufferID()) ); crashes on server
SFXBase* sfx = new SFXOpenAL(buffer, positional, buffer->getGain(), owns_buffer);
#else
SFXBase* sfx = new DummySFX(buffer, positional, buffer->getGain(), owns_buffer);
#endif
sfx->setMasterVolume(m_master_gain);
if (add_to_SFX_list)
{
m_all_sfx.lock();
m_all_sfx.getData().push_back(sfx);
m_all_sfx.unlock();
}
return sfx;
} // createSoundSource
//----------------------------------------------------------------------------
SFXBase* SFXManager::createSoundSource(const std::string &name,
const bool add_to_SFXList)
{
std::map<std::string, SFXBuffer*>::iterator i = m_all_sfx_types.find(name);
if ( i == m_all_sfx_types.end() )
{
Log::error("SFXManager",
"SFXManager::createSoundSource could not find the "
"requested sound effect : '%s'.", name.c_str());
return NULL;
}
return createSoundSource( i->second, add_to_SFXList );
} // createSoundSource
//----------------------------------------------------------------------------
/** Returns true if a sfx with the given name exists.
* \param name The internal name of the sfx (not the name of the ogg file)
*/
bool SFXManager::soundExist(const std::string &name)
{
return m_all_sfx_types.find(name) != m_all_sfx_types.end();
} // soundExist
//----------------------------------------------------------------------------
/** This function removes a sfx buffer info entry from the mapping, and
* frees the openal buffer.
* \param name The name of the mapping entry to remove.
*/
void SFXManager::deleteSFXMapping(const std::string &name)
{
std::map<std::string, SFXBuffer*>::iterator i;
i = m_all_sfx_types.find(name);
if (i == m_all_sfx_types.end())
{
Log::warn("SFXManager",
"SFXManager::deleteSFXMapping : Warning: sfx not found in list.");
return;
}
(*i).second->unload();
m_all_sfx_types.erase(i);
} // deleteSFXMapping
//----------------------------------------------------------------------------
/** Make sure that the sfx thread is started at least once per frame. It also
* adds an update command for the music manager.
* \param dt Time step size.
*/
void SFXManager::update()
{
queue(SFX_UPDATE, (SFXBase*)NULL);
// Wake up the sfx thread to handle all queued up audio commands.
pthread_cond_signal(&m_cond_request);
} // update
//----------------------------------------------------------------------------
/** Updates the status of all playing sfx (to test if they are finished).
* This function is executed once per frame (triggered by the audio thread).
* \param current The sfx command - used to get timestep information.
*/
void SFXManager::reallyUpdateNow(SFXCommand *current)
{
if (m_last_update_time < 0.0)
{
// first time
m_last_update_time = StkTime::getRealTime();
}
double previous_update_time = m_last_update_time;
m_last_update_time = StkTime::getRealTime();
float dt = float(m_last_update_time - previous_update_time);
assert(current->m_command==SFX_UPDATE);
if (music_manager->getCurrentMusic())
music_manager->getCurrentMusic()->update(dt);
m_all_sfx.lock();
for (std::vector<SFXBase*>::iterator i = m_all_sfx.getData().begin();
i != m_all_sfx.getData().end(); i++)
{
if((*i)->getStatus()==SFXBase::SFX_PLAYING)
(*i)->updatePlayingSFX(dt);
} // for i in m_all_sfx
m_all_sfx.unlock();
// We need to lock the quick sounds during update, since adding more
// quick sounds by another thread could invalidate the iterator.
m_quick_sounds.lock();
std::map<std::string, SFXBase*>::iterator i = m_quick_sounds.getData().begin();
for (; i != m_quick_sounds.getData().end(); i++)
{
if (i->second->getStatus() == SFXBase::SFX_PLAYING)
i->second->updatePlayingSFX(dt);
} // for i in m_all_sfx
m_quick_sounds.unlock();
} // reallyUpdateNow
//----------------------------------------------------------------------------
/** Delete a sound effect object, and removes it from the internal list of
* all SFXs. This call deletes the object, and removes it from the list of
* all SFXs.
* \param sfx SFX object to delete.
*/
void SFXManager::deleteSFX(SFXBase *sfx)
{
if(sfx) sfx->reallyStopNow();
std::vector<SFXBase*>::iterator i;
// The whole block needs to be locked, otherwise the iterator
// could become invalid.
m_all_sfx.lock();
i=std::find(m_all_sfx.getData().begin(), m_all_sfx.getData().end(), sfx);
if(i==m_all_sfx.getData().end())
{
Log::warn("SFXManager",
"SFXManager::deleteSFX : Warning: sfx '%s' %lx not found in list.",
sfx->getBuffer()->getFileName().c_str(), sfx);
m_all_sfx.unlock();
return;
}
m_all_sfx.getData().erase(i);
m_all_sfx.unlock();
delete sfx;
} // deleteSFX
//----------------------------------------------------------------------------
/** Pauses all looping SFXs. Non-looping SFX will be finished, since it's
* otherwise not possible to determine which SFX must be resumed (i.e. were
* actually playing at the time pause was called).
*/
void SFXManager::pauseAll()
{
if (!sfxAllowed()) return;
queue(SFX_PAUSE_ALL);
} // pauseAll
//----------------------------------------------------------------------------
/** Pauses all looping SFXs. Non-looping SFX will be finished, since it's
* otherwise not possible to determine which SFX must be resumed (i.e. were
* actually playing at the time pause was called.
*/
void SFXManager::reallyPauseAllNow()
{
m_all_sfx.lock();
for (std::vector<SFXBase*>::iterator i= m_all_sfx.getData().begin();
i!=m_all_sfx.getData().end(); i++)
{
(*i)->reallyPauseNow();
} // for i in m_all_sfx
m_all_sfx.unlock();
} // pauseAll
//----------------------------------------------------------------------------
/** Resumes all paused SFXs. If sound is disabled, does nothing.
*/
void SFXManager::resumeAll()
{
// ignore unpausing if sound is disabled
if (!sfxAllowed()) return;
queue(SFX_RESUME_ALL);
} // resumeAll
//----------------------------------------------------------------------------
/** Resumes all paused SFXs. If sound is disabled, does nothing.
*/
void SFXManager::reallyResumeAllNow()
{
m_all_sfx.lock();
for (std::vector<SFXBase*>::iterator i =m_all_sfx.getData().begin();
i!=m_all_sfx.getData().end(); i++)
{
(*i)->reallyResumeNow();
} // for i in m_all_sfx
m_all_sfx.unlock();
} // resumeAll
//-----------------------------------------------------------------------------
/** Returns whether or not an openal error has occurred. If so, an error
* message is printed containing the given context.
* \param context Context to specify in the error message.
* \return True if no error happened.
*/
bool SFXManager::checkError(const std::string &context)
{
#if HAVE_OGGVORBIS
// Check (and clear) the error flag
int error = alGetError();
if(error != AL_NO_ERROR)
{
Log::error("SFXManager", "SFXOpenAL OpenAL error while %s: %s",
context.c_str(), SFXManager::getErrorString(error).c_str());
return false;
}
#endif
return true;
} // checkError
//-----------------------------------------------------------------------------
/** Sets the master volume for all sound effects.
* \param gain The volume to set.
*/
void SFXManager::setMasterSFXVolume(float gain)
{
if (gain > 1.0) gain = 1.0f;
if (gain < 0.0f) gain = 0.0f;
m_master_gain = gain;
// regular SFX
{
m_all_sfx.lock();
for (std::vector<SFXBase*>::iterator i =m_all_sfx.getData().begin();
i!=m_all_sfx.getData().end(); i++)
{
(*i)->setMasterVolume(m_master_gain);
} // for i in m_all_sfx
m_all_sfx.unlock();
}
// quick SFX
{
m_quick_sounds.lock();
std::map<std::string, SFXBase*>::iterator i = m_quick_sounds.getData().begin();
for (; i != m_quick_sounds.getData().end(); i++)
{
(*i).second->setMasterVolume(m_master_gain);
}
m_quick_sounds.unlock();
}
} // setMasterSFXVolume
//-----------------------------------------------------------------------------
const std::string SFXManager::getErrorString(int err)
{
#if HAVE_OGGVORBIS
switch(err)
{
case AL_NO_ERROR: return std::string("AL_NO_ERROR" );
case AL_INVALID_NAME: return std::string("AL_INVALID_NAME" );
case AL_INVALID_ENUM: return std::string("AL_INVALID_ENUM" );
case AL_INVALID_VALUE: return std::string("AL_INVALID_VALUE" );
case AL_INVALID_OPERATION: return std::string("AL_INVALID_OPERATION");
case AL_OUT_OF_MEMORY: return std::string("AL_OUT_OF_MEMORY" );
default: return std::string("UNKNOWN");
};
#else
return std::string("sound disabled");
#endif
} // getErrorString
//-----------------------------------------------------------------------------
/** Sets the position and orientation of the listener.
* \param position Position of the listener.
* \param front Which way the listener is facing.
* \param up The up direction of the listener.
*/
void SFXManager::positionListener(const Vec3 &position, const Vec3 &front,
const Vec3 &up)
{
m_listener_position.lock();
m_listener_position.getData() = position;
m_listener_front = front;
m_listener_up = up;
m_listener_position.unlock();
queue(SFX_LISTENER);
} // positionListener
//-----------------------------------------------------------------------------
/** Sets the position and orientation of the listener.
* \param position Position of the listener.
* \param front Which way the listener is facing.
*/
void SFXManager::reallyPositionListenerNow()
{
#if HAVE_OGGVORBIS
if (!sfxAllowed()) return;
m_listener_position.lock();
{
//forward vector
float orientation[6];
orientation[0] = m_listener_front.getX();
orientation[1] = m_listener_front.getY();
orientation[2] = -m_listener_front.getZ();
//up vector
orientation[3] = m_listener_up.getX();
orientation[4] = m_listener_up.getY();
orientation[5] = -m_listener_up.getZ();
const Vec3 &pos = m_listener_position.getData();
alListener3f(AL_POSITION, pos.getX(), pos.getY(), -pos.getZ());
alListenerfv(AL_ORIENTATION, orientation);
}
m_listener_position.unlock();
#endif
} // reallyPositionListenerNow
//-----------------------------------------------------------------------------
/** Positional sound is cool, but creating a new object just to play a simple
* menu sound is not. This function allows for 'quick sounds' in a single call.
* \param sound_type Internal name of the sfx to play.
* \return a pointer to the sound, for instance to check when the sound finished.
* don't delete the returned pointer.
*/
SFXBase* SFXManager::quickSound(const std::string &sound_type)
{
if (!sfxAllowed()) return NULL;
MutexLockerHelper lock(m_quick_sounds);
std::map<std::string, SFXBase*>::iterator sound =
m_quick_sounds.getData().find(sound_type);
if (sound == m_quick_sounds.getData().end())
{
// sound not yet in our local list of quick sounds
SFXBase* new_sound = createSoundSource(sound_type, false);
if (new_sound == NULL) return NULL;
new_sound->play();
m_quick_sounds.getData()[sound_type] = new_sound;
return new_sound;
}
else
{
SFXBase *base_sound = sound->second;
if (base_sound->getStatus() != SFXBase::SFX_PLAYING)
base_sound->play();
return base_sound;
}
} // quickSound