mirror of
https://gitlab.com/OpenMW/openmw.git
synced 2025-01-09 03:39:14 +00:00
206 lines
6.3 KiB
C++
206 lines
6.3 KiB
C++
#include "sound_buffer.hpp"
|
|
|
|
#include "../mwbase/environment.hpp"
|
|
#include "../mwworld/esmstore.hpp"
|
|
|
|
#include <components/debug/debuglog.hpp>
|
|
#include <components/esm3/loadsoun.hpp>
|
|
#include <components/misc/resourcehelpers.hpp>
|
|
#include <components/settings/values.hpp>
|
|
#include <components/vfs/pathutil.hpp>
|
|
|
|
#include <algorithm>
|
|
#include <cmath>
|
|
|
|
namespace MWSound
|
|
{
|
|
namespace
|
|
{
|
|
struct AudioParams
|
|
{
|
|
float mAudioDefaultMinDistance;
|
|
float mAudioDefaultMaxDistance;
|
|
float mAudioMinDistanceMult;
|
|
float mAudioMaxDistanceMult;
|
|
};
|
|
|
|
AudioParams makeAudioParams(const MWWorld::Store<ESM::GameSetting>& settings)
|
|
{
|
|
AudioParams params;
|
|
params.mAudioDefaultMinDistance = settings.find("fAudioDefaultMinDistance")->mValue.getFloat();
|
|
params.mAudioDefaultMaxDistance = settings.find("fAudioDefaultMaxDistance")->mValue.getFloat();
|
|
params.mAudioMinDistanceMult = settings.find("fAudioMinDistanceMult")->mValue.getFloat();
|
|
params.mAudioMaxDistanceMult = settings.find("fAudioMaxDistanceMult")->mValue.getFloat();
|
|
return params;
|
|
}
|
|
}
|
|
|
|
SoundBufferPool::SoundBufferPool(Sound_Output& output)
|
|
: mOutput(&output)
|
|
, mBufferCacheMax(Settings::sound().mBufferCacheMax * 1024 * 1024)
|
|
, mBufferCacheMin(
|
|
std::min(static_cast<std::size_t>(Settings::sound().mBufferCacheMin) * 1024 * 1024, mBufferCacheMax))
|
|
{
|
|
}
|
|
|
|
SoundBufferPool::~SoundBufferPool()
|
|
{
|
|
clear();
|
|
}
|
|
|
|
Sound_Buffer* SoundBufferPool::lookup(const ESM::RefId& soundId) const
|
|
{
|
|
const auto it = mBufferNameMap.find(soundId);
|
|
if (it != mBufferNameMap.end())
|
|
{
|
|
Sound_Buffer* sfx = it->second;
|
|
if (sfx->getHandle() != nullptr)
|
|
return sfx;
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
Sound_Buffer* SoundBufferPool::lookup(std::string_view fileName) const
|
|
{
|
|
const auto it = mBufferFileNameMap.find(std::string(fileName));
|
|
if (it != mBufferFileNameMap.end())
|
|
{
|
|
Sound_Buffer* sfx = it->second;
|
|
if (sfx->getHandle() != nullptr)
|
|
return sfx;
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
Sound_Buffer* SoundBufferPool::loadSfx(Sound_Buffer* sfx)
|
|
{
|
|
if (sfx->getHandle() != nullptr)
|
|
return sfx;
|
|
|
|
auto [handle, size] = mOutput->loadSound(sfx->getResourceName());
|
|
if (handle == nullptr)
|
|
return {};
|
|
|
|
sfx->mHandle = handle;
|
|
|
|
mBufferCacheSize += size;
|
|
if (mBufferCacheSize > mBufferCacheMax)
|
|
{
|
|
unloadUnused();
|
|
if (!mUnusedBuffers.empty() && mBufferCacheSize > mBufferCacheMax)
|
|
Log(Debug::Warning) << "No unused sound buffers to free, using " << mBufferCacheSize << " bytes!";
|
|
}
|
|
mUnusedBuffers.push_front(sfx);
|
|
|
|
return sfx;
|
|
}
|
|
|
|
Sound_Buffer* SoundBufferPool::load(const ESM::RefId& soundId)
|
|
{
|
|
if (mBufferNameMap.empty())
|
|
{
|
|
for (const ESM::Sound& sound : MWBase::Environment::get().getESMStore()->get<ESM::Sound>())
|
|
insertSound(sound.mId, sound);
|
|
}
|
|
|
|
Sound_Buffer* sfx;
|
|
const auto it = mBufferNameMap.find(soundId);
|
|
if (it != mBufferNameMap.end())
|
|
sfx = it->second;
|
|
else
|
|
{
|
|
const ESM::Sound* sound = MWBase::Environment::get().getESMStore()->get<ESM::Sound>().search(soundId);
|
|
if (sound == nullptr)
|
|
return {};
|
|
sfx = insertSound(soundId, *sound);
|
|
}
|
|
|
|
return loadSfx(sfx);
|
|
}
|
|
|
|
Sound_Buffer* SoundBufferPool::load(std::string_view fileName)
|
|
{
|
|
Sound_Buffer* sfx;
|
|
const auto it = mBufferFileNameMap.find(std::string(fileName));
|
|
if (it != mBufferFileNameMap.end())
|
|
sfx = it->second;
|
|
else
|
|
{
|
|
sfx = insertSound(fileName);
|
|
}
|
|
|
|
return loadSfx(sfx);
|
|
}
|
|
|
|
void SoundBufferPool::clear()
|
|
{
|
|
for (auto& sfx : mSoundBuffers)
|
|
{
|
|
if (sfx.mHandle)
|
|
mOutput->unloadSound(sfx.mHandle);
|
|
sfx.mHandle = nullptr;
|
|
}
|
|
|
|
mBufferFileNameMap.clear();
|
|
mBufferNameMap.clear();
|
|
mUnusedBuffers.clear();
|
|
}
|
|
|
|
Sound_Buffer* SoundBufferPool::insertSound(std::string_view fileName)
|
|
{
|
|
static const AudioParams audioParams
|
|
= makeAudioParams(MWBase::Environment::get().getESMStore()->get<ESM::GameSetting>());
|
|
|
|
float volume = 1.f;
|
|
float min = std::max(audioParams.mAudioDefaultMinDistance * audioParams.mAudioMinDistanceMult, 1.f);
|
|
float max = std::max(min, audioParams.mAudioDefaultMaxDistance * audioParams.mAudioMaxDistanceMult);
|
|
|
|
min = std::max(min, 1.0f);
|
|
max = std::max(min, max);
|
|
|
|
Sound_Buffer& sfx = mSoundBuffers.emplace_back(fileName, volume, min, max);
|
|
|
|
mBufferFileNameMap.emplace(fileName, &sfx);
|
|
return &sfx;
|
|
}
|
|
|
|
Sound_Buffer* SoundBufferPool::insertSound(const ESM::RefId& soundId, const ESM::Sound& sound)
|
|
{
|
|
static const AudioParams audioParams
|
|
= makeAudioParams(MWBase::Environment::get().getESMStore()->get<ESM::GameSetting>());
|
|
|
|
float volume = static_cast<float>(std::pow(10.0, (sound.mData.mVolume / 255.0 * 3348.0 - 3348.0) / 2000.0));
|
|
float min = sound.mData.mMinRange;
|
|
float max = sound.mData.mMaxRange;
|
|
if (min == 0 && max == 0)
|
|
{
|
|
min = audioParams.mAudioDefaultMinDistance;
|
|
max = audioParams.mAudioDefaultMaxDistance;
|
|
}
|
|
|
|
min *= audioParams.mAudioMinDistanceMult;
|
|
max *= audioParams.mAudioMaxDistanceMult;
|
|
min = std::max(min, 1.0f);
|
|
max = std::max(min, max);
|
|
|
|
Sound_Buffer& sfx = mSoundBuffers.emplace_back(
|
|
Misc::ResourceHelpers::correctSoundPath(VFS::Path::Normalized(sound.mSound)), volume, min, max);
|
|
|
|
mBufferNameMap.emplace(soundId, &sfx);
|
|
return &sfx;
|
|
}
|
|
|
|
void SoundBufferPool::unloadUnused()
|
|
{
|
|
while (!mUnusedBuffers.empty() && mBufferCacheSize > mBufferCacheMin)
|
|
{
|
|
Sound_Buffer* const unused = mUnusedBuffers.back();
|
|
|
|
mBufferCacheSize -= mOutput->unloadSound(unused->getHandle());
|
|
unused->mHandle = nullptr;
|
|
|
|
mUnusedBuffers.pop_back();
|
|
}
|
|
}
|
|
}
|