[colobot] 01/74: Asynchronous sound/music loading
Didier Raboud
odyx at moszumanska.debian.org
Mon Nov 7 07:49:58 UTC 2016
This is an automated email from the git hooks/post-receive script.
odyx pushed a commit to branch debian/master
in repository colobot.
commit 29f0631a2c322d5e86ec2e64b7940d966d7b00f0
Author: krzys-h <krzys_h at interia.pl>
Date: Sun Jul 10 14:56:34 2016 +0200
Asynchronous sound/music loading
This improves the loading time a lot. Time from starting the app to opening game window decreased by almost 5 seconds (it's almost instant now). Mission loading times are significantly better too. As a bonus, the playmusic() CBot function doesn't hang the game if you don't preload the files with CacheAudio in scene file.
---
src/CMakeLists.txt | 2 +
src/app/app.cpp | 18 ++++-
src/common/thread/resource_owning_thread.h | 15 +++-
src/common/thread/sdl_cond_wrapper.h | 12 +++
src/common/thread/sdl_mutex_wrapper.h | 10 +++
src/common/thread/thread.h | 77 ++++++++++++++++++++
src/common/thread/worker_thread.h | 92 +++++++++++++++++++++++
src/sound/oalsound/alsound.cpp | 113 ++++++++++++-----------------
src/sound/oalsound/alsound.h | 11 +--
src/sound/sound.cpp | 28 +------
src/sound/sound.h | 26 ++-----
src/ui/mainui.cpp | 3 +-
12 files changed, 289 insertions(+), 118 deletions(-)
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index bc6f12a..4d5a640 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -151,6 +151,8 @@ set(BASE_SOURCES
common/thread/resource_owning_thread.h
common/thread/sdl_cond_wrapper.h
common/thread/sdl_mutex_wrapper.h
+ common/thread/thread.h
+ common/thread/worker_thread.h
graphics/core/color.cpp
graphics/core/color.h
graphics/core/device.h
diff --git a/src/app/app.cpp b/src/app/app.cpp
index d34532f..6241ef0 100644
--- a/src/app/app.cpp
+++ b/src/app/app.cpp
@@ -34,6 +34,8 @@
#include "common/resources/resourcemanager.h"
+#include "common/thread/thread.h"
+
#include "graphics/core/nulldevice.h"
#include "graphics/opengl/glutil.h"
@@ -514,8 +516,6 @@ bool CApplication::Create()
#endif
m_sound->Create();
- m_sound->CacheAll();
- m_sound->CacheCommonMusic();
GetLogger()->Info("CApplication created successfully\n");
@@ -685,6 +685,20 @@ bool CApplication::Create()
// Create the robot application.
m_controller = MakeUnique<CController>();
+ CThread musicLoadThread([this]() {
+ GetLogger()->Debug("Cache sounds...\n");
+ SystemTimeStamp* musicLoadStart = m_systemUtils->CreateTimeStamp();
+ m_systemUtils->GetCurrentTimeStamp(musicLoadStart);
+
+ m_sound->CacheAll();
+
+ SystemTimeStamp* musicLoadEnd = m_systemUtils->CreateTimeStamp();
+ m_systemUtils->GetCurrentTimeStamp(musicLoadEnd);
+ float musicLoadTime = m_systemUtils->TimeStampDiff(musicLoadStart, musicLoadEnd, STU_MSEC);
+ GetLogger()->Debug("Sound loading took %.2f ms\n", musicLoadTime);
+ }, "Sound loading thread");
+ musicLoadThread.Start();
+
if (m_runSceneCategory == LevelCategory::Max)
m_controller->StartApp();
else
diff --git a/src/common/thread/resource_owning_thread.h b/src/common/thread/resource_owning_thread.h
index 0e4c200..0423796 100644
--- a/src/common/thread/resource_owning_thread.h
+++ b/src/common/thread/resource_owning_thread.h
@@ -59,6 +59,11 @@ public:
m_name(name)
{}
+ ~CResourceOwningThread()
+ {
+ SDL_DetachThread(m_thread);
+ }
+
void Start()
{
CSDLMutexWrapper mutex;
@@ -74,7 +79,7 @@ public:
SDL_LockMutex(*mutex);
- SDL_CreateThread(Run, !m_name.empty() ? m_name.c_str() : nullptr, reinterpret_cast<void*>(&data));
+ m_thread = SDL_CreateThread(Run, !m_name.empty() ? m_name.c_str() : nullptr, reinterpret_cast<void*>(&data));
while (!condition)
{
@@ -84,6 +89,13 @@ public:
SDL_UnlockMutex(*mutex);
}
+ void Join()
+ {
+ if (m_thread == nullptr) return;
+ SDL_WaitThread(m_thread, nullptr);
+ m_thread = nullptr;
+ }
+
private:
static int Run(void* data)
{
@@ -117,4 +129,5 @@ private:
ThreadFunctionPtr m_threadFunction;
ResourceUPtr m_resource;
std::string m_name;
+ SDL_Thread* m_thread = nullptr;
};
diff --git a/src/common/thread/sdl_cond_wrapper.h b/src/common/thread/sdl_cond_wrapper.h
index 5b09d2f..8c8be5e 100644
--- a/src/common/thread/sdl_cond_wrapper.h
+++ b/src/common/thread/sdl_cond_wrapper.h
@@ -19,6 +19,8 @@
#pragma once
+#include "common/thread/sdl_mutex_wrapper.h"
+
#include <SDL_thread.h>
/**
@@ -45,6 +47,16 @@ public:
return m_cond;
}
+ void Signal()
+ {
+ SDL_CondSignal(m_cond);
+ }
+
+ void Wait(SDL_mutex* mutex)
+ {
+ SDL_CondWait(m_cond, mutex);
+ }
+
private:
SDL_cond* m_cond;
};
diff --git a/src/common/thread/sdl_mutex_wrapper.h b/src/common/thread/sdl_mutex_wrapper.h
index bd7d907..59afc0a 100644
--- a/src/common/thread/sdl_mutex_wrapper.h
+++ b/src/common/thread/sdl_mutex_wrapper.h
@@ -45,6 +45,16 @@ public:
return m_mutex;
}
+ void Lock()
+ {
+ SDL_LockMutex(m_mutex);
+ }
+
+ void Unlock()
+ {
+ SDL_UnlockMutex(m_mutex);
+ }
+
private:
SDL_mutex* m_mutex;
};
diff --git a/src/common/thread/thread.h b/src/common/thread/thread.h
new file mode 100644
index 0000000..d0b6b87
--- /dev/null
+++ b/src/common/thread/thread.h
@@ -0,0 +1,77 @@
+/*
+ * This file is part of the Colobot: Gold Edition source code
+ * Copyright (C) 2001-2016, Daniel Roux, EPSITEC SA & TerranovaTeam
+ * http://epsitec.ch; http://colobot.info; http://github.com/colobot
+ *
+ * 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, see http://gnu.org/licenses
+ */
+
+#pragma once
+
+#include "common/make_unique.h"
+
+#include "common/thread/resource_owning_thread.h"
+
+#include <functional>
+#include <string>
+#include <memory>
+
+/**
+ * \class CThread
+ * \brief Wrapper for using SDL_thread with std::function
+ */
+class CThread
+{
+public:
+ using ThreadFunctionPtr = std::function<void()>;
+
+private:
+ struct ThreadData
+ {
+ ThreadFunctionPtr func;
+ };
+
+public:
+ CThread(ThreadFunctionPtr func, std::string name = "")
+ : m_func(std::move(func))
+ , m_name(name)
+ {}
+
+ void Start()
+ {
+ std::unique_ptr<ThreadData> data = MakeUnique<ThreadData>();
+ data->func = m_func;
+ m_thread = MakeUnique<CResourceOwningThread<ThreadData>>(Run, std::move(data), m_name);
+ m_thread->Start();
+ }
+
+ void Join()
+ {
+ if (!m_thread) return;
+ m_thread->Join();
+ }
+
+ CThread(const CThread&) = delete;
+ CThread& operator=(const CThread&) = delete;
+
+private:
+ static void Run(std::unique_ptr<ThreadData> data)
+ {
+ data->func();
+ }
+
+ std::unique_ptr<CResourceOwningThread<ThreadData>> m_thread;
+ ThreadFunctionPtr m_func;
+ std::string m_name;
+};
diff --git a/src/common/thread/worker_thread.h b/src/common/thread/worker_thread.h
new file mode 100644
index 0000000..aa2ce3e
--- /dev/null
+++ b/src/common/thread/worker_thread.h
@@ -0,0 +1,92 @@
+/*
+ * This file is part of the Colobot: Gold Edition source code
+ * Copyright (C) 2001-2016, Daniel Roux, EPSITEC SA & TerranovaTeam
+ * http://epsitec.ch; http://colobot.info; http://github.com/colobot
+ *
+ * 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, see http://gnu.org/licenses
+ */
+
+#pragma once
+
+#include "common/make_unique.h"
+
+#include "common/thread/sdl_cond_wrapper.h"
+#include "common/thread/sdl_mutex_wrapper.h"
+#include "common/thread/thread.h"
+
+#include <functional>
+#include <string>
+#include <queue>
+
+/**
+ * \class CWorkerThread
+ * \brief Thread that runs functions, one at a time
+ */
+class CWorkerThread
+{
+public:
+ using ThreadFunctionPtr = std::function<void()>;
+
+public:
+ CWorkerThread(std::string name = "")
+ : m_thread(std::bind(&CWorkerThread::Run, this), name)
+ {
+ m_thread.Start();
+ }
+
+ ~CWorkerThread()
+ {
+ m_mutex.Lock();
+ m_running = false;
+ m_cond.Signal();
+ m_mutex.Unlock();
+ m_thread.Join();
+ }
+
+ void Start(ThreadFunctionPtr func)
+ {
+ m_mutex.Lock();
+ m_queue.push(func);
+ m_cond.Signal();
+ m_mutex.Unlock();
+ }
+
+ CWorkerThread(const CWorkerThread&) = delete;
+ CWorkerThread& operator=(const CWorkerThread&) = delete;
+
+private:
+ void Run()
+ {
+ m_mutex.Lock();
+ while (true)
+ {
+ while (m_queue.empty() && m_running)
+ {
+ m_cond.Wait(*m_mutex);
+ }
+ if (!m_running) break;
+
+ ThreadFunctionPtr func = m_queue.front();
+ m_queue.pop();
+ func();
+ }
+ m_mutex.Unlock();
+ }
+
+ CThread m_thread;
+ CSDLMutexWrapper m_mutex;
+ CSDLCondWrapper m_cond;
+ bool m_running = true;
+ std::queue<ThreadFunctionPtr> m_queue;
+};
diff --git a/src/sound/oalsound/alsound.cpp b/src/sound/oalsound/alsound.cpp
index cfa335d..f51887b 100644
--- a/src/sound/oalsound/alsound.cpp
+++ b/src/sound/oalsound/alsound.cpp
@@ -32,7 +32,8 @@ CALSound::CALSound()
m_musicVolume(1.0f),
m_channelsLimit(2048),
m_device{},
- m_context{}
+ m_context{},
+ m_thread("Music loading thread")
{
}
@@ -144,18 +145,19 @@ bool CALSound::Cache(SoundType sound, const std::string &filename)
return false;
}
-bool CALSound::CacheMusic(const std::string &filename)
+void CALSound::CacheMusic(const std::string &filename)
{
- if (m_music.find(filename) == m_music.end())
+ m_thread.Start([this, filename]()
{
- auto buffer = MakeUnique<CBuffer>();
- if (buffer->LoadFromFile(filename, static_cast<SoundType>(-1)))
+ if (m_music.find(filename) == m_music.end())
{
- m_music[filename] = std::move(buffer);
- return true;
+ auto buffer = MakeUnique<CBuffer>();
+ if (buffer->LoadFromFile(filename, static_cast<SoundType>(-1)))
+ {
+ m_music[filename] = std::move(buffer);
+ }
}
- }
- return false;
+ });
}
bool CALSound::IsCached(SoundType sound)
@@ -572,53 +574,54 @@ void CALSound::SetListener(const Math::Vector &eye, const Math::Vector &lookat)
alListenerfv(AL_ORIENTATION, orientation);
}
-bool CALSound::PlayMusic(const std::string &filename, bool repeat, float fadeTime)
+void CALSound::PlayMusic(const std::string &filename, bool repeat, float fadeTime)
{
if (!m_enabled)
{
- return false;
+ return;
}
- CBuffer *buffer = nullptr;
-
- // check if we have music in cache
- if (m_music.find(filename) == m_music.end())
+ m_thread.Start([this, filename, repeat, fadeTime]()
{
- GetLogger()->Debug("Music %s was not cached!\n", filename.c_str());
+ CBuffer* buffer = nullptr;
- auto newBuffer = MakeUnique<CBuffer>();
- buffer = newBuffer.get();
- if (!newBuffer->LoadFromFile(filename, static_cast<SoundType>(-1)))
+ // check if we have music in cache
+ if (m_music.find(filename) == m_music.end())
{
- return false;
- }
- m_music[filename] = std::move(newBuffer);
- }
- else
- {
- GetLogger()->Debug("Music loaded from cache\n");
- buffer = m_music[filename].get();
- }
+ GetLogger()->Debug("Music %s was not cached!\n", filename.c_str());
- if (m_currentMusic)
- {
- OldMusic old;
- old.music = std::move(m_currentMusic);
- old.fadeTime = fadeTime;
- old.currentTime = 0.0f;
- m_oldMusic.push_back(std::move(old));
- }
+ auto newBuffer = MakeUnique<CBuffer>();
+ buffer = newBuffer.get();
+ if (!newBuffer->LoadFromFile(filename, static_cast<SoundType>(-1)))
+ {
+ return;
+ }
+ m_music[filename] = std::move(newBuffer);
+ }
+ else
+ {
+ GetLogger()->Debug("Music loaded from cache\n");
+ buffer = m_music[filename].get();
+ }
- m_currentMusic = MakeUnique<CChannel>();
- m_currentMusic->SetBuffer(buffer);
- m_currentMusic->SetVolume(m_musicVolume);
- m_currentMusic->SetLoop(repeat);
- m_currentMusic->Play();
+ if (m_currentMusic)
+ {
+ OldMusic old;
+ old.music = std::move(m_currentMusic);
+ old.fadeTime = fadeTime;
+ old.currentTime = 0.0f;
+ m_oldMusic.push_back(std::move(old));
+ }
- return true;
+ m_currentMusic = MakeUnique<CChannel>();
+ m_currentMusic->SetBuffer(buffer);
+ m_currentMusic->SetVolume(m_musicVolume);
+ m_currentMusic->SetLoop(repeat);
+ m_currentMusic->Play();
+ });
}
-bool CALSound::PlayPauseMusic(const std::string &filename, bool repeat)
+void CALSound::PlayPauseMusic(const std::string &filename, bool repeat)
{
if (m_previousMusic.fadeTime > 0.0f)
{
@@ -640,7 +643,7 @@ bool CALSound::PlayPauseMusic(const std::string &filename, bool repeat)
m_previousMusic.currentTime = 0.0f;
}
}
- return PlayMusic(filename, repeat);
+ PlayMusic(filename, repeat);
}
void CALSound::StopPauseMusic()
@@ -662,18 +665,6 @@ void CALSound::StopPauseMusic()
}
}
-bool CALSound::RestartMusic()
-{
- if (!m_enabled || m_currentMusic == nullptr)
- {
- return false;
- }
-
- m_currentMusic->Stop();
- m_currentMusic->Play();
- return true;
-}
-
void CALSound::StopMusic(float fadeTime)
{
if (!m_enabled || m_currentMusic == nullptr)
@@ -698,16 +689,6 @@ bool CALSound::IsPlayingMusic()
return m_currentMusic->IsPlaying();
}
-void CALSound::SuspendMusic()
-{
- if (!m_enabled || m_currentMusic == nullptr)
- {
- return;
- }
-
- m_currentMusic->Stop();
-}
-
bool CALSound::CheckChannel(int &channel)
{
int id = (channel >> 16) & 0xffff;
diff --git a/src/sound/oalsound/alsound.h b/src/sound/oalsound/alsound.h
index e4656e1..03d5bd4 100644
--- a/src/sound/oalsound/alsound.h
+++ b/src/sound/oalsound/alsound.h
@@ -26,6 +26,8 @@
#include "sound/sound.h"
+#include "common/thread/worker_thread.h"
+
#include "sound/oalsound/buffer.h"
#include "sound/oalsound/channel.h"
#include "sound/oalsound/check.h"
@@ -83,7 +85,7 @@ public:
bool Create() override;
bool Cache(SoundType, const std::string &) override;
- bool CacheMusic(const std::string &) override;
+ void CacheMusic(const std::string &) override;
bool IsCached(SoundType) override;
bool IsCachedMusic(const std::string &) override;
@@ -106,12 +108,10 @@ public:
bool StopAll() override;
bool MuteAll(bool mute) override;
- bool PlayMusic(const std::string &filename, bool repeat, float fadeTime=2.0f) override;
- bool RestartMusic() override;
- void SuspendMusic() override;
+ void PlayMusic(const std::string &filename, bool repeat, float fadeTime = 2.0f) override;
void StopMusic(float fadeTime=2.0f) override;
bool IsPlayingMusic() override;
- bool PlayPauseMusic(const std::string &filename, bool repeat) override;
+ void PlayPauseMusic(const std::string &filename, bool repeat) override;
void StopPauseMusic() override;
private:
@@ -134,4 +134,5 @@ private:
OldMusic m_previousMusic;
Math::Vector m_eye;
Math::Vector m_lookat;
+ CWorkerThread m_thread;
};
diff --git a/src/sound/sound.cpp b/src/sound/sound.cpp
index 3640c58..e5b2929 100644
--- a/src/sound/sound.cpp
+++ b/src/sound/sound.cpp
@@ -52,22 +52,13 @@ void CSoundInterface::CacheAll()
}
}
-void CSoundInterface::CacheCommonMusic()
-{
- CacheMusic("music/Intro1.ogg");
- CacheMusic("music/Intro2.ogg");
- CacheMusic("music/music010.ogg");
- CacheMusic("music/music011.ogg");
-}
-
bool CSoundInterface::Cache(SoundType sound, const std::string &file)
{
return true;
}
-bool CSoundInterface::CacheMusic(const std::string &file)
+void CSoundInterface::CacheMusic(const std::string &file)
{
- return true;
}
bool CSoundInterface::IsCached(SoundType sound)
@@ -156,17 +147,7 @@ bool CSoundInterface::MuteAll(bool mute)
return true;
}
-bool CSoundInterface::PlayMusic(const std::string &filename, bool repeat, float fadeTime)
-{
- return true;
-}
-
-bool CSoundInterface::RestartMusic()
-{
- return true;
-}
-
-void CSoundInterface::SuspendMusic()
+void CSoundInterface::PlayMusic(const std::string &filename, bool repeat, float fadeTime)
{
}
@@ -176,12 +157,11 @@ void CSoundInterface::StopMusic(float fadeTime)
bool CSoundInterface::IsPlayingMusic()
{
- return true;
+ return false;
}
-bool CSoundInterface::PlayPauseMusic(const std::string &filename, bool repeat)
+void CSoundInterface::PlayPauseMusic(const std::string &filename, bool repeat)
{
- return true;
}
void CSoundInterface::StopPauseMusic()
diff --git a/src/sound/sound.h b/src/sound/sound.h
index 3799d6f..a2dce31 100644
--- a/src/sound/sound.h
+++ b/src/sound/sound.h
@@ -72,9 +72,6 @@ public:
*/
void CacheAll();
- /** Function called to add all music files to list */
- void CacheCommonMusic();
-
/** Function called to cache sound effect file.
* This function is called by plugin interface for each file.
* \param sound - id of a file, will be used to identify sound files
@@ -85,10 +82,10 @@ public:
/** Function called to cache music file.
* This function is called by CRobotMain for each file used in the mission.
+ * This function is executed asynchronously
* \param file - file to load
- * \return return true on success
*/
- virtual bool CacheMusic(const std::string &file);
+ virtual void CacheMusic(const std::string &file);
/** Function to check if sound effect file was cached.
* \param sound - id of a sound effect file
@@ -205,22 +202,12 @@ public:
virtual bool MuteAll(bool mute);
/** Start playing music
+ * This function is executed asynchronously
* \param filename - name of file to play
* \param repeat - repeat playing
- * \param fadeTime - time of transition between music
- * \return return true on success
- */
- virtual bool PlayMusic(const std::string &filename, bool repeat, float fadeTime=2.0f);
-
- /** Restart music
- * \return return true on success
- */
- virtual bool RestartMusic();
-
- /** Susspend playing music
- * \return nothing
+ * \param fadeTime - time of transition between music, 0 to disable
*/
- virtual void SuspendMusic();
+ virtual void PlayMusic(const std::string &filename, bool repeat, float fadeTime = 2.0f);
/** Stop playing music
* \return nothing
@@ -233,11 +220,12 @@ public:
virtual bool IsPlayingMusic();
/** Start playing pause music
+ * This function is executed asynchronously
* \param filename - name of file to play
* \param repeat - repeat playing
* \return return true on success
*/
- virtual bool PlayPauseMusic(const std::string &filename, bool repeat);
+ virtual void PlayPauseMusic(const std::string &filename, bool repeat);
/** Stop playing pause music and return to the mission music
* \return nothing
diff --git a/src/ui/mainui.cpp b/src/ui/mainui.cpp
index f373d69..503cc49 100644
--- a/src/ui/mainui.cpp
+++ b/src/ui/mainui.cpp
@@ -213,9 +213,10 @@ void CMainUserInterface::ChangePhase(Phase phase)
if ( IsMainMenuPhase(m_phase) )
{
- if (!m_sound->IsPlayingMusic() && m_sound->IsCachedMusic("music/Intro1.ogg"))
+ if (!m_sound->IsPlayingMusic())
{
m_sound->PlayMusic("music/Intro1.ogg", false);
+ m_sound->CacheMusic("music/Intro2.ogg");
}
}
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-games/colobot.git
More information about the Pkg-games-commits
mailing list