mirror of
https://github.com/OpenMW/openmw.git
synced 2025-04-01 19:36:40 +00:00
Merge branch 'vfs_normalized_path_6' into 'master'
Use normalized path for music related functions See merge request OpenMW/openmw!4038
This commit is contained in:
commit
023c0f7a46
29 changed files with 159 additions and 80 deletions
|
@ -115,7 +115,7 @@ void readVFS(std::unique_ptr<VFS::Archive>&& archive, const std::filesystem::pat
|
||||||
vfs.addArchive(std::move(archive));
|
vfs.addArchive(std::move(archive));
|
||||||
vfs.buildIndex();
|
vfs.buildIndex();
|
||||||
|
|
||||||
for (const auto& name : vfs.getRecursiveDirectoryIterator(""))
|
for (const auto& name : vfs.getRecursiveDirectoryIterator())
|
||||||
{
|
{
|
||||||
if (isNIF(name.value()) || isMaterial(name.value()))
|
if (isNIF(name.value()) || isMaterial(name.value()))
|
||||||
{
|
{
|
||||||
|
|
|
@ -28,7 +28,7 @@ void CSMWorld::Resources::recreate(const VFS::Manager* vfs, const char* const* e
|
||||||
|
|
||||||
size_t baseSize = mBaseDirectory.size();
|
size_t baseSize = mBaseDirectory.size();
|
||||||
|
|
||||||
for (const auto& filepath : vfs->getRecursiveDirectoryIterator(""))
|
for (const auto& filepath : vfs->getRecursiveDirectoryIterator())
|
||||||
{
|
{
|
||||||
const std::string_view view = filepath.view();
|
const std::string_view view = filepath.view();
|
||||||
if (view.size() < baseSize + 1 || !view.starts_with(mBaseDirectory) || view[baseSize] != '/')
|
if (view.size() < baseSize + 1 || !view.starts_with(mBaseDirectory) || view[baseSize] != '/')
|
||||||
|
|
|
@ -64,6 +64,7 @@
|
||||||
#include "mwscript/interpretercontext.hpp"
|
#include "mwscript/interpretercontext.hpp"
|
||||||
#include "mwscript/scriptmanagerimp.hpp"
|
#include "mwscript/scriptmanagerimp.hpp"
|
||||||
|
|
||||||
|
#include "mwsound/constants.hpp"
|
||||||
#include "mwsound/soundmanagerimp.hpp"
|
#include "mwsound/soundmanagerimp.hpp"
|
||||||
|
|
||||||
#include "mwworld/class.hpp"
|
#include "mwworld/class.hpp"
|
||||||
|
@ -987,9 +988,8 @@ void OMW::Engine::go()
|
||||||
// start in main menu
|
// start in main menu
|
||||||
mWindowManager->pushGuiMode(MWGui::GM_MainMenu);
|
mWindowManager->pushGuiMode(MWGui::GM_MainMenu);
|
||||||
|
|
||||||
std::string titlefile = "music/special/morrowind title.mp3";
|
if (mVFS->exists(MWSound::titleMusic))
|
||||||
if (mVFS->exists(titlefile))
|
mSoundManager->streamMusic(MWSound::titleMusic, MWSound::MusicType::Special);
|
||||||
mSoundManager->streamMusic(titlefile, MWSound::MusicType::Special);
|
|
||||||
else
|
else
|
||||||
Log(Debug::Warning) << "Title music not found";
|
Log(Debug::Warning) << "Title music not found";
|
||||||
|
|
||||||
|
|
|
@ -117,7 +117,7 @@ namespace MWBase
|
||||||
virtual void stopMusic() = 0;
|
virtual void stopMusic() = 0;
|
||||||
///< Stops music if it's playing
|
///< Stops music if it's playing
|
||||||
|
|
||||||
virtual void streamMusic(const std::string& filename, MWSound::MusicType type, float fade = 1.f) = 0;
|
virtual void streamMusic(VFS::Path::NormalizedView filename, MWSound::MusicType type, float fade = 1.f) = 0;
|
||||||
///< Play a soundifle
|
///< Play a soundifle
|
||||||
/// \param filename name of a sound file in the data directory.
|
/// \param filename name of a sound file in the data directory.
|
||||||
/// \param type music type.
|
/// \param type music type.
|
||||||
|
@ -126,7 +126,7 @@ namespace MWBase
|
||||||
virtual bool isMusicPlaying() = 0;
|
virtual bool isMusicPlaying() = 0;
|
||||||
///< Returns true if music is playing
|
///< Returns true if music is playing
|
||||||
|
|
||||||
virtual void playPlaylist(const std::string& playlist) = 0;
|
virtual void playPlaylist(VFS::Path::NormalizedView playlist) = 0;
|
||||||
///< Start playing music from the selected folder
|
///< Start playing music from the selected folder
|
||||||
/// \param name of the folder that contains the playlist
|
/// \param name of the folder that contains the playlist
|
||||||
/// Title music playlist is predefined
|
/// Title music playlist is predefined
|
||||||
|
|
|
@ -22,6 +22,8 @@
|
||||||
#include "../mwmechanics/creaturestats.hpp"
|
#include "../mwmechanics/creaturestats.hpp"
|
||||||
#include "../mwmechanics/npcstats.hpp"
|
#include "../mwmechanics/npcstats.hpp"
|
||||||
|
|
||||||
|
#include "../mwsound/constants.hpp"
|
||||||
|
|
||||||
#include "class.hpp"
|
#include "class.hpp"
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
|
@ -216,8 +218,7 @@ namespace MWGui
|
||||||
center();
|
center();
|
||||||
|
|
||||||
// Play LevelUp Music
|
// Play LevelUp Music
|
||||||
MWBase::Environment::get().getSoundManager()->streamMusic(
|
MWBase::Environment::get().getSoundManager()->streamMusic(MWSound::triumphMusic, MWSound::MusicType::Special);
|
||||||
"Music/Special/MW_Triumph.mp3", MWSound::MusicType::Special);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void LevelupDialog::onOkButtonClicked(MyGUI::Widget* sender)
|
void LevelupDialog::onOkButtonClicked(MyGUI::Widget* sender)
|
||||||
|
|
|
@ -67,7 +67,8 @@ namespace MWGui
|
||||||
!= supported_extensions.end();
|
!= supported_extensions.end();
|
||||||
};
|
};
|
||||||
|
|
||||||
for (const auto& name : mResourceSystem->getVFS()->getRecursiveDirectoryIterator("Splash/"))
|
constexpr VFS::Path::NormalizedView splash("splash/");
|
||||||
|
for (const auto& name : mResourceSystem->getVFS()->getRecursiveDirectoryIterator(splash))
|
||||||
{
|
{
|
||||||
if (isSupportedExtension(Misc::getFileExtension(name)))
|
if (isSupportedExtension(Misc::getFileExtension(name)))
|
||||||
mSplashScreens.push_back(name);
|
mSplashScreens.push_back(name);
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
|
|
||||||
#include <components/settings/values.hpp>
|
#include <components/settings/values.hpp>
|
||||||
#include <components/vfs/manager.hpp>
|
#include <components/vfs/manager.hpp>
|
||||||
|
#include <components/vfs/pathutil.hpp>
|
||||||
#include <components/widgets/imagebutton.hpp>
|
#include <components/widgets/imagebutton.hpp>
|
||||||
|
|
||||||
#include "../mwbase/environment.hpp"
|
#include "../mwbase/environment.hpp"
|
||||||
|
@ -37,7 +38,9 @@ namespace MWGui
|
||||||
getWidget(mVersionText, "VersionText");
|
getWidget(mVersionText, "VersionText");
|
||||||
mVersionText->setCaption(versionDescription);
|
mVersionText->setCaption(versionDescription);
|
||||||
|
|
||||||
mHasAnimatedMenu = mVFS->exists("video/menu_background.bik");
|
constexpr VFS::Path::NormalizedView menuBackgroundVideo("video/menu_background.bik");
|
||||||
|
|
||||||
|
mHasAnimatedMenu = mVFS->exists(menuBackgroundVideo);
|
||||||
|
|
||||||
updateMenu();
|
updateMenu();
|
||||||
}
|
}
|
||||||
|
|
|
@ -402,7 +402,8 @@ namespace MWGui
|
||||||
|
|
||||||
std::vector<std::string> availableLanguages;
|
std::vector<std::string> availableLanguages;
|
||||||
const VFS::Manager* vfs = MWBase::Environment::get().getResourceSystem()->getVFS();
|
const VFS::Manager* vfs = MWBase::Environment::get().getResourceSystem()->getVFS();
|
||||||
for (const auto& path : vfs->getRecursiveDirectoryIterator("l10n/"))
|
constexpr VFS::Path::NormalizedView l10n("l10n/");
|
||||||
|
for (const auto& path : vfs->getRecursiveDirectoryIterator(l10n))
|
||||||
{
|
{
|
||||||
if (Misc::getFileExtension(path) == "yaml")
|
if (Misc::getFileExtension(path) == "yaml")
|
||||||
{
|
{
|
||||||
|
|
|
@ -356,7 +356,8 @@ namespace MWGui
|
||||||
mWindows.push_back(std::move(console));
|
mWindows.push_back(std::move(console));
|
||||||
trackWindow(mConsole, makeConsoleWindowSettingValues());
|
trackWindow(mConsole, makeConsoleWindowSettingValues());
|
||||||
|
|
||||||
bool questList = mResourceSystem->getVFS()->exists("textures/tx_menubook_options_over.dds");
|
constexpr VFS::Path::NormalizedView menubookOptionsOverTexture("textures/tx_menubook_options_over.dds");
|
||||||
|
const bool questList = mResourceSystem->getVFS()->exists(menubookOptionsOverTexture);
|
||||||
auto journal = JournalWindow::create(JournalViewModel::create(), questList, mEncoding);
|
auto journal = JournalWindow::create(JournalViewModel::create(), questList, mEncoding);
|
||||||
mGuiModeStates[GM_Journal] = GuiModeState(journal.get());
|
mGuiModeStates[GM_Journal] = GuiModeState(journal.get());
|
||||||
mWindows.push_back(std::move(journal));
|
mWindows.push_back(std::move(journal));
|
||||||
|
|
|
@ -141,7 +141,7 @@ namespace MWLua
|
||||||
api["streamMusic"] = [](std::string_view fileName, const sol::optional<sol::table>& options) {
|
api["streamMusic"] = [](std::string_view fileName, const sol::optional<sol::table>& options) {
|
||||||
auto args = getStreamMusicArgs(options);
|
auto args = getStreamMusicArgs(options);
|
||||||
MWBase::SoundManager* sndMgr = MWBase::Environment::get().getSoundManager();
|
MWBase::SoundManager* sndMgr = MWBase::Environment::get().getSoundManager();
|
||||||
sndMgr->streamMusic(std::string(fileName), MWSound::MusicType::Scripted, args.mFade);
|
sndMgr->streamMusic(VFS::Path::Normalized(fileName), MWSound::MusicType::Scripted, args.mFade);
|
||||||
};
|
};
|
||||||
|
|
||||||
api["say"]
|
api["say"]
|
||||||
|
|
|
@ -39,6 +39,8 @@
|
||||||
|
|
||||||
#include "../mwrender/vismask.hpp"
|
#include "../mwrender/vismask.hpp"
|
||||||
|
|
||||||
|
#include "../mwsound/constants.hpp"
|
||||||
|
|
||||||
#include "actor.hpp"
|
#include "actor.hpp"
|
||||||
#include "actorutil.hpp"
|
#include "actorutil.hpp"
|
||||||
#include "aicombataction.hpp"
|
#include "aicombataction.hpp"
|
||||||
|
@ -1798,7 +1800,7 @@ namespace MWMechanics
|
||||||
MWBase::Environment::get().getStateManager()->askLoadRecent();
|
MWBase::Environment::get().getStateManager()->askLoadRecent();
|
||||||
// Play Death Music if it was the player dying
|
// Play Death Music if it was the player dying
|
||||||
MWBase::Environment::get().getSoundManager()->streamMusic(
|
MWBase::Environment::get().getSoundManager()->streamMusic(
|
||||||
"Music/Special/MW_Death.mp3", MWSound::MusicType::Special);
|
MWSound::deathMusic, MWSound::MusicType::Special);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
|
@ -29,6 +29,8 @@
|
||||||
#include "../mwbase/windowmanager.hpp"
|
#include "../mwbase/windowmanager.hpp"
|
||||||
#include "../mwbase/world.hpp"
|
#include "../mwbase/world.hpp"
|
||||||
|
|
||||||
|
#include "../mwsound/constants.hpp"
|
||||||
|
|
||||||
#include "actor.hpp"
|
#include "actor.hpp"
|
||||||
#include "actors.hpp"
|
#include "actors.hpp"
|
||||||
#include "actorutil.hpp"
|
#include "actorutil.hpp"
|
||||||
|
@ -1678,12 +1680,12 @@ namespace MWMechanics
|
||||||
if (mMusicType != MWSound::MusicType::Explore && !hasHostiles
|
if (mMusicType != MWSound::MusicType::Explore && !hasHostiles
|
||||||
&& !(player.getClass().getCreatureStats(player).isDead() && musicPlaying))
|
&& !(player.getClass().getCreatureStats(player).isDead() && musicPlaying))
|
||||||
{
|
{
|
||||||
MWBase::Environment::get().getSoundManager()->playPlaylist(std::string("Explore"));
|
MWBase::Environment::get().getSoundManager()->playPlaylist(MWSound::explorePlaylist);
|
||||||
mMusicType = MWSound::MusicType::Explore;
|
mMusicType = MWSound::MusicType::Explore;
|
||||||
}
|
}
|
||||||
else if (mMusicType != MWSound::MusicType::Battle && hasHostiles)
|
else if (mMusicType != MWSound::MusicType::Battle && hasHostiles)
|
||||||
{
|
{
|
||||||
MWBase::Environment::get().getSoundManager()->playPlaylist(std::string("Battle"));
|
MWBase::Environment::get().getSoundManager()->playPlaylist(MWSound::battlePlaylist);
|
||||||
mMusicType = MWSound::MusicType::Battle;
|
mMusicType = MWSound::MusicType::Battle;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -63,7 +63,7 @@ namespace MWScript
|
||||||
public:
|
public:
|
||||||
void execute(Interpreter::Runtime& runtime) override
|
void execute(Interpreter::Runtime& runtime) override
|
||||||
{
|
{
|
||||||
std::string music{ runtime.getStringLiteral(runtime[0].mInteger) };
|
const VFS::Path::Normalized music(runtime.getStringLiteral(runtime[0].mInteger));
|
||||||
runtime.pop();
|
runtime.pop();
|
||||||
|
|
||||||
MWBase::Environment::get().getSoundManager()->streamMusic(
|
MWBase::Environment::get().getSoundManager()->streamMusic(
|
||||||
|
|
15
apps/openmw/mwsound/constants.hpp
Normal file
15
apps/openmw/mwsound/constants.hpp
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
#ifndef OPENMW_APPS_OPENMW_MWSOUND_CONSTANTS_H
|
||||||
|
#define OPENMW_APPS_OPENMW_MWSOUND_CONSTANTS_H
|
||||||
|
|
||||||
|
#include <components/vfs/pathutil.hpp>
|
||||||
|
|
||||||
|
namespace MWSound
|
||||||
|
{
|
||||||
|
constexpr VFS::Path::NormalizedView battlePlaylist("battle");
|
||||||
|
constexpr VFS::Path::NormalizedView explorePlaylist("explore");
|
||||||
|
constexpr VFS::Path::NormalizedView titleMusic("music/special/morrowind title.mp3");
|
||||||
|
constexpr VFS::Path::NormalizedView triumphMusic("music/special/mw_triumph.mp3");
|
||||||
|
constexpr VFS::Path::NormalizedView deathMusic("music/special/mw_death.mp3");
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -208,7 +208,7 @@ namespace MWSound
|
||||||
return dec;
|
return dec;
|
||||||
}
|
}
|
||||||
|
|
||||||
void FFmpeg_Decoder::open(const std::string& fname)
|
void FFmpeg_Decoder::open(VFS::Path::NormalizedView fname)
|
||||||
{
|
{
|
||||||
close();
|
close();
|
||||||
mDataStream = mResourceMgr->get(fname);
|
mDataStream = mResourceMgr->get(fname);
|
||||||
|
@ -224,7 +224,7 @@ namespace MWSound
|
||||||
formatCtx->pb = ioCtx.get();
|
formatCtx->pb = ioCtx.get();
|
||||||
|
|
||||||
// avformat_open_input frees user supplied AVFormatContext on failure
|
// avformat_open_input frees user supplied AVFormatContext on failure
|
||||||
if (avformat_open_input(&formatCtx, fname.c_str(), nullptr, nullptr) != 0)
|
if (avformat_open_input(&formatCtx, fname.value().data(), nullptr, nullptr) != 0)
|
||||||
throw std::runtime_error("Failed to open input");
|
throw std::runtime_error("Failed to open input");
|
||||||
|
|
||||||
AVFormatContextPtr formatCtxPtr(std::exchange(formatCtx, nullptr));
|
AVFormatContextPtr formatCtxPtr(std::exchange(formatCtx, nullptr));
|
||||||
|
|
|
@ -93,7 +93,7 @@ namespace MWSound
|
||||||
bool getAVAudioData();
|
bool getAVAudioData();
|
||||||
size_t readAVAudioData(void* data, size_t length);
|
size_t readAVAudioData(void* data, size_t length);
|
||||||
|
|
||||||
void open(const std::string& fname) override;
|
void open(VFS::Path::NormalizedView fname) override;
|
||||||
void close() override;
|
void close() override;
|
||||||
|
|
||||||
std::string getName() override;
|
std::string getName() override;
|
||||||
|
|
|
@ -24,8 +24,10 @@ namespace MWSound
|
||||||
private:
|
private:
|
||||||
MWSound::MovieAudioDecoder* mDecoder;
|
MWSound::MovieAudioDecoder* mDecoder;
|
||||||
|
|
||||||
void open(const std::string& fname) override;
|
void open(VFS::Path::NormalizedView fname) override { throw std::runtime_error("Method not implemented"); }
|
||||||
void close() override;
|
|
||||||
|
void close() override {}
|
||||||
|
|
||||||
std::string getName() override;
|
std::string getName() override;
|
||||||
void getInfo(int* samplerate, ChannelConfig* chans, SampleType* type) override;
|
void getInfo(int* samplerate, ChannelConfig* chans, SampleType* type) override;
|
||||||
size_t read(char* buffer, size_t bytes) override;
|
size_t read(char* buffer, size_t bytes) override;
|
||||||
|
@ -92,12 +94,6 @@ namespace MWSound
|
||||||
std::shared_ptr<MWSoundDecoderBridge> mDecoderBridge;
|
std::shared_ptr<MWSoundDecoderBridge> mDecoderBridge;
|
||||||
};
|
};
|
||||||
|
|
||||||
void MWSoundDecoderBridge::open(const std::string& fname)
|
|
||||||
{
|
|
||||||
throw std::runtime_error("Method not implemented");
|
|
||||||
}
|
|
||||||
void MWSoundDecoderBridge::close() {}
|
|
||||||
|
|
||||||
std::string MWSoundDecoderBridge::getName()
|
std::string MWSoundDecoderBridge::getName()
|
||||||
{
|
{
|
||||||
return mDecoder->getStreamName();
|
return mDecoder->getStreamName();
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
#ifndef GAME_SOUND_SOUND_DECODER_H
|
#ifndef GAME_SOUND_SOUND_DECODER_H
|
||||||
#define GAME_SOUND_SOUND_DECODER_H
|
#define GAME_SOUND_SOUND_DECODER_H
|
||||||
|
|
||||||
|
#include <components/vfs/pathutil.hpp>
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
@ -36,7 +38,7 @@ namespace MWSound
|
||||||
{
|
{
|
||||||
const VFS::Manager* mResourceMgr;
|
const VFS::Manager* mResourceMgr;
|
||||||
|
|
||||||
virtual void open(const std::string& fname) = 0;
|
virtual void open(VFS::Path::NormalizedView fname) = 0;
|
||||||
virtual void close() = 0;
|
virtual void close() = 0;
|
||||||
|
|
||||||
virtual std::string getName() = 0;
|
virtual std::string getName() = 0;
|
||||||
|
|
|
@ -26,14 +26,14 @@
|
||||||
|
|
||||||
#include "../mwmechanics/actorutil.hpp"
|
#include "../mwmechanics/actorutil.hpp"
|
||||||
|
|
||||||
|
#include "constants.hpp"
|
||||||
|
#include "ffmpeg_decoder.hpp"
|
||||||
|
#include "openal_output.hpp"
|
||||||
#include "sound.hpp"
|
#include "sound.hpp"
|
||||||
#include "sound_buffer.hpp"
|
#include "sound_buffer.hpp"
|
||||||
#include "sound_decoder.hpp"
|
#include "sound_decoder.hpp"
|
||||||
#include "sound_output.hpp"
|
#include "sound_output.hpp"
|
||||||
|
|
||||||
#include "ffmpeg_decoder.hpp"
|
|
||||||
#include "openal_output.hpp"
|
|
||||||
|
|
||||||
namespace MWSound
|
namespace MWSound
|
||||||
{
|
{
|
||||||
namespace
|
namespace
|
||||||
|
@ -252,13 +252,13 @@ namespace MWSound
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SoundManager::streamMusicFull(const std::string& filename)
|
void SoundManager::streamMusicFull(VFS::Path::NormalizedView filename)
|
||||||
{
|
{
|
||||||
if (!mOutput->isInitialized())
|
if (!mOutput->isInitialized())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
stopMusic();
|
stopMusic();
|
||||||
if (filename.empty())
|
if (filename.value().empty())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
Log(Debug::Info) << "Playing \"" << filename << "\"";
|
Log(Debug::Info) << "Playing \"" << filename << "\"";
|
||||||
|
@ -269,7 +269,7 @@ namespace MWSound
|
||||||
{
|
{
|
||||||
decoder->open(filename);
|
decoder->open(filename);
|
||||||
}
|
}
|
||||||
catch (std::exception& e)
|
catch (const std::exception& e)
|
||||||
{
|
{
|
||||||
Log(Debug::Error) << "Failed to load audio from \"" << filename << "\": " << e.what();
|
Log(Debug::Error) << "Failed to load audio from \"" << filename << "\": " << e.what();
|
||||||
return;
|
return;
|
||||||
|
@ -285,7 +285,7 @@ namespace MWSound
|
||||||
mOutput->streamSound(std::move(decoder), mMusic.get());
|
mOutput->streamSound(std::move(decoder), mMusic.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
void SoundManager::advanceMusic(const std::string& filename, float fadeOut)
|
void SoundManager::advanceMusic(VFS::Path::NormalizedView filename, float fadeOut)
|
||||||
{
|
{
|
||||||
if (!isMusicPlaying())
|
if (!isMusicPlaying())
|
||||||
{
|
{
|
||||||
|
@ -300,13 +300,16 @@ namespace MWSound
|
||||||
|
|
||||||
void SoundManager::startRandomTitle()
|
void SoundManager::startRandomTitle()
|
||||||
{
|
{
|
||||||
const std::vector<std::string>& filelist = mMusicFiles[mCurrentPlaylist];
|
const auto playlist = mMusicFiles.find(mCurrentPlaylist);
|
||||||
if (filelist.empty())
|
|
||||||
|
if (playlist == mMusicFiles.end() || playlist->second.empty())
|
||||||
{
|
{
|
||||||
advanceMusic(std::string());
|
advanceMusic(VFS::Path::NormalizedView());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const std::vector<VFS::Path::Normalized>& filelist = playlist->second;
|
||||||
|
|
||||||
auto& tracklist = mMusicToPlay[mCurrentPlaylist];
|
auto& tracklist = mMusicToPlay[mCurrentPlaylist];
|
||||||
|
|
||||||
// Do a Fisher-Yates shuffle
|
// Do a Fisher-Yates shuffle
|
||||||
|
@ -335,7 +338,7 @@ namespace MWSound
|
||||||
return mMusic && mOutput->isStreamPlaying(mMusic.get());
|
return mMusic && mOutput->isStreamPlaying(mMusic.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
void SoundManager::streamMusic(const std::string& filename, MusicType type, float fade)
|
void SoundManager::streamMusic(VFS::Path::NormalizedView filename, MusicType type, float fade)
|
||||||
{
|
{
|
||||||
const auto mechanicsManager = MWBase::Environment::get().getMechanicsManager();
|
const auto mechanicsManager = MWBase::Environment::get().getMechanicsManager();
|
||||||
|
|
||||||
|
@ -344,35 +347,36 @@ namespace MWSound
|
||||||
&& type != MusicType::Special)
|
&& type != MusicType::Special)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
std::string normalizedName = VFS::Path::normalizeFilename(filename);
|
|
||||||
|
|
||||||
mechanicsManager->setMusicType(type);
|
mechanicsManager->setMusicType(type);
|
||||||
advanceMusic(normalizedName, fade);
|
advanceMusic(filename, fade);
|
||||||
if (type == MWSound::MusicType::Battle)
|
if (type == MWSound::MusicType::Battle)
|
||||||
mCurrentPlaylist = "Battle";
|
mCurrentPlaylist = battlePlaylist;
|
||||||
else if (type == MWSound::MusicType::Explore)
|
else if (type == MWSound::MusicType::Explore)
|
||||||
mCurrentPlaylist = "Explore";
|
mCurrentPlaylist = explorePlaylist;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SoundManager::playPlaylist(const std::string& playlist)
|
void SoundManager::playPlaylist(VFS::Path::NormalizedView playlist)
|
||||||
{
|
{
|
||||||
if (mCurrentPlaylist == playlist)
|
if (mCurrentPlaylist == playlist)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (mMusicFiles.find(playlist) == mMusicFiles.end())
|
auto it = mMusicFiles.find(playlist);
|
||||||
|
|
||||||
|
if (it == mMusicFiles.end())
|
||||||
{
|
{
|
||||||
std::vector<std::string> filelist;
|
std::vector<VFS::Path::Normalized> filelist;
|
||||||
auto playlistPath = Misc::ResourceHelpers::correctMusicPath(playlist) + '/';
|
const VFS::Path::Normalized playlistPath
|
||||||
for (const auto& name : mVFS->getRecursiveDirectoryIterator(playlistPath))
|
= Misc::ResourceHelpers::correctMusicPath(playlist) / VFS::Path::NormalizedView();
|
||||||
|
for (const auto& name : mVFS->getRecursiveDirectoryIterator(VFS::Path::NormalizedView(playlistPath)))
|
||||||
filelist.push_back(name);
|
filelist.push_back(name);
|
||||||
|
|
||||||
mMusicFiles[playlist] = std::move(filelist);
|
it = mMusicFiles.emplace_hint(it, playlist, std::move(filelist));
|
||||||
}
|
}
|
||||||
|
|
||||||
// No Battle music? Use Explore playlist
|
// No Battle music? Use Explore playlist
|
||||||
if (playlist == "Battle" && mMusicFiles[playlist].empty())
|
if (playlist == battlePlaylist && it->second.empty())
|
||||||
{
|
{
|
||||||
playPlaylist("Explore");
|
playPlaylist(explorePlaylist);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1019,7 +1023,7 @@ namespace MWSound
|
||||||
mTimePassed = 0.0f;
|
mTimePassed = 0.0f;
|
||||||
|
|
||||||
// Make sure music is still playing
|
// Make sure music is still playing
|
||||||
if (!isMusicPlaying() && !mCurrentPlaylist.empty())
|
if (!isMusicPlaying() && !mCurrentPlaylist.value().empty())
|
||||||
startRandomTitle();
|
startRandomTitle();
|
||||||
|
|
||||||
Environment env = Env_Normal;
|
Environment env = Env_Normal;
|
||||||
|
@ -1137,10 +1141,10 @@ namespace MWSound
|
||||||
if (!mMusic || !mMusic->updateFade(duration) || !mOutput->isStreamPlaying(mMusic.get()))
|
if (!mMusic || !mMusic->updateFade(duration) || !mOutput->isStreamPlaying(mMusic.get()))
|
||||||
{
|
{
|
||||||
stopMusic();
|
stopMusic();
|
||||||
if (!mNextMusic.empty())
|
if (!mNextMusic.value().empty())
|
||||||
{
|
{
|
||||||
streamMusicFull(mNextMusic);
|
streamMusicFull(mNextMusic);
|
||||||
mNextMusic.clear();
|
mNextMusic = VFS::Path::Normalized();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -1160,9 +1164,8 @@ namespace MWSound
|
||||||
|
|
||||||
if (isMainMenu && !isMusicPlaying())
|
if (isMainMenu && !isMusicPlaying())
|
||||||
{
|
{
|
||||||
std::string titlefile = "music/special/morrowind title.mp3";
|
if (mVFS->exists(MWSound::titleMusic))
|
||||||
if (mVFS->exists(titlefile))
|
streamMusic(MWSound::titleMusic, MWSound::MusicType::Special);
|
||||||
streamMusic(titlefile, MWSound::MusicType::Special);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
updateSounds(duration);
|
updateSounds(duration);
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
|
|
||||||
#include <components/fallback/fallback.hpp>
|
#include <components/fallback/fallback.hpp>
|
||||||
#include <components/misc/objectpool.hpp>
|
#include <components/misc/objectpool.hpp>
|
||||||
|
#include <components/misc/strings/algorithm.hpp>
|
||||||
#include <components/settings/settings.hpp>
|
#include <components/settings/settings.hpp>
|
||||||
|
|
||||||
#include "../mwbase/soundmanager.hpp"
|
#include "../mwbase/soundmanager.hpp"
|
||||||
|
@ -52,9 +53,10 @@ namespace MWSound
|
||||||
std::unique_ptr<Sound_Output> mOutput;
|
std::unique_ptr<Sound_Output> mOutput;
|
||||||
|
|
||||||
// Caches available music tracks by <playlist name, (sound files) >
|
// Caches available music tracks by <playlist name, (sound files) >
|
||||||
std::unordered_map<std::string, std::vector<std::string>> mMusicFiles;
|
std::unordered_map<VFS::Path::Normalized, std::vector<VFS::Path::Normalized>, VFS::Path::Hash, std::equal_to<>>
|
||||||
|
mMusicFiles;
|
||||||
std::unordered_map<std::string, std::vector<int>> mMusicToPlay; // A list with music files not yet played
|
std::unordered_map<std::string, std::vector<int>> mMusicToPlay; // A list with music files not yet played
|
||||||
std::string mLastPlayedMusic; // The music file that was last played
|
VFS::Path::Normalized mLastPlayedMusic; // The music file that was last played
|
||||||
|
|
||||||
WaterSoundUpdater mWaterSoundUpdater;
|
WaterSoundUpdater mWaterSoundUpdater;
|
||||||
|
|
||||||
|
@ -90,7 +92,7 @@ namespace MWSound
|
||||||
TrackList mActiveTracks;
|
TrackList mActiveTracks;
|
||||||
|
|
||||||
StreamPtr mMusic;
|
StreamPtr mMusic;
|
||||||
std::string mCurrentPlaylist;
|
VFS::Path::Normalized mCurrentPlaylist;
|
||||||
|
|
||||||
bool mListenerUnderwater;
|
bool mListenerUnderwater;
|
||||||
osg::Vec3f mListenerPos;
|
osg::Vec3f mListenerPos;
|
||||||
|
@ -102,7 +104,7 @@ namespace MWSound
|
||||||
Sound* mUnderwaterSound;
|
Sound* mUnderwaterSound;
|
||||||
Sound* mNearWaterSound;
|
Sound* mNearWaterSound;
|
||||||
|
|
||||||
std::string mNextMusic;
|
VFS::Path::Normalized mNextMusic;
|
||||||
bool mPlaybackPaused;
|
bool mPlaybackPaused;
|
||||||
|
|
||||||
RegionSoundSelector mRegionSoundSelector;
|
RegionSoundSelector mRegionSoundSelector;
|
||||||
|
@ -123,8 +125,8 @@ namespace MWSound
|
||||||
|
|
||||||
StreamPtr playVoice(DecoderPtr decoder, const osg::Vec3f& pos, bool playlocal);
|
StreamPtr playVoice(DecoderPtr decoder, const osg::Vec3f& pos, bool playlocal);
|
||||||
|
|
||||||
void streamMusicFull(const std::string& filename);
|
void streamMusicFull(VFS::Path::NormalizedView filename);
|
||||||
void advanceMusic(const std::string& filename, float fadeOut = 1.f);
|
void advanceMusic(VFS::Path::NormalizedView filename, float fadeOut = 1.f);
|
||||||
void startRandomTitle();
|
void startRandomTitle();
|
||||||
|
|
||||||
void cull3DSound(SoundBase* sound);
|
void cull3DSound(SoundBase* sound);
|
||||||
|
@ -174,7 +176,7 @@ namespace MWSound
|
||||||
void stopMusic() override;
|
void stopMusic() override;
|
||||||
///< Stops music if it's playing
|
///< Stops music if it's playing
|
||||||
|
|
||||||
void streamMusic(const std::string& filename, MWSound::MusicType type, float fade = 1.f) override;
|
void streamMusic(VFS::Path::NormalizedView filename, MWSound::MusicType type, float fade = 1.f) override;
|
||||||
///< Play a soundifle
|
///< Play a soundifle
|
||||||
/// \param filename name of a sound file in the data directory.
|
/// \param filename name of a sound file in the data directory.
|
||||||
/// \param type music type.
|
/// \param type music type.
|
||||||
|
@ -183,7 +185,7 @@ namespace MWSound
|
||||||
bool isMusicPlaying() override;
|
bool isMusicPlaying() override;
|
||||||
///< Returns true if music is playing
|
///< Returns true if music is playing
|
||||||
|
|
||||||
void playPlaylist(const std::string& playlist) override;
|
void playPlaylist(VFS::Path::NormalizedView playlist) override;
|
||||||
///< Start playing music from the selected folder
|
///< Start playing music from the selected folder
|
||||||
/// \param name of the folder that contains the playlist
|
/// \param name of the folder that contains the playlist
|
||||||
/// Title music playlist is predefined
|
/// Title music playlist is predefined
|
||||||
|
|
|
@ -90,6 +90,8 @@
|
||||||
#include "../mwphysics/object.hpp"
|
#include "../mwphysics/object.hpp"
|
||||||
#include "../mwphysics/physicssystem.hpp"
|
#include "../mwphysics/physicssystem.hpp"
|
||||||
|
|
||||||
|
#include "../mwsound/constants.hpp"
|
||||||
|
|
||||||
#include "actionteleport.hpp"
|
#include "actionteleport.hpp"
|
||||||
#include "cellstore.hpp"
|
#include "cellstore.hpp"
|
||||||
#include "containerstore.hpp"
|
#include "containerstore.hpp"
|
||||||
|
@ -394,7 +396,7 @@ namespace MWWorld
|
||||||
{
|
{
|
||||||
// Make sure that we do not continue to play a Title music after a new game video.
|
// Make sure that we do not continue to play a Title music after a new game video.
|
||||||
MWBase::Environment::get().getSoundManager()->stopMusic();
|
MWBase::Environment::get().getSoundManager()->stopMusic();
|
||||||
MWBase::Environment::get().getSoundManager()->playPlaylist(std::string("Explore"));
|
MWBase::Environment::get().getSoundManager()->playPlaylist(MWSound::explorePlaylist);
|
||||||
MWBase::Environment::get().getWindowManager()->playVideo(video, true);
|
MWBase::Environment::get().getWindowManager()->playVideo(video, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,19 +8,22 @@ namespace
|
||||||
TEST(CorrectSoundPath, wav_files_not_overridden_with_mp3_in_vfs_are_not_corrected)
|
TEST(CorrectSoundPath, wav_files_not_overridden_with_mp3_in_vfs_are_not_corrected)
|
||||||
{
|
{
|
||||||
std::unique_ptr<VFS::Manager> mVFS = TestingOpenMW::createTestVFS({ { "sound/bar.wav", nullptr } });
|
std::unique_ptr<VFS::Manager> mVFS = TestingOpenMW::createTestVFS({ { "sound/bar.wav", nullptr } });
|
||||||
EXPECT_EQ(correctSoundPath("sound/bar.wav", *mVFS), "sound/bar.wav");
|
constexpr VFS::Path::NormalizedView path("sound/bar.wav");
|
||||||
|
EXPECT_EQ(correctSoundPath(path, *mVFS), "sound/bar.wav");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(CorrectSoundPath, wav_files_overridden_with_mp3_in_vfs_are_corrected)
|
TEST(CorrectSoundPath, wav_files_overridden_with_mp3_in_vfs_are_corrected)
|
||||||
{
|
{
|
||||||
std::unique_ptr<VFS::Manager> mVFS = TestingOpenMW::createTestVFS({ { "sound/foo.mp3", nullptr } });
|
std::unique_ptr<VFS::Manager> mVFS = TestingOpenMW::createTestVFS({ { "sound/foo.mp3", nullptr } });
|
||||||
EXPECT_EQ(correctSoundPath("sound/foo.wav", *mVFS), "sound/foo.mp3");
|
constexpr VFS::Path::NormalizedView path("sound/foo.wav");
|
||||||
|
EXPECT_EQ(correctSoundPath(path, *mVFS), "sound/foo.mp3");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(CorrectSoundPath, corrected_path_does_not_check_existence_in_vfs)
|
TEST(CorrectSoundPath, corrected_path_does_not_check_existence_in_vfs)
|
||||||
{
|
{
|
||||||
std::unique_ptr<VFS::Manager> mVFS = TestingOpenMW::createTestVFS({});
|
std::unique_ptr<VFS::Manager> mVFS = TestingOpenMW::createTestVFS({});
|
||||||
EXPECT_EQ(correctSoundPath("sound/foo.wav", *mVFS), "sound/foo.mp3");
|
constexpr VFS::Path::NormalizedView path("sound/foo.wav");
|
||||||
|
EXPECT_EQ(correctSoundPath(path, *mVFS), "sound/foo.mp3");
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
|
|
|
@ -39,7 +39,7 @@ namespace VFS::Path
|
||||||
|
|
||||||
TEST(NormalizedTest, shouldSupportConstructorFromNormalizedView)
|
TEST(NormalizedTest, shouldSupportConstructorFromNormalizedView)
|
||||||
{
|
{
|
||||||
const NormalizedView view = "foo/bar/baz";
|
const NormalizedView view("foo/bar/baz");
|
||||||
const Normalized value(view);
|
const Normalized value(view);
|
||||||
EXPECT_EQ(value.view(), "foo/bar/baz");
|
EXPECT_EQ(value.view(), "foo/bar/baz");
|
||||||
}
|
}
|
||||||
|
|
|
@ -191,9 +191,10 @@ VFS::Path::Normalized Misc::ResourceHelpers::correctSoundPath(VFS::Path::Normali
|
||||||
return prefix / resPath;
|
return prefix / resPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Misc::ResourceHelpers::correctMusicPath(const std::string& resPath)
|
VFS::Path::Normalized Misc::ResourceHelpers::correctMusicPath(VFS::Path::NormalizedView resPath)
|
||||||
{
|
{
|
||||||
return "music\\" + resPath;
|
static constexpr VFS::Path::NormalizedView prefix("music");
|
||||||
|
return prefix / resPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string_view Misc::ResourceHelpers::meshPathForESM3(std::string_view resPath)
|
std::string_view Misc::ResourceHelpers::meshPathForESM3(std::string_view resPath)
|
||||||
|
|
|
@ -39,11 +39,11 @@ namespace Misc
|
||||||
// Adds "meshes\\".
|
// Adds "meshes\\".
|
||||||
std::string correctMeshPath(std::string_view resPath);
|
std::string correctMeshPath(std::string_view resPath);
|
||||||
|
|
||||||
// Adds "sound\\".
|
// Prepends "sound/".
|
||||||
VFS::Path::Normalized correctSoundPath(VFS::Path::NormalizedView resPath);
|
VFS::Path::Normalized correctSoundPath(VFS::Path::NormalizedView resPath);
|
||||||
|
|
||||||
// Adds "music\\".
|
// Prepends "music/".
|
||||||
std::string correctMusicPath(const std::string& resPath);
|
VFS::Path::Normalized correctMusicPath(VFS::Path::NormalizedView resPath);
|
||||||
|
|
||||||
// Removes "meshes\\".
|
// Removes "meshes\\".
|
||||||
std::string_view meshPathForESM3(std::string_view resPath);
|
std::string_view meshPathForESM3(std::string_view resPath);
|
||||||
|
|
|
@ -933,7 +933,8 @@ namespace Resource
|
||||||
<< ", using embedded marker_error instead";
|
<< ", using embedded marker_error instead";
|
||||||
}
|
}
|
||||||
Files::IMemStream file(ErrorMarker::sValue.data(), ErrorMarker::sValue.size());
|
Files::IMemStream file(ErrorMarker::sValue.data(), ErrorMarker::sValue.size());
|
||||||
return loadNonNif("error_marker.osgt", file, mImageManager);
|
constexpr VFS::Path::NormalizedView errorMarker("error_marker.osgt");
|
||||||
|
return loadNonNif(errorMarker, file, mImageManager);
|
||||||
}
|
}
|
||||||
|
|
||||||
osg::ref_ptr<osg::Node> SceneManager::cloneErrorMarker()
|
osg::ref_ptr<osg::Node> SceneManager::cloneErrorMarker()
|
||||||
|
|
|
@ -99,4 +99,21 @@ namespace VFS
|
||||||
++normalized.back();
|
++normalized.back();
|
||||||
return { it, mIndex.lower_bound(normalized) };
|
return { it, mIndex.lower_bound(normalized) };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RecursiveDirectoryRange Manager::getRecursiveDirectoryIterator(VFS::Path::NormalizedView path) const
|
||||||
|
{
|
||||||
|
if (path.value().empty())
|
||||||
|
return { mIndex.begin(), mIndex.end() };
|
||||||
|
const auto it = mIndex.lower_bound(path);
|
||||||
|
if (it == mIndex.end() || !it->first.view().starts_with(path.value()))
|
||||||
|
return { it, it };
|
||||||
|
std::string copy(path.value());
|
||||||
|
++copy.back();
|
||||||
|
return { it, mIndex.lower_bound(copy) };
|
||||||
|
}
|
||||||
|
|
||||||
|
RecursiveDirectoryRange Manager::getRecursiveDirectoryIterator() const
|
||||||
|
{
|
||||||
|
return { mIndex.begin(), mIndex.end() };
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -65,6 +65,10 @@ namespace VFS
|
||||||
/// @note May be called from any thread once the index has been built.
|
/// @note May be called from any thread once the index has been built.
|
||||||
RecursiveDirectoryRange getRecursiveDirectoryIterator(std::string_view path) const;
|
RecursiveDirectoryRange getRecursiveDirectoryIterator(std::string_view path) const;
|
||||||
|
|
||||||
|
RecursiveDirectoryRange getRecursiveDirectoryIterator(VFS::Path::NormalizedView path) const;
|
||||||
|
|
||||||
|
RecursiveDirectoryRange getRecursiveDirectoryIterator() const;
|
||||||
|
|
||||||
/// Retrieve the absolute path to the file
|
/// Retrieve the absolute path to the file
|
||||||
/// @note Throws an exception if the file can not be found.
|
/// @note Throws an exception if the file can not be found.
|
||||||
/// @note May be called from any thread once the index has been built.
|
/// @note May be called from any thread once the index has been built.
|
||||||
|
|
|
@ -79,7 +79,7 @@ namespace VFS::Path
|
||||||
public:
|
public:
|
||||||
constexpr NormalizedView() noexcept = default;
|
constexpr NormalizedView() noexcept = default;
|
||||||
|
|
||||||
constexpr NormalizedView(const char* value)
|
constexpr explicit NormalizedView(const char* value)
|
||||||
: mValue(value)
|
: mValue(value)
|
||||||
{
|
{
|
||||||
if (!isNormalized(mValue))
|
if (!isNormalized(mValue))
|
||||||
|
@ -179,6 +179,12 @@ namespace VFS::Path
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Normalized& operator=(NormalizedView value)
|
||||||
|
{
|
||||||
|
mValue = value.value();
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
Normalized& operator/=(NormalizedView value)
|
Normalized& operator/=(NormalizedView value)
|
||||||
{
|
{
|
||||||
mValue.reserve(mValue.size() + value.value().size() + 1);
|
mValue.reserve(mValue.size() + value.value().size() + 1);
|
||||||
|
@ -258,6 +264,22 @@ namespace VFS::Path
|
||||||
result /= rhs;
|
result /= rhs;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct Hash
|
||||||
|
{
|
||||||
|
using is_transparent = void;
|
||||||
|
|
||||||
|
[[nodiscard]] std::size_t operator()(std::string_view sv) const { return std::hash<std::string_view>{}(sv); }
|
||||||
|
|
||||||
|
[[nodiscard]] std::size_t operator()(const std::string& s) const { return std::hash<std::string>{}(s); }
|
||||||
|
|
||||||
|
[[nodiscard]] std::size_t operator()(const Normalized& s) const { return std::hash<std::string>{}(s.value()); }
|
||||||
|
|
||||||
|
[[nodiscard]] std::size_t operator()(NormalizedView s) const
|
||||||
|
{
|
||||||
|
return std::hash<std::string_view>{}(s.value());
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
Loading…
Reference in a new issue