Handle playing and stopping of music in separate thread. Make the sfx

manager thread use CanBeDeleted to allow for timed deleting.S
This commit is contained in:
hiker 2015-02-03 16:27:05 +11:00
parent b9a7c11dab
commit 4658bd83de
9 changed files with 94 additions and 34 deletions

View File

@ -187,7 +187,7 @@ void MusicInformation::startMusic()
if(StringUtils::getExtension(m_fast_filename)!="ogg")
{
Log::warn(
"Music file %s format not recognized, fast music is ignored\n",
"Music file %s format not recognized, fast music is ignored",
m_fast_filename.c_str());
return;
}
@ -334,7 +334,8 @@ void MusicInformation::switchToFastMusic()
bool MusicInformation::isPlaying() const
{
return (m_normal_music != NULL && m_normal_music->isPlaying()) || (m_fast_music != NULL && m_fast_music->isPlaying());
return (m_normal_music != NULL && m_normal_music->isPlaying()) ||
(m_fast_music != NULL && m_fast_music->isPlaying());
}
//-----------------------------------------------------------------------------

View File

@ -93,8 +93,6 @@ MusicManager::MusicManager()
//-----------------------------------------------------------------------------
MusicManager::~MusicManager()
{
stopMusic();
for(std::map<std::string,MusicInformation*>::iterator
i=m_all_music.begin(); i!=m_all_music.end(); i++)
{
@ -185,16 +183,18 @@ void MusicManager::startMusic(MusicInformation* mi, bool start_right_now)
if(!mi || !UserConfigParams::m_music || !m_initialized) return;
mi->volumeMusic(m_masterGain);
if (start_right_now)
mi->startMusic();
else
mi->setMusicWaiting();
SFXManager::get()->queue(start_right_now ? SFXManager::SFX_MUSIC_START
: SFXManager::SFX_MUSIC_WAITING,
mi);
} // startMusic
//-----------------------------------------------------------------------------
/** Queues a stop current music event for the audio thread.
*/
void MusicManager::stopMusic()
{
if(m_current_music) m_current_music->stopMusic();
if (m_current_music)
SFXManager::get()->queue(SFXManager::SFX_MUSIC_STOP, m_current_music);
} // stopMusic
//-----------------------------------------------------------------------------

View File

@ -56,7 +56,7 @@ public:
void startMusic(MusicInformation* mi,
bool start_right_now=true);
void stopMusic();
bool initialized() const {return m_initialized; }
bool initialized() const { return m_initialized; }
void update(float dt) {if(m_current_music)
m_current_music->update(dt); }
void pauseMusic() {if(m_current_music)

View File

@ -61,12 +61,14 @@ bool MusicOggStream::load(const std::string& filename)
if(!m_oggFile)
{
Log::error("MusicOgg", "Loading Music: %s failed (fopen returned NULL)\n", m_fileName.c_str());
Log::error("MusicOgg", "Loading Music: %s failed (fopen returned NULL)",
m_fileName.c_str());
return false;
}
#if defined( WIN32 ) || defined( WIN64 )
const int result = ov_open_callbacks((void *)m_oggFile, &m_oggStream, NULL, 0, OV_CALLBACKS_DEFAULT);
const int result = ov_open_callbacks((void *)m_oggFile, &m_oggStream, NULL,
0, OV_CALLBACKS_DEFAULT );
#else
const int result = ov_open(m_oggFile, &m_oggStream, NULL, 0);
#endif
@ -98,7 +100,8 @@ bool MusicOggStream::load(const std::string& filename)
errorMessage = "Unknown Error";
}
Log::error("MusicOgg", "Loading Music: %s failed : ov_open returned error code %i (%s)\n",
Log::error("MusicOgg", "Loading Music: %s failed : "
"ov_open returned error code %i (%s)",
m_fileName.c_str(), result, errorMessage);
return false;
}
@ -359,7 +362,8 @@ bool MusicOggStream::check(const char* what)
if (error != AL_NO_ERROR)
{
Log::error("MusicOgg", "[MusicOggStream] OpenAL error at %s : %s (%i)\n", what, SFXManager::getErrorString(error).c_str(), error);
Log::error("MusicOgg", "OpenAL error at %s : %s (%i)", what,
SFXManager::getErrorString(error).c_str(), error);
return false;
}

View File

@ -85,7 +85,9 @@ private:
ALenum nb_channels;
bool m_pausedMusic;
static const int m_buffer_size = 11025*4;//one full second of audio at 44100 samples per second
//one full second of audio at 44100 samples per second
static const int m_buffer_size = 11025*4;
};
#endif

