mirror of
https://github.com/minetest/minetest.git
synced 2024-11-27 01:53:45 +01:00
Move soundmanager into its own thread
Fixes sound queues running empty on client step hiccups.
This commit is contained in:
parent
591e45657f
commit
8fa2ea71ef
@ -38,5 +38,5 @@ std::shared_ptr<SoundManagerSingleton> createSoundManagerSingleton()
|
||||
std::unique_ptr<ISoundManager> createOpenALSoundManager(SoundManagerSingleton *smg,
|
||||
std::unique_ptr<SoundFallbackPathProvider> fallback_path_provider)
|
||||
{
|
||||
return std::make_unique<OpenALSoundManager>(smg, std::move(fallback_path_provider));
|
||||
return std::make_unique<ProxySoundManager>(smg, std::move(fallback_path_provider));
|
||||
};
|
||||
|
@ -25,7 +25,6 @@ with this program; ifnot, write to the Free Software Foundation, Inc.,
|
||||
#include "sound_openal_internal.h"
|
||||
|
||||
#include "util/numeric.h" // myrand()
|
||||
#include "../sound.h"
|
||||
#include "filesys.h"
|
||||
#include "settings.h"
|
||||
#include <algorithm>
|
||||
@ -806,7 +805,7 @@ std::string OpenALSoundManager::getLoadedSoundNameFromGroup(const std::string &g
|
||||
|
||||
auto it_groups = m_sound_groups.find(group_name);
|
||||
if (it_groups == m_sound_groups.end())
|
||||
return chosen_sound_name;
|
||||
return "";
|
||||
|
||||
std::vector<std::string> &group_sounds = it_groups->second;
|
||||
while (!group_sounds.empty()) {
|
||||
@ -817,7 +816,7 @@ std::string OpenALSoundManager::getLoadedSoundNameFromGroup(const std::string &g
|
||||
// find chosen one
|
||||
std::shared_ptr<ISoundDataOpen> snd = openSingleSound(chosen_sound_name);
|
||||
if (snd)
|
||||
break;
|
||||
return chosen_sound_name;
|
||||
|
||||
// it doesn't exist
|
||||
// remove it from the group and try again
|
||||
@ -825,7 +824,7 @@ std::string OpenALSoundManager::getLoadedSoundNameFromGroup(const std::string &g
|
||||
group_sounds.pop_back();
|
||||
}
|
||||
|
||||
return chosen_sound_name;
|
||||
return "";
|
||||
}
|
||||
|
||||
std::string OpenALSoundManager::getOrLoadLoadedSoundNameFromGroup(const std::string &group_name)
|
||||
@ -887,8 +886,7 @@ void OpenALSoundManager::playSoundGeneric(sound_handle_t id, const std::string &
|
||||
bool loop, f32 volume, f32 fade, f32 pitch, bool use_local_fallback,
|
||||
f32 start_time, const std::optional<std::pair<v3f, v3f>> &pos_vel_opt)
|
||||
{
|
||||
if (id == 0)
|
||||
id = allocateId(1);
|
||||
assert(id != 0);
|
||||
|
||||
if (group_name.empty()) {
|
||||
reportRemovedSound(id);
|
||||
@ -963,6 +961,7 @@ int OpenALSoundManager::removeDeadSounds()
|
||||
|
||||
OpenALSoundManager::OpenALSoundManager(SoundManagerSingleton *smg,
|
||||
std::unique_ptr<SoundFallbackPathProvider> fallback_path_provider) :
|
||||
Thread("OpenALSoundManager"),
|
||||
m_fallback_path_provider(std::move(fallback_path_provider)),
|
||||
m_device(smg->m_device.get()),
|
||||
m_context(smg->m_context.get())
|
||||
@ -1053,8 +1052,7 @@ bool OpenALSoundManager::loadSoundFile(const std::string &name, const std::strin
|
||||
if (!fs::IsFile(filepath))
|
||||
return false;
|
||||
|
||||
// remember for lazy loading
|
||||
m_sound_datas_unopen.emplace(name, std::make_unique<SoundDataUnopenFile>(filepath));
|
||||
loadSoundFileNoCheck(name, filepath);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -1064,9 +1062,20 @@ bool OpenALSoundManager::loadSoundData(const std::string &name, std::string &&fi
|
||||
if (m_sound_datas_open.count(name) != 0 || m_sound_datas_unopen.count(name) != 0)
|
||||
return false;
|
||||
|
||||
loadSoundDataNoCheck(name, std::move(filedata));
|
||||
return true;
|
||||
}
|
||||
|
||||
void OpenALSoundManager::loadSoundFileNoCheck(const std::string &name, const std::string &filepath)
|
||||
{
|
||||
// remember for lazy loading
|
||||
m_sound_datas_unopen.emplace(name, std::make_unique<SoundDataUnopenFile>(filepath));
|
||||
}
|
||||
|
||||
void OpenALSoundManager::loadSoundDataNoCheck(const std::string &name, std::string &&filedata)
|
||||
{
|
||||
// remember for lazy loading
|
||||
m_sound_datas_unopen.emplace(name, std::make_unique<SoundDataUnopenBuffer>(std::move(filedata)));
|
||||
return true;
|
||||
}
|
||||
|
||||
void OpenALSoundManager::addSoundToGroup(const std::string &sound_name, const std::string &group_name)
|
||||
@ -1126,3 +1135,229 @@ void OpenALSoundManager::updateSoundPosVel(sound_handle_t id, const v3f &pos_,
|
||||
return;
|
||||
i->second->updatePosVel(pos, vel);
|
||||
}
|
||||
|
||||
void *OpenALSoundManager::run()
|
||||
{
|
||||
using namespace sound_manager_messages_to_mgr;
|
||||
|
||||
struct MsgVisitor {
|
||||
enum class Result { Ok, Empty, StopRequested };
|
||||
|
||||
OpenALSoundManager &mgr;
|
||||
|
||||
Result operator()(std::monostate &&) {
|
||||
return Result::Empty; }
|
||||
|
||||
Result operator()(PauseAll &&) {
|
||||
mgr.pauseAll(); return Result::Ok; }
|
||||
Result operator()(ResumeAll &&) {
|
||||
mgr.resumeAll(); return Result::Ok; }
|
||||
|
||||
Result operator()(UpdateListener &&msg) {
|
||||
mgr.updateListener(msg.pos_, msg.vel_, msg.at_, msg.up_); return Result::Ok; }
|
||||
Result operator()(SetListenerGain &&msg) {
|
||||
mgr.setListenerGain(msg.gain); return Result::Ok; }
|
||||
|
||||
Result operator()(LoadSoundFile &&msg) {
|
||||
mgr.loadSoundFileNoCheck(msg.name, msg.filepath); return Result::Ok; }
|
||||
Result operator()(LoadSoundData &&msg) {
|
||||
mgr.loadSoundDataNoCheck(msg.name, std::move(msg.filedata)); return Result::Ok; }
|
||||
Result operator()(AddSoundToGroup &&msg) {
|
||||
mgr.addSoundToGroup(msg.sound_name, msg.group_name); return Result::Ok; }
|
||||
|
||||
Result operator()(PlaySound &&msg) {
|
||||
mgr.playSound(msg.id, msg.spec); return Result::Ok; }
|
||||
Result operator()(PlaySoundAt &&msg) {
|
||||
mgr.playSoundAt(msg.id, msg.spec, msg.pos_, msg.vel_); return Result::Ok; }
|
||||
Result operator()(StopSound &&msg) {
|
||||
mgr.stopSound(msg.sound); return Result::Ok; }
|
||||
Result operator()(FadeSound &&msg) {
|
||||
mgr.fadeSound(msg.soundid, msg.step, msg.target_gain); return Result::Ok; }
|
||||
Result operator()(UpdateSoundPosVel &&msg) {
|
||||
mgr.updateSoundPosVel(msg.sound, msg.pos_, msg.vel_); return Result::Ok; }
|
||||
|
||||
Result operator()(PleaseStop &&msg) {
|
||||
return Result::StopRequested; }
|
||||
};
|
||||
|
||||
u64 t_step_start = porting::getTimeMs();
|
||||
while (true) {
|
||||
auto get_time_since_last_step = [&] {
|
||||
return (f32)(porting::getTimeMs() - t_step_start);
|
||||
};
|
||||
auto get_remaining_timeout = [&] {
|
||||
return (s32)((1.0e3f * PROXYSOUNDMGR_DTIME) - get_time_since_last_step());
|
||||
};
|
||||
|
||||
bool stop_requested = false;
|
||||
|
||||
while (true) {
|
||||
SoundManagerMsgToMgr msg =
|
||||
m_queue_to_mgr.pop_frontNoEx(std::max(get_remaining_timeout(), 0));
|
||||
|
||||
MsgVisitor::Result res = std::visit(MsgVisitor{*this}, std::move(msg));
|
||||
|
||||
if (res == MsgVisitor::Result::Empty && get_remaining_timeout() <= 0) {
|
||||
break; // finished sleeping
|
||||
} else if (res == MsgVisitor::Result::StopRequested) {
|
||||
stop_requested = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (stop_requested)
|
||||
break;
|
||||
|
||||
f32 dtime = get_time_since_last_step();
|
||||
t_step_start = porting::getTimeMs();
|
||||
step(dtime);
|
||||
}
|
||||
|
||||
send(sound_manager_messages_to_proxy::Stopped{});
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/*
|
||||
* ProxySoundManager class
|
||||
*/
|
||||
|
||||
ProxySoundManager::MsgResult ProxySoundManager::handleMsg(SoundManagerMsgToProxy &&msg)
|
||||
{
|
||||
using namespace sound_manager_messages_to_proxy;
|
||||
|
||||
return std::visit([&](auto &&msg) {
|
||||
using T = std::decay_t<decltype(msg)>;
|
||||
|
||||
if constexpr (std::is_same_v<T, std::monostate>)
|
||||
return MsgResult::Empty;
|
||||
else if constexpr (std::is_same_v<T, ReportRemovedSound>)
|
||||
reportRemovedSound(msg.id);
|
||||
else if constexpr (std::is_same_v<T, Stopped>)
|
||||
return MsgResult::Stopped;
|
||||
|
||||
return MsgResult::Ok;
|
||||
},
|
||||
std::move(msg));
|
||||
}
|
||||
|
||||
ProxySoundManager::~ProxySoundManager()
|
||||
{
|
||||
if (m_sound_manager.isRunning()) {
|
||||
send(sound_manager_messages_to_mgr::PleaseStop{});
|
||||
|
||||
// recv until it stopped
|
||||
auto recv = [&] {
|
||||
return m_sound_manager.m_queue_to_proxy.pop_frontNoEx();
|
||||
};
|
||||
|
||||
while (true) {
|
||||
if (handleMsg(recv()) == MsgResult::Stopped)
|
||||
break;
|
||||
}
|
||||
|
||||
// join
|
||||
m_sound_manager.stop();
|
||||
SANITY_CHECK(m_sound_manager.wait());
|
||||
}
|
||||
}
|
||||
|
||||
void ProxySoundManager::step(f32 dtime)
|
||||
{
|
||||
auto recv = [&] {
|
||||
return m_sound_manager.m_queue_to_proxy.pop_frontNoEx(0);
|
||||
};
|
||||
|
||||
while (true) {
|
||||
MsgResult res = handleMsg(recv());
|
||||
if (res == MsgResult::Empty)
|
||||
break;
|
||||
else if (res == MsgResult::Stopped)
|
||||
throw std::runtime_error("OpenALSoundManager stopped unexpectedly");
|
||||
}
|
||||
}
|
||||
|
||||
void ProxySoundManager::pauseAll()
|
||||
{
|
||||
send(sound_manager_messages_to_mgr::PauseAll{});
|
||||
}
|
||||
|
||||
void ProxySoundManager::resumeAll()
|
||||
{
|
||||
send(sound_manager_messages_to_mgr::ResumeAll{});
|
||||
}
|
||||
|
||||
void ProxySoundManager::updateListener(const v3f &pos_, const v3f &vel_,
|
||||
const v3f &at_, const v3f &up_)
|
||||
{
|
||||
send(sound_manager_messages_to_mgr::UpdateListener{pos_, vel_, at_, up_});
|
||||
}
|
||||
|
||||
void ProxySoundManager::setListenerGain(f32 gain)
|
||||
{
|
||||
send(sound_manager_messages_to_mgr::SetListenerGain{gain});
|
||||
}
|
||||
|
||||
bool ProxySoundManager::loadSoundFile(const std::string &name,
|
||||
const std::string &filepath)
|
||||
{
|
||||
// do not add twice
|
||||
if (m_known_sound_names.count(name) != 0)
|
||||
return false;
|
||||
|
||||
// coarse check
|
||||
if (!fs::IsFile(filepath))
|
||||
return false;
|
||||
|
||||
send(sound_manager_messages_to_mgr::LoadSoundFile{name, filepath});
|
||||
|
||||
m_known_sound_names.insert(name);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ProxySoundManager::loadSoundData(const std::string &name, std::string &&filedata)
|
||||
{
|
||||
// do not add twice
|
||||
if (m_known_sound_names.count(name) != 0)
|
||||
return false;
|
||||
|
||||
send(sound_manager_messages_to_mgr::LoadSoundData{name, std::move(filedata)});
|
||||
|
||||
m_known_sound_names.insert(name);
|
||||
return true;
|
||||
}
|
||||
|
||||
void ProxySoundManager::addSoundToGroup(const std::string &sound_name,
|
||||
const std::string &group_name)
|
||||
{
|
||||
send(sound_manager_messages_to_mgr::AddSoundToGroup{sound_name, group_name});
|
||||
}
|
||||
|
||||
void ProxySoundManager::playSound(sound_handle_t id, const SoundSpec &spec)
|
||||
{
|
||||
if (id == 0)
|
||||
id = allocateId(1);
|
||||
send(sound_manager_messages_to_mgr::PlaySound{id, spec});
|
||||
}
|
||||
|
||||
void ProxySoundManager::playSoundAt(sound_handle_t id, const SoundSpec &spec, const v3f &pos_,
|
||||
const v3f &vel_)
|
||||
{
|
||||
if (id == 0)
|
||||
id = allocateId(1);
|
||||
send(sound_manager_messages_to_mgr::PlaySoundAt{id, spec, pos_, vel_});
|
||||
}
|
||||
|
||||
void ProxySoundManager::stopSound(sound_handle_t sound)
|
||||
{
|
||||
send(sound_manager_messages_to_mgr::StopSound{sound});
|
||||
}
|
||||
|
||||
void ProxySoundManager::fadeSound(sound_handle_t soundid, f32 step, f32 target_gain)
|
||||
{
|
||||
send(sound_manager_messages_to_mgr::FadeSound{soundid, step, target_gain});
|
||||
}
|
||||
|
||||
void ProxySoundManager::updateSoundPosVel(sound_handle_t sound, const v3f &pos_, const v3f &vel_)
|
||||
{
|
||||
send(sound_manager_messages_to_mgr::UpdateSoundPosVel{sound, pos_, vel_});
|
||||
}
|
||||
|
@ -27,7 +27,10 @@ with this program; ifnot, write to the Free Software Foundation, Inc.,
|
||||
#include "log.h"
|
||||
#include "porting.h"
|
||||
#include "sound_openal.h"
|
||||
#include "../sound.h"
|
||||
#include "threading/thread.h"
|
||||
#include "util/basic_macros.h"
|
||||
#include "util/container.h"
|
||||
|
||||
#if defined(_WIN32)
|
||||
#include <al.h>
|
||||
@ -48,6 +51,7 @@ with this program; ifnot, write to the Free Software Foundation, Inc.,
|
||||
#include <optional>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
|
||||
|
||||
@ -141,6 +145,8 @@ constexpr f32 SOUND_DURATION_MAX_SINGLE = 3.0f;
|
||||
constexpr f32 MIN_STREAM_BUFFER_LENGTH = 1.0f;
|
||||
// duration in seconds of one bigstep
|
||||
constexpr f32 STREAM_BIGSTEP_TIME = 0.3f;
|
||||
// step duration for the ProxySoundManager, in seconds
|
||||
constexpr f32 PROXYSOUNDMGR_DTIME = 0.016f;
|
||||
|
||||
static_assert(MIN_STREAM_BUFFER_LENGTH > STREAM_BIGSTEP_TIME * 2.0f,
|
||||
"See [Streaming of sounds].");
|
||||
@ -506,10 +512,67 @@ public:
|
||||
|
||||
|
||||
/*
|
||||
* The public ISoundManager interface
|
||||
* The SoundManager thread
|
||||
*/
|
||||
|
||||
class OpenALSoundManager final : public ISoundManager
|
||||
namespace sound_manager_messages_to_mgr {
|
||||
struct PauseAll {};
|
||||
struct ResumeAll {};
|
||||
|
||||
struct UpdateListener { v3f pos_; v3f vel_; v3f at_; v3f up_; };
|
||||
struct SetListenerGain { f32 gain; };
|
||||
|
||||
struct LoadSoundFile { std::string name; std::string filepath; };
|
||||
struct LoadSoundData { std::string name; std::string filedata; };
|
||||
struct AddSoundToGroup { std::string sound_name; std::string group_name; };
|
||||
|
||||
struct PlaySound { sound_handle_t id; SoundSpec spec; };
|
||||
struct PlaySoundAt { sound_handle_t id; SoundSpec spec; v3f pos_; v3f vel_; };
|
||||
struct StopSound { sound_handle_t sound; };
|
||||
struct FadeSound { sound_handle_t soundid; f32 step; f32 target_gain; };
|
||||
struct UpdateSoundPosVel { sound_handle_t sound; v3f pos_; v3f vel_; };
|
||||
|
||||
struct PleaseStop {};
|
||||
}
|
||||
|
||||
using SoundManagerMsgToMgr = std::variant<
|
||||
std::monostate,
|
||||
|
||||
sound_manager_messages_to_mgr::PauseAll,
|
||||
sound_manager_messages_to_mgr::ResumeAll,
|
||||
|
||||
sound_manager_messages_to_mgr::UpdateListener,
|
||||
sound_manager_messages_to_mgr::SetListenerGain,
|
||||
|
||||
sound_manager_messages_to_mgr::LoadSoundFile,
|
||||
sound_manager_messages_to_mgr::LoadSoundData,
|
||||
sound_manager_messages_to_mgr::AddSoundToGroup,
|
||||
|
||||
sound_manager_messages_to_mgr::PlaySound,
|
||||
sound_manager_messages_to_mgr::PlaySoundAt,
|
||||
sound_manager_messages_to_mgr::StopSound,
|
||||
sound_manager_messages_to_mgr::FadeSound,
|
||||
sound_manager_messages_to_mgr::UpdateSoundPosVel,
|
||||
|
||||
sound_manager_messages_to_mgr::PleaseStop
|
||||
>;
|
||||
|
||||
namespace sound_manager_messages_to_proxy {
|
||||
struct ReportRemovedSound { sound_handle_t id; };
|
||||
|
||||
struct Stopped {};
|
||||
}
|
||||
|
||||
using SoundManagerMsgToProxy = std::variant<
|
||||
std::monostate,
|
||||
|
||||
sound_manager_messages_to_proxy::ReportRemovedSound,
|
||||
|
||||
sound_manager_messages_to_proxy::Stopped
|
||||
>;
|
||||
|
||||
// not an ISoundManager. doesn't allocate ids, and doesn't accept id 0
|
||||
class OpenALSoundManager final : public Thread
|
||||
{
|
||||
private:
|
||||
std::unique_ptr<SoundFallbackPathProvider> m_fallback_path_provider;
|
||||
@ -540,6 +603,11 @@ private:
|
||||
// if true, all sounds will be directly paused after creation
|
||||
bool m_is_paused = false;
|
||||
|
||||
public:
|
||||
// used for communication with ProxySoundManager
|
||||
MutexedQueue<SoundManagerMsgToMgr> m_queue_to_mgr;
|
||||
MutexedQueue<SoundManagerMsgToProxy> m_queue_to_proxy;
|
||||
|
||||
private:
|
||||
void stepStreams(f32 dtime);
|
||||
void doFades(f32 dtime);
|
||||
@ -591,6 +659,75 @@ public:
|
||||
|
||||
DISABLE_CLASS_COPY(OpenALSoundManager)
|
||||
|
||||
private:
|
||||
/* Similar to ISoundManager */
|
||||
|
||||
void step(f32 dtime);
|
||||
void pauseAll();
|
||||
void resumeAll();
|
||||
|
||||
void updateListener(const v3f &pos_, const v3f &vel_, const v3f &at_, const v3f &up_);
|
||||
void setListenerGain(f32 gain);
|
||||
|
||||
bool loadSoundFile(const std::string &name, const std::string &filepath);
|
||||
bool loadSoundData(const std::string &name, std::string &&filedata);
|
||||
void loadSoundFileNoCheck(const std::string &name, const std::string &filepath);
|
||||
void loadSoundDataNoCheck(const std::string &name, std::string &&filedata);
|
||||
void addSoundToGroup(const std::string &sound_name, const std::string &group_name);
|
||||
|
||||
void playSound(sound_handle_t id, const SoundSpec &spec);
|
||||
void playSoundAt(sound_handle_t id, const SoundSpec &spec, const v3f &pos_,
|
||||
const v3f &vel_);
|
||||
void stopSound(sound_handle_t sound);
|
||||
void fadeSound(sound_handle_t soundid, f32 step, f32 target_gain);
|
||||
void updateSoundPosVel(sound_handle_t sound, const v3f &pos_, const v3f &vel_);
|
||||
|
||||
protected:
|
||||
/* Thread stuff */
|
||||
|
||||
void *run() override;
|
||||
|
||||
private:
|
||||
void send(SoundManagerMsgToProxy msg)
|
||||
{
|
||||
m_queue_to_proxy.push_back(std::move(msg));
|
||||
}
|
||||
|
||||
void reportRemovedSound(sound_handle_t id)
|
||||
{
|
||||
send(sound_manager_messages_to_proxy::ReportRemovedSound{id});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* The public ISoundManager interface
|
||||
*/
|
||||
|
||||
class ProxySoundManager final : public ISoundManager
|
||||
{
|
||||
OpenALSoundManager m_sound_manager;
|
||||
// sound names from loadSoundData and loadSoundFile
|
||||
std::unordered_set<std::string> m_known_sound_names;
|
||||
|
||||
void send(SoundManagerMsgToMgr msg)
|
||||
{
|
||||
m_sound_manager.m_queue_to_mgr.push_back(std::move(msg));
|
||||
}
|
||||
|
||||
enum class MsgResult { Ok, Empty, Stopped};
|
||||
MsgResult handleMsg(SoundManagerMsgToProxy &&msg);
|
||||
|
||||
public:
|
||||
ProxySoundManager(SoundManagerSingleton *smg,
|
||||
std::unique_ptr<SoundFallbackPathProvider> fallback_path_provider) :
|
||||
m_sound_manager(smg, std::move(fallback_path_provider))
|
||||
{
|
||||
m_sound_manager.start();
|
||||
}
|
||||
|
||||
~ProxySoundManager() override;
|
||||
|
||||
/* Interface */
|
||||
|
||||
void step(f32 dtime) override;
|
||||
|
Loading…
Reference in New Issue
Block a user