Load loudness data asynchronously

Currently abuses the output audio streams' background processing thread to do
the work, since there's no generalized threaded processing mechanism.
openmw-38
Chris Robinson 9 years ago
parent 0f05ccf72a
commit 4ee409af84

@ -52,6 +52,8 @@ void Sound_Loudness::analyzeLoudness(const std::vector< char >& data, int sample
mSamples.push_back(rms); mSamples.push_back(rms);
++segment; ++segment;
} }
mReady = true;
} }

@ -12,9 +12,10 @@ class Sound_Loudness {
// Loudness sample info // Loudness sample info
float mSamplesPerSec; float mSamplesPerSec;
std::vector<float> mSamples; std::vector<float> mSamples;
volatile bool mReady;
public: public:
Sound_Loudness() : mSamplesPerSec(0.0f) { } Sound_Loudness() : mSamplesPerSec(0.0f), mReady(false) { }
/** /**
* Analyzes the energy (closely related to loudness) of a sound buffer. * Analyzes the energy (closely related to loudness) of a sound buffer.
@ -30,6 +31,7 @@ public:
ChannelConfig chans, SampleType type, ChannelConfig chans, SampleType type,
float valuesPerSecond); float valuesPerSecond);
bool isReady() { return mReady; }
float getLoudnessAtTime(float sec) const; float getLoudnessAtTime(float sec) const;
}; };

@ -23,6 +23,13 @@
#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 sLoudnessFPS = 20; // loudness values per second of audio
}
namespace MWSound namespace MWSound
{ {
@ -217,6 +224,10 @@ public:
struct OpenAL_Output::StreamThread { struct OpenAL_Output::StreamThread {
typedef std::vector<OpenAL_SoundStream*> StreamVec; typedef std::vector<OpenAL_SoundStream*> StreamVec;
StreamVec mStreams; StreamVec mStreams;
typedef std::vector<std::pair<DecoderPtr,Sound_Loudness*> > DecoderLoudnessVec;
DecoderLoudnessVec mDecoderLoudness;
volatile bool mQuitNow; volatile bool mQuitNow;
boost::mutex mMutex; boost::mutex mMutex;
boost::condition_variable mCondVar; boost::condition_variable mCondVar;
@ -248,6 +259,33 @@ struct OpenAL_Output::StreamThread {
else else
++iter; ++iter;
} }
// Only do one loudness decode at a time, in case it takes particularly long we don't
// want to block up anything.
DecoderLoudnessVec::iterator dliter = mDecoderLoudness.begin();
if(dliter != mDecoderLoudness.end())
{
DecoderPtr decoder = dliter->first;
Sound_Loudness *loudness = dliter->second;
mDecoderLoudness.erase(dliter);
lock.unlock();
std::vector<char> data;
ChannelConfig chans = ChannelConfig_Mono;
SampleType type = SampleType_Int16;
int srate = 48000;
try {
decoder->getInfo(&srate, &chans, &type);
decoder->readAll(data);
}
catch(std::exception &e) {
std::cerr<< "Failed to decode audio: "<<e.what() <<std::endl;
}
loudness->analyzeLoudness(data, srate, chans, type, static_cast<float>(sLoudnessFPS));
lock.lock();
continue;
}
mCondVar.timed_wait(lock, boost::posix_time::milliseconds(50)); mCondVar.timed_wait(lock, boost::posix_time::milliseconds(50));
} }
} }
@ -274,6 +312,15 @@ struct OpenAL_Output::StreamThread {
{ {
boost::lock_guard<boost::mutex> lock(mMutex); boost::lock_guard<boost::mutex> lock(mMutex);
mStreams.clear(); mStreams.clear();
mDecoderLoudness.clear();
}
void add(DecoderPtr decoder, Sound_Loudness *loudness)
{
boost::unique_lock<boost::mutex> lock(mMutex);
mDecoderLoudness.push_back(std::make_pair(decoder, loudness));
lock.unlock();
mCondVar.notify_all();
} }
private: private:
@ -1052,6 +1099,12 @@ void OpenAL_Output::resumeSounds(int types)
} }
void OpenAL_Output::loadLoudnessAsync(DecoderPtr decoder, Sound_Loudness *loudness)
{
mStreamThread->add(decoder, loudness);
}
OpenAL_Output::OpenAL_Output(SoundManager &mgr) OpenAL_Output::OpenAL_Output(SoundManager &mgr)
: Sound_Output(mgr), mDevice(0), mContext(0), mLastEnvironment(Env_Normal), : Sound_Output(mgr), mDevice(0), mContext(0), mLastEnvironment(Env_Normal),
mStreamThread(new StreamThread) mStreamThread(new StreamThread)

@ -57,6 +57,8 @@ namespace MWSound
virtual void pauseSounds(int types); virtual void pauseSounds(int types);
virtual void resumeSounds(int types); virtual void resumeSounds(int types);
virtual void loadLoudnessAsync(DecoderPtr decoder, Sound_Loudness *loudness);
OpenAL_Output& operator=(const OpenAL_Output &rhs); OpenAL_Output& operator=(const OpenAL_Output &rhs);
OpenAL_Output(const OpenAL_Output &rhs); OpenAL_Output(const OpenAL_Output &rhs);

@ -47,6 +47,11 @@ namespace MWSound
virtual void pauseSounds(int types) = 0; virtual void pauseSounds(int types) = 0;
virtual void resumeSounds(int types) = 0; virtual void resumeSounds(int types) = 0;
// HACK: The sound output implementation really shouldn't be handling
// asynchronous loudness data loading, but it's currently where we have
// a background processing thread.
virtual void loadLoudnessAsync(DecoderPtr decoder, Sound_Loudness *loudness) = 0;
Sound_Output& operator=(const Sound_Output &rhs); Sound_Output& operator=(const Sound_Output &rhs);
Sound_Output(const Sound_Output &rhs); Sound_Output(const Sound_Output &rhs);

@ -30,11 +30,6 @@
#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)
@ -234,27 +229,38 @@ namespace MWSound
return decoder; return decoder;
} }
ChannelConfig chans; mVoiceLipBuffers.insert(mVoiceLipBuffers.end(), Sound_Loudness());
SampleType type;
int srate;
decoder->getInfo(&srate, &chans, &type);
std::vector<char> data;
decoder->readAll(data);
Sound_Loudness loudness;
loudness.analyzeLoudness(data, srate, chans, type, static_cast<float>(sLoudnessFPS));
mVoiceLipBuffers.insert(mVoiceLipBuffers.end(), loudness);
lipiter = mVoiceLipNameMap.insert( lipiter = mVoiceLipNameMap.insert(
std::make_pair(voicefile, &mVoiceLipBuffers.back()) std::make_pair(voicefile, &mVoiceLipBuffers.back())
).first; ).first;
decoder->rewind(); mOutput->loadLoudnessAsync(decoder, lipiter->second);
*lipdata = lipiter->second; *lipdata = lipiter->second;
return decoder; return decoder;
} }
MWBase::SoundPtr SoundManager::playVoice(DecoderPtr decoder, const osg::Vec3f &pos, bool playlocal)
{
MWBase::World* world = MWBase::Environment::get().getWorld();
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);
float basevol = volumeFromType(Play_TypeVoice);
if(playlocal)
return mOutput->streamSound(decoder,
basevol, 1.0f, Play_Normal|Play_TypeVoice|Play_2D
);
return mOutput->streamSound3D(decoder,
pos, 1.0f, basevol, 1.0f, minDistance, maxDistance,
Play_Normal|Play_TypeVoice|Play_3D
);
}
// Gets the combined volume settings for the given sound type // Gets the combined volume settings for the given sound type
float SoundManager::volumeFromType(PlayType type) const float SoundManager::volumeFromType(PlayType type) const
@ -375,16 +381,7 @@ namespace MWSound
return; return;
try try
{ {
MWBase::World* world = MWBase::Environment::get().getWorld();
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/"+filename; std::string voicefile = "Sound/"+filename;
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());
@ -392,17 +389,13 @@ namespace MWSound
mVFS->normalizeFilename(voicefile); mVFS->normalizeFilename(voicefile);
DecoderPtr decoder = loadVoice(voicefile, &loudness); DecoderPtr decoder = loadVoice(voicefile, &loudness);
MWBase::SoundPtr sound; if(!loudness->isReady())
if(ptr == MWMechanics::getPlayer()) mPendingSaySounds[ptr] = std::make_pair(decoder, loudness);
sound = mOutput->streamSound(decoder,
basevol, 1.0f, Play_Normal|Play_TypeVoice|Play_2D
);
else else
sound = mOutput->streamSound3D(decoder, {
objpos, 1.0f, basevol, 1.0f, minDistance, maxDistance, MWBase::SoundPtr sound = playVoice(decoder, objpos, (ptr == MWMechanics::getPlayer()));
Play_Normal|Play_TypeVoice|Play_3D mActiveSaySounds[ptr] = std::make_pair(sound, loudness);
); }
mActiveSaySounds[ptr] = std::make_pair(sound, loudness);
} }
catch(std::exception &e) catch(std::exception &e)
{ {
@ -432,16 +425,18 @@ namespace MWSound
try try
{ {
std::string voicefile = "Sound/"+filename; std::string voicefile = "Sound/"+filename;
float basevol = volumeFromType(Play_TypeVoice);
Sound_Loudness *loudness; Sound_Loudness *loudness;
mVFS->normalizeFilename(voicefile); mVFS->normalizeFilename(voicefile);
DecoderPtr decoder = loadVoice(voicefile, &loudness); DecoderPtr decoder = loadVoice(voicefile, &loudness);
MWBase::SoundPtr sound = mOutput->streamSound(decoder, if(!loudness->isReady())
basevol, 1.0f, Play_Normal|Play_TypeVoice|Play_2D mPendingSaySounds[MWWorld::Ptr()] = std::make_pair(decoder, loudness);
); else
mActiveSaySounds[MWWorld::Ptr()] = std::make_pair(sound, loudness); {
MWBase::SoundPtr sound = playVoice(decoder, osg::Vec3f(), true);
mActiveSaySounds[MWWorld::Ptr()] = std::make_pair(sound, loudness);
}
} }
catch(std::exception &e) catch(std::exception &e)
{ {
@ -456,8 +451,9 @@ namespace MWSound
{ {
if(snditer->second.first->isPlaying()) if(snditer->second.first->isPlaying())
return false; return false;
return true;
} }
return true; return mPendingSaySounds.find(ptr) == mPendingSaySounds.end();
} }
void SoundManager::stopSay(const MWWorld::Ptr &ptr) void SoundManager::stopSay(const MWWorld::Ptr &ptr)
@ -468,6 +464,7 @@ namespace MWSound
snditer->second.first->stop(); snditer->second.first->stop();
mActiveSaySounds.erase(snditer); mActiveSaySounds.erase(snditer);
} }
mPendingSaySounds.erase(ptr);
} }
@ -832,6 +829,35 @@ namespace MWSound
++snditer; ++snditer;
} }
SayDecoderMap::iterator penditer = mPendingSaySounds.begin();
while(penditer != mPendingSaySounds.end())
{
Sound_Loudness *loudness = penditer->second.second;
if(loudness->isReady())
{
try {
DecoderPtr decoder = penditer->second.first;
decoder->rewind();
MWWorld::Ptr ptr = penditer->first;
const ESM::Position &pos = ptr.getRefData().getPosition();
const osg::Vec3f objpos(pos.asVec3());
MWBase::SoundPtr sound = playVoice(decoder,
objpos, (ptr == MWMechanics::getPlayer())
);
mActiveSaySounds[ptr] = std::make_pair(sound, loudness);
}
catch(std::exception &e) {
std::cerr<< "Sound Error: "<<e.what() <<std::endl;
}
mPendingSaySounds.erase(penditer++);
}
else
++penditer;
}
SaySoundMap::iterator sayiter = mActiveSaySounds.begin(); SaySoundMap::iterator sayiter = mActiveSaySounds.begin();
while(sayiter != mActiveSaySounds.end()) while(sayiter != mActiveSaySounds.end())
{ {
@ -947,6 +973,13 @@ namespace MWSound
mActiveSaySounds.erase(sayiter); mActiveSaySounds.erase(sayiter);
mActiveSaySounds[updated] = sndlist; mActiveSaySounds[updated] = sndlist;
} }
SayDecoderMap::iterator penditer = mPendingSaySounds.find(old);
if(penditer != mPendingSaySounds.end())
{
DecoderLoudnessPair dl = penditer->second;
mPendingSaySounds.erase(penditer);
mPendingSaySounds[updated] = dl;
}
} }
// Default readAll implementation, for decoders that can't do anything // Default readAll implementation, for decoders that can't do anything
@ -1033,6 +1066,7 @@ namespace MWSound
for(;sayiter != mActiveSaySounds.end();++sayiter) for(;sayiter != mActiveSaySounds.end();++sayiter)
sayiter->second.first->stop(); sayiter->second.first->stop();
mActiveSaySounds.clear(); mActiveSaySounds.clear();
mPendingSaySounds.clear();
mUnderwaterSound.reset(); mUnderwaterSound.reset();
stopMusic(); stopMusic();
} }

@ -90,6 +90,10 @@ namespace MWSound
typedef std::map<MWWorld::Ptr,SoundLoudnessPair> SaySoundMap; typedef std::map<MWWorld::Ptr,SoundLoudnessPair> SaySoundMap;
SaySoundMap mActiveSaySounds; SaySoundMap mActiveSaySounds;
typedef std::pair<DecoderPtr,Sound_Loudness*> DecoderLoudnessPair;
typedef std::map<MWWorld::Ptr,DecoderLoudnessPair> SayDecoderMap;
SayDecoderMap mPendingSaySounds;
MWBase::SoundPtr mUnderwaterSound; MWBase::SoundPtr mUnderwaterSound;
bool mListenerUnderwater; bool mListenerUnderwater;
@ -104,10 +108,12 @@ namespace MWSound
Sound_Buffer *lookupSound(const std::string &soundId) const; Sound_Buffer *lookupSound(const std::string &soundId) const;
Sound_Buffer *loadSound(const std::string &soundId); Sound_Buffer *loadSound(const std::string &soundId);
// Ensures the loudness/"lip" data is loaded, and returns a decoder to // Ensures the loudness/"lip" data gets loaded, and returns a decoder
// start streaming // to start streaming
DecoderPtr loadVoice(const std::string &voicefile, Sound_Loudness **lipdata); DecoderPtr loadVoice(const std::string &voicefile, Sound_Loudness **lipdata);
MWBase::SoundPtr playVoice(DecoderPtr decoder, const osg::Vec3f &pos, bool playlocal);
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);
void updateSounds(float duration); void updateSounds(float duration);

Loading…
Cancel
Save