mirror of
https://github.com/TES3MP/openmw-tes3mp.git
synced 2025-01-19 21:23:52 +00:00
Merge branch 'sound_buffer_pool_2' into 'master'
Separate sound buffer pool from sound manager See merge request OpenMW/openmw!520
This commit is contained in:
commit
9eba086c34
7 changed files with 284 additions and 206 deletions
|
@ -58,6 +58,7 @@ add_openmw_dir (mwscript
|
||||||
add_openmw_dir (mwsound
|
add_openmw_dir (mwsound
|
||||||
soundmanagerimp openal_output ffmpeg_decoder sound sound_buffer sound_decoder sound_output
|
soundmanagerimp openal_output ffmpeg_decoder sound sound_buffer sound_decoder sound_output
|
||||||
loudness movieaudiofactory alext efx efx-presets regionsoundselector watersoundupdater volumesettings
|
loudness movieaudiofactory alext efx efx-presets regionsoundselector watersoundupdater volumesettings
|
||||||
|
sound_buffer
|
||||||
)
|
)
|
||||||
|
|
||||||
add_openmw_dir (mwworld
|
add_openmw_dir (mwworld
|
||||||
|
|
|
@ -7,6 +7,13 @@
|
||||||
|
|
||||||
namespace MWSound
|
namespace MWSound
|
||||||
{
|
{
|
||||||
|
// Extra play flags, not intended for caller use
|
||||||
|
enum PlayModeEx
|
||||||
|
{
|
||||||
|
Play_2D = 0,
|
||||||
|
Play_3D = 1 << 31,
|
||||||
|
};
|
||||||
|
|
||||||
// For testing individual PlayMode flags
|
// For testing individual PlayMode flags
|
||||||
inline int operator&(int a, PlayMode b) { return a & static_cast<int>(b); }
|
inline int operator&(int a, PlayMode b) { return a & static_cast<int>(b); }
|
||||||
inline int operator&(PlayMode a, PlayMode b) { return static_cast<int>(a) & static_cast<int>(b); }
|
inline int operator&(PlayMode a, PlayMode b) { return static_cast<int>(a) & static_cast<int>(b); }
|
||||||
|
|
152
apps/openmw/mwsound/sound_buffer.cpp
Normal file
152
apps/openmw/mwsound/sound_buffer.cpp
Normal file
|
@ -0,0 +1,152 @@
|
||||||
|
#include "sound_buffer.hpp"
|
||||||
|
|
||||||
|
#include "../mwbase/environment.hpp"
|
||||||
|
#include "../mwbase/world.hpp"
|
||||||
|
#include "../mwworld/esmstore.hpp"
|
||||||
|
|
||||||
|
#include <components/debug/debuglog.hpp>
|
||||||
|
#include <components/settings/settings.hpp>
|
||||||
|
#include <components/vfs/manager.hpp>
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
|
namespace MWSound
|
||||||
|
{
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
struct AudioParams
|
||||||
|
{
|
||||||
|
float mAudioDefaultMinDistance;
|
||||||
|
float mAudioDefaultMaxDistance;
|
||||||
|
float mAudioMinDistanceMult;
|
||||||
|
float mAudioMaxDistanceMult;
|
||||||
|
};
|
||||||
|
|
||||||
|
AudioParams makeAudioParams(const MWBase::World& world)
|
||||||
|
{
|
||||||
|
const auto& settings = world.getStore().get<ESM::GameSetting>();
|
||||||
|
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(const VFS::Manager& vfs, Sound_Output& output) :
|
||||||
|
mVfs(&vfs),
|
||||||
|
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 std::string& 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 std::string& soundId)
|
||||||
|
{
|
||||||
|
if (mBufferNameMap.empty())
|
||||||
|
{
|
||||||
|
for (const ESM::Sound& sound : MWBase::Environment::get().getWorld()->getStore().get<ESM::Sound>())
|
||||||
|
insertSound(Misc::StringUtils::lowerCase(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().getWorld()->getStore().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 std::string& soundId, const ESM::Sound& sound)
|
||||||
|
{
|
||||||
|
static const AudioParams audioParams = makeAudioParams(*MWBase::Environment::get().getWorld());
|
||||||
|
|
||||||
|
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);
|
||||||
|
mVfs->normalizeFilename(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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,27 +1,105 @@
|
||||||
#ifndef GAME_SOUND_SOUND_BUFFER_H
|
#ifndef GAME_SOUND_SOUND_BUFFER_H
|
||||||
#define GAME_SOUND_SOUND_BUFFER_H
|
#define GAME_SOUND_SOUND_BUFFER_H
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <deque>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
#include "sound_output.hpp"
|
#include "sound_output.hpp"
|
||||||
|
|
||||||
|
namespace ESM
|
||||||
|
{
|
||||||
|
struct Sound;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace VFS
|
||||||
|
{
|
||||||
|
class Manager;
|
||||||
|
}
|
||||||
|
|
||||||
namespace MWSound
|
namespace MWSound
|
||||||
{
|
{
|
||||||
|
class SoundBufferPool;
|
||||||
|
|
||||||
class Sound_Buffer
|
class Sound_Buffer
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
std::string mResourceName;
|
template <class T>
|
||||||
|
Sound_Buffer(T&& resname, float volume, float mindist, float maxdist)
|
||||||
|
: mResourceName(std::forward<T>(resname)), mVolume(volume), mMinDist(mindist), mMaxDist(maxdist)
|
||||||
|
{}
|
||||||
|
|
||||||
float mVolume;
|
const std::string& getResourceName() const noexcept { return mResourceName; }
|
||||||
float mMinDist, mMaxDist;
|
|
||||||
|
|
||||||
Sound_Handle mHandle;
|
Sound_Handle getHandle() const noexcept { return mHandle; }
|
||||||
|
|
||||||
size_t mUses;
|
float getVolume() const noexcept { return mVolume; }
|
||||||
|
|
||||||
Sound_Buffer(std::string resname, float volume, float mindist, float maxdist)
|
float getMinDist() const noexcept { return mMinDist; }
|
||||||
: mResourceName(resname), mVolume(volume), mMinDist(mindist), mMaxDist(maxdist), mHandle(nullptr), mUses(0)
|
|
||||||
{ }
|
float getMaxDist() const noexcept { return mMaxDist; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string mResourceName;
|
||||||
|
float mVolume;
|
||||||
|
float mMinDist;
|
||||||
|
float mMaxDist;
|
||||||
|
Sound_Handle mHandle = nullptr;
|
||||||
|
std::size_t mUses = 0;
|
||||||
|
|
||||||
|
friend class SoundBufferPool;
|
||||||
|
};
|
||||||
|
|
||||||
|
class SoundBufferPool
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
SoundBufferPool(const VFS::Manager& vfs, Sound_Output& output);
|
||||||
|
|
||||||
|
SoundBufferPool(const SoundBufferPool&) = delete;
|
||||||
|
|
||||||
|
~SoundBufferPool();
|
||||||
|
|
||||||
|
/// Lookup a soundId for its sound data (resource name, local volume,
|
||||||
|
/// minRange, and maxRange)
|
||||||
|
Sound_Buffer* lookup(const std::string& soundId) const;
|
||||||
|
|
||||||
|
/// Lookup a soundId for its sound data (resource name, local volume,
|
||||||
|
/// minRange, and maxRange), and ensure it's ready for use.
|
||||||
|
Sound_Buffer* load(const std::string& soundId);
|
||||||
|
|
||||||
|
void use(Sound_Buffer& sfx)
|
||||||
|
{
|
||||||
|
if (sfx.mUses++ == 0)
|
||||||
|
{
|
||||||
|
const auto it = std::find(mUnusedBuffers.begin(), mUnusedBuffers.end(), &sfx);
|
||||||
|
if (it != mUnusedBuffers.end())
|
||||||
|
mUnusedBuffers.erase(it);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void release(Sound_Buffer& sfx)
|
||||||
|
{
|
||||||
|
if (--sfx.mUses == 0)
|
||||||
|
mUnusedBuffers.push_front(&sfx);
|
||||||
|
}
|
||||||
|
|
||||||
|
void clear();
|
||||||
|
|
||||||
|
private:
|
||||||
|
const VFS::Manager* const mVfs;
|
||||||
|
Sound_Output* mOutput;
|
||||||
|
std::deque<Sound_Buffer> mSoundBuffers;
|
||||||
|
std::unordered_map<std::string, Sound_Buffer*> mBufferNameMap;
|
||||||
|
std::size_t mBufferCacheMax;
|
||||||
|
std::size_t mBufferCacheMin;
|
||||||
|
std::size_t mBufferCacheSize = 0;
|
||||||
|
// NOTE: unused buffers are stored in front-newest order.
|
||||||
|
std::deque<Sound_Buffer*> mUnusedBuffers;
|
||||||
|
|
||||||
|
inline Sound_Buffer* insertSound(const std::string& soundId, const ESM::Sound& sound);
|
||||||
|
|
||||||
|
inline void unloadUnused();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "soundmanagerimp.hpp"
|
#include "../mwbase/soundmanager.hpp"
|
||||||
|
|
||||||
namespace MWSound
|
namespace MWSound
|
||||||
{
|
{
|
||||||
|
@ -25,6 +25,12 @@ namespace MWSound
|
||||||
Auto
|
Auto
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum Environment
|
||||||
|
{
|
||||||
|
Env_Normal,
|
||||||
|
Env_Underwater
|
||||||
|
};
|
||||||
|
|
||||||
class Sound_Output
|
class Sound_Output
|
||||||
{
|
{
|
||||||
SoundManager &mManager;
|
SoundManager &mManager;
|
||||||
|
@ -81,6 +87,7 @@ namespace MWSound
|
||||||
|
|
||||||
friend class OpenAL_Output;
|
friend class OpenAL_Output;
|
||||||
friend class SoundManager;
|
friend class SoundManager;
|
||||||
|
friend class SoundBufferPool;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -56,8 +56,7 @@ namespace MWSound
|
||||||
: mVFS(vfs)
|
: mVFS(vfs)
|
||||||
, mOutput(new DEFAULT_OUTPUT(*this))
|
, mOutput(new DEFAULT_OUTPUT(*this))
|
||||||
, mWaterSoundUpdater(makeWaterSoundUpdaterSettings())
|
, mWaterSoundUpdater(makeWaterSoundUpdaterSettings())
|
||||||
, mSoundBuffers(new SoundBufferList::element_type())
|
, mSoundBuffers(*vfs, *mOutput)
|
||||||
, mBufferCacheSize(0)
|
|
||||||
, mListenerUnderwater(false)
|
, mListenerUnderwater(false)
|
||||||
, mListenerPos(0,0,0)
|
, mListenerPos(0,0,0)
|
||||||
, mListenerDir(1,0,0)
|
, mListenerDir(1,0,0)
|
||||||
|
@ -69,11 +68,6 @@ namespace MWSound
|
||||||
, mLastCell(nullptr)
|
, mLastCell(nullptr)
|
||||||
, mCurrentRegionSound(nullptr)
|
, mCurrentRegionSound(nullptr)
|
||||||
{
|
{
|
||||||
mBufferCacheMin = std::max(Settings::Manager::getInt("buffer cache min", "Sound"), 1);
|
|
||||||
mBufferCacheMax = std::max(Settings::Manager::getInt("buffer cache max", "Sound"), 1);
|
|
||||||
mBufferCacheMax *= 1024*1024;
|
|
||||||
mBufferCacheMin = std::min(mBufferCacheMin*1024*1024, mBufferCacheMax);
|
|
||||||
|
|
||||||
if(!useSound)
|
if(!useSound)
|
||||||
{
|
{
|
||||||
Log(Debug::Info) << "Sound disabled.";
|
Log(Debug::Info) << "Sound disabled.";
|
||||||
|
@ -116,13 +110,7 @@ namespace MWSound
|
||||||
SoundManager::~SoundManager()
|
SoundManager::~SoundManager()
|
||||||
{
|
{
|
||||||
clear();
|
clear();
|
||||||
for(Sound_Buffer &sfx : *mSoundBuffers)
|
mSoundBuffers.clear();
|
||||||
{
|
|
||||||
if(sfx.mHandle)
|
|
||||||
mOutput->unloadSound(sfx.mHandle);
|
|
||||||
sfx.mHandle = nullptr;
|
|
||||||
}
|
|
||||||
mUnusedBuffers.clear();
|
|
||||||
mOutput.reset();
|
mOutput.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -132,114 +120,6 @@ namespace MWSound
|
||||||
return DecoderPtr(new DEFAULT_DECODER (mVFS));
|
return DecoderPtr(new DEFAULT_DECODER (mVFS));
|
||||||
}
|
}
|
||||||
|
|
||||||
Sound_Buffer *SoundManager::insertSound(const std::string &soundId, const ESM::Sound *sound)
|
|
||||||
{
|
|
||||||
MWBase::World* world = MWBase::Environment::get().getWorld();
|
|
||||||
static const float fAudioDefaultMinDistance = world->getStore().get<ESM::GameSetting>().find("fAudioDefaultMinDistance")->mValue.getFloat();
|
|
||||||
static const float fAudioDefaultMaxDistance = world->getStore().get<ESM::GameSetting>().find("fAudioDefaultMaxDistance")->mValue.getFloat();
|
|
||||||
static const float fAudioMinDistanceMult = world->getStore().get<ESM::GameSetting>().find("fAudioMinDistanceMult")->mValue.getFloat();
|
|
||||||
static const float fAudioMaxDistanceMult = world->getStore().get<ESM::GameSetting>().find("fAudioMaxDistanceMult")->mValue.getFloat();
|
|
||||||
float volume, min, max;
|
|
||||||
|
|
||||||
volume = static_cast<float>(pow(10.0, (sound->mData.mVolume / 255.0*3348.0 - 3348.0) / 2000.0));
|
|
||||||
min = sound->mData.mMinRange;
|
|
||||||
max = sound->mData.mMaxRange;
|
|
||||||
if (min == 0 && max == 0)
|
|
||||||
{
|
|
||||||
min = fAudioDefaultMinDistance;
|
|
||||||
max = fAudioDefaultMaxDistance;
|
|
||||||
}
|
|
||||||
|
|
||||||
min *= fAudioMinDistanceMult;
|
|
||||||
max *= fAudioMaxDistanceMult;
|
|
||||||
min = std::max(min, 1.0f);
|
|
||||||
max = std::max(min, max);
|
|
||||||
|
|
||||||
Sound_Buffer *sfx = &*mSoundBuffers->insert(mSoundBuffers->end(),
|
|
||||||
Sound_Buffer("Sound/"+sound->mSound, volume, min, max)
|
|
||||||
);
|
|
||||||
mVFS->normalizeFilename(sfx->mResourceName);
|
|
||||||
|
|
||||||
mBufferNameMap.insert(std::make_pair(soundId, sfx));
|
|
||||||
|
|
||||||
return sfx;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Lookup a soundId for its sound data (resource name, local volume,
|
|
||||||
// minRange, and maxRange)
|
|
||||||
Sound_Buffer *SoundManager::lookupSound(const std::string &soundId) const
|
|
||||||
{
|
|
||||||
NameBufferMap::const_iterator snd = mBufferNameMap.find(soundId);
|
|
||||||
if(snd != mBufferNameMap.end())
|
|
||||||
{
|
|
||||||
Sound_Buffer *sfx = snd->second;
|
|
||||||
if(sfx->mHandle) return sfx;
|
|
||||||
}
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Lookup a soundId for its sound data (resource name, local volume,
|
|
||||||
// minRange, and maxRange), and ensure it's ready for use.
|
|
||||||
Sound_Buffer *SoundManager::loadSound(const std::string &soundId)
|
|
||||||
{
|
|
||||||
#ifdef __GNUC__
|
|
||||||
#define LIKELY(x) __builtin_expect((bool)(x), true)
|
|
||||||
#define UNLIKELY(x) __builtin_expect((bool)(x), false)
|
|
||||||
#else
|
|
||||||
#define LIKELY(x) (bool)(x)
|
|
||||||
#define UNLIKELY(x) (bool)(x)
|
|
||||||
#endif
|
|
||||||
if(UNLIKELY(mBufferNameMap.empty()))
|
|
||||||
{
|
|
||||||
MWBase::World *world = MWBase::Environment::get().getWorld();
|
|
||||||
for(const ESM::Sound &sound : world->getStore().get<ESM::Sound>())
|
|
||||||
insertSound(Misc::StringUtils::lowerCase(sound.mId), &sound);
|
|
||||||
}
|
|
||||||
|
|
||||||
Sound_Buffer *sfx;
|
|
||||||
NameBufferMap::const_iterator snd = mBufferNameMap.find(soundId);
|
|
||||||
if(LIKELY(snd != mBufferNameMap.end()))
|
|
||||||
sfx = snd->second;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
MWBase::World *world = MWBase::Environment::get().getWorld();
|
|
||||||
const ESM::Sound *sound = world->getStore().get<ESM::Sound>().search(soundId);
|
|
||||||
if(!sound) return nullptr;
|
|
||||||
sfx = insertSound(soundId, sound);
|
|
||||||
}
|
|
||||||
#undef LIKELY
|
|
||||||
#undef UNLIKELY
|
|
||||||
|
|
||||||
if(!sfx->mHandle)
|
|
||||||
{
|
|
||||||
size_t size;
|
|
||||||
std::tie(sfx->mHandle, size) = mOutput->loadSound(sfx->mResourceName);
|
|
||||||
if(!sfx->mHandle) return nullptr;
|
|
||||||
|
|
||||||
mBufferCacheSize += size;
|
|
||||||
if(mBufferCacheSize > mBufferCacheMax)
|
|
||||||
{
|
|
||||||
do {
|
|
||||||
if(mUnusedBuffers.empty())
|
|
||||||
{
|
|
||||||
Log(Debug::Warning) << "No unused sound buffers to free, using " << mBufferCacheSize << " bytes!";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
Sound_Buffer *unused = mUnusedBuffers.back();
|
|
||||||
|
|
||||||
size = mOutput->unloadSound(unused->mHandle);
|
|
||||||
mBufferCacheSize -= size;
|
|
||||||
unused->mHandle = nullptr;
|
|
||||||
|
|
||||||
mUnusedBuffers.pop_back();
|
|
||||||
} while(mBufferCacheSize > mBufferCacheMin);
|
|
||||||
}
|
|
||||||
mUnusedBuffers.push_front(sfx);
|
|
||||||
}
|
|
||||||
|
|
||||||
return sfx;
|
|
||||||
}
|
|
||||||
|
|
||||||
DecoderPtr SoundManager::loadVoice(const std::string &voicefile)
|
DecoderPtr SoundManager::loadVoice(const std::string &voicefile)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
|
@ -615,7 +495,7 @@ namespace MWSound
|
||||||
if(!mOutput->isInitialized())
|
if(!mOutput->isInitialized())
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
Sound_Buffer *sfx = loadSound(Misc::StringUtils::lowerCase(soundId));
|
Sound_Buffer *sfx = mSoundBuffers.load(Misc::StringUtils::lowerCase(soundId));
|
||||||
if(!sfx) return nullptr;
|
if(!sfx) return nullptr;
|
||||||
|
|
||||||
// Only one copy of given sound can be played at time, so stop previous copy
|
// Only one copy of given sound can be played at time, so stop previous copy
|
||||||
|
@ -624,23 +504,18 @@ namespace MWSound
|
||||||
SoundPtr sound = getSoundRef();
|
SoundPtr sound = getSoundRef();
|
||||||
sound->init([&] {
|
sound->init([&] {
|
||||||
SoundParams params;
|
SoundParams params;
|
||||||
params.mVolume = volume * sfx->mVolume;
|
params.mVolume = volume * sfx->getVolume();
|
||||||
params.mBaseVolume = volumeFromType(type);
|
params.mBaseVolume = volumeFromType(type);
|
||||||
params.mPitch = pitch;
|
params.mPitch = pitch;
|
||||||
params.mFlags = mode | type | Play_2D;
|
params.mFlags = mode | type | Play_2D;
|
||||||
return params;
|
return params;
|
||||||
} ());
|
} ());
|
||||||
if(!mOutput->playSound(sound.get(), sfx->mHandle, offset))
|
if(!mOutput->playSound(sound.get(), sfx->getHandle(), offset))
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
if(sfx->mUses++ == 0)
|
|
||||||
{
|
|
||||||
SoundList::iterator iter = std::find(mUnusedBuffers.begin(), mUnusedBuffers.end(), sfx);
|
|
||||||
if(iter != mUnusedBuffers.end())
|
|
||||||
mUnusedBuffers.erase(iter);
|
|
||||||
}
|
|
||||||
Sound* result = sound.get();
|
Sound* result = sound.get();
|
||||||
mActiveSounds[MWWorld::ConstPtr()].emplace_back(std::move(sound), sfx);
|
mActiveSounds[MWWorld::ConstPtr()].emplace_back(std::move(sound), sfx);
|
||||||
|
mSoundBuffers.use(*sfx);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -656,7 +531,7 @@ namespace MWSound
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
// Look up the sound in the ESM data
|
// Look up the sound in the ESM data
|
||||||
Sound_Buffer *sfx = loadSound(Misc::StringUtils::lowerCase(soundId));
|
Sound_Buffer *sfx = mSoundBuffers.load(Misc::StringUtils::lowerCase(soundId));
|
||||||
if(!sfx) return nullptr;
|
if(!sfx) return nullptr;
|
||||||
|
|
||||||
// Only one copy of given sound can be played at time on ptr, so stop previous copy
|
// Only one copy of given sound can be played at time on ptr, so stop previous copy
|
||||||
|
@ -668,40 +543,35 @@ namespace MWSound
|
||||||
{
|
{
|
||||||
sound->init([&] {
|
sound->init([&] {
|
||||||
SoundParams params;
|
SoundParams params;
|
||||||
params.mVolume = volume * sfx->mVolume;
|
params.mVolume = volume * sfx->getVolume();
|
||||||
params.mBaseVolume = volumeFromType(type);
|
params.mBaseVolume = volumeFromType(type);
|
||||||
params.mPitch = pitch;
|
params.mPitch = pitch;
|
||||||
params.mFlags = mode | type | Play_2D;
|
params.mFlags = mode | type | Play_2D;
|
||||||
return params;
|
return params;
|
||||||
} ());
|
} ());
|
||||||
played = mOutput->playSound(sound.get(), sfx->mHandle, offset);
|
played = mOutput->playSound(sound.get(), sfx->getHandle(), offset);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
sound->init([&] {
|
sound->init([&] {
|
||||||
SoundParams params;
|
SoundParams params;
|
||||||
params.mPos = objpos;
|
params.mPos = objpos;
|
||||||
params.mVolume = volume * sfx->mVolume;
|
params.mVolume = volume * sfx->getVolume();
|
||||||
params.mBaseVolume = volumeFromType(type);
|
params.mBaseVolume = volumeFromType(type);
|
||||||
params.mPitch = pitch;
|
params.mPitch = pitch;
|
||||||
params.mMinDistance = sfx->mMinDist;
|
params.mMinDistance = sfx->getMinDist();
|
||||||
params.mMaxDistance = sfx->mMaxDist;
|
params.mMaxDistance = sfx->getMaxDist();
|
||||||
params.mFlags = mode | type | Play_3D;
|
params.mFlags = mode | type | Play_3D;
|
||||||
return params;
|
return params;
|
||||||
} ());
|
} ());
|
||||||
played = mOutput->playSound3D(sound.get(), sfx->mHandle, offset);
|
played = mOutput->playSound3D(sound.get(), sfx->getHandle(), offset);
|
||||||
}
|
}
|
||||||
if(!played)
|
if(!played)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
if(sfx->mUses++ == 0)
|
|
||||||
{
|
|
||||||
SoundList::iterator iter = std::find(mUnusedBuffers.begin(), mUnusedBuffers.end(), sfx);
|
|
||||||
if(iter != mUnusedBuffers.end())
|
|
||||||
mUnusedBuffers.erase(iter);
|
|
||||||
}
|
|
||||||
Sound* result = sound.get();
|
Sound* result = sound.get();
|
||||||
mActiveSounds[ptr].emplace_back(std::move(sound), sfx);
|
mActiveSounds[ptr].emplace_back(std::move(sound), sfx);
|
||||||
|
mSoundBuffers.use(*sfx);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -713,32 +583,27 @@ namespace MWSound
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
// Look up the sound in the ESM data
|
// Look up the sound in the ESM data
|
||||||
Sound_Buffer *sfx = loadSound(Misc::StringUtils::lowerCase(soundId));
|
Sound_Buffer *sfx = mSoundBuffers.load(Misc::StringUtils::lowerCase(soundId));
|
||||||
if(!sfx) return nullptr;
|
if(!sfx) return nullptr;
|
||||||
|
|
||||||
SoundPtr sound = getSoundRef();
|
SoundPtr sound = getSoundRef();
|
||||||
sound->init([&] {
|
sound->init([&] {
|
||||||
SoundParams params;
|
SoundParams params;
|
||||||
params.mPos = initialPos;
|
params.mPos = initialPos;
|
||||||
params.mVolume = volume * sfx->mVolume;
|
params.mVolume = volume * sfx->getVolume();
|
||||||
params.mBaseVolume = volumeFromType(type);
|
params.mBaseVolume = volumeFromType(type);
|
||||||
params.mPitch = pitch;
|
params.mPitch = pitch;
|
||||||
params.mMinDistance = sfx->mMinDist;
|
params.mMinDistance = sfx->getMinDist();
|
||||||
params.mMaxDistance = sfx->mMaxDist;
|
params.mMaxDistance = sfx->getMaxDist();
|
||||||
params.mFlags = mode | type | Play_3D;
|
params.mFlags = mode | type | Play_3D;
|
||||||
return params;
|
return params;
|
||||||
} ());
|
} ());
|
||||||
if(!mOutput->playSound3D(sound.get(), sfx->mHandle, offset))
|
if(!mOutput->playSound3D(sound.get(), sfx->getHandle(), offset))
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
if(sfx->mUses++ == 0)
|
|
||||||
{
|
|
||||||
SoundList::iterator iter = std::find(mUnusedBuffers.begin(), mUnusedBuffers.end(), sfx);
|
|
||||||
if(iter != mUnusedBuffers.end())
|
|
||||||
mUnusedBuffers.erase(iter);
|
|
||||||
}
|
|
||||||
Sound* result = sound.get();
|
Sound* result = sound.get();
|
||||||
mActiveSounds[MWWorld::ConstPtr()].emplace_back(std::move(sound), sfx);
|
mActiveSounds[MWWorld::ConstPtr()].emplace_back(std::move(sound), sfx);
|
||||||
|
mSoundBuffers.use(*sfx);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -766,7 +631,7 @@ namespace MWSound
|
||||||
if(!mOutput->isInitialized())
|
if(!mOutput->isInitialized())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
Sound_Buffer *sfx = lookupSound(Misc::StringUtils::lowerCase(soundId));
|
Sound_Buffer *sfx = mSoundBuffers.lookup(Misc::StringUtils::lowerCase(soundId));
|
||||||
if (!sfx) return;
|
if (!sfx) return;
|
||||||
|
|
||||||
stopSound(sfx, ptr);
|
stopSound(sfx, ptr);
|
||||||
|
@ -818,7 +683,7 @@ namespace MWSound
|
||||||
SoundMap::iterator snditer = mActiveSounds.find(ptr);
|
SoundMap::iterator snditer = mActiveSounds.find(ptr);
|
||||||
if(snditer != mActiveSounds.end())
|
if(snditer != mActiveSounds.end())
|
||||||
{
|
{
|
||||||
Sound_Buffer *sfx = lookupSound(Misc::StringUtils::lowerCase(soundId));
|
Sound_Buffer *sfx = mSoundBuffers.lookup(Misc::StringUtils::lowerCase(soundId));
|
||||||
if (sfx == nullptr)
|
if (sfx == nullptr)
|
||||||
return;
|
return;
|
||||||
for(SoundBufferRefPair &sndbuf : snditer->second)
|
for(SoundBufferRefPair &sndbuf : snditer->second)
|
||||||
|
@ -834,7 +699,7 @@ namespace MWSound
|
||||||
SoundMap::const_iterator snditer = mActiveSounds.find(ptr);
|
SoundMap::const_iterator snditer = mActiveSounds.find(ptr);
|
||||||
if(snditer != mActiveSounds.end())
|
if(snditer != mActiveSounds.end())
|
||||||
{
|
{
|
||||||
Sound_Buffer *sfx = lookupSound(Misc::StringUtils::lowerCase(soundId));
|
Sound_Buffer *sfx = mSoundBuffers.lookup(Misc::StringUtils::lowerCase(soundId));
|
||||||
return std::find_if(snditer->second.cbegin(), snditer->second.cend(),
|
return std::find_if(snditer->second.cbegin(), snditer->second.cend(),
|
||||||
[this,sfx](const SoundBufferRefPair &snd) -> bool
|
[this,sfx](const SoundBufferRefPair &snd) -> bool
|
||||||
{ return snd.second == sfx && mOutput->isSoundPlaying(snd.first.get()); }
|
{ return snd.second == sfx && mOutput->isSoundPlaying(snd.first.get()); }
|
||||||
|
@ -921,7 +786,7 @@ namespace MWSound
|
||||||
case WaterSoundAction::DoNothing:
|
case WaterSoundAction::DoNothing:
|
||||||
break;
|
break;
|
||||||
case WaterSoundAction::SetVolume:
|
case WaterSoundAction::SetVolume:
|
||||||
mNearWaterSound->setVolume(update.mVolume * sfx->mVolume);
|
mNearWaterSound->setVolume(update.mVolume * sfx->getVolume());
|
||||||
break;
|
break;
|
||||||
case WaterSoundAction::FinishSound:
|
case WaterSoundAction::FinishSound:
|
||||||
mOutput->finishSound(mNearWaterSound);
|
mOutput->finishSound(mNearWaterSound);
|
||||||
|
@ -947,7 +812,7 @@ namespace MWSound
|
||||||
|
|
||||||
bool soundIdChanged = false;
|
bool soundIdChanged = false;
|
||||||
|
|
||||||
Sound_Buffer* sfx = lookupSound(update.mId);
|
Sound_Buffer* sfx = mSoundBuffers.lookup(update.mId);
|
||||||
if (mLastCell != cell)
|
if (mLastCell != cell)
|
||||||
{
|
{
|
||||||
const auto snditer = mActiveSounds.find(MWWorld::ConstPtr());
|
const auto snditer = mActiveSounds.find(MWWorld::ConstPtr());
|
||||||
|
@ -1028,7 +893,6 @@ namespace MWSound
|
||||||
while(sndidx != snditer->second.end())
|
while(sndidx != snditer->second.end())
|
||||||
{
|
{
|
||||||
Sound *sound = sndidx->first.get();
|
Sound *sound = sndidx->first.get();
|
||||||
Sound_Buffer *sfx = sndidx->second;
|
|
||||||
|
|
||||||
if(!ptr.isEmpty() && sound->getIs3D())
|
if(!ptr.isEmpty() && sound->getIs3D())
|
||||||
{
|
{
|
||||||
|
@ -1050,8 +914,7 @@ namespace MWSound
|
||||||
mUnderwaterSound = nullptr;
|
mUnderwaterSound = nullptr;
|
||||||
if (sound == mNearWaterSound)
|
if (sound == mNearWaterSound)
|
||||||
mNearWaterSound = nullptr;
|
mNearWaterSound = nullptr;
|
||||||
if(sfx->mUses-- == 1)
|
mSoundBuffers.release(*sndidx->second);
|
||||||
mUnusedBuffers.push_front(sfx);
|
|
||||||
sndidx = snditer->second.erase(sndidx);
|
sndidx = snditer->second.erase(sndidx);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -1313,9 +1176,7 @@ namespace MWSound
|
||||||
for(SoundBufferRefPair &sndbuf : snd.second)
|
for(SoundBufferRefPair &sndbuf : snd.second)
|
||||||
{
|
{
|
||||||
mOutput->finishSound(sndbuf.first.get());
|
mOutput->finishSound(sndbuf.first.get());
|
||||||
Sound_Buffer *sfx = sndbuf.second;
|
mSoundBuffers.release(*sndbuf.second);
|
||||||
if(sfx->mUses-- == 1)
|
|
||||||
mUnusedBuffers.push_front(sfx);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mActiveSounds.clear();
|
mActiveSounds.clear();
|
||||||
|
|
|
@ -4,7 +4,6 @@
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <deque>
|
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
|
||||||
|
@ -18,6 +17,7 @@
|
||||||
#include "watersoundupdater.hpp"
|
#include "watersoundupdater.hpp"
|
||||||
#include "type.hpp"
|
#include "type.hpp"
|
||||||
#include "volumesettings.hpp"
|
#include "volumesettings.hpp"
|
||||||
|
#include "sound_buffer.hpp"
|
||||||
|
|
||||||
namespace VFS
|
namespace VFS
|
||||||
{
|
{
|
||||||
|
@ -36,17 +36,6 @@ namespace MWSound
|
||||||
struct Sound_Decoder;
|
struct Sound_Decoder;
|
||||||
class Sound;
|
class Sound;
|
||||||
class Stream;
|
class Stream;
|
||||||
class Sound_Buffer;
|
|
||||||
|
|
||||||
enum Environment {
|
|
||||||
Env_Normal,
|
|
||||||
Env_Underwater
|
|
||||||
};
|
|
||||||
// Extra play flags, not intended for caller use
|
|
||||||
enum PlayModeEx {
|
|
||||||
Play_2D = 0,
|
|
||||||
Play_3D = 1<<31
|
|
||||||
};
|
|
||||||
|
|
||||||
using SoundPtr = Misc::ObjectPtr<Sound>;
|
using SoundPtr = Misc::ObjectPtr<Sound>;
|
||||||
using StreamPtr = Misc::ObjectPtr<Stream>;
|
using StreamPtr = Misc::ObjectPtr<Stream>;
|
||||||
|
@ -66,21 +55,7 @@ namespace MWSound
|
||||||
|
|
||||||
WaterSoundUpdater mWaterSoundUpdater;
|
WaterSoundUpdater mWaterSoundUpdater;
|
||||||
|
|
||||||
typedef std::unique_ptr<std::deque<Sound_Buffer> > SoundBufferList;
|
SoundBufferPool mSoundBuffers;
|
||||||
// List of sound buffers, grown as needed. New enties are added to the
|
|
||||||
// back, allowing existing Sound_Buffer references/pointers to remain
|
|
||||||
// valid.
|
|
||||||
SoundBufferList mSoundBuffers;
|
|
||||||
size_t mBufferCacheMin;
|
|
||||||
size_t mBufferCacheMax;
|
|
||||||
size_t mBufferCacheSize;
|
|
||||||
|
|
||||||
typedef std::unordered_map<std::string,Sound_Buffer*> NameBufferMap;
|
|
||||||
NameBufferMap mBufferNameMap;
|
|
||||||
|
|
||||||
// NOTE: unused buffers are stored in front-newest order.
|
|
||||||
typedef std::deque<Sound_Buffer*> SoundList;
|
|
||||||
SoundList mUnusedBuffers;
|
|
||||||
|
|
||||||
Misc::ObjectPool<Sound> mSounds;
|
Misc::ObjectPool<Sound> mSounds;
|
||||||
|
|
||||||
|
@ -124,9 +99,6 @@ namespace MWSound
|
||||||
|
|
||||||
Sound_Buffer *insertSound(const std::string &soundId, const ESM::Sound *sound);
|
Sound_Buffer *insertSound(const std::string &soundId, const ESM::Sound *sound);
|
||||||
|
|
||||||
Sound_Buffer *lookupSound(const std::string &soundId) const;
|
|
||||||
Sound_Buffer *loadSound(const std::string &soundId);
|
|
||||||
|
|
||||||
// returns a decoder to start streaming, or nullptr if the sound was not found
|
// returns a decoder to start streaming, or nullptr if the sound was not found
|
||||||
DecoderPtr loadVoice(const std::string &voicefile);
|
DecoderPtr loadVoice(const std::string &voicefile);
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue