From 3d6fd2818f8a48f9774cec8d2fe0aaaec880d87f Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Tue, 31 Mar 2020 12:29:58 +0400 Subject: [PATCH 1/3] Support for per-type sound blockers --- apps/openmw/mwbase/soundmanager.hpp | 4 ++-- apps/openmw/mwgui/windowmanagerimp.cpp | 4 ++-- apps/openmw/mwsound/soundmanagerimp.cpp | 23 ++++++++++++++++------- apps/openmw/mwsound/soundmanagerimp.hpp | 6 +++--- 4 files changed, 23 insertions(+), 14 deletions(-) diff --git a/apps/openmw/mwbase/soundmanager.hpp b/apps/openmw/mwbase/soundmanager.hpp index a6d0d1223c..a780e5ccdd 100644 --- a/apps/openmw/mwbase/soundmanager.hpp +++ b/apps/openmw/mwbase/soundmanager.hpp @@ -168,10 +168,10 @@ namespace MWBase ///< Is the given sound currently playing on the given object? /// If you want to check if sound played with playSound is playing, use empty Ptr - virtual void pauseSounds(int types=static_cast(Type::Mask)) = 0; + virtual void pauseSounds(const std::string& blockerId, int types=int(Type::Mask)) = 0; ///< Pauses all currently playing sounds, including music. - virtual void resumeSounds(int types=static_cast(Type::Mask)) = 0; + virtual void resumeSounds(const std::string& blockerId) = 0; ///< Resumes all previously paused sounds. virtual void update(float duration) = 0; diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 87a1242342..ac2d5f27fa 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -1893,7 +1893,7 @@ namespace MWGui setCursorVisible(false); if (mVideoWidget->hasAudioStream()) - MWBase::Environment::get().getSoundManager()->pauseSounds( + MWBase::Environment::get().getSoundManager()->pauseSounds("Video", ~MWSound::Type::Movie & MWSound::Type::Mask ); osg::Timer frameTimer; @@ -1921,7 +1921,7 @@ namespace MWGui } mVideoWidget->stop(); - MWBase::Environment::get().getSoundManager()->resumeSounds(); + MWBase::Environment::get().getSoundManager()->resumeSounds("Video"); setKeyFocusWidget(oldKeyFocus); diff --git a/apps/openmw/mwsound/soundmanagerimp.cpp b/apps/openmw/mwsound/soundmanagerimp.cpp index 5c277d09e4..d8d59e2431 100644 --- a/apps/openmw/mwsound/soundmanagerimp.cpp +++ b/apps/openmw/mwsound/soundmanagerimp.cpp @@ -50,7 +50,6 @@ namespace MWSound , mListenerPos(0,0,0) , mListenerDir(1,0,0) , mListenerUp(0,0,1) - , mPausedSoundTypes(0) , mUnderwaterSound(nullptr) , mNearWaterSound(nullptr) { @@ -858,27 +857,36 @@ namespace MWSound } - void SoundManager::pauseSounds(int types) + void SoundManager::pauseSounds(const std::string& blockerId, int types) { if(mOutput->isInitialized()) { + auto found = mPausedSoundTypes.find(blockerId); + if (found != mPausedSoundTypes.end() && found->second != types) + resumeSounds(blockerId); + types = types & Type::Mask; mOutput->pauseSounds(types); - mPausedSoundTypes |= types; + mPausedSoundTypes[blockerId] = types; } } - void SoundManager::resumeSounds(int types) + void SoundManager::resumeSounds(const std::string& blockerId) { if(mOutput->isInitialized()) { - types = types & Type::Mask & mPausedSoundTypes; + mPausedSoundTypes.erase(blockerId); + int types = int(Type::Mask); + for (auto& blocker : mPausedSoundTypes) + { + if (blocker.first != blockerId) + types &= ~blocker.second; + } + mOutput->resumeSounds(types); - mPausedSoundTypes &= ~types; } } - void SoundManager::updateRegionSound(float duration) { static float sTimeToNextEnvSound = 0.0f; @@ -1399,5 +1407,6 @@ namespace MWSound mUnusedStreams.push_back(sound); } mActiveTracks.clear(); + mPausedSoundTypes.clear(); } } diff --git a/apps/openmw/mwsound/soundmanagerimp.hpp b/apps/openmw/mwsound/soundmanagerimp.hpp index 786e3a5a79..a8ebfeca31 100644 --- a/apps/openmw/mwsound/soundmanagerimp.hpp +++ b/apps/openmw/mwsound/soundmanagerimp.hpp @@ -107,7 +107,7 @@ namespace MWSound osg::Vec3f mListenerDir; osg::Vec3f mListenerUp; - int mPausedSoundTypes; + std::unordered_map mPausedSoundTypes; Sound *mUnderwaterSound; Sound *mNearWaterSound; @@ -244,10 +244,10 @@ namespace MWSound virtual bool getSoundPlaying(const MWWorld::ConstPtr &reference, const std::string& soundId) const; ///< Is the given sound currently playing on the given object? - virtual void pauseSounds(int types); + virtual void pauseSounds(const std::string& blockerId, int types=int(Type::Mask)); ///< Pauses all currently playing sounds, including music. - virtual void resumeSounds(int types); + virtual void resumeSounds(const std::string& blockerId); ///< Resumes all previously paused sounds. virtual void update(float duration); From 2254256db9a00172cc2ae07befea6037bf5309ae Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Tue, 31 Mar 2020 13:15:26 +0400 Subject: [PATCH 2/3] Pause both audio and video playback when the game is minimized (feature #4944) --- CHANGELOG.md | 1 + apps/openmw/engine.cpp | 5 ++++ apps/openmw/mwbase/soundmanager.hpp | 3 +++ apps/openmw/mwgui/videowidget.cpp | 15 ++++++++++++ apps/openmw/mwgui/videowidget.hpp | 4 ++++ apps/openmw/mwgui/windowmanagerimp.cpp | 6 +++++ apps/openmw/mwsound/openal_output.cpp | 32 +++++++++++++++++++++++++ apps/openmw/mwsound/openal_output.hpp | 3 +++ apps/openmw/mwsound/sound_output.hpp | 3 +++ apps/openmw/mwsound/soundmanagerimp.cpp | 23 ++++++++++++++++-- apps/openmw/mwsound/soundmanagerimp.hpp | 8 +++++-- 11 files changed, 99 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5d765c1bcc..e8c5f4de57 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -243,6 +243,7 @@ Feature #4882: Support for NiPalette node Feature #4887: Add openmw command option to set initial random seed Feature #4890: Make Distant Terrain configurable + Feature #4944: Pause audio when OpenMW is minimized Feature #4958: Support eight blood types Feature #4962: Add casting animations for magic items Feature #4968: Scalable UI widget skins diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 435db0841f..ec8c1e3059 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -96,7 +96,12 @@ bool OMW::Engine::frame(float frametime) // If we are not currently rendering, then RenderItems will not be reused resulting in a memory leak upon changing widget textures (fixed in MyGUI 3.3.2), // and destroyed widgets will not be deleted (not fixed yet, https://github.com/MyGUI/mygui/issues/21) if (!mEnvironment.getInputManager()->isWindowVisible()) + { + mEnvironment.getSoundManager()->pausePlayback(); return false; + } + else + mEnvironment.getSoundManager()->resumePlayback(); // sound if (mUseSound) diff --git a/apps/openmw/mwbase/soundmanager.hpp b/apps/openmw/mwbase/soundmanager.hpp index a780e5ccdd..ac3e05ac74 100644 --- a/apps/openmw/mwbase/soundmanager.hpp +++ b/apps/openmw/mwbase/soundmanager.hpp @@ -174,6 +174,9 @@ namespace MWBase virtual void resumeSounds(const std::string& blockerId) = 0; ///< Resumes all previously paused sounds. + virtual void pausePlayback() = 0; + virtual void resumePlayback() = 0; + virtual void update(float duration) = 0; virtual void setListenerPosDir(const osg::Vec3f &pos, const osg::Vec3f &dir, const osg::Vec3f &up, bool underwater) = 0; diff --git a/apps/openmw/mwgui/videowidget.cpp b/apps/openmw/mwgui/videowidget.cpp index 5dec6e48ad..2aea0018d5 100644 --- a/apps/openmw/mwgui/videowidget.cpp +++ b/apps/openmw/mwgui/videowidget.cpp @@ -76,6 +76,21 @@ void VideoWidget::stop() mPlayer->close(); } +void VideoWidget::pause() +{ + mPlayer->pause(); +} + +void VideoWidget::resume() +{ + mPlayer->play(); +} + +bool VideoWidget::isPaused() const +{ + return mPlayer->isPaused(); +} + bool VideoWidget::hasAudioStream() { return mPlayer->hasAudioStream(); diff --git a/apps/openmw/mwgui/videowidget.hpp b/apps/openmw/mwgui/videowidget.hpp index dadd1471a3..814b9ca73a 100644 --- a/apps/openmw/mwgui/videowidget.hpp +++ b/apps/openmw/mwgui/videowidget.hpp @@ -47,6 +47,10 @@ namespace MWGui /// Stop video and free resources (done automatically on destruction) void stop(); + void pause(); + void resume(); + bool isPaused() const; + /// Adjust the coordinates of this video widget relative to its parent, /// based on the dimensions of the playing video. /// @param stretch Stretch the video to fill the whole screen? If false, diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index ac2d5f27fa..c6bc06f8fc 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -1905,9 +1905,15 @@ namespace MWGui MWBase::Environment::get().getInputManager()->update(dt, true, false); if (!MWBase::Environment::get().getInputManager()->isWindowVisible()) + { + mVideoWidget->pause(); OpenThreads::Thread::microSleep(5000); + } else { + if (mVideoWidget->isPaused()) + mVideoWidget->resume(); + mViewer->eventTraversal(); mViewer->updateTraversal(); mViewer->renderingTraversals(); diff --git a/apps/openmw/mwsound/openal_output.cpp b/apps/openmw/mwsound/openal_output.cpp index 9c87cac190..5955187261 100644 --- a/apps/openmw/mwsound/openal_output.cpp +++ b/apps/openmw/mwsound/openal_output.cpp @@ -1454,6 +1454,38 @@ void OpenAL_Output::pauseSounds(int types) } } +void OpenAL_Output::pauseActiveDevice() +{ + if (mDevice == nullptr) + return; + + if(alcIsExtensionPresent(mDevice, "ALC_SOFT_PAUSE_DEVICE")) + { + LPALCDEVICEPAUSESOFT alcDevicePauseSOFT = 0; + getALCFunc(alcDevicePauseSOFT, mDevice, "alcDevicePauseSOFT"); + alcDevicePauseSOFT(mDevice); + getALCError(mDevice); + } + + alListenerf(AL_GAIN, 0.0f); +} + +void OpenAL_Output::resumeActiveDevice() +{ + if (mDevice == nullptr) + return; + + if(alcIsExtensionPresent(mDevice, "ALC_SOFT_PAUSE_DEVICE")) + { + LPALCDEVICERESUMESOFT alcDeviceResumeSOFT = 0; + getALCFunc(alcDeviceResumeSOFT, mDevice, "alcDeviceResumeSOFT"); + alcDeviceResumeSOFT(mDevice); + getALCError(mDevice); + } + + alListenerf(AL_GAIN, 1.0f); +} + void OpenAL_Output::resumeSounds(int types) { std::vector sources; diff --git a/apps/openmw/mwsound/openal_output.hpp b/apps/openmw/mwsound/openal_output.hpp index d91320ab4c..6039d97d60 100644 --- a/apps/openmw/mwsound/openal_output.hpp +++ b/apps/openmw/mwsound/openal_output.hpp @@ -92,6 +92,9 @@ namespace MWSound virtual void pauseSounds(int types); virtual void resumeSounds(int types); + virtual void pauseActiveDevice(); + virtual void resumeActiveDevice(); + OpenAL_Output(SoundManager &mgr); virtual ~OpenAL_Output(); }; diff --git a/apps/openmw/mwsound/sound_output.hpp b/apps/openmw/mwsound/sound_output.hpp index eb830c8d00..4075e36ccd 100644 --- a/apps/openmw/mwsound/sound_output.hpp +++ b/apps/openmw/mwsound/sound_output.hpp @@ -62,6 +62,9 @@ namespace MWSound virtual void pauseSounds(int types) = 0; virtual void resumeSounds(int types) = 0; + virtual void pauseActiveDevice() = 0; + virtual void resumeActiveDevice() = 0; + Sound_Output& operator=(const Sound_Output &rhs); Sound_Output(const Sound_Output &rhs); diff --git a/apps/openmw/mwsound/soundmanagerimp.cpp b/apps/openmw/mwsound/soundmanagerimp.cpp index d8d59e2431..8462f09295 100644 --- a/apps/openmw/mwsound/soundmanagerimp.cpp +++ b/apps/openmw/mwsound/soundmanagerimp.cpp @@ -52,6 +52,7 @@ namespace MWSound , mListenerUp(0,0,1) , mUnderwaterSound(nullptr) , mNearWaterSound(nullptr) + , mPlaybackPaused(false) { mMasterVolume = Settings::Manager::getFloat("master volume", "Sound"); mMasterVolume = std::min(std::max(mMasterVolume, 0.0f), 1.0f); @@ -856,7 +857,6 @@ namespace MWSound return false; } - void SoundManager::pauseSounds(const std::string& blockerId, int types) { if(mOutput->isInitialized()) @@ -887,6 +887,24 @@ namespace MWSound } } + void SoundManager::pausePlayback() + { + if (mPlaybackPaused) + return; + + mPlaybackPaused = true; + mOutput->pauseActiveDevice(); + } + + void SoundManager::resumePlayback() + { + if (!mPlaybackPaused) + return; + + mPlaybackPaused = false; + mOutput->resumeActiveDevice(); + } + void SoundManager::updateRegionSound(float duration) { static float sTimeToNextEnvSound = 0.0f; @@ -1209,7 +1227,7 @@ namespace MWSound void SoundManager::update(float duration) { - if(!mOutput->isInitialized()) + if(!mOutput->isInitialized() || mPlaybackPaused) return; updateSounds(duration); @@ -1408,5 +1426,6 @@ namespace MWSound } mActiveTracks.clear(); mPausedSoundTypes.clear(); + mPlaybackPaused = false; } } diff --git a/apps/openmw/mwsound/soundmanagerimp.hpp b/apps/openmw/mwsound/soundmanagerimp.hpp index a8ebfeca31..b2ccc702a8 100644 --- a/apps/openmw/mwsound/soundmanagerimp.hpp +++ b/apps/openmw/mwsound/soundmanagerimp.hpp @@ -112,6 +112,9 @@ namespace MWSound Sound *mUnderwaterSound; Sound *mNearWaterSound; + std::string mNextMusic; + bool mPlaybackPaused; + Sound_Buffer *insertSound(const std::string &soundId, const ESM::Sound *sound); Sound_Buffer *lookupSound(const std::string &soundId) const; @@ -134,8 +137,6 @@ namespace MWSound void updateWaterSound(float duration); void updateMusic(float duration); - std::string mNextMusic; - float volumeFromType(Type type) const; SoundManager(const SoundManager &rhs); @@ -250,6 +251,9 @@ namespace MWSound virtual void resumeSounds(const std::string& blockerId); ///< Resumes all previously paused sounds. + virtual void pausePlayback(); + virtual void resumePlayback(); + virtual void update(float duration); virtual void setListenerPosDir(const osg::Vec3f &pos, const osg::Vec3f &dir, const osg::Vec3f &up, bool underwater); From e4447669018c20e70565402f22aca98a309b126e Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Sun, 5 Apr 2020 18:10:05 +0400 Subject: [PATCH 3/3] Use enums for blockers IDs instead of strings --- apps/openmw/mwbase/soundmanager.hpp | 12 ++++++++++-- apps/openmw/mwgui/windowmanagerimp.cpp | 5 +++-- apps/openmw/mwsound/soundmanagerimp.cpp | 21 ++++++++++----------- apps/openmw/mwsound/soundmanagerimp.hpp | 6 +++--- 4 files changed, 26 insertions(+), 18 deletions(-) diff --git a/apps/openmw/mwbase/soundmanager.hpp b/apps/openmw/mwbase/soundmanager.hpp index ac3e05ac74..cc933d4bbb 100644 --- a/apps/openmw/mwbase/soundmanager.hpp +++ b/apps/openmw/mwbase/soundmanager.hpp @@ -14,6 +14,14 @@ namespace MWWorld namespace MWSound { + // Each entry excepts of MaxCount should be used only in one place + enum BlockerType + { + VideoPlayback, + + MaxCount + }; + class Sound; class Stream; struct Sound_Decoder; @@ -168,10 +176,10 @@ namespace MWBase ///< Is the given sound currently playing on the given object? /// If you want to check if sound played with playSound is playing, use empty Ptr - virtual void pauseSounds(const std::string& blockerId, int types=int(Type::Mask)) = 0; + virtual void pauseSounds(MWSound::BlockerType blocker, int types=int(Type::Mask)) = 0; ///< Pauses all currently playing sounds, including music. - virtual void resumeSounds(const std::string& blockerId) = 0; + virtual void resumeSounds(MWSound::BlockerType blocker) = 0; ///< Resumes all previously paused sounds. virtual void pausePlayback() = 0; diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index c6bc06f8fc..4f36032e1d 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -1893,9 +1893,10 @@ namespace MWGui setCursorVisible(false); if (mVideoWidget->hasAudioStream()) - MWBase::Environment::get().getSoundManager()->pauseSounds("Video", + MWBase::Environment::get().getSoundManager()->pauseSounds(MWSound::VideoPlayback, ~MWSound::Type::Movie & MWSound::Type::Mask ); + osg::Timer frameTimer; while (mVideoWidget->update() && !MWBase::Environment::get().getStateManager()->hasQuitRequest()) { @@ -1927,7 +1928,7 @@ namespace MWGui } mVideoWidget->stop(); - MWBase::Environment::get().getSoundManager()->resumeSounds("Video"); + MWBase::Environment::get().getSoundManager()->resumeSounds(MWSound::VideoPlayback); setKeyFocusWidget(oldKeyFocus); diff --git a/apps/openmw/mwsound/soundmanagerimp.cpp b/apps/openmw/mwsound/soundmanagerimp.cpp index 8462f09295..eff1cf0fda 100644 --- a/apps/openmw/mwsound/soundmanagerimp.cpp +++ b/apps/openmw/mwsound/soundmanagerimp.cpp @@ -857,30 +857,29 @@ namespace MWSound return false; } - void SoundManager::pauseSounds(const std::string& blockerId, int types) + void SoundManager::pauseSounds(BlockerType blocker, int types) { if(mOutput->isInitialized()) { - auto found = mPausedSoundTypes.find(blockerId); - if (found != mPausedSoundTypes.end() && found->second != types) - resumeSounds(blockerId); + if (mPausedSoundTypes[blocker] != 0) + resumeSounds(blocker); types = types & Type::Mask; mOutput->pauseSounds(types); - mPausedSoundTypes[blockerId] = types; + mPausedSoundTypes[blocker] = types; } } - void SoundManager::resumeSounds(const std::string& blockerId) + void SoundManager::resumeSounds(BlockerType blocker) { if(mOutput->isInitialized()) { - mPausedSoundTypes.erase(blockerId); + mPausedSoundTypes[blocker] = 0; int types = int(Type::Mask); - for (auto& blocker : mPausedSoundTypes) + for (int currentBlocker = 0; currentBlocker < BlockerType::MaxCount; currentBlocker++) { - if (blocker.first != blockerId) - types &= ~blocker.second; + if (currentBlocker != blocker) + types &= ~mPausedSoundTypes[currentBlocker]; } mOutput->resumeSounds(types); @@ -1425,7 +1424,7 @@ namespace MWSound mUnusedStreams.push_back(sound); } mActiveTracks.clear(); - mPausedSoundTypes.clear(); mPlaybackPaused = false; + std::fill(std::begin(mPausedSoundTypes), std::end(mPausedSoundTypes), 0); } } diff --git a/apps/openmw/mwsound/soundmanagerimp.hpp b/apps/openmw/mwsound/soundmanagerimp.hpp index b2ccc702a8..55588d06fa 100644 --- a/apps/openmw/mwsound/soundmanagerimp.hpp +++ b/apps/openmw/mwsound/soundmanagerimp.hpp @@ -107,7 +107,7 @@ namespace MWSound osg::Vec3f mListenerDir; osg::Vec3f mListenerUp; - std::unordered_map mPausedSoundTypes; + int mPausedSoundTypes[BlockerType::MaxCount] = {}; Sound *mUnderwaterSound; Sound *mNearWaterSound; @@ -245,10 +245,10 @@ namespace MWSound virtual bool getSoundPlaying(const MWWorld::ConstPtr &reference, const std::string& soundId) const; ///< Is the given sound currently playing on the given object? - virtual void pauseSounds(const std::string& blockerId, int types=int(Type::Mask)); + virtual void pauseSounds(MWSound::BlockerType blocker, int types=int(Type::Mask)); ///< Pauses all currently playing sounds, including music. - virtual void resumeSounds(const std::string& blockerId); + virtual void resumeSounds(MWSound::BlockerType blocker); ///< Resumes all previously paused sounds. virtual void pausePlayback();