diff --git a/apps/openmw/mwbase/soundmanager.hpp b/apps/openmw/mwbase/soundmanager.hpp index a91149777..46f245c46 100644 --- a/apps/openmw/mwbase/soundmanager.hpp +++ b/apps/openmw/mwbase/soundmanager.hpp @@ -15,6 +15,7 @@ namespace MWWorld namespace MWSound { class Sound; + class Stream; struct Sound_Decoder; typedef boost::shared_ptr DecoderPtr; } @@ -22,6 +23,7 @@ namespace MWSound namespace MWBase { typedef boost::shared_ptr SoundPtr; + typedef boost::shared_ptr SoundStreamPtr; /// \brief Interface for sound manager (implemented in MWSound) class SoundManager @@ -104,9 +106,17 @@ namespace MWBase /// and get an average loudness value (scale [0,1]) at the current time position. /// If the actor is not saying anything, returns 0. - virtual SoundPtr playTrack(const MWSound::DecoderPtr& decoder, PlayType type) = 0; + virtual SoundStreamPtr playTrack(const MWSound::DecoderPtr& decoder, PlayType type) = 0; ///< Play a 2D audio track, using a custom decoder + virtual void stopTrack(SoundStreamPtr stream) = 0; + ///< Stop the given audio track from playing + + virtual double getTrackTimeDelay(SoundStreamPtr stream) = 0; + ///< Retives the time delay, in seconds, of the audio track (must be a sound + /// returned by \ref playTrack). Only intended to be called by the track + /// decoder's read method. + virtual SoundPtr playSound(const std::string& soundId, float volume, float pitch, PlayType type=Play_TypeSfx, PlayMode mode=Play_Normal, float offset=0) = 0; @@ -123,6 +133,9 @@ namespace MWBase float volume, float pitch, PlayType type=Play_TypeSfx, PlayMode mode=Play_Normal, float offset=0) = 0; ///< Play a 3D sound at \a initialPos. If the sound should be moving, it must be updated using Sound::setPosition. + virtual void stopSound(SoundPtr sound) = 0; + ///< Stop the given sound from playing + virtual void stopSound3D(const MWWorld::Ptr &reference, const std::string& soundId) = 0; ///< Stop the given object from playing the given sound, @@ -155,7 +168,7 @@ namespace MWBase virtual void setListenerPosDir(const osg::Vec3f &pos, const osg::Vec3f &dir, const osg::Vec3f &up) = 0; - virtual void updatePtr (const MWWorld::Ptr& old, const MWWorld::Ptr& updated) = 0; + virtual void updatePtr(const MWWorld::Ptr& old, const MWWorld::Ptr& updated) = 0; virtual void clear() = 0; }; diff --git a/apps/openmw/mwsound/movieaudiofactory.cpp b/apps/openmw/mwsound/movieaudiofactory.cpp index 925c966da..554b62d26 100644 --- a/apps/openmw/mwsound/movieaudiofactory.cpp +++ b/apps/openmw/mwsound/movieaudiofactory.cpp @@ -61,7 +61,7 @@ namespace MWSound virtual double getAudioClock() { return (double)getSampleOffset()/(double)mAVStream->codec->sample_rate - - mAudioTrack->getStreamDelay(); + MWBase::Environment::get().getSoundManager()->getTrackTimeDelay(mAudioTrack); } virtual void adjustAudioSettings(AVSampleFormat& sampleFormat, uint64_t& channelLayout, int& sampleRate) @@ -86,11 +86,13 @@ namespace MWSound public: ~MovieAudioDecoder() { + if(mAudioTrack.get()) + MWBase::Environment::get().getSoundManager()->stopTrack(mAudioTrack); mAudioTrack.reset(); mDecoderBridge.reset(); } - MWBase::SoundPtr mAudioTrack; + MWBase::SoundStreamPtr mAudioTrack; boost::shared_ptr mDecoderBridge; }; @@ -161,7 +163,8 @@ namespace MWSound boost::shared_ptr decoder(new MWSound::MovieAudioDecoder(videoState)); decoder->setupFormat(); - MWBase::SoundPtr sound = MWBase::Environment::get().getSoundManager()->playTrack(decoder->mDecoderBridge, MWBase::SoundManager::Play_TypeMovie); + MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); + MWBase::SoundStreamPtr sound = sndMgr->playTrack(decoder->mDecoderBridge, MWBase::SoundManager::Play_TypeMovie); if (!sound.get()) { decoder.reset(); diff --git a/apps/openmw/mwsound/openal_output.cpp b/apps/openmw/mwsound/openal_output.cpp index d4c890cee..54a5efb68 100644 --- a/apps/openmw/mwsound/openal_output.cpp +++ b/apps/openmw/mwsound/openal_output.cpp @@ -157,17 +157,14 @@ static ALenum getALFormat(ChannelConfig chans, SampleType type) // // A streaming OpenAL sound. // -class OpenAL_SoundStream : public Sound +class OpenAL_SoundStream { static const ALuint sNumBuffers = 6; static const ALfloat sBufferLength; -protected: - OpenAL_Output &mOutput; - +private: ALuint mSource; -private: ALuint mBuffers[sNumBuffers]; ALint mCurrentBufIdx; @@ -189,34 +186,18 @@ private: friend class OpenAL_Output; public: - OpenAL_SoundStream(OpenAL_Output &output, ALuint src, DecoderPtr decoder, const osg::Vec3f& pos, float vol, float basevol, float pitch, float mindist, float maxdist, int flags); - virtual ~OpenAL_SoundStream(); + OpenAL_SoundStream(ALuint src, DecoderPtr decoder); + ~OpenAL_SoundStream(); - virtual void stop(); - virtual bool isPlaying(); - virtual double getTimeOffset(); - virtual double getStreamDelay() const; - virtual void applyUpdates(); + bool isPlaying(); + double getStreamDelay() const; + double getStreamOffset() const; - void play(); bool process(); ALint refillQueue(); }; const ALfloat OpenAL_SoundStream::sBufferLength = 0.125f; -class OpenAL_SoundStream3D : public OpenAL_SoundStream -{ - OpenAL_SoundStream3D(const OpenAL_SoundStream3D &rhs); - OpenAL_SoundStream3D& operator=(const OpenAL_SoundStream3D &rhs); - -public: - OpenAL_SoundStream3D(OpenAL_Output &output, ALuint src, DecoderPtr decoder, const osg::Vec3f& pos, float vol, float basevol, float pitch, float mindist, float maxdist, int flags) - : OpenAL_SoundStream(output, src, decoder, pos, vol, basevol, pitch, mindist, maxdist, flags) - { } - - virtual void applyUpdates(); -}; - // // A background streaming thread (keeps active streams processed) @@ -329,13 +310,9 @@ private: }; -OpenAL_SoundStream::OpenAL_SoundStream(OpenAL_Output &output, ALuint src, DecoderPtr decoder, const osg::Vec3f& pos, float vol, float basevol, float pitch, float mindist, float maxdist, int flags) - : Sound(pos, vol, basevol, pitch, mindist, maxdist, flags) - , mOutput(output), mSource(src), mCurrentBufIdx(0), mFrameSize(0), mSilence(0) - , mDecoder(decoder), mIsFinished(true) +OpenAL_SoundStream::OpenAL_SoundStream(ALuint src, DecoderPtr decoder) + : mSource(src), mCurrentBufIdx(0), mFrameSize(0), mSilence(0), mDecoder(decoder), mIsFinished(false) { - throwALerror(); - alGenBuffers(sNumBuffers, mBuffers); throwALerror(); try @@ -358,8 +335,6 @@ OpenAL_SoundStream::OpenAL_SoundStream(OpenAL_Output &output, ALuint src, Decode mFrameSize = framesToBytes(1, chans, type); mBufferSize = static_cast(sBufferLength*srate); mBufferSize *= mFrameSize; - - mOutput.mActiveStreams.push_back(this); } catch(std::exception&) { @@ -367,51 +342,20 @@ OpenAL_SoundStream::OpenAL_SoundStream(OpenAL_Output &output, ALuint src, Decode alGetError(); throw; } + mIsFinished = false; } OpenAL_SoundStream::~OpenAL_SoundStream() { - mOutput.mStreamThread->remove(this); - - alSourceStop(mSource); - alSourcei(mSource, AL_BUFFER, 0); - - mOutput.mFreeSources.push_back(mSource); alDeleteBuffers(sNumBuffers, mBuffers); alGetError(); mDecoder->close(); - - mOutput.mActiveStreams.erase(std::find(mOutput.mActiveStreams.begin(), - mOutput.mActiveStreams.end(), this)); -} - -void OpenAL_SoundStream::play() -{ - alSourceStop(mSource); - alSourcei(mSource, AL_BUFFER, 0); - throwALerror(); - - mIsFinished = false; - mOutput.mStreamThread->add(this); -} - -void OpenAL_SoundStream::stop() -{ - mOutput.mStreamThread->remove(this); - mIsFinished = true; - - alSourceStop(mSource); - alSourcei(mSource, AL_BUFFER, 0); - throwALerror(); - - mDecoder->rewind(); } bool OpenAL_SoundStream::isPlaying() { ALint state; - boost::lock_guard lock(mOutput.mStreamThread->mMutex); alGetSourcei(mSource, AL_SOURCE_STATE, &state); throwALerror(); @@ -420,13 +364,12 @@ bool OpenAL_SoundStream::isPlaying() return !mIsFinished; } -double OpenAL_SoundStream::getTimeOffset() +double OpenAL_SoundStream::getStreamDelay() const { ALint state = AL_STOPPED; + double d = 0.0; ALint offset; - double t; - boost::lock_guard lock(mOutput.mStreamThread->mMutex); alGetSourcei(mSource, AL_SAMPLE_OFFSET, &offset); alGetSourcei(mSource, AL_SOURCE_STATE, &state); if(state == AL_PLAYING || state == AL_PAUSED) @@ -434,24 +377,18 @@ double OpenAL_SoundStream::getTimeOffset() ALint queued; alGetSourcei(mSource, AL_BUFFERS_QUEUED, &queued); ALint inqueue = mBufferSize/mFrameSize*queued - offset; - t = (double)(mDecoder->getSampleOffset() - inqueue) / (double)mSampleRate; - } - else - { - /* Underrun, or not started yet. The decoder offset is where we'll play - * next. */ - t = (double)mDecoder->getSampleOffset() / (double)mSampleRate; + d = (double)inqueue / (double)mSampleRate; } throwALerror(); - return t; + return d; } -double OpenAL_SoundStream::getStreamDelay() const +double OpenAL_SoundStream::getStreamOffset() const { ALint state = AL_STOPPED; - double d = 0.0; ALint offset; + double t; alGetSourcei(mSource, AL_SAMPLE_OFFSET, &offset); alGetSourcei(mSource, AL_SOURCE_STATE, &state); @@ -460,48 +397,17 @@ double OpenAL_SoundStream::getStreamDelay() const ALint queued; alGetSourcei(mSource, AL_BUFFERS_QUEUED, &queued); ALint inqueue = mBufferSize/mFrameSize*queued - offset; - d = (double)inqueue / (double)mSampleRate; - } - - throwALerror(); - return d; -} - -void OpenAL_SoundStream::updateAll(bool local) -{ - alSourcef(mSource, AL_REFERENCE_DISTANCE, mMinDistance); - alSourcef(mSource, AL_MAX_DISTANCE, mMaxDistance); - if(local) - { - alSourcef(mSource, AL_ROLLOFF_FACTOR, 0.0f); - alSourcei(mSource, AL_SOURCE_RELATIVE, AL_TRUE); + t = (double)(mDecoder->getSampleOffset() - inqueue) / (double)mSampleRate; } else { - alSourcef(mSource, AL_ROLLOFF_FACTOR, 1.0f); - alSourcei(mSource, AL_SOURCE_RELATIVE, AL_FALSE); - } - alSourcei(mSource, AL_LOOPING, AL_FALSE); - - applyUpdates(); -} - -void OpenAL_SoundStream::applyUpdates() -{ - ALfloat gain = mVolume*mBaseVolume; - ALfloat pitch = mPitch; - if(!(mFlags&MWBase::SoundManager::Play_NoEnv) && mOutput.mLastEnvironment == Env_Underwater) - { - gain *= 0.9f; - pitch *= 0.7f; + /* Underrun, or not started yet. The decoder offset is where we'll play + * next. */ + t = (double)mDecoder->getSampleOffset() / (double)mSampleRate; } - alSourcef(mSource, AL_GAIN, gain); - alSourcef(mSource, AL_PITCH, pitch); - alSource3f(mSource, AL_POSITION, mPos[0], mPos[1], mPos[2]); - alSource3f(mSource, AL_DIRECTION, 0.0f, 0.0f, 0.0f); - alSource3f(mSource, AL_VELOCITY, 0.0f, 0.0f, 0.0f); throwALerror(); + return t; } bool OpenAL_SoundStream::process() @@ -562,172 +468,6 @@ ALint OpenAL_SoundStream::refillQueue() return queued; } -void OpenAL_SoundStream3D::applyUpdates() -{ - ALfloat gain = mVolume*mBaseVolume; - ALfloat pitch = mPitch; - if((mPos - mOutput.mPos).length2() > mMaxDistance*mMaxDistance) - gain = 0.0f; - else if(!(mFlags&MWBase::SoundManager::Play_NoEnv) && mOutput.mLastEnvironment == Env_Underwater) - { - gain *= 0.9f; - pitch *= 0.7f; - } - - alSourcef(mSource, AL_GAIN, gain); - alSourcef(mSource, AL_PITCH, pitch); - alSource3f(mSource, AL_POSITION, mPos[0], mPos[1], mPos[2]); - alSource3f(mSource, AL_DIRECTION, 0.0f, 0.0f, 0.0f); - alSource3f(mSource, AL_VELOCITY, 0.0f, 0.0f, 0.0f); - throwALerror(); -} - - -// -// A regular 2D OpenAL sound -// -class OpenAL_Sound : public Sound -{ -protected: - OpenAL_Output &mOutput; - - ALuint mSource; - - friend class OpenAL_Output; - - void updateAll(bool local); - -private: - OpenAL_Sound(const OpenAL_Sound &rhs); - OpenAL_Sound& operator=(const OpenAL_Sound &rhs); - -public: - OpenAL_Sound(OpenAL_Output &output, ALuint src, const osg::Vec3f& pos, float vol, float basevol, float pitch, float mindist, float maxdist, int flags); - virtual ~OpenAL_Sound(); - - virtual void stop(); - virtual bool isPlaying(); - virtual double getTimeOffset(); - virtual void applyUpdates(); -}; - -// -// A regular 3D OpenAL sound -// -class OpenAL_Sound3D : public OpenAL_Sound -{ - OpenAL_Sound3D(const OpenAL_Sound &rhs); - OpenAL_Sound3D& operator=(const OpenAL_Sound &rhs); - -public: - OpenAL_Sound3D(OpenAL_Output &output, ALuint src, const osg::Vec3f& pos, float vol, float basevol, float pitch, float mindist, float maxdist, int flags) - : OpenAL_Sound(output, src, pos, vol, basevol, pitch, mindist, maxdist, flags) - { } - - virtual void applyUpdates(); -}; - -OpenAL_Sound::OpenAL_Sound(OpenAL_Output &output, ALuint src, const osg::Vec3f& pos, float vol, float basevol, float pitch, float mindist, float maxdist, int flags) - : Sound(pos, vol, basevol, pitch, mindist, maxdist, flags) - , mOutput(output), mSource(src) -{ - mOutput.mActiveSounds.push_back(this); -} -OpenAL_Sound::~OpenAL_Sound() -{ - alSourceStop(mSource); - alSourcei(mSource, AL_BUFFER, 0); - - mOutput.mFreeSources.push_back(mSource); - - mOutput.mActiveSounds.erase(std::find(mOutput.mActiveSounds.begin(), - mOutput.mActiveSounds.end(), this)); -} - -void OpenAL_Sound::stop() -{ - alSourceStop(mSource); - throwALerror(); -} - -bool OpenAL_Sound::isPlaying() -{ - ALint state; - - alGetSourcei(mSource, AL_SOURCE_STATE, &state); - throwALerror(); - - return state==AL_PLAYING || state==AL_PAUSED; -} - -double OpenAL_Sound::getTimeOffset() -{ - ALfloat t; - - alGetSourcef(mSource, AL_SEC_OFFSET, &t); - throwALerror(); - - return t; -} - -void OpenAL_Sound::updateAll(bool local) -{ - alSourcef(mSource, AL_REFERENCE_DISTANCE, mMinDistance); - alSourcef(mSource, AL_MAX_DISTANCE, mMaxDistance); - if(local) - { - alSourcef(mSource, AL_ROLLOFF_FACTOR, 0.0f); - alSourcei(mSource, AL_SOURCE_RELATIVE, AL_TRUE); - } - else - { - alSourcef(mSource, AL_ROLLOFF_FACTOR, 1.0f); - alSourcei(mSource, AL_SOURCE_RELATIVE, AL_FALSE); - } - alSourcei(mSource, AL_LOOPING, (mFlags&MWBase::SoundManager::Play_Loop) ? AL_TRUE : AL_FALSE); - - applyUpdates(); -} - -void OpenAL_Sound::applyUpdates() -{ - ALfloat gain = mVolume*mBaseVolume; - ALfloat pitch = mPitch; - - if(!(mFlags&MWBase::SoundManager::Play_NoEnv) && mOutput.mLastEnvironment == Env_Underwater) - { - gain *= 0.9f; - pitch *= 0.7f; - } - - alSourcef(mSource, AL_GAIN, gain); - alSourcef(mSource, AL_PITCH, pitch); - alSource3f(mSource, AL_POSITION, mPos[0], mPos[1], mPos[2]); - alSource3f(mSource, AL_DIRECTION, 0.0f, 0.0f, 0.0f); - alSource3f(mSource, AL_VELOCITY, 0.0f, 0.0f, 0.0f); - throwALerror(); -} - -void OpenAL_Sound3D::applyUpdates() -{ - ALfloat gain = mVolume*mBaseVolume; - ALfloat pitch = mPitch; - if((mPos - mOutput.mPos).length2() > mMaxDistance*mMaxDistance) - gain = 0.0f; - else if(!(mFlags&MWBase::SoundManager::Play_NoEnv) && mOutput.mLastEnvironment == Env_Underwater) - { - gain *= 0.9f; - pitch *= 0.7f; - } - - alSourcef(mSource, AL_GAIN, gain); - alSourcef(mSource, AL_PITCH, pitch); - alSource3f(mSource, AL_POSITION, mPos[0], mPos[1], mPos[2]); - alSource3f(mSource, AL_DIRECTION, 0.0f, 0.0f, 0.0f); - alSource3f(mSource, AL_VELOCITY, 0.0f, 0.0f, 0.0f); - throwALerror(); -} - // // An OpenAL output device @@ -881,15 +621,16 @@ void OpenAL_Output::unloadSound(Sound_Handle data) SoundVec::const_iterator iter = mActiveSounds.begin(); for(;iter != mActiveSounds.end();++iter) { - if(!(*iter)->mSource) + if(!(*iter)->mHandle) continue; + ALuint source = GET_PTRID((*iter)->mHandle); ALint srcbuf; - alGetSourcei((*iter)->mSource, AL_BUFFER, &srcbuf); + alGetSourcei(source, AL_BUFFER, &srcbuf); if((ALuint)srcbuf == buffer) { - alSourceStop((*iter)->mSource); - alSourcei((*iter)->mSource, AL_BUFFER, 0); + alSourceStop(source); + alSourcei(source, AL_BUFFER, 0); } } alDeleteBuffers(1, &buffer); @@ -907,121 +648,278 @@ size_t OpenAL_Output::getSoundDataSize(Sound_Handle data) const } -MWBase::SoundPtr OpenAL_Output::playSound(Sound_Handle data, float vol, float basevol, float pitch, int flags,float offset) +void OpenAL_Output::initCommon2D(ALuint source, const osg::Vec3f &pos, ALfloat gain, ALfloat pitch, bool loop, bool useenv) { - boost::shared_ptr sound; - ALuint src; + alSourcef(source, AL_REFERENCE_DISTANCE, 1.0f); + alSourcef(source, AL_MAX_DISTANCE, 1000.0f); + alSourcef(source, AL_ROLLOFF_FACTOR, 0.0f); + alSourcei(source, AL_SOURCE_RELATIVE, AL_TRUE); + alSourcei(source, AL_LOOPING, loop ? AL_TRUE : AL_FALSE); + + if(useenv && mListenerEnv == Env_Underwater) + { + gain *= 0.9f; + pitch *= 0.7f; + } + + alSourcef(source, AL_GAIN, gain); + alSourcef(source, AL_PITCH, pitch); + alSourcefv(source, AL_POSITION, pos.ptr()); + alSource3f(source, AL_DIRECTION, 0.0f, 0.0f, 0.0f); + alSource3f(source, AL_VELOCITY, 0.0f, 0.0f, 0.0f); +} + +void OpenAL_Output::initCommon3D(ALuint source, const osg::Vec3f &pos, ALfloat mindist, ALfloat maxdist, ALfloat gain, ALfloat pitch, bool loop, bool useenv) +{ + alSourcef(source, AL_REFERENCE_DISTANCE, mindist); + alSourcef(source, AL_MAX_DISTANCE, maxdist); + alSourcef(source, AL_ROLLOFF_FACTOR, 1.0f); + alSourcei(source, AL_SOURCE_RELATIVE, AL_FALSE); + alSourcei(source, AL_LOOPING, loop ? AL_TRUE : AL_FALSE); + + if((pos - mListenerPos).length2() > maxdist*maxdist) + gain = 0.0f; + if(useenv && mListenerEnv == Env_Underwater) + { + gain *= 0.9f; + pitch *= 0.7f; + } + + alSourcef(source, AL_GAIN, gain); + alSourcef(source, AL_PITCH, pitch); + alSourcefv(source, AL_POSITION, pos.ptr()); + alSource3f(source, AL_DIRECTION, 0.0f, 0.0f, 0.0f); + alSource3f(source, AL_VELOCITY, 0.0f, 0.0f, 0.0f); +} + +void OpenAL_Output::updateCommon(ALuint source, const osg::Vec3f& pos, ALfloat maxdist, ALfloat gain, ALfloat pitch, bool useenv, bool is3d) +{ + if(is3d) + { + if((pos - mListenerPos).length2() > maxdist*maxdist) + gain = 0.0f; + } + if(useenv && mListenerEnv == Env_Underwater) + { + gain *= 0.9f; + pitch *= 0.7f; + } + + alSourcef(source, AL_GAIN, gain); + alSourcef(source, AL_PITCH, pitch); + alSourcefv(source, AL_POSITION, pos.ptr()); + alSource3f(source, AL_DIRECTION, 0.0f, 0.0f, 0.0f); + alSource3f(source, AL_VELOCITY, 0.0f, 0.0f, 0.0f); +} + + +void OpenAL_Output::playSound(MWBase::SoundPtr sound, Sound_Handle data, float offset) +{ + ALuint source; if(mFreeSources.empty()) fail("No free sources"); - src = mFreeSources.front(); + source = mFreeSources.front(); mFreeSources.pop_front(); try { - sound.reset(new OpenAL_Sound(*this, src, osg::Vec3f(0.f, 0.f, 0.f), vol, basevol, pitch, 1.0f, 1000.0f, flags)); + initCommon2D(source, sound->getPosition(), sound->getRealVolume(), sound->getPitch(), + sound->getIsLooping(), sound->getUseEnv()); + + alSourcef(source, AL_SEC_OFFSET, offset); + throwALerror(); + + alSourcei(source, AL_BUFFER, GET_PTRID(data)); + alSourcePlay(source); + throwALerror(); + + mActiveSounds.push_back(sound); } - catch(std::exception&) - { - mFreeSources.push_back(src); + catch(std::exception&) { + mFreeSources.push_back(source); throw; } - sound->updateAll(true); - alSourcei(src, AL_BUFFER, GET_PTRID(data)); - alSourcef(src, AL_SEC_OFFSET, offset/pitch); - - alSourcePlay(src); - throwALerror(); - - return sound; + sound->mHandle = MAKE_PTRID(source); } -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) +void OpenAL_Output::playSound3D(MWBase::SoundPtr sound, Sound_Handle data, float offset) { - boost::shared_ptr sound; - ALuint src; + ALuint source; if(mFreeSources.empty()) fail("No free sources"); - src = mFreeSources.front(); + source = mFreeSources.front(); mFreeSources.pop_front(); try { - sound.reset(new OpenAL_Sound3D(*this, src, pos, vol, basevol, pitch, min, max, flags)); + initCommon3D(source, sound->getPosition(), sound->getMinDistance(), sound->getMaxDistance(), + sound->getRealVolume(), sound->getPitch(), sound->getIsLooping(), + sound->getUseEnv()); + + alSourcef(source, AL_SEC_OFFSET, offset); + throwALerror(); + + alSourcei(source, AL_BUFFER, GET_PTRID(data)); + alSourcePlay(source); + throwALerror(); + + mActiveSounds.push_back(sound); } - catch(std::exception&) - { - mFreeSources.push_back(src); + catch(std::exception&) { + mFreeSources.push_back(source); throw; } - sound->updateAll(false); - alSourcei(src, AL_BUFFER, GET_PTRID(data)); - alSourcef(src, AL_SEC_OFFSET, offset/pitch); + sound->mHandle = MAKE_PTRID(source); +} + +void OpenAL_Output::stopSound(MWBase::SoundPtr sound) +{ + if(!sound->mHandle) return; + ALuint source = GET_PTRID(sound->mHandle); + sound->mHandle = 0; + + alSourceStop(source); + alSourcei(source, AL_BUFFER, 0); - alSourcePlay(src); + mFreeSources.push_back(source); + mActiveSounds.erase(std::find(mActiveSounds.begin(), mActiveSounds.end(), sound)); +} + +bool OpenAL_Output::isSoundPlaying(MWBase::SoundPtr sound) +{ + if(!sound->mHandle) return false; + ALuint source = GET_PTRID(sound->mHandle); + ALint state; + + alGetSourcei(source, AL_SOURCE_STATE, &state); throwALerror(); - return sound; + return state == AL_PLAYING || state == AL_PAUSED; +} + +void OpenAL_Output::updateSound(MWBase::SoundPtr sound) +{ + if(!sound->mHandle) return; + ALuint source = GET_PTRID(sound->mHandle); + + updateCommon(source, sound->getPosition(), sound->getMaxDistance(), sound->getRealVolume(), + sound->getPitch(), sound->getUseEnv(), sound->getIs3D()); } -MWBase::SoundPtr OpenAL_Output::streamSound(DecoderPtr decoder, float basevol, float pitch, int flags) +void OpenAL_Output::streamSound(DecoderPtr decoder, MWBase::SoundStreamPtr sound) { - boost::shared_ptr sound; - ALuint src; + OpenAL_SoundStream *stream = 0; + ALuint source; if(mFreeSources.empty()) fail("No free sources"); - src = mFreeSources.front(); + source = mFreeSources.front(); mFreeSources.pop_front(); - if((flags&MWBase::SoundManager::Play_Loop)) + if(sound->getIsLooping()) std::cout <<"Warning: cannot loop stream \""<getName()<<"\""<< std::endl; - try - { - sound.reset(new OpenAL_SoundStream(*this, src, decoder, osg::Vec3f(0.0f, 0.0f, 0.0f), 1.0f, basevol, pitch, 1.0f, 1000.0f, flags)); + try { + initCommon2D(source, sound->getPosition(), sound->getRealVolume(), sound->getPitch(), + false, sound->getUseEnv()); + throwALerror(); + + stream = new OpenAL_SoundStream(source, decoder); + mStreamThread->add(stream); + mActiveStreams.push_back(sound); } - catch(std::exception&) - { - mFreeSources.push_back(src); + catch(std::exception&) { + mStreamThread->remove(stream); + delete stream; + mFreeSources.push_back(source); throw; } - sound->updateAll(true); - - sound->play(); - return sound; + sound->mHandle = stream; } - -MWBase::SoundPtr OpenAL_Output::streamSound3D(DecoderPtr decoder, const osg::Vec3f &pos, float volume, float basevol, float pitch, float min, float max, int flags) +void OpenAL_Output::streamSound3D(DecoderPtr decoder, MWBase::SoundStreamPtr sound) { - boost::shared_ptr sound; - ALuint src; + OpenAL_SoundStream *stream = 0; + ALuint source; if(mFreeSources.empty()) fail("No free sources"); - src = mFreeSources.front(); + source = mFreeSources.front(); mFreeSources.pop_front(); - if((flags&MWBase::SoundManager::Play_Loop)) + if(sound->getIsLooping()) std::cout <<"Warning: cannot loop stream \""<getName()<<"\""<< std::endl; - try - { - sound.reset(new OpenAL_SoundStream3D(*this, src, decoder, pos, volume, basevol, pitch, min, max, flags)); + try { + initCommon3D(source, sound->getPosition(), sound->getMinDistance(), sound->getMaxDistance(), + sound->getRealVolume(), sound->getPitch(), false, sound->getUseEnv()); + throwALerror(); + + stream = new OpenAL_SoundStream(source, decoder); + mStreamThread->add(stream); + mActiveStreams.push_back(sound); } - catch(std::exception&) - { - mFreeSources.push_back(src); + catch(std::exception&) { + mStreamThread->remove(stream); + delete stream; + mFreeSources.push_back(source); throw; } - sound->updateAll(false); + sound->mHandle = stream; +} + +void OpenAL_Output::stopStream(MWBase::SoundStreamPtr sound) +{ + if(!sound->mHandle) return; + OpenAL_SoundStream *stream = reinterpret_cast(sound->mHandle); + ALuint source = stream->mSource; + + sound->mHandle = 0; + mStreamThread->remove(stream); + + alSourceStop(source); + alSourcei(source, AL_BUFFER, 0); + + mFreeSources.push_back(source); + mActiveStreams.erase(std::find(mActiveStreams.begin(), mActiveStreams.end(), sound)); + + delete stream; +} + +double OpenAL_Output::getStreamDelay(MWBase::SoundStreamPtr sound) +{ + if(!sound->mHandle) return 0.0; + OpenAL_SoundStream *stream = reinterpret_cast(sound->mHandle); + return stream->getStreamDelay(); +} + +double OpenAL_Output::getStreamOffset(MWBase::SoundStreamPtr sound) +{ + if(!sound->mHandle) return 0.0; + OpenAL_SoundStream *stream = reinterpret_cast(sound->mHandle); + boost::lock_guard lock(mStreamThread->mMutex); + return stream->getStreamOffset(); +} + +bool OpenAL_Output::isStreamPlaying(MWBase::SoundStreamPtr sound) +{ + if(!sound->mHandle) return false; + OpenAL_SoundStream *stream = reinterpret_cast(sound->mHandle); + boost::lock_guard lock(mStreamThread->mMutex); + return stream->isPlaying(); +} - sound->play(); - return sound; +void OpenAL_Output::updateStream(MWBase::SoundStreamPtr sound) +{ + if(!sound->mHandle) return; + OpenAL_SoundStream *stream = reinterpret_cast(sound->mHandle); + ALuint source = stream->mSource; + + updateCommon(source, sound->getPosition(), sound->getMaxDistance(), sound->getRealVolume(), + sound->getPitch(), sound->getUseEnv(), sound->getIs3D()); } @@ -1038,19 +936,19 @@ void OpenAL_Output::finishUpdate() void OpenAL_Output::updateListener(const osg::Vec3f &pos, const osg::Vec3f &atdir, const osg::Vec3f &updir, Environment env) { - mPos = pos; - mLastEnvironment = env; - if(mContext) { ALfloat orient[6] = { atdir.x(), atdir.y(), atdir.z(), updir.x(), updir.y(), updir.z() }; - alListener3f(AL_POSITION, mPos.x(), mPos.y(), mPos.z()); + alListenerfv(AL_POSITION, pos.ptr()); alListenerfv(AL_ORIENTATION, orient); throwALerror(); } + + mListenerPos = pos; + mListenerEnv = env; } @@ -1060,14 +958,17 @@ void OpenAL_Output::pauseSounds(int types) SoundVec::const_iterator sound = mActiveSounds.begin(); for(;sound != mActiveSounds.end();++sound) { - if(*sound && (*sound)->mSource && ((*sound)->getPlayType()&types)) - sources.push_back((*sound)->mSource); + if(*sound && (*sound)->mHandle && ((*sound)->getPlayType()&types)) + sources.push_back(GET_PTRID((*sound)->mHandle)); } StreamVec::const_iterator stream = mActiveStreams.begin(); for(;stream != mActiveStreams.end();++stream) { - if(*stream && (*stream)->mSource && ((*stream)->getPlayType()&types)) - sources.push_back((*stream)->mSource); + if(*stream && (*stream)->mHandle && ((*stream)->getPlayType()&types)) + { + OpenAL_SoundStream *strm = reinterpret_cast((*stream)->mHandle); + sources.push_back(strm->mSource); + } } if(!sources.empty()) { @@ -1082,14 +983,17 @@ void OpenAL_Output::resumeSounds(int types) SoundVec::const_iterator sound = mActiveSounds.begin(); for(;sound != mActiveSounds.end();++sound) { - if(*sound && (*sound)->mSource && ((*sound)->getPlayType()&types)) - sources.push_back((*sound)->mSource); + if(*sound && (*sound)->mHandle && ((*sound)->getPlayType()&types)) + sources.push_back(GET_PTRID((*sound)->mHandle)); } StreamVec::const_iterator stream = mActiveStreams.begin(); for(;stream != mActiveStreams.end();++stream) { - if(*stream && (*stream)->mSource && ((*stream)->getPlayType()&types)) - sources.push_back((*stream)->mSource); + if(*stream && (*stream)->mHandle && ((*stream)->getPlayType()&types)) + { + OpenAL_SoundStream *strm = reinterpret_cast((*stream)->mHandle); + sources.push_back(strm->mSource); + } } if(!sources.empty()) { @@ -1106,8 +1010,9 @@ void OpenAL_Output::loadLoudnessAsync(DecoderPtr decoder, Sound_Loudness *loudne OpenAL_Output::OpenAL_Output(SoundManager &mgr) - : Sound_Output(mgr), mDevice(0), mContext(0), mLastEnvironment(Env_Normal), - mStreamThread(new StreamThread) + : Sound_Output(mgr), mDevice(0), mContext(0) + , mListenerPos(0.0f, 0.0f, 0.0f), mListenerEnv(Env_Normal) + , mStreamThread(new StreamThread) { } diff --git a/apps/openmw/mwsound/openal_output.hpp b/apps/openmw/mwsound/openal_output.hpp index 751ec4fd2..d00e8cd67 100644 --- a/apps/openmw/mwsound/openal_output.hpp +++ b/apps/openmw/mwsound/openal_output.hpp @@ -16,9 +16,6 @@ namespace MWSound class SoundManager; class Sound; - class OpenAL_Sound; - class OpenAL_SoundStream; - class OpenAL_Output : public Sound_Output { ALCdevice *mDevice; @@ -27,13 +24,26 @@ namespace MWSound typedef std::deque IDDq; IDDq mFreeSources; - typedef std::vector SoundVec; + typedef std::vector SoundVec; SoundVec mActiveSounds; - typedef std::vector StreamVec; + typedef std::vector StreamVec; StreamVec mActiveStreams; - Environment mLastEnvironment; + osg::Vec3f mListenerPos; + Environment mListenerEnv; + + struct StreamThread; + std::auto_ptr mStreamThread; + + void initCommon2D(ALuint source, const osg::Vec3f &pos, ALfloat gain, ALfloat pitch, bool loop, bool useenv); + void initCommon3D(ALuint source, const osg::Vec3f &pos, ALfloat mindist, ALfloat maxdist, ALfloat gain, ALfloat pitch, bool loop, bool useenv); + + void updateCommon(ALuint source, const osg::Vec3f &pos, ALfloat maxdist, ALfloat gain, ALfloat pitch, bool useenv, bool is3d); + + OpenAL_Output& operator=(const OpenAL_Output &rhs); + OpenAL_Output(const OpenAL_Output &rhs); + public: virtual std::vector enumerate(); virtual void init(const std::string &devname=""); virtual void deinit(); @@ -42,12 +52,19 @@ namespace MWSound virtual void unloadSound(Sound_Handle data); virtual size_t getSoundDataSize(Sound_Handle data) const; - virtual MWBase::SoundPtr playSound(Sound_Handle data, float vol, float basevol, float pitch, int flags, float offset); - 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 basevol, float pitch, int flags); - virtual MWBase::SoundPtr streamSound3D(DecoderPtr decoder, const osg::Vec3f &pos, - float vol, float basevol, float pitch, float min, float max, int flags); + virtual void playSound(MWBase::SoundPtr sound, Sound_Handle data, float offset); + virtual void playSound3D(MWBase::SoundPtr sound, Sound_Handle data, float offset); + virtual void stopSound(MWBase::SoundPtr sound); + virtual bool isSoundPlaying(MWBase::SoundPtr sound); + virtual void updateSound(MWBase::SoundPtr sound); + + virtual void streamSound(DecoderPtr decoder, MWBase::SoundStreamPtr sound); + virtual void streamSound3D(DecoderPtr decoder, MWBase::SoundStreamPtr sound); + virtual void stopStream(MWBase::SoundStreamPtr sound); + virtual double getStreamDelay(MWBase::SoundStreamPtr sound); + virtual double getStreamOffset(MWBase::SoundStreamPtr sound); + virtual bool isStreamPlaying(MWBase::SoundStreamPtr sound); + virtual void updateStream(MWBase::SoundStreamPtr sound); virtual void startUpdate(); virtual void finishUpdate(); @@ -59,20 +76,8 @@ namespace MWSound virtual void loadLoudnessAsync(DecoderPtr decoder, Sound_Loudness *loudness); - OpenAL_Output& operator=(const OpenAL_Output &rhs); - OpenAL_Output(const OpenAL_Output &rhs); - OpenAL_Output(SoundManager &mgr); virtual ~OpenAL_Output(); - - struct StreamThread; - std::auto_ptr mStreamThread; - - friend class OpenAL_Sound; - friend class OpenAL_Sound3D; - friend class OpenAL_SoundStream; - friend class OpenAL_SoundStream3D; - friend class SoundManager; }; #ifndef DEFAULT_OUTPUT #define DEFAULT_OUTPUT(x) ::MWSound::OpenAL_Output((x)) diff --git a/apps/openmw/mwsound/sound.hpp b/apps/openmw/mwsound/sound.hpp index f95ff169d..a59027fc1 100644 --- a/apps/openmw/mwsound/sound.hpp +++ b/apps/openmw/mwsound/sound.hpp @@ -1,16 +1,14 @@ #ifndef GAME_SOUND_SOUND_H #define GAME_SOUND_SOUND_H -#include "soundmanagerimp.hpp" +#include "sound_output.hpp" namespace MWSound { - class Sound - { + class Sound { Sound& operator=(const Sound &rhs); Sound(const Sound &rhs); - protected: osg::Vec3f mPos; float mVolume; /* NOTE: Real volume = mVolume*mBaseVolume */ float mBaseVolume; @@ -21,12 +19,12 @@ namespace MWSound float mFadeOutTime; + protected: + Sound_Instance mHandle; + + friend class OpenAL_Output; + public: - virtual void stop() = 0; - virtual bool isPlaying() = 0; - virtual double getTimeOffset() = 0; - virtual double getStreamDelay() const { return 0.0; } - virtual void applyUpdates() = 0; void setPosition(const osg::Vec3f &pos) { mPos = pos; } void setVolume(float volume) { mVolume = volume; } void setBaseVolume(float volume) { mBaseVolume = volume; } @@ -41,22 +39,43 @@ namespace MWSound } } + const osg::Vec3f &getPosition() const { return mPos; } + float getRealVolume() const { return mVolume * mBaseVolume; } + float getPitch() const { return mPitch; } + float getMinDistance() const { return mMinDistance; } + float getMaxDistance() const { return mMaxDistance; } + MWBase::SoundManager::PlayType getPlayType() const { return (MWBase::SoundManager::PlayType)(mFlags&MWBase::SoundManager::Play_TypeMask); } + bool getUseEnv() const { return !(mFlags&MWBase::SoundManager::Play_NoEnv); } + bool getIsLooping() const { return mFlags&MWBase::SoundManager::Play_Loop; } bool getDistanceCull() const { return mFlags&MWBase::SoundManager::Play_RemoveAtDistance; } bool getIs3D() const { return mFlags&Play_3D; } Sound(const osg::Vec3f& pos, float vol, float basevol, float pitch, float mindist, float maxdist, int flags) - : mPos(pos) - , mVolume(vol) - , mBaseVolume(basevol) - , mPitch(pitch) - , mMinDistance(mindist) - , mMaxDistance(maxdist) - , mFlags(flags) - , mFadeOutTime(0.0f) + : mPos(pos), mVolume(vol), mBaseVolume(basevol), mPitch(pitch) + , mMinDistance(mindist), mMaxDistance(maxdist), mFlags(flags) + , mFadeOutTime(0.0f), mHandle(0) + { } + Sound(float vol, float basevol, float pitch, int flags) + : mPos(0.0f, 0.0f, 0.0f), mVolume(vol), mBaseVolume(basevol), mPitch(pitch) + , mMinDistance(1.0f), mMaxDistance(1000.0f), mFlags(flags) + , mFadeOutTime(0.0f), mHandle(0) + { } + }; + + // Same as above, but it's a different type since the output handles them differently + class Stream : public Sound { + Stream& operator=(const Stream &rhs); + Stream(const Stream &rhs); + + public: + Stream(const osg::Vec3f& pos, float vol, float basevol, float pitch, float mindist, float maxdist, int flags) + : Sound(pos, vol, basevol, pitch, mindist, maxdist, flags) + { } + Stream(float vol, float basevol, float pitch, int flags) + : Sound(vol, basevol, pitch, flags) { } - virtual ~Sound() { } }; } diff --git a/apps/openmw/mwsound/sound_output.hpp b/apps/openmw/mwsound/sound_output.hpp index 0f69498aa..2f459d09b 100644 --- a/apps/openmw/mwsound/sound_output.hpp +++ b/apps/openmw/mwsound/sound_output.hpp @@ -3,11 +3,10 @@ #include #include +#include #include "soundmanagerimp.hpp" -#include "../mwworld/ptr.hpp" - namespace MWSound { class SoundManager; @@ -17,6 +16,8 @@ namespace MWSound // An opaque handle for the implementation's sound buffers. typedef void *Sound_Handle; + // An opaque handle for the implementation's sound instances. + typedef void *Sound_Instance; class Sound_Output { @@ -30,14 +31,19 @@ namespace MWSound virtual void unloadSound(Sound_Handle data) = 0; virtual size_t getSoundDataSize(Sound_Handle data) const = 0; - /// @param offset Number of seconds into the sound to start playback. - virtual MWBase::SoundPtr playSound(Sound_Handle data, float vol, float basevol, float pitch, int flags, float offset) = 0; - /// @param offset Number of seconds into the sound to start playback. - 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 basevol, float pitch, int flags) = 0; - virtual MWBase::SoundPtr streamSound3D(DecoderPtr decoder, const osg::Vec3f &pos, - float vol, float basevol, float pitch, float min, float max, int flags) = 0; + virtual void playSound(MWBase::SoundPtr sound, Sound_Handle data, float offset) = 0; + virtual void playSound3D(MWBase::SoundPtr sound, Sound_Handle data, float offset) = 0; + virtual void stopSound(MWBase::SoundPtr sound) = 0; + virtual bool isSoundPlaying(MWBase::SoundPtr sound) = 0; + virtual void updateSound(MWBase::SoundPtr sound) = 0; + + virtual void streamSound(DecoderPtr decoder, MWBase::SoundStreamPtr sound) = 0; + virtual void streamSound3D(DecoderPtr decoder, MWBase::SoundStreamPtr sound) = 0; + virtual void stopStream(MWBase::SoundStreamPtr sound) = 0; + virtual double getStreamDelay(MWBase::SoundStreamPtr sound) = 0; + virtual double getStreamOffset(MWBase::SoundStreamPtr sound) = 0; + virtual bool isStreamPlaying(MWBase::SoundStreamPtr sound) = 0; + virtual void updateStream(MWBase::SoundStreamPtr sound) = 0; virtual void startUpdate() = 0; virtual void finishUpdate() = 0; @@ -57,12 +63,9 @@ namespace MWSound protected: bool mInitialized; - osg::Vec3f mPos; Sound_Output(SoundManager &mgr) - : mManager(mgr) - , mInitialized(false) - , mPos(0.0f, 0.0f, 0.0f) + : mManager(mgr), mInitialized(false) { } public: virtual ~Sound_Output() { } diff --git a/apps/openmw/mwsound/soundmanagerimp.cpp b/apps/openmw/mwsound/soundmanagerimp.cpp index ceff1a619..044a8ba48 100644 --- a/apps/openmw/mwsound/soundmanagerimp.cpp +++ b/apps/openmw/mwsound/soundmanagerimp.cpp @@ -241,7 +241,7 @@ namespace MWSound return decoder; } - MWBase::SoundPtr SoundManager::playVoice(DecoderPtr decoder, const osg::Vec3f &pos, bool playlocal) + MWBase::SoundStreamPtr SoundManager::playVoice(DecoderPtr decoder, const osg::Vec3f &pos, bool playlocal) { MWBase::World* world = MWBase::Environment::get().getWorld(); static const float fAudioMinDistanceMult = world->getStore().get().find("fAudioMinDistanceMult")->getFloat(); @@ -251,15 +251,20 @@ namespace MWSound static float minDistance = std::max(fAudioVoiceDefaultMinDistance * fAudioMinDistanceMult, 1.0f); static float maxDistance = std::max(fAudioVoiceDefaultMaxDistance * fAudioMaxDistanceMult, minDistance); + MWBase::SoundStreamPtr sound; 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 - ); + { + sound.reset(new Stream(1.0f, basevol, 1.0f, Play_Normal|Play_TypeVoice|Play_2D)); + mOutput->streamSound(decoder, sound); + } + else + { + sound.reset(new Stream(pos, 1.0f, basevol, 1.0f, minDistance, maxDistance, + Play_Normal|Play_TypeVoice|Play_3D)); + mOutput->streamSound3D(decoder, sound); + } + return sound; } @@ -293,7 +298,7 @@ namespace MWSound void SoundManager::stopMusic() { if(mMusic) - mMusic->stop(); + mOutput->stopStream(mMusic); mMusic.reset(); } @@ -303,19 +308,19 @@ namespace MWSound return; std::cout <<"Playing "<open(filename); - mMusic = mOutput->streamSound(decoder, volumeFromType(Play_TypeMusic), - 1.0f, Play_NoEnv|Play_TypeMusic|Play_2D); + mMusic.reset(new Stream(1.0f, volumeFromType(Play_TypeMusic), 1.0f, + Play_NoEnv|Play_TypeMusic|Play_2D)); + mOutput->streamSound(decoder, mMusic); } - catch(std::exception &e) - { + catch(std::exception &e) { std::cout << "Music Error: " << e.what() << "\n"; + mMusic.reset(); } } @@ -366,7 +371,7 @@ namespace MWSound bool SoundManager::isMusicPlaying() { - return mMusic && mMusic->isPlaying(); + return mMusic && mOutput->isStreamPlaying(mMusic); } void SoundManager::playPlaylist(const std::string &playlist) @@ -394,7 +399,7 @@ namespace MWSound mPendingSaySounds[ptr] = std::make_pair(decoder, loudness); else { - MWBase::SoundPtr sound = playVoice(decoder, objpos, (ptr == MWMechanics::getPlayer())); + MWBase::SoundStreamPtr sound = playVoice(decoder, objpos, (ptr == MWMechanics::getPlayer())); mActiveSaySounds[ptr] = std::make_pair(sound, loudness); } } @@ -409,11 +414,10 @@ namespace MWSound SaySoundMap::const_iterator snditer = mActiveSaySounds.find(ptr); if(snditer != mActiveSaySounds.end()) { - MWBase::SoundPtr sound = snditer->second.first; + MWBase::SoundStreamPtr sound = snditer->second.first; Sound_Loudness *loudness = snditer->second.second; - float sec = sound->getTimeOffset(); - if(sound->isPlaying()) - return loudness->getLoudnessAtTime(sec); + float sec = mOutput->getStreamOffset(sound); + return loudness->getLoudnessAtTime(sec); } return 0.0f; @@ -435,7 +439,7 @@ namespace MWSound mPendingSaySounds[MWWorld::Ptr()] = std::make_pair(decoder, loudness); else { - MWBase::SoundPtr sound = playVoice(decoder, osg::Vec3f(), true); + MWBase::SoundStreamPtr sound = playVoice(decoder, osg::Vec3f(), true); mActiveSaySounds[MWWorld::Ptr()] = std::make_pair(sound, loudness); } } @@ -450,7 +454,7 @@ namespace MWSound SaySoundMap::const_iterator snditer = mActiveSaySounds.find(ptr); if(snditer != mActiveSaySounds.end()) { - if(snditer->second.first->isPlaying()) + if(mOutput->isStreamPlaying(snditer->second.first)) return false; return true; } @@ -462,21 +466,25 @@ namespace MWSound SaySoundMap::iterator snditer = mActiveSaySounds.find(ptr); if(snditer != mActiveSaySounds.end()) { - snditer->second.first->stop(); + mOutput->stopStream(snditer->second.first); mActiveSaySounds.erase(snditer); } mPendingSaySounds.erase(ptr); } - MWBase::SoundPtr SoundManager::playTrack(const DecoderPtr& decoder, PlayType type) + MWBase::SoundStreamPtr SoundManager::playTrack(const DecoderPtr& decoder, PlayType type) { - MWBase::SoundPtr track; + MWBase::SoundStreamPtr track; if(!mOutput->isInitialized()) return track; try { - track = mOutput->streamSound(decoder, volumeFromType(type), 1.0f, Play_NoEnv|type); + track.reset(new Stream(1.0f, volumeFromType(type), 1.0f, Play_NoEnv|type|Play_2D)); + mOutput->streamSound(decoder, track); + + TrackList::iterator iter = std::lower_bound(mActiveTracks.begin(), mActiveTracks.end(), track); + mActiveTracks.insert(iter, track); } catch(std::exception &e) { @@ -485,6 +493,19 @@ namespace MWSound return track; } + void SoundManager::stopTrack(MWBase::SoundStreamPtr stream) + { + mOutput->stopStream(stream); + TrackList::iterator iter = std::lower_bound(mActiveTracks.begin(), mActiveTracks.end(), stream); + if(iter != mActiveTracks.end() && *iter == stream) + mActiveTracks.erase(iter); + } + + double SoundManager::getTrackTimeDelay(MWBase::SoundStreamPtr stream) + { + return mOutput->getStreamDelay(stream); + } + MWBase::SoundPtr SoundManager::playSound(const std::string& soundId, float volume, float pitch, PlayType type, PlayMode mode, float offset) { @@ -496,9 +517,8 @@ namespace MWSound Sound_Buffer *sfx = loadSound(Misc::StringUtils::lowerCase(soundId)); float basevol = volumeFromType(type); - sound = mOutput->playSound(sfx->mHandle, - volume * sfx->mVolume, basevol, pitch, mode|type|Play_2D, offset - ); + sound.reset(new Sound(volume * sfx->mVolume, basevol, pitch, mode|type|Play_2D)); + mOutput->playSound(sound, sfx->mHandle, offset); if(sfx->mUses++ == 0) { SoundList::iterator iter = std::find(mUnusedBuffers.begin(), mUnusedBuffers.end(), sfx); @@ -510,6 +530,7 @@ namespace MWSound catch(std::exception&) { //std::cout <<"Sound Error: "<playSound(sfx->mHandle, - volume * sfx->mVolume, basevol, pitch, mode|type|Play_2D, offset - ); + { + sound.reset(new Sound(volume * sfx->mVolume, basevol, pitch, mode|type|Play_2D)); + mOutput->playSound(sound, sfx->mHandle, offset); + } else - sound = mOutput->playSound3D(sfx->mHandle, - objpos, volume * sfx->mVolume, basevol, pitch, sfx->mMinDist, sfx->mMaxDist, - mode|type|Play_3D, offset - ); + { + sound.reset(new Sound(objpos, volume * sfx->mVolume, basevol, pitch, + sfx->mMinDist, sfx->mMaxDist, mode|type|Play_3D)); + mOutput->playSound3D(sound, sfx->mHandle, offset); + } if(sfx->mUses++ == 0) { SoundList::iterator iter = std::find(mUnusedBuffers.begin(), mUnusedBuffers.end(), sfx); @@ -551,6 +574,7 @@ namespace MWSound catch(std::exception&) { //std::cout <<"Sound Error: "<playSound3D(sfx->mHandle, - initialPos, volume * sfx->mVolume, basevol, pitch, sfx->mMinDist, sfx->mMaxDist, - mode|type|Play_3D, offset - ); + sound.reset(new Sound(initialPos, volume * sfx->mVolume, basevol, pitch, + sfx->mMinDist, sfx->mMaxDist, mode|type|Play_3D)); + mOutput->playSound3D(sound, sfx->mHandle, offset); if(sfx->mUses++ == 0) { SoundList::iterator iter = std::find(mUnusedBuffers.begin(), mUnusedBuffers.end(), sfx); @@ -582,10 +605,16 @@ namespace MWSound catch(std::exception &) { //std::cout <<"Sound Error: "<stopSound(sound); + } + void SoundManager::stopSound3D(const MWWorld::Ptr &ptr, const std::string& soundId) { SoundMap::iterator snditer = mActiveSounds.find(ptr); @@ -596,7 +625,7 @@ namespace MWSound for(;sndidx != snditer->second.end();++sndidx) { if(sndidx->second == sfx) - sndidx->first->stop(); + mOutput->stopSound(sndidx->first); } } } @@ -608,7 +637,7 @@ namespace MWSound { SoundBufferRefPairList::iterator sndidx = snditer->second.begin(); for(;sndidx != snditer->second.end();++sndidx) - sndidx->first->stop(); + mOutput->stopSound(sndidx->first); } } @@ -623,7 +652,7 @@ namespace MWSound { SoundBufferRefPairList::iterator sndidx = snditer->second.begin(); for(;sndidx != snditer->second.end();++sndidx) - sndidx->first->stop(); + mOutput->stopSound(sndidx->first); } ++snditer; } @@ -634,7 +663,7 @@ namespace MWSound sayiter->first != MWMechanics::getPlayer() && sayiter->first.getCell() == cell) { - sayiter->second.first->stop(); + mOutput->stopStream(sayiter->second.first); } ++sayiter; } @@ -650,7 +679,7 @@ namespace MWSound for(;sndidx != snditer->second.end();++sndidx) { if(sndidx->second == sfx) - sndidx->first->stop(); + mOutput->stopSound(sndidx->first); } } } @@ -680,7 +709,7 @@ namespace MWSound SoundBufferRefPairList::const_iterator sndidx = snditer->second.begin(); for(;sndidx != snditer->second.end();++sndidx) { - if(sndidx->second == sfx && sndidx->first->isPlaying()) + if(sndidx->second == sfx && mOutput->isSoundPlaying(sndidx->first)) return true; } } @@ -788,7 +817,7 @@ namespace MWSound env = Env_Underwater; else if(mUnderwaterSound) { - mUnderwaterSound->stop(); + mOutput->stopSound(mUnderwaterSound); mUnderwaterSound.reset(); } @@ -803,7 +832,7 @@ namespace MWSound if(mListenerUnderwater) { // Play underwater sound (after updating listener) - if(!(mUnderwaterSound && mUnderwaterSound->isPlaying())) + if(!(mUnderwaterSound && mOutput->isSoundPlaying(mUnderwaterSound))) mUnderwaterSound = playSound("Underwater", 1.0f, 1.0f, Play_TypeSfx, Play_LoopNoEnv); } @@ -814,15 +843,36 @@ namespace MWSound SoundBufferRefPairList::iterator sndidx = snditer->second.begin(); while(sndidx != snditer->second.end()) { - if(!updateSound(sndidx->first, snditer->first, duration)) + MWWorld::Ptr ptr = snditer->first; + MWBase::SoundPtr sound = sndidx->first; + if(!ptr.isEmpty() && sound->getIs3D()) { + const ESM::Position &pos = ptr.getRefData().getPosition(); + const osg::Vec3f objpos(pos.asVec3()); + sound->setPosition(objpos); + + if(sound->getDistanceCull()) + { + if((mListenerPos - objpos).length2() > 2000*2000) + mOutput->stopSound(sound); + } + } + + if(!mOutput->isSoundPlaying(sound)) + { + mOutput->stopSound(sound); Sound_Buffer *sfx = sndidx->second; if(sfx->mUses-- == 1) mUnusedBuffers.push_front(sfx); sndidx = snditer->second.erase(sndidx); } else + { + sound->updateFade(duration); + + mOutput->updateSound(sound); ++sndidx; + } } if(snditer->second.empty()) mActiveSounds.erase(snditer++); @@ -840,13 +890,17 @@ namespace MWSound DecoderPtr decoder = penditer->second.first; decoder->rewind(); + MWBase::SoundStreamPtr sound; MWWorld::Ptr ptr = penditer->first; - const ESM::Position &pos = ptr.getRefData().getPosition(); - const osg::Vec3f objpos(pos.asVec3()); + if(ptr == MWWorld::Ptr()) + sound = playVoice(decoder, osg::Vec3f(), true); + else + { + const ESM::Position &pos = ptr.getRefData().getPosition(); + const osg::Vec3f objpos(pos.asVec3()); - MWBase::SoundPtr sound = playVoice(decoder, - objpos, (ptr == MWMechanics::getPlayer()) - ); + sound = playVoice(decoder, objpos, (ptr == MWMechanics::getPlayer())); + } mActiveSaySounds[ptr] = std::make_pair(sound, loudness); } catch(std::exception &e) { @@ -862,36 +916,53 @@ namespace MWSound SaySoundMap::iterator sayiter = mActiveSaySounds.begin(); while(sayiter != mActiveSaySounds.end()) { - if(!updateSound(sayiter->second.first, sayiter->first, duration)) + MWWorld::Ptr ptr = sayiter->first; + MWBase::SoundStreamPtr sound = sayiter->second.first; + if(!ptr.isEmpty() && sound->getIs3D()) + { + const ESM::Position &pos = ptr.getRefData().getPosition(); + const osg::Vec3f objpos(pos.asVec3()); + sound->setPosition(objpos); + + if(sound->getDistanceCull()) + { + if((mListenerPos - objpos).length2() > 2000*2000) + mOutput->stopStream(sound); + } + } + + if(!mOutput->isStreamPlaying(sound)) + { + mOutput->stopStream(sound); mActiveSaySounds.erase(sayiter++); + } else + { + sound->updateFade(duration); + + mOutput->updateStream(sound); ++sayiter; + } } - mOutput->finishUpdate(); - } - bool SoundManager::updateSound(MWBase::SoundPtr sound, const MWWorld::Ptr& ptr, float duration) - { - if(!ptr.isEmpty() && sound->getIs3D()) + TrackList::iterator trkiter = mActiveTracks.begin(); + for(;trkiter != mActiveTracks.end();++trkiter) { - const ESM::Position &pos = ptr.getRefData().getPosition(); - const osg::Vec3f objpos(pos.asVec3()); - sound->setPosition(objpos); - - if(sound->getDistanceCull()) + MWBase::SoundStreamPtr sound = *trkiter; + if(!mOutput->isStreamPlaying(sound)) { - if((mListenerPos - objpos).length2() > 2000*2000) - sound->stop(); + mOutput->stopStream(sound); + trkiter = mActiveTracks.erase(trkiter); } - } - - if(!sound->isPlaying()) - return false; - - sound->updateFade(duration); + else + { + sound->updateFade(duration); - sound->applyUpdates(); - return true; + mOutput->updateStream(sound); + ++trkiter; + } + } + mOutput->finishUpdate(); } @@ -928,20 +999,27 @@ namespace MWSound { MWBase::SoundPtr sound = sndidx->first; sound->setBaseVolume(volumeFromType(sound->getPlayType())); - sound->applyUpdates(); + mOutput->updateSound(sound); } } SaySoundMap::iterator sayiter = mActiveSaySounds.begin(); for(;sayiter != mActiveSaySounds.end();++sayiter) { - MWBase::SoundPtr sound = sayiter->second.first; + MWBase::SoundStreamPtr sound = sayiter->second.first; + sound->setBaseVolume(volumeFromType(sound->getPlayType())); + mOutput->updateStream(sound); + } + TrackList::iterator trkiter = mActiveTracks.begin(); + for(;trkiter != mActiveTracks.end();++trkiter) + { + MWBase::SoundStreamPtr sound = *trkiter; sound->setBaseVolume(volumeFromType(sound->getPlayType())); - sound->applyUpdates(); + mOutput->updateStream(sound); } if(mMusic) { mMusic->setBaseVolume(volumeFromType(mMusic->getPlayType())); - mMusic->applyUpdates(); + mOutput->updateStream(mMusic); } mOutput->finishUpdate(); } @@ -1056,7 +1134,7 @@ namespace MWSound SoundBufferRefPairList::iterator sndidx = snditer->second.begin(); for(;sndidx != snditer->second.end();++sndidx) { - sndidx->first->stop(); + mOutput->stopSound(sndidx->first); Sound_Buffer *sfx = sndidx->second; if(sfx->mUses-- == 1) mUnusedBuffers.push_front(sfx); @@ -1065,8 +1143,12 @@ namespace MWSound mActiveSounds.clear(); SaySoundMap::iterator sayiter = mActiveSaySounds.begin(); for(;sayiter != mActiveSaySounds.end();++sayiter) - sayiter->second.first->stop(); + mOutput->stopStream(sayiter->second.first); mActiveSaySounds.clear(); + TrackList::iterator trkiter = mActiveTracks.begin(); + for(;trkiter != mActiveTracks.end();++trkiter) + mOutput->stopStream(*trkiter); + mActiveTracks.clear(); mPendingSaySounds.clear(); mUnderwaterSound.reset(); stopMusic(); diff --git a/apps/openmw/mwsound/soundmanagerimp.hpp b/apps/openmw/mwsound/soundmanagerimp.hpp index 9c090585b..8ec46bd73 100644 --- a/apps/openmw/mwsound/soundmanagerimp.hpp +++ b/apps/openmw/mwsound/soundmanagerimp.hpp @@ -79,15 +79,12 @@ namespace MWSound typedef std::deque SoundList; SoundList mUnusedBuffers; - boost::shared_ptr mMusic; - std::string mCurrentPlaylist; - typedef std::pair SoundBufferRefPair; typedef std::vector SoundBufferRefPairList; typedef std::map SoundMap; SoundMap mActiveSounds; - typedef std::pair SoundLoudnessPair; + typedef std::pair SoundLoudnessPair; typedef std::map SaySoundMap; SaySoundMap mActiveSaySounds; @@ -95,7 +92,11 @@ namespace MWSound typedef std::map SayDecoderMap; SayDecoderMap mPendingSaySounds; - MWBase::SoundPtr mUnderwaterSound; + typedef std::vector TrackList; + TrackList mActiveTracks; + + MWBase::SoundStreamPtr mMusic; + std::string mCurrentPlaylist; bool mListenerUnderwater; osg::Vec3f mListenerPos; @@ -104,6 +105,8 @@ namespace MWSound int mPausedSoundTypes; + MWBase::SoundPtr mUnderwaterSound; + Sound_Buffer *insertSound(const std::string &soundId, const ESM::Sound *sound); Sound_Buffer *lookupSound(const std::string &soundId) const; @@ -113,10 +116,9 @@ namespace MWSound // to start streaming DecoderPtr loadVoice(const std::string &voicefile, Sound_Loudness **lipdata); - MWBase::SoundPtr playVoice(DecoderPtr decoder, const osg::Vec3f &pos, bool playlocal); + MWBase::SoundStreamPtr playVoice(DecoderPtr decoder, const osg::Vec3f &pos, bool playlocal); void streamMusicFull(const std::string& filename); - bool updateSound(MWBase::SoundPtr sound, const MWWorld::Ptr &ptr, float duration); void updateSounds(float duration); void updateRegionSound(float duration); @@ -171,9 +173,17 @@ namespace MWSound /// and get an average loudness value (scale [0,1]) at the current time position. /// If the actor is not saying anything, returns 0. - virtual MWBase::SoundPtr playTrack(const DecoderPtr& decoder, PlayType type); + virtual MWBase::SoundStreamPtr playTrack(const DecoderPtr& decoder, PlayType type); ///< Play a 2D audio track, using a custom decoder + virtual void stopTrack(MWBase::SoundStreamPtr stream); + ///< Stop the given audio track from playing + + virtual double getTrackTimeDelay(MWBase::SoundStreamPtr stream); + ///< Retives the time delay, in seconds, of the audio track (must be a sound + /// returned by \ref playTrack). Only intended to be called by the track + /// decoder's read method. + virtual MWBase::SoundPtr playSound(const std::string& soundId, float volume, float pitch, PlayType type=Play_TypeSfx, PlayMode mode=Play_Normal, float offset=0); ///< Play a sound, independently of 3D-position ///< @param offset Number of seconds into the sound to start playback. @@ -189,6 +199,9 @@ namespace MWSound ///< Play a 3D sound at \a initialPos. If the sound should be moving, it must be updated using Sound::setPosition. ///< @param offset Number of seconds into the sound to start playback. + virtual void stopSound(MWBase::SoundPtr sound); + ///< Stop the given sound from playing + virtual void stopSound3D(const MWWorld::Ptr &reference, const std::string& soundId); ///< Stop the given object from playing the given sound, diff --git a/apps/openmw/mwworld/projectilemanager.cpp b/apps/openmw/mwworld/projectilemanager.cpp index 4ec4d1432..08ae6f2b0 100644 --- a/apps/openmw/mwworld/projectilemanager.cpp +++ b/apps/openmw/mwworld/projectilemanager.cpp @@ -190,7 +190,7 @@ namespace MWWorld { MWBase::Environment::get().getWorld()->explodeSpell(pos, it->mEffects, caster, ESM::RT_Target, it->mSpellId, it->mSourceName); - it->mSound->stop(); + MWBase::Environment::get().getSoundManager()->stopSound(it->mSound); mParent->removeChild(it->mNode); it = mMagicBolts.erase(it); @@ -264,7 +264,7 @@ namespace MWWorld for (std::vector::iterator it = mMagicBolts.begin(); it != mMagicBolts.end(); ++it) { mParent->removeChild(it->mNode); - it->mSound->stop(); + MWBase::Environment::get().getSoundManager()->stopSound(it->mSound); } mMagicBolts.clear(); } diff --git a/apps/openmw/mwworld/weather.cpp b/apps/openmw/mwworld/weather.cpp index afa819121..9a93f3827 100644 --- a/apps/openmw/mwworld/weather.cpp +++ b/apps/openmw/mwworld/weather.cpp @@ -738,7 +738,7 @@ void WeatherManager::update(float duration, bool paused) void WeatherManager::stopSounds() { if (mAmbientSound.get()) - mAmbientSound->stop(); + MWBase::Environment::get().getSoundManager()->stopSound(mAmbientSound); mAmbientSound.reset(); mPlayingSoundID.clear(); }