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

@ -16,12 +16,6 @@ namespace MWSound
class SoundManager;
class Sound;
struct CachedSound
{
ALuint mALBuffer;
std::vector<float> mLoudnessVector;
};
class OpenAL_Output : public Sound_Output
{
ALCdevice *mDevice;
@ -29,33 +23,24 @@ namespace MWSound
typedef std::deque<ALuint> IDDq;
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;
SoundVec mActiveSounds;
const CachedSound& getBuffer(const std::string &fname);
void bufferFinished(ALuint buffer);
Environment mLastEnvironment;
virtual std::vector<std::string> enumerate();
virtual void init(const std::string &devname="");
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.
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.
virtual MWBase::SoundPtr playSound3D(const std::string &fname, const osg::Vec3f &pos,
float vol, float basevol, float pitch, float min, float max, int flags, float offset, bool extractLoudness=false);
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);
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);

@ -4,6 +4,7 @@
#include <string>
#include "soundmanagerimp.hpp"
#include "sound_output.hpp"
#include "../mwworld/ptr.hpp"
@ -17,8 +18,10 @@ namespace MWSound
float mVolume;
float mMinDist, mMaxDist;
Sound_Handle mHandle;
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;
class Sound;
// An opaque handle for the implementation's sound buffers.
typedef void *Sound_Handle;
class Sound_Output
{
SoundManager &mManager;
@ -22,11 +25,14 @@ namespace MWSound
virtual void init(const std::string &devname="") = 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.
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.
virtual MWBase::SoundPtr playSound3D(const std::string &fname, const osg::Vec3f &pos,
float vol, float basevol, float pitch, float min, float max, int flags, float offset, bool extractLoudness=false) = 0;
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) = 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;

@ -92,6 +92,16 @@ namespace MWSound
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();
mActiveSounds.clear();
mMusic.reset();
@ -144,7 +154,11 @@ namespace MWSound
soundId, Sound_Buffer("Sound/"+snd->mSound, volume, min, max)
)).first;
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;
}
@ -278,6 +292,7 @@ namespace MWSound
return;
try
{
#if 0
float basevol = volumeFromType(Play_TypeVoice);
std::string filePath = "sound/"+filename;
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,
minDistance, maxDistance, Play_Normal|Play_TypeVoice, 0, true);
mActiveSounds[sound] = std::make_pair(ptr, std::string("_say_sound"));
#else
throw std::runtime_error("say disabled");
#endif
}
catch(std::exception &e)
{
@ -325,11 +343,15 @@ namespace MWSound
return;
try
{
#if 0
float basevol = volumeFromType(Play_TypeVoice);
std::string filePath = "Sound/"+filename;
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"));
#else
throw std::runtime_error("say disabled");
#endif
}
catch(std::exception &e)
{
@ -385,7 +407,7 @@ namespace MWSound
const Sound_Buffer *sfx = lookup(Misc::StringUtils::lowerCase(soundId));
float basevol = volumeFromType(type);
sound = mOutput->playSound(sfx->mResourceName,
sound = mOutput->playSound(sfx->mHandle,
volume * sfx->mVolume, basevol, pitch, mode|type, offset
);
mActiveSounds[sound] = std::make_pair(MWWorld::Ptr(), soundId);
@ -414,7 +436,7 @@ namespace MWSound
if((mode&Play_RemoveAtDistance) && (mListenerPos-objpos).length2() > 2000*2000)
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
);
if((mode&Play_NoTrack))
@ -441,7 +463,7 @@ namespace MWSound
const Sound_Buffer *sfx = lookup(Misc::StringUtils::lowerCase(soundId));
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
);
mActiveSounds[sound] = std::make_pair(MWWorld::Ptr(), soundId);

Loading…
Cancel
Save