diff --git a/apps/openmw/mwsound/openal_output.cpp b/apps/openmw/mwsound/openal_output.cpp index a984fffa9..b71fe4d80 100644 --- a/apps/openmw/mwsound/openal_output.cpp +++ b/apps/openmw/mwsound/openal_output.cpp @@ -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 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 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(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(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(*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 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(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 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(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(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) { } diff --git a/apps/openmw/mwsound/openal_output.hpp b/apps/openmw/mwsound/openal_output.hpp index 755a0e5b6..8af46a478 100644 --- a/apps/openmw/mwsound/openal_output.hpp +++ b/apps/openmw/mwsound/openal_output.hpp @@ -16,12 +16,6 @@ namespace MWSound class SoundManager; class Sound; - struct CachedSound - { - ALuint mALBuffer; - std::vector mLoudnessVector; - }; - class OpenAL_Output : public Sound_Output { ALCdevice *mDevice; @@ -29,33 +23,24 @@ namespace MWSound typedef std::deque IDDq; IDDq mFreeSources; - IDDq mUnusedBuffers; - - typedef std::map NameMap; - NameMap mBufferCache; - - typedef std::map IDRefMap; - IDRefMap mBufferRefs; - - uint64_t mBufferCacheMemSize; typedef std::vector SoundVec; SoundVec mActiveSounds; - const CachedSound& getBuffer(const std::string &fname); - void bufferFinished(ALuint buffer); - Environment mLastEnvironment; virtual std::vector 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); diff --git a/apps/openmw/mwsound/sound_buffer.hpp b/apps/openmw/mwsound/sound_buffer.hpp index fcef7963a..37f49e518 100644 --- a/apps/openmw/mwsound/sound_buffer.hpp +++ b/apps/openmw/mwsound/sound_buffer.hpp @@ -4,6 +4,7 @@ #include #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) { } }; } diff --git a/apps/openmw/mwsound/sound_output.hpp b/apps/openmw/mwsound/sound_output.hpp index a0c6fb17b..cbda19d59 100644 --- a/apps/openmw/mwsound/sound_output.hpp +++ b/apps/openmw/mwsound/sound_output.hpp @@ -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; diff --git a/apps/openmw/mwsound/soundmanagerimp.cpp b/apps/openmw/mwsound/soundmanagerimp.cpp index 084524074..820a9841d 100644 --- a/apps/openmw/mwsound/soundmanagerimp.cpp +++ b/apps/openmw/mwsound/soundmanagerimp.cpp @@ -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);