mirror of
https://github.com/OpenMW/openmw.git
synced 2025-03-31 07:06:45 +00:00
Separate sound buffer pool from sound manager
This commit is contained in:
parent
2d61db555b
commit
944033db4e
7 changed files with 276 additions and 184 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); }
|
||||||
|
|
154
apps/openmw/mwsound/sound_buffer.cpp
Normal file
154
apps/openmw/mwsound/sound_buffer.cpp
Normal file
|
@ -0,0 +1,154 @@
|
||||||
|
#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)
|
||||||
|
{
|
||||||
|
Sound_Handle handle;
|
||||||
|
size_t size;
|
||||||
|
std::tie(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,100 @@
|
||||||
#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;
|
|
||||||
|
|
||||||
float mVolume;
|
|
||||||
float mMinDist, mMaxDist;
|
|
||||||
|
|
||||||
Sound_Handle mHandle;
|
|
||||||
|
|
||||||
size_t mUses;
|
|
||||||
|
|
||||||
Sound_Buffer(std::string resname, float volume, float mindist, float maxdist)
|
Sound_Buffer(std::string resname, float volume, float mindist, float maxdist)
|
||||||
: mResourceName(resname), mVolume(volume), mMinDist(mindist), mMaxDist(maxdist), mHandle(nullptr), mUses(0)
|
: mResourceName(std::move(resname)), mVolume(volume), mMinDist(mindist), mMaxDist(maxdist)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
|
const std::string& getResourceName() const noexcept { return mResourceName; }
|
||||||
|
|
||||||
|
Sound_Handle getHandle() const noexcept { return mHandle; }
|
||||||
|
|
||||||
|
float getVolume() const noexcept { return mVolume; }
|
||||||
|
|
||||||
|
float getMinDist() const noexcept { return mMinDist; }
|
||||||
|
|
||||||
|
float getMaxDist() const noexcept { return mMaxDist; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string mResourceName;
|
||||||
|
float mVolume = 0;
|
||||||
|
float mMinDist = 0;
|
||||||
|
float mMaxDist = 0;
|
||||||
|
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();
|
||||||
|
|
||||||
|
Sound_Buffer* lookup(const std::string& soundId) const;
|
||||||
|
|
||||||
|
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,112 +120,18 @@ 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,
|
// Lookup a soundId for its sound data (resource name, local volume,
|
||||||
// minRange, and maxRange)
|
// minRange, and maxRange)
|
||||||
Sound_Buffer *SoundManager::lookupSound(const std::string &soundId) const
|
Sound_Buffer *SoundManager::lookupSound(const std::string &soundId) const
|
||||||
{
|
{
|
||||||
NameBufferMap::const_iterator snd = mBufferNameMap.find(soundId);
|
return mSoundBuffers.lookup(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,
|
// Lookup a soundId for its sound data (resource name, local volume,
|
||||||
// minRange, and maxRange), and ensure it's ready for use.
|
// minRange, and maxRange), and ensure it's ready for use.
|
||||||
Sound_Buffer *SoundManager::loadSound(const std::string &soundId)
|
Sound_Buffer *SoundManager::loadSound(const std::string &soundId)
|
||||||
{
|
{
|
||||||
#ifdef __GNUC__
|
return mSoundBuffers.load(soundId);
|
||||||
#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)
|
||||||
|
@ -624,23 +518,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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -668,40 +557,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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -720,25 +604,20 @@ namespace MWSound
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -921,7 +800,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);
|
||||||
|
@ -1028,7 +907,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 +928,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 +1190,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;
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue