mirror of
https://gitlab.com/OpenMW/openmw.git
synced 2025-03-29 22:20:33 +00:00
Implement Lua bindings for sound system
This commit is contained in:
parent
18f3e937cb
commit
7ce9fc25c5
@ -61,7 +61,7 @@ add_openmw_dir (mwscript
|
|||||||
add_openmw_dir (mwlua
|
add_openmw_dir (mwlua
|
||||||
luamanagerimp object worldview userdataserializer luaevents engineevents objectvariant
|
luamanagerimp object worldview userdataserializer luaevents engineevents objectvariant
|
||||||
context globalscripts localscripts playerscripts luabindings objectbindings cellbindings mwscriptbindings
|
context globalscripts localscripts playerscripts luabindings objectbindings cellbindings mwscriptbindings
|
||||||
camerabindings uibindings inputbindings nearbybindings postprocessingbindings stats debugbindings
|
camerabindings uibindings soundbindings inputbindings nearbybindings postprocessingbindings stats debugbindings
|
||||||
types/types types/door types/item types/actor types/container types/lockable types/weapon types/npc types/creature types/player types/activator types/book types/lockpick types/probe types/apparatus types/potion types/ingredient types/misc types/repair types/armor types/light types/static types/clothing types/levelledlist types/terminal
|
types/types types/door types/item types/actor types/container types/lockable types/weapon types/npc types/creature types/player types/activator types/book types/lockpick types/probe types/apparatus types/potion types/ingredient types/misc types/repair types/armor types/light types/static types/clothing types/levelledlist types/terminal
|
||||||
worker magicbindings
|
worker magicbindings
|
||||||
)
|
)
|
||||||
|
@ -52,6 +52,7 @@ namespace MWSound
|
|||||||
NoScaling = 1 << 4, /* Don't scale audio with simulation time */
|
NoScaling = 1 << 4, /* Don't scale audio with simulation time */
|
||||||
NoEnvNoScaling = NoEnv | NoScaling,
|
NoEnvNoScaling = NoEnv | NoScaling,
|
||||||
LoopNoEnv = Loop | NoEnv,
|
LoopNoEnv = Loop | NoEnv,
|
||||||
|
LoopNoEnvNoScaling = Loop | NoEnv | NoScaling,
|
||||||
LoopRemoveAtDistance = Loop | RemoveAtDistance
|
LoopRemoveAtDistance = Loop | RemoveAtDistance
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -117,11 +118,11 @@ namespace MWBase
|
|||||||
|
|
||||||
virtual void say(const MWWorld::ConstPtr& reference, const std::string& filename) = 0;
|
virtual void say(const MWWorld::ConstPtr& reference, const std::string& filename) = 0;
|
||||||
///< Make an actor say some text.
|
///< Make an actor say some text.
|
||||||
/// \param filename name of a sound file in "Sound/" in the data directory.
|
/// \param filename name of a sound file in the VFS
|
||||||
|
|
||||||
virtual void say(const std::string& filename) = 0;
|
virtual void say(const std::string& filename) = 0;
|
||||||
///< Say some text, without an actor ref
|
///< Say some text, without an actor ref
|
||||||
/// \param filename name of a sound file in "Sound/" in the data directory.
|
/// \param filename name of a sound file in the VFS
|
||||||
|
|
||||||
virtual bool sayActive(const MWWorld::ConstPtr& reference = MWWorld::ConstPtr()) const = 0;
|
virtual bool sayActive(const MWWorld::ConstPtr& reference = MWWorld::ConstPtr()) const = 0;
|
||||||
///< Is actor not speaking?
|
///< Is actor not speaking?
|
||||||
@ -155,6 +156,12 @@ namespace MWBase
|
|||||||
///< Play a sound, independently of 3D-position
|
///< Play a sound, independently of 3D-position
|
||||||
///< @param offset Number of seconds into the sound to start playback.
|
///< @param offset Number of seconds into the sound to start playback.
|
||||||
|
|
||||||
|
virtual Sound* playSound(std::string_view fileName, float volume, float pitch, Type type = Type::Sfx,
|
||||||
|
PlayMode mode = PlayMode::Normal, float offset = 0)
|
||||||
|
= 0;
|
||||||
|
///< Play a sound, independently of 3D-position
|
||||||
|
///< @param offset Number of seconds into the sound to start playback.
|
||||||
|
|
||||||
virtual Sound* playSound3D(const MWWorld::ConstPtr& reference, const ESM::RefId& soundId, float volume,
|
virtual Sound* playSound3D(const MWWorld::ConstPtr& reference, const ESM::RefId& soundId, float volume,
|
||||||
float pitch, Type type = Type::Sfx, PlayMode mode = PlayMode::Normal, float offset = 0)
|
float pitch, Type type = Type::Sfx, PlayMode mode = PlayMode::Normal, float offset = 0)
|
||||||
= 0;
|
= 0;
|
||||||
@ -162,6 +169,13 @@ namespace MWBase
|
|||||||
///< Play_NoTrack is specified.
|
///< Play_NoTrack is specified.
|
||||||
///< @param offset Number of seconds into the sound to start playback.
|
///< @param offset Number of seconds into the sound to start playback.
|
||||||
|
|
||||||
|
virtual Sound* playSound3D(const MWWorld::ConstPtr& reference, std::string_view fileName, float volume,
|
||||||
|
float pitch, Type type = Type::Sfx, PlayMode mode = PlayMode::Normal, float offset = 0)
|
||||||
|
= 0;
|
||||||
|
///< Play a 3D sound attached to an MWWorld::Ptr. Will be updated automatically with the Ptr's position, unless
|
||||||
|
///< Play_NoTrack is specified.
|
||||||
|
///< @param offset Number of seconds into the sound to start playback.
|
||||||
|
|
||||||
virtual Sound* playSound3D(const osg::Vec3f& initialPos, const ESM::RefId& soundId, float volume, float pitch,
|
virtual Sound* playSound3D(const osg::Vec3f& initialPos, const ESM::RefId& soundId, float volume, float pitch,
|
||||||
Type type = Type::Sfx, PlayMode mode = PlayMode::Normal, float offset = 0)
|
Type type = Type::Sfx, PlayMode mode = PlayMode::Normal, float offset = 0)
|
||||||
= 0;
|
= 0;
|
||||||
@ -172,7 +186,10 @@ namespace MWBase
|
|||||||
///< Stop the given sound from playing
|
///< Stop the given sound from playing
|
||||||
|
|
||||||
virtual void stopSound3D(const MWWorld::ConstPtr& reference, const ESM::RefId& soundId) = 0;
|
virtual void stopSound3D(const MWWorld::ConstPtr& reference, const ESM::RefId& soundId) = 0;
|
||||||
///< Stop the given object from playing the given sound,
|
///< Stop the given object from playing the given sound.
|
||||||
|
|
||||||
|
virtual void stopSound3D(const MWWorld::ConstPtr& reference, std::string_view fileName) = 0;
|
||||||
|
///< Stop the given object from playing the given sound.
|
||||||
|
|
||||||
virtual void stopSound3D(const MWWorld::ConstPtr& reference) = 0;
|
virtual void stopSound3D(const MWWorld::ConstPtr& reference) = 0;
|
||||||
///< Stop the given object from playing all sounds.
|
///< Stop the given object from playing all sounds.
|
||||||
@ -190,6 +207,10 @@ namespace MWBase
|
|||||||
///< Is the given sound currently playing on the given object?
|
///< 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
|
/// If you want to check if sound played with playSound is playing, use empty Ptr
|
||||||
|
|
||||||
|
virtual bool getSoundPlaying(const MWWorld::ConstPtr& reference, std::string_view fileName) const = 0;
|
||||||
|
///< 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(MWSound::BlockerType blocker, 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.
|
///< Pauses all currently playing sounds, including music.
|
||||||
|
|
||||||
|
@ -23,6 +23,8 @@
|
|||||||
#include <components/interpreter/defines.hpp>
|
#include <components/interpreter/defines.hpp>
|
||||||
#include <components/interpreter/interpreter.hpp>
|
#include <components/interpreter/interpreter.hpp>
|
||||||
|
|
||||||
|
#include <components/misc/resourcehelpers.hpp>
|
||||||
|
|
||||||
#include <components/settings/values.hpp>
|
#include <components/settings/values.hpp>
|
||||||
|
|
||||||
#include "../mwbase/environment.hpp"
|
#include "../mwbase/environment.hpp"
|
||||||
@ -650,7 +652,7 @@ namespace MWDialogue
|
|||||||
if (Settings::gui().mSubtitles)
|
if (Settings::gui().mSubtitles)
|
||||||
winMgr->messageBox(info->mResponse);
|
winMgr->messageBox(info->mResponse);
|
||||||
if (!info->mSound.empty())
|
if (!info->mSound.empty())
|
||||||
sndMgr->say(actor, info->mSound);
|
sndMgr->say(actor, Misc::ResourceHelpers::correctSoundPath(info->mSound));
|
||||||
if (!info->mResultScript.empty())
|
if (!info->mResultScript.empty())
|
||||||
executeScript(info->mResultScript, actor);
|
executeScript(info->mResultScript, actor);
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
#include <components/debug/debuglog.hpp>
|
#include <components/debug/debuglog.hpp>
|
||||||
#include <components/fallback/fallback.hpp>
|
#include <components/fallback/fallback.hpp>
|
||||||
|
#include <components/misc/resourcehelpers.hpp>
|
||||||
#include <components/misc/rng.hpp>
|
#include <components/misc/rng.hpp>
|
||||||
|
|
||||||
#include "../mwbase/environment.hpp"
|
#include "../mwbase/environment.hpp"
|
||||||
@ -656,7 +657,7 @@ namespace MWGui
|
|||||||
+= MyGUI::newDelegate(this, &CharacterCreation::onClassQuestionChosen);
|
+= MyGUI::newDelegate(this, &CharacterCreation::onClassQuestionChosen);
|
||||||
mGenerateClassQuestionDialog->setVisible(true);
|
mGenerateClassQuestionDialog->setVisible(true);
|
||||||
|
|
||||||
MWBase::Environment::get().getSoundManager()->say(step.mSound);
|
MWBase::Environment::get().getSoundManager()->say(Misc::ResourceHelpers::correctSoundPath(step.mSound));
|
||||||
}
|
}
|
||||||
|
|
||||||
void CharacterCreation::selectGeneratedClass()
|
void CharacterCreation::selectGeneratedClass()
|
||||||
|
@ -38,6 +38,7 @@
|
|||||||
#include "nearbybindings.hpp"
|
#include "nearbybindings.hpp"
|
||||||
#include "objectbindings.hpp"
|
#include "objectbindings.hpp"
|
||||||
#include "postprocessingbindings.hpp"
|
#include "postprocessingbindings.hpp"
|
||||||
|
#include "soundbindings.hpp"
|
||||||
#include "types/types.hpp"
|
#include "types/types.hpp"
|
||||||
#include "uibindings.hpp"
|
#include "uibindings.hpp"
|
||||||
|
|
||||||
@ -123,6 +124,7 @@ namespace MWLua
|
|||||||
{ std::move(eventName), LuaUtil::serialize(eventData, context.mSerializer) });
|
{ std::move(eventName), LuaUtil::serialize(eventData, context.mSerializer) });
|
||||||
};
|
};
|
||||||
api["contentFiles"] = initContentFilesBindings(lua->sol());
|
api["contentFiles"] = initContentFilesBindings(lua->sol());
|
||||||
|
api["sound"] = initCoreSoundBindings(context);
|
||||||
api["getFormId"] = [](std::string_view contentFile, unsigned int index) -> std::string {
|
api["getFormId"] = [](std::string_view contentFile, unsigned int index) -> std::string {
|
||||||
const std::vector<std::string>& contentList = MWBase::Environment::get().getWorld()->getContentFiles();
|
const std::vector<std::string>& contentList = MWBase::Environment::get().getWorld()->getContentFiles();
|
||||||
for (size_t i = 0; i < contentList.size(); ++i)
|
for (size_t i = 0; i < contentList.size(); ++i)
|
||||||
@ -324,6 +326,7 @@ namespace MWLua
|
|||||||
std::map<std::string, sol::object> initPlayerPackages(const Context& context)
|
std::map<std::string, sol::object> initPlayerPackages(const Context& context)
|
||||||
{
|
{
|
||||||
return {
|
return {
|
||||||
|
{ "openmw.ambient", initAmbientPackage(context) },
|
||||||
{ "openmw.camera", initCameraPackage(context.mLua->sol()) },
|
{ "openmw.camera", initCameraPackage(context.mLua->sol()) },
|
||||||
{ "openmw.debug", initDebugPackage(context) },
|
{ "openmw.debug", initDebugPackage(context) },
|
||||||
{ "openmw.input", initInputPackage(context) },
|
{ "openmw.input", initInputPackage(context) },
|
||||||
|
159
apps/openmw/mwlua/soundbindings.cpp
Normal file
159
apps/openmw/mwlua/soundbindings.cpp
Normal file
@ -0,0 +1,159 @@
|
|||||||
|
#include "soundbindings.hpp"
|
||||||
|
#include "luabindings.hpp"
|
||||||
|
|
||||||
|
#include "../mwbase/environment.hpp"
|
||||||
|
#include "../mwbase/soundmanager.hpp"
|
||||||
|
#include "../mwbase/windowmanager.hpp"
|
||||||
|
|
||||||
|
#include "luamanagerimp.hpp"
|
||||||
|
|
||||||
|
namespace MWLua
|
||||||
|
{
|
||||||
|
struct PlaySoundArgs
|
||||||
|
{
|
||||||
|
bool mScale = true;
|
||||||
|
bool mLoop = false;
|
||||||
|
float mVolume = 1.f;
|
||||||
|
float mPitch = 1.f;
|
||||||
|
float mTimeOffset = 0.f;
|
||||||
|
};
|
||||||
|
|
||||||
|
PlaySoundArgs getPlaySoundArgs(const sol::optional<sol::table>& options)
|
||||||
|
{
|
||||||
|
PlaySoundArgs args;
|
||||||
|
|
||||||
|
if (options.has_value())
|
||||||
|
{
|
||||||
|
args.mLoop = options->get_or("loop", false);
|
||||||
|
args.mVolume = options->get_or("volume", 1.f);
|
||||||
|
args.mPitch = options->get_or("pitch", 1.f);
|
||||||
|
args.mTimeOffset = options->get_or("timeOffset", 0.f);
|
||||||
|
args.mScale = options->get_or("scale", true);
|
||||||
|
}
|
||||||
|
return args;
|
||||||
|
}
|
||||||
|
|
||||||
|
MWSound::PlayMode getPlayMode(const PlaySoundArgs& args, bool is3D)
|
||||||
|
{
|
||||||
|
if (is3D)
|
||||||
|
{
|
||||||
|
if (args.mLoop)
|
||||||
|
return MWSound::PlayMode::LoopRemoveAtDistance;
|
||||||
|
return MWSound::PlayMode::Normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (args.mLoop && !args.mScale)
|
||||||
|
return MWSound::PlayMode::LoopNoEnvNoScaling;
|
||||||
|
else if (args.mLoop)
|
||||||
|
return MWSound::PlayMode::LoopNoEnv;
|
||||||
|
else if (!args.mScale)
|
||||||
|
return MWSound::PlayMode::NoEnvNoScaling;
|
||||||
|
return MWSound::PlayMode::NoEnv;
|
||||||
|
}
|
||||||
|
|
||||||
|
sol::table initAmbientPackage(const Context& context)
|
||||||
|
{
|
||||||
|
sol::table api(context.mLua->sol(), sol::create);
|
||||||
|
|
||||||
|
api["playSound"] = [](std::string_view soundId, const sol::optional<sol::table>& options) {
|
||||||
|
auto args = getPlaySoundArgs(options);
|
||||||
|
auto playMode = getPlayMode(args, false);
|
||||||
|
ESM::RefId sound = ESM::RefId::deserializeText(soundId);
|
||||||
|
|
||||||
|
MWBase::Environment::get().getSoundManager()->playSound(
|
||||||
|
sound, args.mVolume, args.mPitch, MWSound::Type::Sfx, playMode, args.mTimeOffset);
|
||||||
|
};
|
||||||
|
api["playSoundFile"] = [](std::string_view fileName, const sol::optional<sol::table>& options) {
|
||||||
|
auto args = getPlaySoundArgs(options);
|
||||||
|
auto playMode = getPlayMode(args, false);
|
||||||
|
|
||||||
|
MWBase::Environment::get().getSoundManager()->playSound(
|
||||||
|
fileName, args.mVolume, args.mPitch, MWSound::Type::Sfx, playMode, args.mTimeOffset);
|
||||||
|
};
|
||||||
|
|
||||||
|
api["stopSound"] = [](std::string_view soundId) {
|
||||||
|
ESM::RefId sound = ESM::RefId::deserializeText(soundId);
|
||||||
|
MWBase::Environment::get().getSoundManager()->stopSound3D(MWWorld::Ptr(), sound);
|
||||||
|
};
|
||||||
|
api["stopSoundFile"] = [](std::string_view fileName) {
|
||||||
|
MWBase::Environment::get().getSoundManager()->stopSound3D(MWWorld::Ptr(), fileName);
|
||||||
|
};
|
||||||
|
|
||||||
|
api["isSoundPlaying"] = [](std::string_view soundId) {
|
||||||
|
ESM::RefId sound = ESM::RefId::deserializeText(soundId);
|
||||||
|
return MWBase::Environment::get().getSoundManager()->getSoundPlaying(MWWorld::Ptr(), sound);
|
||||||
|
};
|
||||||
|
api["isSoundFilePlaying"] = [](std::string_view fileName) {
|
||||||
|
return MWBase::Environment::get().getSoundManager()->getSoundPlaying(MWWorld::Ptr(), fileName);
|
||||||
|
};
|
||||||
|
|
||||||
|
return LuaUtil::makeReadOnly(api);
|
||||||
|
}
|
||||||
|
|
||||||
|
sol::table initCoreSoundBindings(const Context& context)
|
||||||
|
{
|
||||||
|
sol::table api(context.mLua->sol(), sol::create);
|
||||||
|
|
||||||
|
api["playSound3d"]
|
||||||
|
= [](std::string_view soundId, const Object& object, const sol::optional<sol::table>& options) {
|
||||||
|
auto args = getPlaySoundArgs(options);
|
||||||
|
auto playMode = getPlayMode(args, true);
|
||||||
|
|
||||||
|
ESM::RefId sound = ESM::RefId::deserializeText(soundId);
|
||||||
|
|
||||||
|
MWBase::Environment::get().getSoundManager()->playSound3D(
|
||||||
|
object.ptr(), sound, args.mVolume, args.mPitch, MWSound::Type::Sfx, playMode, args.mTimeOffset);
|
||||||
|
};
|
||||||
|
api["playSoundFile3d"]
|
||||||
|
= [](std::string_view fileName, const Object& object, const sol::optional<sol::table>& options) {
|
||||||
|
auto args = getPlaySoundArgs(options);
|
||||||
|
auto playMode = getPlayMode(args, true);
|
||||||
|
|
||||||
|
MWBase::Environment::get().getSoundManager()->playSound3D(object.ptr(), fileName, args.mVolume,
|
||||||
|
args.mPitch, MWSound::Type::Sfx, playMode, args.mTimeOffset);
|
||||||
|
};
|
||||||
|
|
||||||
|
api["stopSound3d"] = [](std::string_view soundId, const Object& object) {
|
||||||
|
ESM::RefId sound = ESM::RefId::deserializeText(soundId);
|
||||||
|
MWBase::Environment::get().getSoundManager()->stopSound3D(object.ptr(), sound);
|
||||||
|
};
|
||||||
|
api["stopSoundFile3d"] = [](std::string_view fileName, const Object& object) {
|
||||||
|
MWBase::Environment::get().getSoundManager()->stopSound3D(object.ptr(), fileName);
|
||||||
|
};
|
||||||
|
|
||||||
|
api["isSoundPlaying"] = [](std::string_view soundId, const Object& object) {
|
||||||
|
ESM::RefId sound = ESM::RefId::deserializeText(soundId);
|
||||||
|
return MWBase::Environment::get().getSoundManager()->getSoundPlaying(object.ptr(), sound);
|
||||||
|
};
|
||||||
|
api["isSoundFilePlaying"] = [](std::string_view fileName, const Object& object) {
|
||||||
|
return MWBase::Environment::get().getSoundManager()->getSoundPlaying(object.ptr(), fileName);
|
||||||
|
};
|
||||||
|
|
||||||
|
api["say"] = sol::overload(
|
||||||
|
[luaManager = context.mLuaManager](
|
||||||
|
std::string_view fileName, const Object& object, sol::optional<std::string_view> text) {
|
||||||
|
MWBase::Environment::get().getSoundManager()->say(object.ptr(), std::string(fileName));
|
||||||
|
if (text)
|
||||||
|
luaManager->addUIMessage(*text);
|
||||||
|
},
|
||||||
|
[luaManager = context.mLuaManager](std::string_view fileName, sol::optional<std::string_view> text) {
|
||||||
|
MWBase::Environment::get().getSoundManager()->say(std::string(fileName));
|
||||||
|
if (text)
|
||||||
|
luaManager->addUIMessage(*text);
|
||||||
|
});
|
||||||
|
api["stopSay"] = sol::overload(
|
||||||
|
[](const Object& object) {
|
||||||
|
const MWWorld::Ptr& objPtr = object.ptr();
|
||||||
|
MWBase::Environment::get().getSoundManager()->stopSay(objPtr);
|
||||||
|
},
|
||||||
|
[]() { MWBase::Environment::get().getSoundManager()->stopSay(MWWorld::ConstPtr()); });
|
||||||
|
api["isSayActive"] = sol::overload(
|
||||||
|
[](const Object& object) {
|
||||||
|
const MWWorld::Ptr& objPtr = object.ptr();
|
||||||
|
return MWBase::Environment::get().getSoundManager()->sayActive(objPtr);
|
||||||
|
},
|
||||||
|
[]() { return MWBase::Environment::get().getSoundManager()->sayActive(MWWorld::ConstPtr()); });
|
||||||
|
|
||||||
|
return LuaUtil::makeReadOnly(api);
|
||||||
|
}
|
||||||
|
}
|
15
apps/openmw/mwlua/soundbindings.hpp
Normal file
15
apps/openmw/mwlua/soundbindings.hpp
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
#ifndef MWLUA_SOUNDBINDINGS_H
|
||||||
|
#define MWLUA_SOUNDBINDINGS_H
|
||||||
|
|
||||||
|
#include <sol/forward.hpp>
|
||||||
|
|
||||||
|
#include "context.hpp"
|
||||||
|
|
||||||
|
namespace MWLua
|
||||||
|
{
|
||||||
|
sol::table initCoreSoundBindings(const Context&);
|
||||||
|
|
||||||
|
sol::table initAmbientPackage(const Context& context);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // MWLUA_SOUNDBINDINGS_H
|
@ -5,6 +5,7 @@
|
|||||||
#include <components/interpreter/interpreter.hpp>
|
#include <components/interpreter/interpreter.hpp>
|
||||||
#include <components/interpreter/opcodes.hpp>
|
#include <components/interpreter/opcodes.hpp>
|
||||||
#include <components/interpreter/runtime.hpp>
|
#include <components/interpreter/runtime.hpp>
|
||||||
|
#include <components/misc/resourcehelpers.hpp>
|
||||||
#include <components/settings/values.hpp>
|
#include <components/settings/values.hpp>
|
||||||
|
|
||||||
#include "../mwbase/environment.hpp"
|
#include "../mwbase/environment.hpp"
|
||||||
@ -38,7 +39,7 @@ namespace MWScript
|
|||||||
std::string_view text = runtime.getStringLiteral(runtime[0].mInteger);
|
std::string_view text = runtime.getStringLiteral(runtime[0].mInteger);
|
||||||
runtime.pop();
|
runtime.pop();
|
||||||
|
|
||||||
MWBase::Environment::get().getSoundManager()->say(ptr, file);
|
MWBase::Environment::get().getSoundManager()->say(ptr, Misc::ResourceHelpers::correctSoundPath(file));
|
||||||
|
|
||||||
if (Settings::gui().mSubtitles)
|
if (Settings::gui().mSubtitles)
|
||||||
context.messageBox(text);
|
context.messageBox(text);
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
|
|
||||||
#include <components/debug/debuglog.hpp>
|
#include <components/debug/debuglog.hpp>
|
||||||
#include <components/esm3/loadsoun.hpp>
|
#include <components/esm3/loadsoun.hpp>
|
||||||
|
#include <components/misc/resourcehelpers.hpp>
|
||||||
#include <components/settings/settings.hpp>
|
#include <components/settings/settings.hpp>
|
||||||
#include <components/vfs/pathutil.hpp>
|
#include <components/vfs/pathutil.hpp>
|
||||||
|
|
||||||
@ -61,6 +62,35 @@ namespace MWSound
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Sound_Buffer* SoundBufferPool::lookup(std::string_view fileName) const
|
||||||
|
{
|
||||||
|
auto soundId = ESM::RefId::stringRefId(fileName);
|
||||||
|
return lookup(soundId);
|
||||||
|
}
|
||||||
|
|
||||||
|
Sound_Buffer* SoundBufferPool::loadSfx(Sound_Buffer* sfx)
|
||||||
|
{
|
||||||
|
if (sfx->getHandle() != nullptr)
|
||||||
|
return sfx;
|
||||||
|
|
||||||
|
auto [handle, size] = mOutput->loadSound(sfx->getResourceName());
|
||||||
|
if (handle == nullptr)
|
||||||
|
return {};
|
||||||
|
|
||||||
|
sfx->mHandle = handle;
|
||||||
|
|
||||||
|
mBufferCacheSize += size;
|
||||||
|
if (mBufferCacheSize > mBufferCacheMax)
|
||||||
|
{
|
||||||
|
unloadUnused();
|
||||||
|
if (!mUnusedBuffers.empty() && mBufferCacheSize > mBufferCacheMax)
|
||||||
|
Log(Debug::Warning) << "No unused sound buffers to free, using " << mBufferCacheSize << " bytes!";
|
||||||
|
}
|
||||||
|
mUnusedBuffers.push_front(sfx);
|
||||||
|
|
||||||
|
return sfx;
|
||||||
|
}
|
||||||
|
|
||||||
Sound_Buffer* SoundBufferPool::load(const ESM::RefId& soundId)
|
Sound_Buffer* SoundBufferPool::load(const ESM::RefId& soundId)
|
||||||
{
|
{
|
||||||
if (mBufferNameMap.empty())
|
if (mBufferNameMap.empty())
|
||||||
@ -81,25 +111,23 @@ namespace MWSound
|
|||||||
sfx = insertSound(soundId, *sound);
|
sfx = insertSound(soundId, *sound);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sfx->getHandle() == nullptr)
|
return loadSfx(sfx);
|
||||||
|
}
|
||||||
|
|
||||||
|
Sound_Buffer* SoundBufferPool::load(std::string_view fileName)
|
||||||
|
{
|
||||||
|
auto soundId = ESM::RefId::stringRefId(fileName);
|
||||||
|
|
||||||
|
Sound_Buffer* sfx;
|
||||||
|
const auto it = mBufferNameMap.find(soundId);
|
||||||
|
if (it != mBufferNameMap.end())
|
||||||
|
sfx = it->second;
|
||||||
|
else
|
||||||
{
|
{
|
||||||
auto [handle, size] = mOutput->loadSound(sfx->getResourceName());
|
sfx = insertSound(fileName);
|
||||||
if (handle == nullptr)
|
|
||||||
return {};
|
|
||||||
|
|
||||||
sfx->mHandle = handle;
|
|
||||||
|
|
||||||
mBufferCacheSize += size;
|
|
||||||
if (mBufferCacheSize > mBufferCacheMax)
|
|
||||||
{
|
|
||||||
unloadUnused();
|
|
||||||
if (!mUnusedBuffers.empty() && mBufferCacheSize > mBufferCacheMax)
|
|
||||||
Log(Debug::Warning) << "No unused sound buffers to free, using " << mBufferCacheSize << " bytes!";
|
|
||||||
}
|
|
||||||
mUnusedBuffers.push_front(sfx);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return sfx;
|
return loadSfx(sfx);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SoundBufferPool::clear()
|
void SoundBufferPool::clear()
|
||||||
@ -113,6 +141,24 @@ namespace MWSound
|
|||||||
mUnusedBuffers.clear();
|
mUnusedBuffers.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Sound_Buffer* SoundBufferPool::insertSound(std::string_view fileName)
|
||||||
|
{
|
||||||
|
static const AudioParams audioParams
|
||||||
|
= makeAudioParams(MWBase::Environment::get().getESMStore()->get<ESM::GameSetting>());
|
||||||
|
|
||||||
|
float volume = 1.f;
|
||||||
|
float min = std::max(audioParams.mAudioDefaultMinDistance * audioParams.mAudioMinDistanceMult, 1.f);
|
||||||
|
float max = std::max(min, audioParams.mAudioDefaultMaxDistance * audioParams.mAudioMaxDistanceMult);
|
||||||
|
|
||||||
|
min = std::max(min, 1.0f);
|
||||||
|
max = std::max(min, max);
|
||||||
|
|
||||||
|
Sound_Buffer& sfx = mSoundBuffers.emplace_back(fileName, volume, min, max);
|
||||||
|
|
||||||
|
mBufferNameMap.emplace(ESM::RefId::stringRefId(fileName), &sfx);
|
||||||
|
return &sfx;
|
||||||
|
}
|
||||||
|
|
||||||
Sound_Buffer* SoundBufferPool::insertSound(const ESM::RefId& soundId, const ESM::Sound& sound)
|
Sound_Buffer* SoundBufferPool::insertSound(const ESM::RefId& soundId, const ESM::Sound& sound)
|
||||||
{
|
{
|
||||||
static const AudioParams audioParams
|
static const AudioParams audioParams
|
||||||
@ -132,7 +178,8 @@ namespace MWSound
|
|||||||
min = std::max(min, 1.0f);
|
min = std::max(min, 1.0f);
|
||||||
max = std::max(min, max);
|
max = std::max(min, max);
|
||||||
|
|
||||||
Sound_Buffer& sfx = mSoundBuffers.emplace_back("Sound/" + sound.mSound, volume, min, max);
|
Sound_Buffer& sfx
|
||||||
|
= mSoundBuffers.emplace_back(Misc::ResourceHelpers::correctSoundPath(sound.mSound), volume, min, max);
|
||||||
VFS::Path::normalizeFilenameInPlace(sfx.mResourceName);
|
VFS::Path::normalizeFilenameInPlace(sfx.mResourceName);
|
||||||
|
|
||||||
mBufferNameMap.emplace(soundId, &sfx);
|
mBufferNameMap.emplace(soundId, &sfx);
|
||||||
|
@ -69,10 +69,17 @@ namespace MWSound
|
|||||||
/// minRange, and maxRange)
|
/// minRange, and maxRange)
|
||||||
Sound_Buffer* lookup(const ESM::RefId& soundId) const;
|
Sound_Buffer* lookup(const ESM::RefId& soundId) const;
|
||||||
|
|
||||||
|
/// Lookup a sound by file name for its sound data (resource name, local volume,
|
||||||
|
/// minRange, and maxRange)
|
||||||
|
Sound_Buffer* lookup(std::string_view fileName) const;
|
||||||
|
|
||||||
/// Lookup a soundId for its sound data (resource name, local volume,
|
/// Lookup a soundId for its sound data (resource name, local volume,
|
||||||
/// minRange, and maxRange), and ensure it's ready for use.
|
/// minRange, and maxRange), and ensure it's ready for use.
|
||||||
Sound_Buffer* load(const ESM::RefId& soundId);
|
Sound_Buffer* load(const ESM::RefId& soundId);
|
||||||
|
|
||||||
|
// Lookup for a sound by file name, and ensure it's ready for use.
|
||||||
|
Sound_Buffer* load(std::string_view fileName);
|
||||||
|
|
||||||
void use(Sound_Buffer& sfx)
|
void use(Sound_Buffer& sfx)
|
||||||
{
|
{
|
||||||
if (sfx.mUses++ == 0)
|
if (sfx.mUses++ == 0)
|
||||||
@ -92,6 +99,8 @@ namespace MWSound
|
|||||||
void clear();
|
void clear();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
Sound_Buffer* loadSfx(Sound_Buffer* sfx);
|
||||||
|
|
||||||
Sound_Output* mOutput;
|
Sound_Output* mOutput;
|
||||||
std::deque<Sound_Buffer> mSoundBuffers;
|
std::deque<Sound_Buffer> mSoundBuffers;
|
||||||
std::unordered_map<ESM::RefId, Sound_Buffer*> mBufferNameMap;
|
std::unordered_map<ESM::RefId, Sound_Buffer*> mBufferNameMap;
|
||||||
@ -102,6 +111,7 @@ namespace MWSound
|
|||||||
std::deque<Sound_Buffer*> mUnusedBuffers;
|
std::deque<Sound_Buffer*> mUnusedBuffers;
|
||||||
|
|
||||||
inline Sound_Buffer* insertSound(const ESM::RefId& soundId, const ESM::Sound& sound);
|
inline Sound_Buffer* insertSound(const ESM::RefId& soundId, const ESM::Sound& sound);
|
||||||
|
inline Sound_Buffer* insertSound(std::string_view fileName);
|
||||||
|
|
||||||
inline void unloadUnused();
|
inline void unloadUnused();
|
||||||
};
|
};
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
#include <components/misc/resourcehelpers.hpp>
|
#include <components/misc/resourcehelpers.hpp>
|
||||||
#include <components/misc/rng.hpp>
|
#include <components/misc/rng.hpp>
|
||||||
#include <components/vfs/manager.hpp>
|
#include <components/vfs/manager.hpp>
|
||||||
|
#include <components/vfs/pathutil.hpp>
|
||||||
|
|
||||||
#include "../mwbase/environment.hpp"
|
#include "../mwbase/environment.hpp"
|
||||||
#include "../mwbase/statemanager.hpp"
|
#include "../mwbase/statemanager.hpp"
|
||||||
@ -36,6 +37,7 @@ namespace MWSound
|
|||||||
constexpr float sMinUpdateInterval = 1.0f / 30.0f;
|
constexpr float sMinUpdateInterval = 1.0f / 30.0f;
|
||||||
constexpr float sSfxFadeInDuration = 1.0f;
|
constexpr float sSfxFadeInDuration = 1.0f;
|
||||||
constexpr float sSfxFadeOutDuration = 1.0f;
|
constexpr float sSfxFadeOutDuration = 1.0f;
|
||||||
|
constexpr float sSoundCullDistance = 2000.f;
|
||||||
|
|
||||||
WaterSoundUpdaterSettings makeWaterSoundUpdaterSettings()
|
WaterSoundUpdaterSettings makeWaterSoundUpdaterSettings()
|
||||||
{
|
{
|
||||||
@ -357,7 +359,7 @@ namespace MWSound
|
|||||||
if (!mOutput->isInitialized())
|
if (!mOutput->isInitialized())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
DecoderPtr decoder = loadVoice("Sound/" + filename);
|
DecoderPtr decoder = loadVoice(filename);
|
||||||
if (!decoder)
|
if (!decoder)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -389,7 +391,7 @@ namespace MWSound
|
|||||||
if (!mOutput->isInitialized())
|
if (!mOutput->isInitialized())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
DecoderPtr decoder = loadVoice("Sound/" + filename);
|
DecoderPtr decoder = loadVoice(filename);
|
||||||
if (!decoder)
|
if (!decoder)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -486,14 +488,22 @@ namespace MWSound
|
|||||||
return mOutput->getStreamDelay(stream);
|
return mOutput->getStreamDelay(stream);
|
||||||
}
|
}
|
||||||
|
|
||||||
Sound* SoundManager::playSound(
|
bool SoundManager::remove3DSoundAtDistance(PlayMode mode, const MWWorld::ConstPtr& ptr) const
|
||||||
const ESM::RefId& soundId, float volume, float pitch, Type type, PlayMode mode, float offset)
|
|
||||||
{
|
{
|
||||||
if (!mOutput->isInitialized())
|
if (!mOutput->isInitialized())
|
||||||
return nullptr;
|
return true;
|
||||||
|
|
||||||
Sound_Buffer* sfx = mSoundBuffers.load(soundId);
|
const osg::Vec3f objpos(ptr.getRefData().getPosition().asVec3());
|
||||||
if (!sfx)
|
const float squaredDist = (mListenerPos - objpos).length2();
|
||||||
|
if ((mode & PlayMode::RemoveAtDistance) && squaredDist > sSoundCullDistance * sSoundCullDistance)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Sound* SoundManager::playSound(Sound_Buffer* sfx, float volume, float pitch, Type type, PlayMode mode, float offset)
|
||||||
|
{
|
||||||
|
if (!mOutput->isInitialized())
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
// Only one copy of given sound can be played at time, so stop previous copy
|
// Only one copy of given sound can be played at time, so stop previous copy
|
||||||
@ -517,25 +527,45 @@ namespace MWSound
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
Sound* SoundManager::playSound3D(const MWWorld::ConstPtr& ptr, const ESM::RefId& soundId, float volume, float pitch,
|
Sound* SoundManager::playSound(
|
||||||
|
std::string_view fileName, float volume, float pitch, Type type, PlayMode mode, float offset)
|
||||||
|
{
|
||||||
|
if (!mOutput->isInitialized())
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
std::string normalizedName = VFS::Path::normalizeFilename(fileName);
|
||||||
|
Sound_Buffer* sfx = mSoundBuffers.load(normalizedName);
|
||||||
|
if (!sfx)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
return playSound(sfx, volume, pitch, type, mode, offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
Sound* SoundManager::playSound(
|
||||||
|
const ESM::RefId& soundId, float volume, float pitch, Type type, PlayMode mode, float offset)
|
||||||
|
{
|
||||||
|
if (!mOutput->isInitialized())
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
Sound_Buffer* sfx = mSoundBuffers.load(soundId);
|
||||||
|
if (!sfx)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
return playSound(sfx, volume, pitch, type, mode, offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
Sound* SoundManager::playSound3D(const MWWorld::ConstPtr& ptr, Sound_Buffer* sfx, float volume, float pitch,
|
||||||
Type type, PlayMode mode, float offset)
|
Type type, PlayMode mode, float offset)
|
||||||
{
|
{
|
||||||
if (!mOutput->isInitialized())
|
if (!mOutput->isInitialized())
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
const osg::Vec3f objpos(ptr.getRefData().getPosition().asVec3());
|
|
||||||
const float squaredDist = (mListenerPos - objpos).length2();
|
|
||||||
if ((mode & PlayMode::RemoveAtDistance) && squaredDist > 2000 * 2000)
|
|
||||||
return nullptr;
|
|
||||||
|
|
||||||
// Look up the sound in the ESM data
|
|
||||||
Sound_Buffer* sfx = mSoundBuffers.load(soundId);
|
|
||||||
if (!sfx)
|
|
||||||
return nullptr;
|
|
||||||
|
|
||||||
// Only one copy of given sound can be played at time on ptr, so stop previous copy
|
// Only one copy of given sound can be played at time on ptr, so stop previous copy
|
||||||
stopSound(sfx, ptr);
|
stopSound(sfx, ptr);
|
||||||
|
|
||||||
|
const osg::Vec3f objpos(ptr.getRefData().getPosition().asVec3());
|
||||||
|
const float squaredDist = (mListenerPos - objpos).length2();
|
||||||
|
|
||||||
bool played;
|
bool played;
|
||||||
SoundPtr sound = getSoundRef();
|
SoundPtr sound = getSoundRef();
|
||||||
if (!(mode & PlayMode::NoPlayerLocal) && ptr == MWMechanics::getPlayer())
|
if (!(mode & PlayMode::NoPlayerLocal) && ptr == MWMechanics::getPlayer())
|
||||||
@ -578,6 +608,35 @@ namespace MWSound
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Sound* SoundManager::playSound3D(const MWWorld::ConstPtr& ptr, const ESM::RefId& soundId, float volume, float pitch,
|
||||||
|
Type type, PlayMode mode, float offset)
|
||||||
|
{
|
||||||
|
if (remove3DSoundAtDistance(mode, ptr))
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
// Look up the sound in the ESM data
|
||||||
|
Sound_Buffer* sfx = mSoundBuffers.load(soundId);
|
||||||
|
if (!sfx)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
return playSound3D(ptr, sfx, volume, pitch, type, mode, offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
Sound* SoundManager::playSound3D(const MWWorld::ConstPtr& ptr, std::string_view fileName, float volume, float pitch,
|
||||||
|
Type type, PlayMode mode, float offset)
|
||||||
|
{
|
||||||
|
if (remove3DSoundAtDistance(mode, ptr))
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
// Look up the sound
|
||||||
|
std::string normalizedName = VFS::Path::normalizeFilename(fileName);
|
||||||
|
Sound_Buffer* sfx = mSoundBuffers.load(normalizedName);
|
||||||
|
if (!sfx)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
return playSound3D(ptr, sfx, volume, pitch, type, mode, offset);
|
||||||
|
}
|
||||||
|
|
||||||
Sound* SoundManager::playSound3D(const osg::Vec3f& initialPos, const ESM::RefId& soundId, float volume, float pitch,
|
Sound* SoundManager::playSound3D(const osg::Vec3f& initialPos, const ESM::RefId& soundId, float volume, float pitch,
|
||||||
Type type, PlayMode mode, float offset)
|
Type type, PlayMode mode, float offset)
|
||||||
{
|
{
|
||||||
@ -644,6 +703,13 @@ namespace MWSound
|
|||||||
stopSound(sfx, ptr);
|
stopSound(sfx, ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SoundManager::stopSound3D(const MWWorld::ConstPtr& ptr, std::string_view fileName)
|
||||||
|
{
|
||||||
|
std::string normalizedName = VFS::Path::normalizeFilename(fileName);
|
||||||
|
auto soundId = ESM::RefId::stringRefId(normalizedName);
|
||||||
|
stopSound3D(ptr, soundId);
|
||||||
|
}
|
||||||
|
|
||||||
void SoundManager::stopSound3D(const MWWorld::ConstPtr& ptr)
|
void SoundManager::stopSound3D(const MWWorld::ConstPtr& ptr)
|
||||||
{
|
{
|
||||||
SoundMap::iterator snditer = mActiveSounds.find(ptr.mRef);
|
SoundMap::iterator snditer = mActiveSounds.find(ptr.mRef);
|
||||||
@ -700,6 +766,13 @@ namespace MWSound
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool SoundManager::getSoundPlaying(const MWWorld::ConstPtr& ptr, std::string_view fileName) const
|
||||||
|
{
|
||||||
|
std::string normalizedName = VFS::Path::normalizeFilename(fileName);
|
||||||
|
auto soundId = ESM::RefId::stringRefId(normalizedName);
|
||||||
|
return getSoundPlaying(ptr, soundId);
|
||||||
|
}
|
||||||
|
|
||||||
bool SoundManager::getSoundPlaying(const MWWorld::ConstPtr& ptr, const ESM::RefId& soundId) const
|
bool SoundManager::getSoundPlaying(const MWWorld::ConstPtr& ptr, const ESM::RefId& soundId) const
|
||||||
{
|
{
|
||||||
SoundMap::const_iterator snditer = mActiveSounds.find(ptr.mRef);
|
SoundMap::const_iterator snditer = mActiveSounds.find(ptr.mRef);
|
||||||
@ -849,8 +922,8 @@ namespace MWSound
|
|||||||
|
|
||||||
void SoundManager::cull3DSound(SoundBase* sound)
|
void SoundManager::cull3DSound(SoundBase* sound)
|
||||||
{
|
{
|
||||||
// Hard-coded distance of 2000.0f is from vanilla Morrowind
|
// Hard-coded distance is from an original engine
|
||||||
const float maxDist = sound->getDistanceCull() ? 2000.0f : sound->getMaxDistance();
|
const float maxDist = sound->getDistanceCull() ? sSoundCullDistance : sound->getMaxDistance();
|
||||||
const float squaredMaxDist = maxDist * maxDist;
|
const float squaredMaxDist = maxDist * maxDist;
|
||||||
|
|
||||||
const osg::Vec3f pos = sound->getPosition();
|
const osg::Vec3f pos = sound->getPosition();
|
||||||
|
@ -132,6 +132,13 @@ namespace MWSound
|
|||||||
|
|
||||||
void cull3DSound(SoundBase* sound);
|
void cull3DSound(SoundBase* sound);
|
||||||
|
|
||||||
|
bool remove3DSoundAtDistance(PlayMode mode, const MWWorld::ConstPtr& ptr) const;
|
||||||
|
|
||||||
|
Sound* playSound(Sound_Buffer* sfx, float volume, float pitch, Type type = Type::Sfx,
|
||||||
|
PlayMode mode = PlayMode::Normal, float offset = 0);
|
||||||
|
Sound* playSound3D(const MWWorld::ConstPtr& ptr, Sound_Buffer* sfx, float volume, float pitch, Type type,
|
||||||
|
PlayMode mode, float offset);
|
||||||
|
|
||||||
void updateSounds(float duration);
|
void updateSounds(float duration);
|
||||||
void updateRegionSound(float duration);
|
void updateRegionSound(float duration);
|
||||||
void updateWaterSound();
|
void updateWaterSound();
|
||||||
@ -183,11 +190,11 @@ namespace MWSound
|
|||||||
|
|
||||||
void say(const MWWorld::ConstPtr& reference, const std::string& filename) override;
|
void say(const MWWorld::ConstPtr& reference, const std::string& filename) override;
|
||||||
///< Make an actor say some text.
|
///< Make an actor say some text.
|
||||||
/// \param filename name of a sound file in "Sound/" in the data directory.
|
/// \param filename name of a sound file in the VFS
|
||||||
|
|
||||||
void say(const std::string& filename) override;
|
void say(const std::string& filename) override;
|
||||||
///< Say some text, without an actor ref
|
///< Say some text, without an actor ref
|
||||||
/// \param filename name of a sound file in "Sound/" in the data directory.
|
/// \param filename name of a sound file in the VFS
|
||||||
|
|
||||||
bool sayActive(const MWWorld::ConstPtr& reference = MWWorld::ConstPtr()) const override;
|
bool sayActive(const MWWorld::ConstPtr& reference = MWWorld::ConstPtr()) const override;
|
||||||
///< Is actor not speaking?
|
///< Is actor not speaking?
|
||||||
@ -219,12 +226,23 @@ namespace MWSound
|
|||||||
///< Play a sound, independently of 3D-position
|
///< Play a sound, independently of 3D-position
|
||||||
///< @param offset Number of seconds into the sound to start playback.
|
///< @param offset Number of seconds into the sound to start playback.
|
||||||
|
|
||||||
|
Sound* playSound(std::string_view fileName, float volume, float pitch, Type type = Type::Sfx,
|
||||||
|
PlayMode mode = PlayMode::Normal, float offset = 0) override;
|
||||||
|
///< Play a sound, independently of 3D-position
|
||||||
|
///< @param offset Number of seconds into the sound to start playback.
|
||||||
|
|
||||||
Sound* playSound3D(const MWWorld::ConstPtr& reference, const ESM::RefId& soundId, float volume, float pitch,
|
Sound* playSound3D(const MWWorld::ConstPtr& reference, const ESM::RefId& soundId, float volume, float pitch,
|
||||||
Type type = Type::Sfx, PlayMode mode = PlayMode::Normal, float offset = 0) override;
|
Type type = Type::Sfx, PlayMode mode = PlayMode::Normal, float offset = 0) override;
|
||||||
///< Play a 3D sound attached to an MWWorld::Ptr. Will be updated automatically with the Ptr's position, unless
|
///< Play a 3D sound attached to an MWWorld::Ptr. Will be updated automatically with the Ptr's position, unless
|
||||||
///< Play_NoTrack is specified.
|
///< Play_NoTrack is specified.
|
||||||
///< @param offset Number of seconds into the sound to start playback.
|
///< @param offset Number of seconds into the sound to start playback.
|
||||||
|
|
||||||
|
Sound* playSound3D(const MWWorld::ConstPtr& reference, std::string_view fileName, float volume, float pitch,
|
||||||
|
Type type = Type::Sfx, PlayMode mode = PlayMode::Normal, float offset = 0) override;
|
||||||
|
///< Play a 3D sound attached to an MWWorld::Ptr. Will be updated automatically with the Ptr's position, unless
|
||||||
|
///< Play_NoTrack is specified.
|
||||||
|
///< @param offset Number of seconds into the sound to start playback.
|
||||||
|
|
||||||
Sound* playSound3D(const osg::Vec3f& initialPos, const ESM::RefId& soundId, float volume, float pitch,
|
Sound* playSound3D(const osg::Vec3f& initialPos, const ESM::RefId& soundId, float volume, float pitch,
|
||||||
Type type, PlayMode mode, float offset = 0) override;
|
Type type, PlayMode mode, float offset = 0) override;
|
||||||
///< Play a 3D sound at \a initialPos. If the sound should be moving, it must be updated using
|
///< Play a 3D sound at \a initialPos. If the sound should be moving, it must be updated using
|
||||||
@ -236,7 +254,10 @@ namespace MWSound
|
|||||||
/// @note no-op if \a sound is null
|
/// @note no-op if \a sound is null
|
||||||
|
|
||||||
void stopSound3D(const MWWorld::ConstPtr& reference, const ESM::RefId& soundId) override;
|
void stopSound3D(const MWWorld::ConstPtr& reference, const ESM::RefId& soundId) override;
|
||||||
///< Stop the given object from playing the given sound,
|
///< Stop the given object from playing the given sound.
|
||||||
|
|
||||||
|
void stopSound3D(const MWWorld::ConstPtr& reference, std::string_view fileName) override;
|
||||||
|
///< Stop the given object from playing the given sound.
|
||||||
|
|
||||||
void stopSound3D(const MWWorld::ConstPtr& reference) override;
|
void stopSound3D(const MWWorld::ConstPtr& reference) override;
|
||||||
///< Stop the given object from playing all sounds.
|
///< Stop the given object from playing all sounds.
|
||||||
@ -253,6 +274,9 @@ namespace MWSound
|
|||||||
bool getSoundPlaying(const MWWorld::ConstPtr& reference, const ESM::RefId& soundId) const override;
|
bool getSoundPlaying(const MWWorld::ConstPtr& reference, const ESM::RefId& soundId) const override;
|
||||||
///< Is the given sound currently playing on the given object?
|
///< Is the given sound currently playing on the given object?
|
||||||
|
|
||||||
|
bool getSoundPlaying(const MWWorld::ConstPtr& reference, std::string_view fileName) const override;
|
||||||
|
///< Is the given sound currently playing on the given object?
|
||||||
|
|
||||||
void pauseSounds(MWSound::BlockerType blocker, int types = int(Type::Mask)) override;
|
void pauseSounds(MWSound::BlockerType blocker, int types = int(Type::Mask)) override;
|
||||||
///< Pauses all currently playing sounds, including music.
|
///< Pauses all currently playing sounds, including music.
|
||||||
|
|
||||||
|
@ -151,6 +151,11 @@ std::string Misc::ResourceHelpers::correctMeshPath(const std::string& resPath, c
|
|||||||
return "meshes\\" + resPath;
|
return "meshes\\" + resPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string Misc::ResourceHelpers::correctSoundPath(const std::string& resPath)
|
||||||
|
{
|
||||||
|
return "sound\\" + resPath;
|
||||||
|
}
|
||||||
|
|
||||||
std::string_view Misc::ResourceHelpers::meshPathForESM3(std::string_view resPath)
|
std::string_view Misc::ResourceHelpers::meshPathForESM3(std::string_view resPath)
|
||||||
{
|
{
|
||||||
constexpr std::string_view prefix = "meshes";
|
constexpr std::string_view prefix = "meshes";
|
||||||
|
@ -35,6 +35,9 @@ namespace Misc
|
|||||||
// Adds "meshes\\".
|
// Adds "meshes\\".
|
||||||
std::string correctMeshPath(const std::string& resPath, const VFS::Manager* vfs);
|
std::string correctMeshPath(const std::string& resPath, const VFS::Manager* vfs);
|
||||||
|
|
||||||
|
// Adds "sound\\".
|
||||||
|
std::string correctSoundPath(const std::string& resPath);
|
||||||
|
|
||||||
// Removes "meshes\\".
|
// Removes "meshes\\".
|
||||||
std::string_view meshPathForESM3(std::string_view resPath);
|
std::string_view meshPathForESM3(std::string_view resPath);
|
||||||
|
|
||||||
|
@ -19,6 +19,7 @@ Lua API reference
|
|||||||
openmw_self
|
openmw_self
|
||||||
openmw_nearby
|
openmw_nearby
|
||||||
openmw_input
|
openmw_input
|
||||||
|
openmw_ambient
|
||||||
openmw_ui
|
openmw_ui
|
||||||
openmw_camera
|
openmw_camera
|
||||||
openmw_postprocessing
|
openmw_postprocessing
|
||||||
|
5
docs/source/reference/lua-scripting/openmw_ambient.rst
Normal file
5
docs/source/reference/lua-scripting/openmw_ambient.rst
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
Package openmw.ambient
|
||||||
|
======================
|
||||||
|
|
||||||
|
.. raw:: html
|
||||||
|
:file: generated_html/openmw_ambient.html
|
@ -21,6 +21,8 @@
|
|||||||
+------------------------------------------------------------+--------------------+---------------------------------------------------------------+
|
+------------------------------------------------------------+--------------------+---------------------------------------------------------------+
|
||||||
|:ref:`openmw.nearby <Package openmw.nearby>` | by local scripts | | Read-only access to the nearest area of the game world. |
|
|:ref:`openmw.nearby <Package openmw.nearby>` | by local scripts | | Read-only access to the nearest area of the game world. |
|
||||||
+------------------------------------------------------------+--------------------+---------------------------------------------------------------+
|
+------------------------------------------------------------+--------------------+---------------------------------------------------------------+
|
||||||
|
|:ref:`openmw.ambient <Package openmw.ambient>` | by player scripts | | Controls background sounds for given player. |
|
||||||
|
+------------------------------------------------------------+--------------------+---------------------------------------------------------------+
|
||||||
|:ref:`openmw.input <Package openmw.input>` | by player scripts | | User input. |
|
|:ref:`openmw.input <Package openmw.input>` | by player scripts | | User input. |
|
||||||
+------------------------------------------------------------+--------------------+---------------------------------------------------------------+
|
+------------------------------------------------------------+--------------------+---------------------------------------------------------------+
|
||||||
|:ref:`openmw.ui <Package openmw.ui>` | by player scripts | | Controls :ref:`user interface <User interface reference>`. |
|
|:ref:`openmw.ui <Package openmw.ui>` | by player scripts | | Controls :ref:`user interface <User interface reference>`. |
|
||||||
|
@ -9,6 +9,7 @@ set(LUA_API_FILES
|
|||||||
math.doclua
|
math.doclua
|
||||||
string.doclua
|
string.doclua
|
||||||
table.doclua
|
table.doclua
|
||||||
|
openmw/ambient.lua
|
||||||
openmw/async.lua
|
openmw/async.lua
|
||||||
openmw/core.lua
|
openmw/core.lua
|
||||||
openmw/nearby.lua
|
openmw/nearby.lua
|
||||||
|
75
files/lua_api/openmw/ambient.lua
Normal file
75
files/lua_api/openmw/ambient.lua
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
---
|
||||||
|
-- `openmw.ambient` controls background sounds, specific to given player (2D-sounds).
|
||||||
|
-- Can be used only by local scripts, that are attached to a player.
|
||||||
|
-- @module ambient
|
||||||
|
-- @usage local ambient = require('openmw.ambient')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
---
|
||||||
|
-- Play a 2D sound
|
||||||
|
-- @function [parent=#ambient] playSound
|
||||||
|
-- @param #string soundId ID of Sound record to play
|
||||||
|
-- @param #table options An optional table with additional optional arguments. Can contain:
|
||||||
|
--
|
||||||
|
-- * `timeOffset` - a floating point number >= 0, to some time (in second) from beginning of sound file (default: 0);
|
||||||
|
-- * `volume` - a floating point number >= 0, to set a sound volume (default: 1);
|
||||||
|
-- * `pitch` - a floating point number >= 0, to set a sound pitch (default: 1);
|
||||||
|
-- * `scale` - a boolean, to set if sound pitch should be scaled by simulation time scaling (default: true);
|
||||||
|
-- * `loop` - a boolean, to set if sound should be repeated when it ends (default: false);
|
||||||
|
-- @usage local params = {
|
||||||
|
-- timeOffset=0.1
|
||||||
|
-- volume=0.3,
|
||||||
|
-- scale=false,
|
||||||
|
-- pitch=1.0,
|
||||||
|
-- loop=true
|
||||||
|
-- };
|
||||||
|
-- ambient.playSound("shock bolt", params)
|
||||||
|
|
||||||
|
---
|
||||||
|
-- Play a 2D sound file
|
||||||
|
-- @function [parent=#ambient] playSoundFile
|
||||||
|
-- @param #string fileName Path to sound file in VFS
|
||||||
|
-- @param #table options An optional table with additional optional arguments. Can contain:
|
||||||
|
--
|
||||||
|
-- * `timeOffset` - a floating point number >= 0, to some time (in second) from beginning of sound file (default: 0);
|
||||||
|
-- * `volume` - a floating point number >= 0, to set a sound volume (default: 1);
|
||||||
|
-- * `pitch` - a floating point number >= 0, to set a sound pitch (default: 1);
|
||||||
|
-- * `scale` - a boolean, to set if sound pitch should be scaled by simulation time scaling (default: true);
|
||||||
|
-- * `loop` - a boolean, to set if sound should be repeated when it ends (default: false);
|
||||||
|
-- @usage local params = {
|
||||||
|
-- timeOffset=0.1
|
||||||
|
-- volume=0.3,
|
||||||
|
-- scale=false,
|
||||||
|
-- pitch=1.0,
|
||||||
|
-- loop=true
|
||||||
|
-- };
|
||||||
|
-- ambient.playSoundFile("Sound\\test.mp3", params)
|
||||||
|
|
||||||
|
---
|
||||||
|
-- Stop a sound
|
||||||
|
-- @function [parent=#ambient] stopSound
|
||||||
|
-- @param #string soundId ID of Sound record to stop
|
||||||
|
-- @usage ambient.stopSound("shock bolt");
|
||||||
|
|
||||||
|
---
|
||||||
|
-- Stop a sound file
|
||||||
|
-- @function [parent=#ambient] stopSoundFile
|
||||||
|
-- @param #string fileName Path to sound file in VFS
|
||||||
|
-- @usage ambient.stopSoundFile("Sound\\test.mp3");
|
||||||
|
|
||||||
|
---
|
||||||
|
-- Check if sound is playing
|
||||||
|
-- @function [parent=#ambient] isSoundPlaying
|
||||||
|
-- @param #string soundId ID of Sound record to check
|
||||||
|
-- @return #boolean
|
||||||
|
-- @usage local isPlaying = ambient.isSoundPlaying("shock bolt");
|
||||||
|
|
||||||
|
---
|
||||||
|
-- Check if sound file is playing
|
||||||
|
-- @function [parent=#ambient] isSoundFilePlaying
|
||||||
|
-- @param #string fileName Path to sound file in VFS
|
||||||
|
-- @return #boolean
|
||||||
|
-- @usage local isPlaying = ambient.isSoundFilePlaying("Sound\\test.mp3");
|
||||||
|
|
||||||
|
return nil
|
@ -737,4 +737,110 @@
|
|||||||
-- @field #number magnitudeBase
|
-- @field #number magnitudeBase
|
||||||
-- @field #number magnitudeModifier
|
-- @field #number magnitudeModifier
|
||||||
|
|
||||||
|
---
|
||||||
|
-- Play a 3D sound, attached to object
|
||||||
|
-- @function [parent=#core] playSound3d
|
||||||
|
-- @param #string soundId ID of Sound record to play
|
||||||
|
-- @param #GameObject object Object to which we attach the sound
|
||||||
|
-- @param #table options An optional table with additional optional arguments. Can contain:
|
||||||
|
--
|
||||||
|
-- * `timeOffset` - a floating point number >= 0, to some time (in second) from beginning of sound file (default: 0);
|
||||||
|
-- * `volume` - a floating point number >= 0, to set a sound volume (default: 1);
|
||||||
|
-- * `pitch` - a floating point number >= 0, to set a sound pitch (default: 1);
|
||||||
|
-- * `loop` - a boolean, to set if sound should be repeated when it ends (default: false);
|
||||||
|
-- @usage local params = {
|
||||||
|
-- timeOffset=0.1
|
||||||
|
-- volume=0.3,
|
||||||
|
-- loop=false,
|
||||||
|
-- pitch=1.0
|
||||||
|
-- };
|
||||||
|
-- core.sound.playSound3d("shock bolt", object, params)
|
||||||
|
|
||||||
|
---
|
||||||
|
-- Play a 3D sound file, attached to object
|
||||||
|
-- @function [parent=#core] playSoundFile3d
|
||||||
|
-- @param #string fileName Path to sound file in VFS
|
||||||
|
-- @param #GameObject object Object to which we attach the sound
|
||||||
|
-- @param #table options An optional table with additional optional arguments. Can contain:
|
||||||
|
--
|
||||||
|
-- * `timeOffset` - a floating point number >= 0, to some time (in second) from beginning of sound file (default: 0);
|
||||||
|
-- * `volume` - a floating point number >= 0, to set a sound volume (default: 1);
|
||||||
|
-- * `pitch` - a floating point number >= 0, to set a sound pitch (default: 1);
|
||||||
|
-- * `loop` - a boolean, to set if sound should be repeated when it ends (default: false);
|
||||||
|
-- @usage local params = {
|
||||||
|
-- timeOffset=0.1
|
||||||
|
-- volume=0.3,
|
||||||
|
-- loop=false,
|
||||||
|
-- pitch=1.0
|
||||||
|
-- };
|
||||||
|
-- core.sound.playSoundFile3d("Sound\\test.mp3", object, params)
|
||||||
|
|
||||||
|
---
|
||||||
|
-- Stop a 3D sound, attached to object
|
||||||
|
-- @function [parent=#core] stopSound3d
|
||||||
|
-- @param #string soundId ID of Sound record to stop
|
||||||
|
-- @param #GameObject object Object on which we want to stop sound
|
||||||
|
-- @usage core.sound.stopSound("shock bolt", object);
|
||||||
|
|
||||||
|
---
|
||||||
|
-- Stop a 3D sound file, attached to object
|
||||||
|
-- @function [parent=#core] stopSoundFile3d
|
||||||
|
-- @param #string fileName Path to sound file in VFS
|
||||||
|
-- @param #GameObject object Object on which we want to stop sound
|
||||||
|
-- @usage core.sound.stopSoundFile("Sound\\test.mp3", object);
|
||||||
|
|
||||||
|
---
|
||||||
|
-- Check if sound is playing on given object
|
||||||
|
-- @function [parent=#core] isSoundPlaying
|
||||||
|
-- @param #string soundId ID of Sound record to check
|
||||||
|
-- @param #GameObject object Object on which we want to check sound
|
||||||
|
-- @return #boolean
|
||||||
|
-- @usage local isPlaying = core.sound.isSoundPlaying("shock bolt", object);
|
||||||
|
|
||||||
|
---
|
||||||
|
-- Check if sound file is playing on given object
|
||||||
|
-- @function [parent=#core] isSoundFilePlaying
|
||||||
|
-- @param #string fileName Path to sound file in VFS
|
||||||
|
-- @param #GameObject object Object on which we want to check sound
|
||||||
|
-- @return #boolean
|
||||||
|
-- @usage local isPlaying = core.sound.isSoundFilePlaying("Sound\\test.mp3", object);
|
||||||
|
|
||||||
|
---
|
||||||
|
-- Play an animated voiceover. Has two overloads:
|
||||||
|
--
|
||||||
|
-- * With an "object" argument: play sound for given object, with speaking animation if possible equipment slots.
|
||||||
|
-- * Without an "object" argument: play sound globally, without object
|
||||||
|
-- @function [parent=#core] say
|
||||||
|
-- @param #string fileName Path to sound file in VFS
|
||||||
|
-- @param #GameObject object Object on which we want to play an animated voiceover (optional)
|
||||||
|
-- @param #string text Subtitle text (optional)
|
||||||
|
-- @usage -- play voiceover for object and print messagebox
|
||||||
|
-- core.sound.say("Sound\\Vo\\Misc\\voice.mp3", object, "Subtitle text")
|
||||||
|
-- @usage -- play voiceover globally and print messagebox
|
||||||
|
-- core.sound.say("Sound\\Vo\\Misc\\voice.mp3", "Subtitle text")
|
||||||
|
-- @usage -- play voiceover for object without messagebox
|
||||||
|
-- core.sound.say("Sound\\Vo\\Misc\\voice.mp3", object)
|
||||||
|
-- @usage -- play voiceover globally without messagebox
|
||||||
|
-- core.sound.say("Sound\\Vo\\Misc\\voice.mp3")
|
||||||
|
|
||||||
|
---
|
||||||
|
-- Stop animated voiceover
|
||||||
|
-- @function [parent=#core] stopSay
|
||||||
|
-- @param #string fileName Path to sound file in VFS
|
||||||
|
-- @param #GameObject object Object on which we want to stop an animated voiceover (optional)
|
||||||
|
-- @usage -- stop voice for given object
|
||||||
|
-- core.sound.stopSay(object);
|
||||||
|
-- @usage -- stop global voice
|
||||||
|
-- core.sound.stopSay();
|
||||||
|
|
||||||
|
---
|
||||||
|
-- Check if animated voiceover is playing
|
||||||
|
-- @function [parent=#core] isSayActive
|
||||||
|
-- @param #GameObject object Object on which we want to check an animated voiceover (optional)
|
||||||
|
-- @return #boolean
|
||||||
|
-- @usage -- check voice for given object
|
||||||
|
-- local isActive = isSayActive(object);
|
||||||
|
-- @usage -- check global voice
|
||||||
|
-- local isActive = isSayActive();
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -127,10 +127,10 @@
|
|||||||
---
|
---
|
||||||
-- Get equipment.
|
-- Get equipment.
|
||||||
-- Has two overloads:
|
-- Has two overloads:
|
||||||
-- 1) With a single argument: returns a table `slot` -> @{openmw.core#GameObject} of currently equipped items.
|
--
|
||||||
-- See @{#EQUIPMENT_SLOT}. Returns empty table if the actor doesn't have
|
-- * With a single argument: returns a table `slot` -> @{openmw.core#GameObject} of currently equipped items.
|
||||||
-- equipment slots.
|
-- See @{#EQUIPMENT_SLOT}. Returns empty table if the actor doesn't have equipment slots.
|
||||||
-- 2) With two arguments: returns an item equipped to the given slot.
|
-- * With two arguments: returns an item equipped to the given slot.
|
||||||
-- @function [parent=#Actor] getEquipment
|
-- @function [parent=#Actor] getEquipment
|
||||||
-- @param openmw.core#GameObject actor
|
-- @param openmw.core#GameObject actor
|
||||||
-- @param #number slot Optional number of the equipment slot
|
-- @param #number slot Optional number of the equipment slot
|
||||||
|
Loading…
x
Reference in New Issue
Block a user