mirror of https://github.com/OpenMW/openmw.git
Merge branch 'sounds' into 'master'
Lua bindings for sound functions See merge request OpenMW/openmw!3247macos_ci_fix
commit
4211665ede
@ -0,0 +1,198 @@
|
||||
#include "soundbindings.hpp"
|
||||
#include "luabindings.hpp"
|
||||
|
||||
#include "../mwbase/environment.hpp"
|
||||
#include "../mwbase/soundmanager.hpp"
|
||||
#include "../mwbase/windowmanager.hpp"
|
||||
|
||||
#include <components/esm3/loadsoun.hpp>
|
||||
#include <components/misc/resourcehelpers.hpp>
|
||||
#include <components/vfs/pathutil.hpp>
|
||||
|
||||
#include "../mwworld/esmstore.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::state_view& lua = context.mLua->sol();
|
||||
sol::table api(lua, 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()); });
|
||||
|
||||
// Sound store
|
||||
using SoundStore = MWWorld::Store<ESM::Sound>;
|
||||
const SoundStore* soundStore = &MWBase::Environment::get().getWorld()->getStore().get<ESM::Sound>();
|
||||
sol::usertype<SoundStore> soundStoreT = lua.new_usertype<SoundStore>("ESM3_SoundStore");
|
||||
soundStoreT[sol::meta_function::to_string]
|
||||
= [](const SoundStore& store) { return "ESM3_SoundStore{" + std::to_string(store.getSize()) + " sounds}"; };
|
||||
soundStoreT[sol::meta_function::length] = [](const SoundStore& store) { return store.getSize(); };
|
||||
soundStoreT[sol::meta_function::index] = sol::overload(
|
||||
[](const SoundStore& store, size_t index) -> const ESM::Sound* { return store.at(index - 1); },
|
||||
[](const SoundStore& store, std::string_view soundId) -> const ESM::Sound* {
|
||||
return store.find(ESM::RefId::deserializeText(soundId));
|
||||
});
|
||||
soundStoreT[sol::meta_function::pairs] = lua["ipairsForArray"].template get<sol::function>();
|
||||
soundStoreT[sol::meta_function::ipairs] = lua["ipairsForArray"].template get<sol::function>();
|
||||
|
||||
api["sounds"] = soundStore;
|
||||
|
||||
// Sound record
|
||||
auto soundT = lua.new_usertype<ESM::Sound>("ESM3_Sound");
|
||||
soundT[sol::meta_function::to_string]
|
||||
= [](const ESM::Sound& rec) -> std::string { return "ESM3_Sound[" + rec.mId.toDebugString() + "]"; };
|
||||
soundT["id"] = sol::readonly_property([](const ESM::Sound& rec) { return rec.mId.serializeText(); });
|
||||
soundT["volume"]
|
||||
= sol::readonly_property([](const ESM::Sound& rec) -> unsigned char { return rec.mData.mVolume; });
|
||||
soundT["minRange"]
|
||||
= sol::readonly_property([](const ESM::Sound& rec) -> unsigned char { return rec.mData.mMinRange; });
|
||||
soundT["maxRange"]
|
||||
= sol::readonly_property([](const ESM::Sound& rec) -> unsigned char { return rec.mData.mMaxRange; });
|
||||
soundT["fileName"] = sol::readonly_property([](const ESM::Sound& rec) -> std::string {
|
||||
return VFS::Path::normalizeFilename(Misc::ResourceHelpers::correctSoundPath(rec.mSound));
|
||||
});
|
||||
|
||||
return LuaUtil::makeReadOnly(api);
|
||||
}
|
||||
}
|
@ -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
|
@ -0,0 +1,5 @@
|
||||
Package openmw.ambient
|
||||
======================
|
||||
|
||||
.. raw:: html
|
||||
:file: generated_html/openmw_ambient.html
|
@ -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
|
Loading…
Reference in New Issue