Merge branch 'dev-async-music-load' (PR #801)

dev-new-models
krzys-h 2016-08-09 19:20:46 +02:00
commit 0ecdee08cc
12 changed files with 289 additions and 118 deletions

View File

@ -153,6 +153,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

View File

@ -36,6 +36,8 @@
#include "common/system/system.h"
#include "common/thread/thread.h"
#include "graphics/core/nulldevice.h"
#include "graphics/opengl/glutil.h"
@ -503,8 +505,6 @@ bool CApplication::Create()
#endif
m_sound->Create();
m_sound->CacheAll();
m_sound->CacheCommonMusic();
GetLogger()->Info("CApplication created successfully\n");
@ -674,6 +674,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

View File

@ -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;
};

View File

@ -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;
};

View File

@ -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;
};

View File

@ -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;
};

View File

@ -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;
};

View File

@ -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)
@ -573,53 +575,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;
GetLogger()->Debug("Music %s was not cached!\n", filename.c_str());
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_music[filename] = std::move(newBuffer);
}
else
{
GetLogger()->Debug("Music loaded from cache\n");
buffer = m_music[filename].get();
}
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));
}
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));
}
m_currentMusic = MakeUnique<CChannel>();
m_currentMusic->SetBuffer(buffer);
m_currentMusic->SetVolume(m_musicVolume);
m_currentMusic->SetLoop(repeat);
m_currentMusic->Play();
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)
{
@ -641,7 +644,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()
@ -663,18 +666,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)
@ -699,16 +690,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;

View File

@ -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;
};

View File

@ -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()

View File

@ -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
* \param fadeTime - time of transition between music, 0 to disable
*/
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
*/
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

View File

@ -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");
}
}