View File

@ -111,7 +111,8 @@ bool SFXBuffer::load()
if (!loadVorbisBuffer(m_file, m_buffer))
{
Log::error("SFXBuffer", "Could not load sound effect %s\n", m_file.c_str());
Log::error("SFXBuffer", "Could not load sound effect %s",
m_file.c_str());
// TODO: free al buffer here?
return false;
}
@ -165,20 +166,22 @@ bool SFXBuffer::loadVorbisBuffer(const std::string &name, ALuint buffer)
if(!file)
{
Log::error("SFXBuffer", "[SFXBuffer] LoadVorbisBuffer() - couldn't open file!\n");
Log::error("SFXBuffer", "LoadVorbisBuffer() - couldn't open file!");
return false;
}
if (ov_open_callbacks(file, &oggFile, NULL, 0, OV_CALLBACKS_NOCLOSE) != 0)
{
fclose(file);
Log::error("SFXBuffer", "[SFXBuffer] LoadVorbisBuffer() - ov_open_callbacks() failed, file isn't vorbis?\n");
Log::error("SFXBuffer", "LoadVorbisBuffer() - ov_open_callbacks() failed, "
"file isn't vorbis?");
return false;
}
info = ov_info(&oggFile, -1);
long len = (long)ov_pcm_total(&oggFile, -1) * info->channels * 2; // always 16 bit data
// always 16 bit data
long len = (long)ov_pcm_total(&oggFile, -1) * info->channels * 2;
char *data = (char *) malloc(len);
if(!data)
@ -213,12 +216,13 @@ bool SFXBuffer::loadVorbisBuffer(const std::string &name, ALuint buffer)
// duration (which is the norm), compute it:
if(m_duration < 0)
{
ALint buffer_size, frequency, bits_per_sample, channels;
alGetBufferi(buffer, AL_SIZE, &buffer_size );
alGetBufferi(buffer, AL_FREQUENCY, &frequency );
alGetBufferi(buffer, AL_CHANNELS, &channels );
alGetBufferi(buffer, AL_BITS, &bits_per_sample);
m_duration = float(buffer_size) / (frequency*channels*(bits_per_sample / 8));
ALint buffer_size, frequency, bits_per_sample, channels;
alGetBufferi(buffer, AL_SIZE, &buffer_size );
alGetBufferi(buffer, AL_FREQUENCY, &frequency );
alGetBufferi(buffer, AL_CHANNELS, &channels );
alGetBufferi(buffer, AL_BITS, &bits_per_sample);
m_duration = float(buffer_size)
/ (frequency*channels*(bits_per_sample / 8));
}
return success;
#else

View File

