mirror of
https://github.com/TES3MP/openmw-tes3mp.git
synced 2025-01-19 23:23:52 +00:00
Stream voice clips
Voices tend to be a bit long, and not individually replayed often. So it's better to stream them instead of loading theminto a sound buffer. The loudness data is very small, though, so that can be kept buffered indefinitely.
This commit is contained in:
parent
eee6a19e31
commit
fbfcc4050f
6 changed files with 71 additions and 59 deletions
|
@ -19,14 +19,10 @@
|
||||||
#define ALC_ALL_DEVICES_SPECIFIER 0x1013
|
#define ALC_ALL_DEVICES_SPECIFIER 0x1013
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
#define MAKE_PTRID(id) ((void*)(uintptr_t)id)
|
#define MAKE_PTRID(id) ((void*)(uintptr_t)id)
|
||||||
#define GET_PTRID(ptr) ((ALuint)(uintptr_t)ptr)
|
#define GET_PTRID(ptr) ((ALuint)(uintptr_t)ptr)
|
||||||
|
|
||||||
namespace
|
|
||||||
{
|
|
||||||
const int loudnessFPS = 20; // loudness values per second of audio
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace MWSound
|
namespace MWSound
|
||||||
{
|
{
|
||||||
|
|
||||||
|
@ -779,7 +775,7 @@ void OpenAL_Output::deinit()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Sound_Handle OpenAL_Output::loadSound(const std::string &fname, Sound_Loudness *loudness)
|
Sound_Handle OpenAL_Output::loadSound(const std::string &fname)
|
||||||
{
|
{
|
||||||
throwALerror();
|
throwALerror();
|
||||||
|
|
||||||
|
@ -806,9 +802,6 @@ Sound_Handle OpenAL_Output::loadSound(const std::string &fname, Sound_Loudness *
|
||||||
decoder->readAll(data);
|
decoder->readAll(data);
|
||||||
decoder->close();
|
decoder->close();
|
||||||
|
|
||||||
if(loudness != 0)
|
|
||||||
loudness->analyzeLoudness(data, srate, chans, type, static_cast<float>(loudnessFPS));
|
|
||||||
|
|
||||||
ALuint buf = 0;
|
ALuint buf = 0;
|
||||||
try {
|
try {
|
||||||
alGenBuffers(1, &buf);
|
alGenBuffers(1, &buf);
|
||||||
|
@ -975,7 +968,7 @@ MWBase::SoundPtr OpenAL_Output::streamSound3D(DecoderPtr decoder, const osg::Vec
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
|
|
||||||
sound->updateAll(true);
|
sound->updateAll(false);
|
||||||
|
|
||||||
sound->play();
|
sound->play();
|
||||||
return sound;
|
return sound;
|
||||||
|
|
|
@ -38,7 +38,7 @@ namespace MWSound
|
||||||
virtual void init(const std::string &devname="");
|
virtual void init(const std::string &devname="");
|
||||||
virtual void deinit();
|
virtual void deinit();
|
||||||
|
|
||||||
virtual Sound_Handle loadSound(const std::string &fname, Sound_Loudness *loudness);
|
virtual Sound_Handle loadSound(const std::string &fname);
|
||||||
virtual void unloadSound(Sound_Handle data);
|
virtual void unloadSound(Sound_Handle data);
|
||||||
virtual size_t getSoundDataSize(Sound_Handle data) const;
|
virtual size_t getSoundDataSize(Sound_Handle data) const;
|
||||||
|
|
||||||
|
|
|
@ -20,7 +20,6 @@ namespace MWSound
|
||||||
float mMinDist, mMaxDist;
|
float mMinDist, mMaxDist;
|
||||||
|
|
||||||
Sound_Handle mHandle;
|
Sound_Handle mHandle;
|
||||||
Sound_Loudness mLoudness;
|
|
||||||
|
|
||||||
size_t mReferences;
|
size_t mReferences;
|
||||||
|
|
||||||
|
|
|
@ -26,7 +26,7 @@ namespace MWSound
|
||||||
virtual void init(const std::string &devname="") = 0;
|
virtual void init(const std::string &devname="") = 0;
|
||||||
virtual void deinit() = 0;
|
virtual void deinit() = 0;
|
||||||
|
|
||||||
virtual Sound_Handle loadSound(const std::string &fname, Sound_Loudness *loudness=0) = 0;
|
virtual Sound_Handle loadSound(const std::string &fname) = 0;
|
||||||
virtual void unloadSound(Sound_Handle data) = 0;
|
virtual void unloadSound(Sound_Handle data) = 0;
|
||||||
virtual size_t getSoundDataSize(Sound_Handle data) const = 0;
|
virtual size_t getSoundDataSize(Sound_Handle data) const = 0;
|
||||||
|
|
||||||
|
|
|
@ -30,6 +30,11 @@
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
const int sLoudnessFPS = 20; // loudness values per second of audio
|
||||||
|
}
|
||||||
|
|
||||||
namespace MWSound
|
namespace MWSound
|
||||||
{
|
{
|
||||||
SoundManager::SoundManager(const VFS::Manager* vfs, bool useSound)
|
SoundManager::SoundManager(const VFS::Manager* vfs, bool useSound)
|
||||||
|
@ -103,13 +108,6 @@ namespace MWSound
|
||||||
mOutput->unloadSound(sfxiter->second.mHandle);
|
mOutput->unloadSound(sfxiter->second.mHandle);
|
||||||
sfxiter->second.mHandle = 0;
|
sfxiter->second.mHandle = 0;
|
||||||
}
|
}
|
||||||
sfxiter = mVoiceSoundBuffers.begin();
|
|
||||||
for(;sfxiter != mVoiceSoundBuffers.end();++sfxiter)
|
|
||||||
{
|
|
||||||
if(sfxiter->second.mHandle)
|
|
||||||
mOutput->unloadSound(sfxiter->second.mHandle);
|
|
||||||
sfxiter->second.mHandle = 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
mOutput.reset();
|
mOutput.reset();
|
||||||
}
|
}
|
||||||
|
@ -188,32 +186,37 @@ namespace MWSound
|
||||||
return sfx;
|
return sfx;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Sound_Buffer *SoundManager::lookupVoice(const std::string &voicefile)
|
void SoundManager::loadVoice(const std::string &voicefile)
|
||||||
{
|
{
|
||||||
NameBufferMap::iterator sfxiter = mVoiceSoundBuffers.find(voicefile);
|
NameLoudnessMap::iterator lipiter = mVoiceLipBuffers.find(voicefile);
|
||||||
if(sfxiter == mVoiceSoundBuffers.end())
|
if(lipiter != mVoiceLipBuffers.end()) return;
|
||||||
|
|
||||||
|
DecoderPtr decoder = getDecoder();
|
||||||
|
// Workaround: Bethesda at some point converted some of the files to mp3, but the references were kept as .wav.
|
||||||
|
if(decoder->mResourceMgr->exists(voicefile))
|
||||||
|
decoder->open(voicefile);
|
||||||
|
else
|
||||||
{
|
{
|
||||||
MWBase::World* world = MWBase::Environment::get().getWorld();
|
std::string file = voicefile;
|
||||||
static const float fAudioMinDistanceMult = world->getStore().get<ESM::GameSetting>().find("fAudioMinDistanceMult")->getFloat();
|
std::string::size_type pos = file.rfind('.');
|
||||||
static const float fAudioMaxDistanceMult = world->getStore().get<ESM::GameSetting>().find("fAudioMaxDistanceMult")->getFloat();
|
if(pos != std::string::npos)
|
||||||
static const float fAudioVoiceDefaultMinDistance = world->getStore().get<ESM::GameSetting>().find("fAudioVoiceDefaultMinDistance")->getFloat();
|
file = file.substr(0, pos)+".mp3";
|
||||||
static const float fAudioVoiceDefaultMaxDistance = world->getStore().get<ESM::GameSetting>().find("fAudioVoiceDefaultMaxDistance")->getFloat();
|
decoder->open(file);
|
||||||
|
|
||||||
float minDistance = fAudioVoiceDefaultMinDistance * fAudioMinDistanceMult;
|
|
||||||
float maxDistance = fAudioVoiceDefaultMaxDistance * fAudioMaxDistanceMult;
|
|
||||||
minDistance = std::max(minDistance, 1.f);
|
|
||||||
maxDistance = std::max(minDistance, maxDistance);
|
|
||||||
|
|
||||||
sfxiter = mVoiceSoundBuffers.insert(std::make_pair(
|
|
||||||
voicefile, Sound_Buffer("sound/"+voicefile, 1.0f, minDistance, maxDistance)
|
|
||||||
)).first;
|
|
||||||
mVFS->normalizeFilename(sfxiter->second.mResourceName);
|
|
||||||
sfxiter->second.mHandle = mOutput->loadSound(sfxiter->second.mResourceName, &sfxiter->second.mLoudness);
|
|
||||||
}
|
}
|
||||||
else if(!sfxiter->second.mHandle)
|
|
||||||
sfxiter->second.mHandle = mOutput->loadSound(sfxiter->second.mResourceName, &sfxiter->second.mLoudness);
|
|
||||||
|
|
||||||
return &sfxiter->second;
|
ChannelConfig chans;
|
||||||
|
SampleType type;
|
||||||
|
int srate;
|
||||||
|
decoder->getInfo(&srate, &chans, &type);
|
||||||
|
|
||||||
|
std::vector<char> data;
|
||||||
|
decoder->readAll(data);
|
||||||
|
decoder->close();
|
||||||
|
|
||||||
|
Sound_Loudness loudness;
|
||||||
|
loudness.analyzeLoudness(data, srate, chans, type, static_cast<float>(sLoudnessFPS));
|
||||||
|
|
||||||
|
mVoiceLipBuffers.insert(std::make_pair(voicefile, loudness));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -336,14 +339,25 @@ namespace MWSound
|
||||||
return;
|
return;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
std::string voicefile = Misc::StringUtils::lowerCase(filename);
|
MWBase::World* world = MWBase::Environment::get().getWorld();
|
||||||
const Sound_Buffer *sfx = lookupVoice(voicefile);
|
static const float fAudioMinDistanceMult = world->getStore().get<ESM::GameSetting>().find("fAudioMinDistanceMult")->getFloat();
|
||||||
|
static const float fAudioMaxDistanceMult = world->getStore().get<ESM::GameSetting>().find("fAudioMaxDistanceMult")->getFloat();
|
||||||
|
static const float fAudioVoiceDefaultMinDistance = world->getStore().get<ESM::GameSetting>().find("fAudioVoiceDefaultMinDistance")->getFloat();
|
||||||
|
static const float fAudioVoiceDefaultMaxDistance = world->getStore().get<ESM::GameSetting>().find("fAudioVoiceDefaultMaxDistance")->getFloat();
|
||||||
|
static float minDistance = std::max(fAudioVoiceDefaultMinDistance * fAudioMinDistanceMult, 1.0f);
|
||||||
|
static float maxDistance = std::max(fAudioVoiceDefaultMaxDistance * fAudioMaxDistanceMult, minDistance);
|
||||||
|
|
||||||
|
std::string voicefile = "sound/"+Misc::StringUtils::lowerCase(filename);
|
||||||
float basevol = volumeFromType(Play_TypeVoice);
|
float basevol = volumeFromType(Play_TypeVoice);
|
||||||
const ESM::Position &pos = ptr.getRefData().getPosition();
|
const ESM::Position &pos = ptr.getRefData().getPosition();
|
||||||
const osg::Vec3f objpos(pos.asVec3());
|
const osg::Vec3f objpos(pos.asVec3());
|
||||||
|
|
||||||
MWBase::SoundPtr sound = mOutput->playSound3D(sfx->mHandle,
|
loadVoice(voicefile);
|
||||||
objpos, sfx->mVolume, basevol, 1.0f, sfx->mMinDist, sfx->mMaxDist, Play_Normal|Play_TypeVoice, 0
|
DecoderPtr decoder = getDecoder();
|
||||||
|
decoder->open(voicefile);
|
||||||
|
|
||||||
|
MWBase::SoundPtr sound = mOutput->streamSound3D(decoder,
|
||||||
|
objpos, 1.0f, basevol, 1.0f, minDistance, maxDistance, Play_Normal|Play_TypeVoice
|
||||||
);
|
);
|
||||||
mActiveSaySounds[ptr] = std::make_pair(sound, voicefile);
|
mActiveSaySounds[ptr] = std::make_pair(sound, voicefile);
|
||||||
}
|
}
|
||||||
|
@ -358,12 +372,13 @@ namespace MWSound
|
||||||
SaySoundMap::const_iterator snditer = mActiveSaySounds.find(ptr);
|
SaySoundMap::const_iterator snditer = mActiveSaySounds.find(ptr);
|
||||||
if(snditer != mActiveSaySounds.end())
|
if(snditer != mActiveSaySounds.end())
|
||||||
{
|
{
|
||||||
NameBufferMap::const_iterator sfxiter = mVoiceSoundBuffers.find(snditer->second.second);
|
MWBase::SoundPtr sound = snditer->second.first;
|
||||||
if(sfxiter != mVoiceSoundBuffers.end())
|
NameLoudnessMap::const_iterator lipiter = mVoiceLipBuffers.find(snditer->second.second);
|
||||||
|
if(lipiter != mVoiceLipBuffers.end())
|
||||||
{
|
{
|
||||||
float sec = snditer->second.first->getTimeOffset();
|
float sec = sound->getTimeOffset();
|
||||||
if(snditer->second.first->isPlaying())
|
if(sound->isPlaying())
|
||||||
return sfxiter->second.mLoudness.getLoudnessAtTime(sec);
|
return lipiter->second.getLoudnessAtTime(sec);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -376,12 +391,15 @@ namespace MWSound
|
||||||
return;
|
return;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
std::string voicefile = Misc::StringUtils::lowerCase(filename);
|
std::string voicefile = "sound/"+Misc::StringUtils::lowerCase(filename);
|
||||||
const Sound_Buffer *sfx = lookupVoice(voicefile);
|
|
||||||
float basevol = volumeFromType(Play_TypeVoice);
|
float basevol = volumeFromType(Play_TypeVoice);
|
||||||
|
|
||||||
MWBase::SoundPtr sound = mOutput->playSound(sfx->mHandle,
|
loadVoice(voicefile);
|
||||||
sfx->mVolume, basevol, 1.0f, Play_Normal|Play_TypeVoice, 0
|
DecoderPtr decoder = getDecoder();
|
||||||
|
decoder->open(voicefile);
|
||||||
|
|
||||||
|
MWBase::SoundPtr sound = mOutput->streamSound(decoder,
|
||||||
|
basevol, 1.0f, Play_Normal|Play_TypeVoice
|
||||||
);
|
);
|
||||||
mActiveSaySounds[MWWorld::Ptr()] = std::make_pair(sound, voicefile);
|
mActiveSaySounds[MWWorld::Ptr()] = std::make_pair(sound, voicefile);
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
|
|
||||||
#include <components/settings/settings.hpp>
|
#include <components/settings/settings.hpp>
|
||||||
|
|
||||||
|
#include "loudness.hpp"
|
||||||
#include "../mwbase/soundmanager.hpp"
|
#include "../mwbase/soundmanager.hpp"
|
||||||
|
|
||||||
namespace VFS
|
namespace VFS
|
||||||
|
@ -47,9 +48,9 @@ namespace MWSound
|
||||||
typedef std::map<std::string,Sound_Buffer> NameBufferMap;
|
typedef std::map<std::string,Sound_Buffer> NameBufferMap;
|
||||||
NameBufferMap mSoundBuffers;
|
NameBufferMap mSoundBuffers;
|
||||||
size_t mBufferCacheSize;
|
size_t mBufferCacheSize;
|
||||||
// Should stream voices, but that requires handling the "lip" data
|
|
||||||
// separately from buffer loading.
|
typedef std::map<std::string,Sound_Loudness> NameLoudnessMap;
|
||||||
NameBufferMap mVoiceSoundBuffers;
|
NameLoudnessMap mVoiceLipBuffers;
|
||||||
|
|
||||||
typedef std::set<std::string> SoundSet;
|
typedef std::set<std::string> SoundSet;
|
||||||
SoundSet mUnusedBuffers;
|
SoundSet mUnusedBuffers;
|
||||||
|
@ -75,7 +76,8 @@ namespace MWSound
|
||||||
int mPausedSoundTypes;
|
int mPausedSoundTypes;
|
||||||
|
|
||||||
Sound_Buffer *lookup(const std::string &soundId);
|
Sound_Buffer *lookup(const std::string &soundId);
|
||||||
const Sound_Buffer *lookupVoice(const std::string &voicefile);
|
// Ensure the loudness/"lip" data is loaded
|
||||||
|
void loadVoice(const std::string &voicefile);
|
||||||
|
|
||||||
void streamMusicFull(const std::string& filename);
|
void streamMusicFull(const std::string& filename);
|
||||||
bool updateSound(MWBase::SoundPtr sound, const MWWorld::Ptr &ptr, float duration);
|
bool updateSound(MWBase::SoundPtr sound, const MWWorld::Ptr &ptr, float duration);
|
||||||
|
|
Loading…
Reference in a new issue