Load the sound as needed and pass it directly to the play methods

This breaks say sounds, loudness handling, and the cache limit. Fixes are
forthcoming.
openmw-38
Chris Robinson 9 years ago
parent e2beefd8b5
commit 4571218827

@ -19,6 +19,9 @@
#define ALC_ALL_DEVICES_SPECIFIER 0x1013 #define ALC_ALL_DEVICES_SPECIFIER 0x1013
#endif #endif
#define MAKE_PTRID(id) ((void*)(uintptr_t)id)
#define GET_PTRID(ptr) ((ALuint)(uintptr_t)ptr)
namespace namespace
{ {
const int loudnessFPS = 20; // loudness values per second of audio const int loudnessFPS = 20; // loudness values per second of audio
@ -545,7 +548,6 @@ OpenAL_Sound::~OpenAL_Sound()
alSourcei(mSource, AL_BUFFER, 0); alSourcei(mSource, AL_BUFFER, 0);
mOutput.mFreeSources.push_back(mSource); mOutput.mFreeSources.push_back(mSource);
mOutput.bufferFinished(mBuffer);
mOutput.mActiveSounds.erase(std::find(mOutput.mActiveSounds.begin(), mOutput.mActiveSounds.erase(std::find(mOutput.mActiveSounds.begin(),
mOutput.mActiveSounds.end(), this)); mOutput.mActiveSounds.end(), this));
@ -737,14 +739,6 @@ void OpenAL_Output::deinit()
alDeleteSources(1, &mFreeSources[i]); alDeleteSources(1, &mFreeSources[i]);
mFreeSources.clear(); mFreeSources.clear();
mBufferRefs.clear();
mUnusedBuffers.clear();
while(!mBufferCache.empty())
{
alDeleteBuffers(1, &mBufferCache.begin()->second.mALBuffer);
mBufferCache.erase(mBufferCache.begin());
}
alcMakeContextCurrent(0); alcMakeContextCurrent(0);
if(mContext) if(mContext)
alcDestroyContext(mContext); alcDestroyContext(mContext);
@ -757,32 +751,10 @@ void OpenAL_Output::deinit()
} }
const CachedSound& OpenAL_Output::getBuffer(const std::string &fname) Sound_Handle OpenAL_Output::loadSound(const std::string &fname)
{ {
ALuint buf = 0;
NameMap::iterator iditer = mBufferCache.find(fname);
if(iditer != mBufferCache.end())
{
buf = iditer->second.mALBuffer;
if(mBufferRefs[buf]++ == 0)
{
IDDq::iterator iter = std::find(mUnusedBuffers.begin(),
mUnusedBuffers.end(), buf);
if(iter != mUnusedBuffers.end())
mUnusedBuffers.erase(iter);
}
return iditer->second;
}
throwALerror(); throwALerror();
std::vector<char> data;
ChannelConfig chans;
SampleType type;
ALenum format;
int srate;
DecoderPtr decoder = mManager.getDecoder(); DecoderPtr decoder = mManager.getDecoder();
// Workaround: Bethesda at some point converted some of the files to mp3, but the references were kept as .wav. // Workaround: Bethesda at some point converted some of the files to mp3, but the references were kept as .wav.
std::string file = fname; std::string file = fname;
@ -794,87 +766,70 @@ const CachedSound& OpenAL_Output::getBuffer(const std::string &fname)
} }
decoder->open(file); decoder->open(file);
std::vector<char> data;
ChannelConfig chans;
SampleType type;
ALenum format;
int srate;
decoder->getInfo(&srate, &chans, &type); decoder->getInfo(&srate, &chans, &type);
format = getALFormat(chans, type); format = getALFormat(chans, type);
decoder->readAll(data); decoder->readAll(data);
decoder->close(); decoder->close();
CachedSound cached; //analyzeLoudness(data, srate, chans, type, cached.mLoudnessVector, static_cast<float>(loudnessFPS));
analyzeLoudness(data, srate, chans, type, cached.mLoudnessVector, static_cast<float>(loudnessFPS));
alGenBuffers(1, &buf);
throwALerror();
alBufferData(buf, format, &data[0], data.size(), srate);
mBufferRefs[buf] = 1;
cached.mALBuffer = buf;
mBufferCache[fname] = cached;
ALint bufsize = 0; ALuint buf = 0;
alGetBufferi(buf, AL_SIZE, &bufsize); try {
mBufferCacheMemSize += bufsize; alGenBuffers(1, &buf);
alBufferData(buf, format, &data[0], data.size(), srate);
// NOTE: Max buffer cache: 15MB throwALerror();
while(mBufferCacheMemSize > 15*1024*1024)
{
if(mUnusedBuffers.empty())
{
std::cout <<"No more unused buffers to clear!"<< std::endl;
break;
}
ALuint oldbuf = mUnusedBuffers.front();
mUnusedBuffers.pop_front();
NameMap::iterator nameiter = mBufferCache.begin();
while(nameiter != mBufferCache.end())
{
if(nameiter->second.mALBuffer == oldbuf)
mBufferCache.erase(nameiter++);
else
++nameiter;
}
bufsize = 0;
alGetBufferi(oldbuf, AL_SIZE, &bufsize);
alDeleteBuffers(1, &oldbuf);
mBufferCacheMemSize -= bufsize;
} }
catch(...) {
return mBufferCache[fname]; if(buf && alIsBuffer(buf))
alDeleteBuffers(1, &buf);
throw;
}
return MAKE_PTRID(buf);
} }
void OpenAL_Output::bufferFinished(ALuint buf) void OpenAL_Output::unloadSound(Sound_Handle data)
{ {
if(mBufferRefs.at(buf)-- == 1) ALuint buffer = GET_PTRID(data);
// Make sure no sources are playing this buffer before unloading it.
SoundVec::const_iterator iter = mActiveSounds.begin();
for(;iter != mActiveSounds.end();++iter)
{ {
mBufferRefs.erase(mBufferRefs.find(buf)); OpenAL_Sound *sound = dynamic_cast<OpenAL_Sound*>(*iter);
mUnusedBuffers.push_back(buf); if(sound && sound->mSource && sound->mBuffer == buffer)
{
alSourceStop(sound->mSource);
alSourcei(sound->mSource, AL_BUFFER, 0);
sound->mBuffer = 0;
}
} }
alDeleteBuffers(1, &buffer);
} }
MWBase::SoundPtr OpenAL_Output::playSound(const std::string &fname, float vol, float basevol, float pitch, int flags,float offset)
MWBase::SoundPtr OpenAL_Output::playSound(Sound_Handle data, float vol, float basevol, float pitch, int flags,float offset)
{ {
boost::shared_ptr<OpenAL_Sound> sound; boost::shared_ptr<OpenAL_Sound> sound;
ALuint src=0, buf=0; ALuint src=0;
if(mFreeSources.empty()) if(mFreeSources.empty())
fail("No free sources"); fail("No free sources");
src = mFreeSources.front(); src = mFreeSources.front();
mFreeSources.pop_front(); mFreeSources.pop_front();
try ALuint buffer = GET_PTRID(data);
{ try {
buf = getBuffer(fname).mALBuffer; sound.reset(new OpenAL_Sound(*this, src, buffer, osg::Vec3f(0.f, 0.f, 0.f), vol, basevol, pitch, 1.0f, 1000.0f, flags));
sound.reset(new OpenAL_Sound(*this, src, buf, osg::Vec3f(0.f, 0.f, 0.f), vol, basevol, pitch, 1.0f, 1000.0f, flags));
} }
catch(std::exception&) catch(std::exception&)
{ {
mFreeSources.push_back(src); mFreeSources.push_back(src);
if(buf && alIsBuffer(buf))
bufferFinished(buf);
alGetError();
throw; throw;
} }
@ -884,7 +839,7 @@ MWBase::SoundPtr OpenAL_Output::playSound(const std::string &fname, float vol, f
if(offset>1) if(offset>1)
offset=1; offset=1;
alSourcei(src, AL_BUFFER, buf); alSourcei(src, AL_BUFFER, buffer);
alSourcef(src, AL_SEC_OFFSET, static_cast<ALfloat>(sound->getLength()*offset / pitch)); alSourcef(src, AL_SEC_OFFSET, static_cast<ALfloat>(sound->getLength()*offset / pitch));
alSourcePlay(src); alSourcePlay(src);
throwALerror(); throwALerror();
@ -892,32 +847,24 @@ MWBase::SoundPtr OpenAL_Output::playSound(const std::string &fname, float vol, f
return sound; return sound;
} }
MWBase::SoundPtr OpenAL_Output::playSound3D(const std::string &fname, const osg::Vec3f &pos, float vol, float basevol, float pitch, MWBase::SoundPtr OpenAL_Output::playSound3D(Sound_Handle data, const osg::Vec3f &pos, float vol, float basevol, float pitch,
float min, float max, int flags, float offset, bool extractLoudness) float min, float max, int flags, float offset)
{ {
boost::shared_ptr<OpenAL_Sound> sound; boost::shared_ptr<OpenAL_Sound> sound;
ALuint src=0, buf=0; ALuint src=0;
if(mFreeSources.empty()) if(mFreeSources.empty())
fail("No free sources"); fail("No free sources");
src = mFreeSources.front(); src = mFreeSources.front();
mFreeSources.pop_front(); mFreeSources.pop_front();
try ALuint buffer = GET_PTRID(data);
{ try {
const CachedSound& cached = getBuffer(fname); sound.reset(new OpenAL_Sound3D(*this, src, buffer, pos, vol, basevol, pitch, min, max, flags));
buf = cached.mALBuffer;
sound.reset(new OpenAL_Sound3D(*this, src, buf, pos, vol, basevol, pitch, min, max, flags));
if (extractLoudness)
sound->setLoudnessVector(cached.mLoudnessVector, static_cast<float>(loudnessFPS));
} }
catch(std::exception&) catch(std::exception&)
{ {
mFreeSources.push_back(src); mFreeSources.push_back(src);
if(buf && alIsBuffer(buf))
bufferFinished(buf);
alGetError();
throw; throw;
} }
@ -928,7 +875,7 @@ MWBase::SoundPtr OpenAL_Output::playSound3D(const std::string &fname, const osg:
if(offset>1) if(offset>1)
offset=1; offset=1;
alSourcei(src, AL_BUFFER, buf); alSourcei(src, AL_BUFFER, buffer);
alSourcef(src, AL_SEC_OFFSET, static_cast<ALfloat>(sound->getLength()*offset / pitch)); alSourcef(src, AL_SEC_OFFSET, static_cast<ALfloat>(sound->getLength()*offset / pitch));
alSourcePlay(src); alSourcePlay(src);
@ -1041,8 +988,8 @@ void OpenAL_Output::resumeSounds(int types)
OpenAL_Output::OpenAL_Output(SoundManager &mgr) OpenAL_Output::OpenAL_Output(SoundManager &mgr)
: Sound_Output(mgr), mDevice(0), mContext(0), mBufferCacheMemSize(0), : Sound_Output(mgr), mDevice(0), mContext(0), mLastEnvironment(Env_Normal),
mLastEnvironment(Env_Normal), mStreamThread(new StreamThread) mStreamThread(new StreamThread)
{ {
} }

@ -16,12 +16,6 @@ namespace MWSound
class SoundManager; class SoundManager;
class Sound; class Sound;
struct CachedSound
{
ALuint mALBuffer;
std::vector<float> mLoudnessVector;
};
class OpenAL_Output : public Sound_Output class OpenAL_Output : public Sound_Output
{ {
ALCdevice *mDevice; ALCdevice *mDevice;
@ -29,33 +23,24 @@ namespace MWSound
typedef std::deque<ALuint> IDDq; typedef std::deque<ALuint> IDDq;
IDDq mFreeSources; IDDq mFreeSources;
IDDq mUnusedBuffers;
typedef std::map<std::string,CachedSound> NameMap;
NameMap mBufferCache;
typedef std::map<ALuint,ALuint> IDRefMap;
IDRefMap mBufferRefs;
uint64_t mBufferCacheMemSize;
typedef std::vector<Sound*> SoundVec; typedef std::vector<Sound*> SoundVec;
SoundVec mActiveSounds; SoundVec mActiveSounds;
const CachedSound& getBuffer(const std::string &fname);
void bufferFinished(ALuint buffer);
Environment mLastEnvironment; Environment mLastEnvironment;
virtual std::vector<std::string> enumerate(); virtual std::vector<std::string> enumerate();
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);
virtual void unloadSound(Sound_Handle data);
/// @param offset Value from [0,1] meaning from which fraction the sound the playback starts. /// @param offset Value from [0,1] meaning from which fraction the sound the playback starts.
virtual MWBase::SoundPtr playSound(const std::string &fname, float vol, float basevol, float pitch, int flags, float offset); virtual MWBase::SoundPtr playSound(Sound_Handle data, float vol, float basevol, float pitch, int flags, float offset);
/// @param offset Value from [0,1] meaning from which fraction the sound the playback starts. /// @param offset Value from [0,1] meaning from which fraction the sound the playback starts.
virtual MWBase::SoundPtr playSound3D(const std::string &fname, const osg::Vec3f &pos, virtual MWBase::SoundPtr playSound3D(Sound_Handle data, const osg::Vec3f &pos,
float vol, float basevol, float pitch, float min, float max, int flags, float offset, bool extractLoudness=false); float vol, float basevol, float pitch, float min, float max, int flags, float offset);
virtual MWBase::SoundPtr streamSound(DecoderPtr decoder, float volume, float pitch, int flags); virtual MWBase::SoundPtr streamSound(DecoderPtr decoder, float volume, float pitch, int flags);
virtual void updateListener(const osg::Vec3f &pos, const osg::Vec3f &atdir, const osg::Vec3f &updir, Environment env); virtual void updateListener(const osg::Vec3f &pos, const osg::Vec3f &atdir, const osg::Vec3f &updir, Environment env);

@ -4,6 +4,7 @@
#include <string> #include <string>
#include "soundmanagerimp.hpp" #include "soundmanagerimp.hpp"
#include "sound_output.hpp"
#include "../mwworld/ptr.hpp" #include "../mwworld/ptr.hpp"
@ -17,8 +18,10 @@ namespace MWSound
float mVolume; float mVolume;
float mMinDist, mMaxDist; float mMinDist, mMaxDist;
Sound_Handle mHandle;
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) : mResourceName(resname), mVolume(volume), mMinDist(mindist), mMaxDist(maxdist), mHandle(0)
{ } { }
}; };
} }

@ -14,6 +14,9 @@ namespace MWSound
struct Sound_Decoder; struct Sound_Decoder;
class Sound; class Sound;
// An opaque handle for the implementation's sound buffers.
typedef void *Sound_Handle;
class Sound_Output class Sound_Output
{ {
SoundManager &mManager; SoundManager &mManager;
@ -22,11 +25,14 @@ 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) = 0;
virtual void unloadSound(Sound_Handle data) = 0;
/// @param offset Value from [0,1] meaning from which fraction the sound the playback starts. /// @param offset Value from [0,1] meaning from which fraction the sound the playback starts.
virtual MWBase::SoundPtr playSound(const std::string &fname, float vol, float basevol, float pitch, int flags, float offset) = 0; virtual MWBase::SoundPtr playSound(Sound_Handle data, float vol, float basevol, float pitch, int flags, float offset) = 0;
/// @param offset Value from [0,1] meaning from which fraction the sound the playback starts. /// @param offset Value from [0,1] meaning from which fraction the sound the playback starts.
virtual MWBase::SoundPtr playSound3D(const std::string &fname, const osg::Vec3f &pos, virtual MWBase::SoundPtr playSound3D(Sound_Handle data, const osg::Vec3f &pos,
float vol, float basevol, float pitch, float min, float max, int flags, float offset, bool extractLoudness=false) = 0; float vol, float basevol, float pitch, float min, float max, int flags, float offset) = 0;
virtual MWBase::SoundPtr streamSound(DecoderPtr decoder, float volume, float pitch, int flags) = 0; virtual MWBase::SoundPtr streamSound(DecoderPtr decoder, float volume, float pitch, int flags) = 0;
virtual void updateListener(const osg::Vec3f &pos, const osg::Vec3f &atdir, const osg::Vec3f &updir, Environment env) = 0; virtual void updateListener(const osg::Vec3f &pos, const osg::Vec3f &atdir, const osg::Vec3f &updir, Environment env) = 0;

@ -92,6 +92,16 @@ namespace MWSound
SoundManager::~SoundManager() SoundManager::~SoundManager()
{ {
if(mOutput->isInitialized())
{
NameBufferMap::iterator sfxiter = mSoundBuffers.begin();
for(;sfxiter != mSoundBuffers.end();++sfxiter)
{
if(sfxiter->second.mHandle)
mOutput->unloadSound(sfxiter->second.mHandle);
sfxiter->second.mHandle = 0;
}
}
mUnderwaterSound.reset(); mUnderwaterSound.reset();
mActiveSounds.clear(); mActiveSounds.clear();
mMusic.reset(); mMusic.reset();
@ -144,7 +154,11 @@ namespace MWSound
soundId, Sound_Buffer("Sound/"+snd->mSound, volume, min, max) soundId, Sound_Buffer("Sound/"+snd->mSound, volume, min, max)
)).first; )).first;
mVFS->normalizeFilename(sfxiter->second.mResourceName); mVFS->normalizeFilename(sfxiter->second.mResourceName);
sfxiter->second.mHandle = mOutput->loadSound(sfxiter->second.mResourceName);
} }
else if(!sfxiter->second.mHandle)
sfxiter->second.mHandle = mOutput->loadSound(sfxiter->second.mResourceName);
return &sfxiter->second; return &sfxiter->second;
} }
@ -278,6 +292,7 @@ namespace MWSound
return; return;
try try
{ {
#if 0
float basevol = volumeFromType(Play_TypeVoice); float basevol = volumeFromType(Play_TypeVoice);
std::string filePath = "sound/"+filename; std::string filePath = "sound/"+filename;
const ESM::Position &pos = ptr.getRefData().getPosition(); const ESM::Position &pos = ptr.getRefData().getPosition();
@ -297,6 +312,9 @@ namespace MWSound
MWBase::SoundPtr sound = mOutput->playSound3D(filePath, objpos, 1.0f, basevol, 1.0f, MWBase::SoundPtr sound = mOutput->playSound3D(filePath, objpos, 1.0f, basevol, 1.0f,
minDistance, maxDistance, Play_Normal|Play_TypeVoice, 0, true); minDistance, maxDistance, Play_Normal|Play_TypeVoice, 0, true);
mActiveSounds[sound] = std::make_pair(ptr, std::string("_say_sound")); mActiveSounds[sound] = std::make_pair(ptr, std::string("_say_sound"));
#else
throw std::runtime_error("say disabled");
#endif
} }
catch(std::exception &e) catch(std::exception &e)
{ {
@ -325,11 +343,15 @@ namespace MWSound
return; return;
try try
{ {
#if 0
float basevol = volumeFromType(Play_TypeVoice); float basevol = volumeFromType(Play_TypeVoice);
std::string filePath = "Sound/"+filename; std::string filePath = "Sound/"+filename;
MWBase::SoundPtr sound = mOutput->playSound(filePath, 1.0f, basevol, 1.0f, Play_Normal|Play_TypeVoice, 0); MWBase::SoundPtr sound = mOutput->playSound(filePath, 1.0f, basevol, 1.0f, Play_Normal|Play_TypeVoice, 0);
mActiveSounds[sound] = std::make_pair(MWWorld::Ptr(), std::string("_say_sound")); mActiveSounds[sound] = std::make_pair(MWWorld::Ptr(), std::string("_say_sound"));
#else
throw std::runtime_error("say disabled");
#endif
} }
catch(std::exception &e) catch(std::exception &e)
{ {
@ -385,7 +407,7 @@ namespace MWSound
const Sound_Buffer *sfx = lookup(Misc::StringUtils::lowerCase(soundId)); const Sound_Buffer *sfx = lookup(Misc::StringUtils::lowerCase(soundId));
float basevol = volumeFromType(type); float basevol = volumeFromType(type);
sound = mOutput->playSound(sfx->mResourceName, sound = mOutput->playSound(sfx->mHandle,
volume * sfx->mVolume, basevol, pitch, mode|type, offset volume * sfx->mVolume, basevol, pitch, mode|type, offset
); );
mActiveSounds[sound] = std::make_pair(MWWorld::Ptr(), soundId); mActiveSounds[sound] = std::make_pair(MWWorld::Ptr(), soundId);
@ -414,7 +436,7 @@ namespace MWSound
if((mode&Play_RemoveAtDistance) && (mListenerPos-objpos).length2() > 2000*2000) if((mode&Play_RemoveAtDistance) && (mListenerPos-objpos).length2() > 2000*2000)
return MWBase::SoundPtr(); return MWBase::SoundPtr();
sound = mOutput->playSound3D(sfx->mResourceName, sound = mOutput->playSound3D(sfx->mHandle,
objpos, volume * sfx->mVolume, basevol, pitch, sfx->mMinDist, sfx->mMaxDist, mode|type, offset objpos, volume * sfx->mVolume, basevol, pitch, sfx->mMinDist, sfx->mMaxDist, mode|type, offset
); );
if((mode&Play_NoTrack)) if((mode&Play_NoTrack))
@ -441,7 +463,7 @@ namespace MWSound
const Sound_Buffer *sfx = lookup(Misc::StringUtils::lowerCase(soundId)); const Sound_Buffer *sfx = lookup(Misc::StringUtils::lowerCase(soundId));
float basevol = volumeFromType(type); float basevol = volumeFromType(type);
sound = mOutput->playSound3D(sfx->mResourceName, sound = mOutput->playSound3D(sfx->mHandle,
initialPos, volume * sfx->mVolume, basevol, pitch, sfx->mMinDist, sfx->mMaxDist, mode|type, offset initialPos, volume * sfx->mVolume, basevol, pitch, sfx->mMinDist, sfx->mMaxDist, mode|type, offset
); );
mActiveSounds[sound] = std::make_pair(MWWorld::Ptr(), soundId); mActiveSounds[sound] = std::make_pair(MWWorld::Ptr(), soundId);

Loading…
Cancel
Save