You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
openmw/apps/openmw/mwsound/sound_buffer.cpp

156 lines
4.9 KiB
C++

#include "sound_buffer.hpp"
#include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp"
#include "../mwworld/esmstore.hpp"
#include <components/debug/debuglog.hpp>
#include <components/esm3/loadsoun.hpp>
#include <components/settings/settings.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(std::max(Settings::Manager::getInt("buffer cache max", "Sound"), 1) * 1024 * 1024)
, mBufferCacheMin(
std::min(static_cast<std::size_t>(std::max(Settings::Manager::getInt("buffer cache min", "Sound"), 1))
* 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::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);
}
if (sfx->getHandle() == nullptr)
{
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;
}
void SoundBufferPool::clear()
{
for (auto& sfx : mSoundBuffers)
{
if (sfx.mHandle)
mOutput->unloadSound(sfx.mHandle);
sfx.mHandle = nullptr;
}
mUnusedBuffers.clear();
}
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("Sound/" + sound.mSound, volume, min, max);
VFS::Path::normalizeFilenameInPlace(sfx.mResourceName);
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();
}
}
}