@ -201,6 +201,13 @@ void SFXManager::queue(SFXCommands command, SFXBase *sfx, const Vec3 &p)
queueCommand(sfx_command);
} // queue (Vec3)
//----------------------------------------------------------------------------
void SFXManager::queue(SFXCommands command, MusicInformation *mi)
{
SFXCommand *sfx_command = new SFXCommand(command, mi);
queueCommand(sfx_command);
} // queue(MusicInformation)
//----------------------------------------------------------------------------
/** Enqueues a command to the sfx queue threadsafe. Then signal the
* sfx manager to wake up.
@ -213,7 +220,7 @@ void SFXManager::queueCommand(SFXCommand *command)
race_manager->getMinorMode() != RaceManager::MINOR_MODE_CUTSCENE)
{
if(command->m_command==SFX_POSITION || command->m_command==SFX_LOOP ||
command->m_command==SFX_PLAY || command->m_command==SFX_SPEED )
command->m_command==SFX_SPEED )
{
delete command;
static int count_messages = 0;
@ -303,6 +310,12 @@ void* SFXManager::mainLoop(void *obj)
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->startMusic(); break;
case SFX_MUSIC_STOP:
current->m_music_information->stopMusic(); break;
case SFX_MUSIC_WAITING:
current->m_music_information->setMusicWaiting(); break;
default: assert("Not yet supported.");
}
delete current;
@ -311,6 +324,11 @@ void* SFXManager::mainLoop(void *obj)
} // 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())
{
@ -449,7 +467,8 @@ SFXBuffer* SFXManager::addSingleSfx(const std::string &sfx_name,
const bool load)
{
SFXBuffer* buffer = new SFXBuffer(sfx_file, positional, rolloff, max_width, gain);
SFXBuffer* buffer = new SFXBuffer(sfx_file, positional, rolloff,
max_width, gain);
m_all_sfx_types[sfx_name] = buffer;
@ -617,7 +636,7 @@ void SFXManager::reallyUpdateNow(SFXCommand *current)
{
assert(current->m_command==SFX_UPDATE);
float dt = current->m_parameter.getX();
music_manager->update(dt);
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++)

View File

@ -19,6 +19,7 @@
#ifndef HEADER_SFX_MANAGER_HPP
#define HEADER_SFX_MANAGER_HPP
#include "utils/can_be_deleted.hpp"
#include "utils/leak_check.hpp"
#include "utils/no_copy.hpp"
#include "utils/synchronised.hpp"
@ -38,7 +39,7 @@
typedef unsigned int ALuint;
#endif
class MusicInformation;
class SFXBase;
class SFXBuffer;
class XMLNode;
@ -50,7 +51,7 @@ class XMLNode;
* on of the (shared) buffers from the sound manager.
* \ingroup audio
*/
class SFXManager : public NoCopy
class SFXManager : public NoCopy, public CanBeDeleted
{
private:
/** Singleton pointer. */
@ -76,6 +77,10 @@ public:
SFX_LOOP,
SFX_LISTENER,
SFX_UPDATE,
SFX_MUSIC_START,
SFX_MUSIC_STOP,
SFX_MUSIC_UPDATE,
SFX_MUSIC_WAITING,
SFX_EXIT,
}; // SFXCommands
@ -108,10 +113,15 @@ private:
LEAK_CHECK()
public:
/** The sound effect for which the command should be executed. */
SFXBase *m_sfx;
SFXBase *m_sfx;
/** Stores music information for music commands. */
MusicInformation *m_music_information;
/** The command to execute. */
SFXCommands m_command;
/** Optional parameter for commands that need more input. */
/** Optional parameter for commands that need more input. Single
* floating point values are stored in the X component. */
Vec3 m_parameter;
// --------------------------------------------------------------------
SFXCommand(SFXCommands command, SFXBase *base)
@ -120,6 +130,13 @@ private:
m_sfx = base;
} // SFXCommand()
// --------------------------------------------------------------------
/** Constructor for music information commands. */
SFXCommand(SFXCommands command, MusicInformation *mi)
{
m_command = command;
m_music_information = mi;
} // SFXCommnd
// --------------------------------------------------------------------
SFXCommand(SFXCommands command, SFXBase *base, float parameter)
{
m_command = command;
@ -187,6 +204,7 @@ public:
void queue(SFXCommands command, SFXBase *sfx=NULL);
void queue(SFXCommands command, SFXBase *sfx, float f);
void queue(SFXCommands command, SFXBase *sfx, const Vec3 &p);
void queue(SFXCommands command, MusicInformation *mi);
// ------------------------------------------------------------------------
/** Static function to get the singleton sfx manager. */
static SFXManager *get()

View File

@ -1462,6 +1462,10 @@ static void cleanSuperTuxKart()
if(Online::RequestManager::isRunning())
Online::RequestManager::get()->stopNetworkThread();
// Stop music (this request will go into the sfx manager queue, so it needs
// to be done before stopping the thread).
music_manager->stopMusic();
SFXManager::get()->stopThread();
irr_driver->updateConfigIfRelevant();
AchievementsManager::destroy();
@ -1479,8 +1483,6 @@ static void cleanSuperTuxKart()
if(material_manager) delete material_manager;
if(history) delete history;
ReplayRecorder::destroy();
SFXManager::destroy();
if(music_manager) delete music_manager;
delete ParticleKindManager::get();
PlayerManager::destroy();
if(unlock_manager) delete unlock_manager;
@ -1507,6 +1509,16 @@ static void cleanSuperTuxKart()
}
Online::RequestManager::deallocate();
if (!SFXManager::get()->waitForReadyToDeleted(2.0f))
{
Log::info("Thread", "SFXManager not stopping, exiting anyway.");
}
SFXManager::destroy();
// Music manager can not be deleted before the sfx thread is stopped
// (since sfx commands can contain music information).
delete music_manager;
// The addons manager might still be called from a currenty running request
// in the request manager, so it can not be deleted earlier.
if(addons_manager) delete addons_manager;