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.
openmw-38
Chris Robinson 9 years ago
parent eee6a19e31
commit fbfcc4050f

@ -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; ChannelConfig chans;
float maxDistance = fAudioVoiceDefaultMaxDistance * fAudioMaxDistanceMult; SampleType type;
minDistance = std::max(minDistance, 1.f); int srate;
maxDistance = std::max(minDistance, maxDistance); decoder->getInfo(&srate, &chans, &type);
sfxiter = mVoiceSoundBuffers.insert(std::make_pair( std::vector<char> data;
voicefile, Sound_Buffer("sound/"+voicefile, 1.0f, minDistance, maxDistance) decoder->readAll(data);
)).first; decoder->close();
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; 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…
Cancel
Save