diff --git a/CHANGELOG.md b/CHANGELOG.md index 4c244185a..fe7baac90 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -70,6 +70,14 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), - New `SLTerrain` INI property `OrbitDirection`, which defines which direction is considered to be orbit, for the sake of brain-path-to-orbit, dropship spawn/return location, etc. Can be any of `Up`, `Down`, `Left` or `Right`. Defaults to `Up`. +- New FMOD and SoundContainer features: + The game is now divided into SFX, UI, and Music busses which all route into the Master bus. + The SFX bus has compression added for a better listening experience, and a safety volume limiter has been added to the Master bus. + Aside from volume being attenuated, sounds will now also be lowpass filtered as distance increases. + New `SoundContainer` INI and Lua (R/W) property `BusRouting`, which denotes which bus the SoundContainer routes to. Available busses: `SFX, UI, Music`. Defaults to `SFX`. + `Enum` binding for `SoundContainer.BusRouting`: `SFX = 0, UI = 1, MUSIC = 2`. + New `SoundContainer` INI and Lua (R/W) property `PanningStrengthMultiplier`, which will multiply the strength of 3D panning. This can be used to achieve for example a psuedo-Immobile effect where attenuation effects are still applied but the sound does not move from the center. Strongly recommended to keep between 0.0 and 1.0. +
Changed diff --git a/Entities/SoundContainer.cpp b/Entities/SoundContainer.cpp index 791afcb38..c05118eaa 100644 --- a/Entities/SoundContainer.cpp +++ b/Entities/SoundContainer.cpp @@ -11,6 +11,12 @@ namespace RTE { {"Ignore Play", SoundContainer::SoundOverlapMode::IGNORE_PLAY} }; + const std::unordered_map SoundContainer::c_BusRoutingMap = { + {"SFX", SoundContainer::BusRouting::SFX}, + {"UI", SoundContainer::BusRouting::UI}, + {"Music", SoundContainer::BusRouting::MUSIC} + }; + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void SoundContainer::Clear() { @@ -19,8 +25,10 @@ namespace RTE { m_PlayingChannels.clear(); m_SoundOverlapMode = SoundOverlapMode::OVERLAP; + m_BusRouting = BusRouting::SFX; m_Immobile = false; m_AttenuationStartDistance = c_DefaultAttenuationStartDistance; + m_PanningStrengthMultiplier = 1.0F; m_Loops = 0; m_SoundPropertiesUpToDate = false; @@ -43,8 +51,10 @@ namespace RTE { m_PlayingChannels.clear(); m_SoundOverlapMode = reference.m_SoundOverlapMode; + m_BusRouting = reference.m_BusRouting; m_Immobile = reference.m_Immobile; m_AttenuationStartDistance = reference.m_AttenuationStartDistance; + m_PanningStrengthMultiplier = reference.m_PanningStrengthMultiplier; m_Loops = reference.m_Loops; m_Priority = reference.m_Priority; @@ -83,8 +93,21 @@ namespace RTE { } } }); + MatchProperty("BusRouting", { + std::string busRoutingString = reader.ReadPropValue(); + if (c_BusRoutingMap.find(busRoutingString) != c_BusRoutingMap.end()) { + m_BusRouting = c_BusRoutingMap.find(busRoutingString)->second; + } else { + try { + m_BusRouting = static_cast(std::stoi(busRoutingString)); + } catch (const std::exception &) { + reader.ReportError("Tried to route to non-existent sound bus " + busRoutingString); + } + } + }); MatchProperty("Immobile", { reader >> m_Immobile; }); MatchProperty("AttenuationStartDistance", { reader >> m_AttenuationStartDistance; }); + MatchProperty("PanningStrengthMultiplier", { reader >> m_PanningStrengthMultiplier; }); MatchProperty("LoopSetting", { reader >> m_Loops; }); MatchProperty("Priority", { reader >> m_Priority; @@ -100,7 +123,7 @@ namespace RTE { } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - + int SoundContainer::Save(Writer &writer) const { Entity::Save(writer); @@ -117,11 +140,14 @@ namespace RTE { } else { RTEAbort("Tried to write invalid SoundOverlapMode when saving SoundContainer."); } - + writer.NewProperty("BusRouting"); + writer << m_BusRouting; writer.NewProperty("Immobile"); writer << m_Immobile; writer.NewProperty("AttenuationStartDistance"); writer << m_AttenuationStartDistance; + writer.NewProperty("PanningStrengthMultiplier"); + writer << m_PanningStrengthMultiplier; writer.NewProperty("LoopSetting"); writer << m_Loops; @@ -216,7 +242,7 @@ namespace RTE { for (SoundSet::SoundData *soundData : flattenedSoundData) { FMOD_MODE soundMode = (m_Loops == 0) ? FMOD_LOOP_OFF : FMOD_LOOP_NORMAL; if (m_Immobile) { - soundMode |= FMOD_3D_HEADRELATIVE; + soundMode |= FMOD_2D; m_AttenuationStartDistance = c_SoundMaxAudibleDistance; } else if (g_AudioMan.GetSoundPanningEffectStrength() == 1.0F) { soundMode |= FMOD_3D_INVERSEROLLOFF; diff --git a/Entities/SoundContainer.h b/Entities/SoundContainer.h index e17b4e336..a46406716 100644 --- a/Entities/SoundContainer.h +++ b/Entities/SoundContainer.h @@ -19,6 +19,15 @@ namespace RTE { SerializableOverrideMethods; ClassInfoGetters; + /// + /// The FMOD channelgroup/bus this sound routes through. + /// + enum BusRouting { + SFX = 0, // Default diegetic bus for general game SFX. + UI = 1, // Menu sounds and other things that shouldn't be affected by diegetic sound processing. + MUSIC = 2 // Self-explanatory music bus. + }; + /// /// How the SoundContainer should behave when it tries to play again while already playing. /// @@ -48,13 +57,14 @@ namespace RTE { int Create(const SoundContainer &reference); /// - /// Creates a SoundContainer and adds a sound, optionally setting whether it's immobile or affected by global pitch. + /// Creates a SoundContainer and adds a sound, optionally setting immobility, being affected by global pitch, and bus routing. /// /// The path to a sound to add to the first SoundSet of this SoundContainer. /// Whether this SoundContainer's sounds will be treated as immobile, i.e. they won't be affected by 3D sound manipulation. /// Whether this SoundContainer's sounds' frequency will be affected by the global pitch. + /// Bus to route this sound to. /// An error return value signaling success or any particular failure. Anything below 0 is an error signal. - int Create(const std::string &soundFilePath, bool immobile = false, bool affectedByGlobalPitch = true) { m_TopLevelSoundSet.AddSound(soundFilePath, true); SetImmobile(immobile); SetAffectedByGlobalPitch(affectedByGlobalPitch); return 0; } + int Create(const std::string &soundFilePath, bool immobile = false, bool affectedByGlobalPitch = true, BusRouting busRouting = BusRouting::SFX) { m_TopLevelSoundSet.AddSound(soundFilePath, true); SetImmobile(immobile); SetAffectedByGlobalPitch(affectedByGlobalPitch); SetBusRouting(busRouting); return 0; } #pragma endregion #pragma region Destruction @@ -157,6 +167,19 @@ namespace RTE { #pragma endregion #pragma region Sound Property Getters and Setters + + /// + /// Gets the bus this sound routes to. + /// + /// The bus this sound routes to. + BusRouting GetBusRouting() const { return m_BusRouting; } + + /// + /// Sets the bus this sound routes to. + /// + /// The new bus for this sound to route to. + void SetBusRouting(BusRouting newBusRoute) { m_BusRouting = newBusRoute; } + /// /// Gets whether the sounds in this SoundContainer should be considered immobile, i.e. always play at the listener's position. /// @@ -181,6 +204,18 @@ namespace RTE { /// The new attenuation start distance. void SetAttenuationStartDistance(float attenuationStartDistance) { m_AttenuationStartDistance = (attenuationStartDistance < 0) ? c_DefaultAttenuationStartDistance : attenuationStartDistance; m_SoundPropertiesUpToDate = false; } + /// + /// Gets the panning strength multiplier of this SoundContainer. + /// + /// A float with the panning strength multiplier. + float GetPanningStrengthMultiplier() const { return m_PanningStrengthMultiplier; } + + /// + /// Sets the panning strength multiplier of this SoundContainer. + /// + /// The new panning strength multiplier. + void SetPanningStrengthMultiplier(float panningStrengthMultiplier) { m_PanningStrengthMultiplier = panningStrengthMultiplier; m_SoundPropertiesUpToDate = false; } + /// /// Gets the looping setting of this SoundContainer. /// @@ -349,14 +384,18 @@ namespace RTE { static Entity::ClassInfo m_sClass; //!< ClassInfo for this class. static const std::unordered_map c_SoundOverlapModeMap; //!< A map of strings to SoundOverlapModes to support string parsing for the SoundOverlapMode enum. Populated in the implementing cpp file. - + static const std::unordered_map c_BusRoutingMap; //!< A map of strings to BusRoutings to support string parsing for the BusRouting enum. Populated in the implementing cpp file. + SoundSet m_TopLevelSoundSet; //The top level SoundSet that handles all SoundData and sub SoundSets in this SoundContainer. std::unordered_set m_PlayingChannels; //!< The channels this SoundContainer is currently using. SoundOverlapMode m_SoundOverlapMode; //!< The SoundOverlapMode for this SoundContainer, used to determine how it should handle overlapping play calls. - - bool m_Immobile; //!< Whether this SoundContainer's sounds should be treated as immobile, i.e. not affected by 3D sound effects. Mostly used for GUI sounds and the like. + + BusRouting m_BusRouting; //!< What bus this sound routes to. + + bool m_Immobile; //!< Whether this SoundContainer's sounds should be treated as immobile, i.e. not affected by 3D sound effects. float m_AttenuationStartDistance; //!< The distance away from the AudioSystem listener to start attenuating this sound. Attenuation follows FMOD 3D Inverse roll-off model. + float m_PanningStrengthMultiplier; //!< Multiplier for panning strength, 0.0 to 1.0. int m_Loops; //!< Number of loops (repeats) the SoundContainer's sounds should play when played. 0 means it plays once, -1 means it plays until stopped. bool m_SoundPropertiesUpToDate = false; //!< Whether this SoundContainer's sounds' modes and properties are up to date. Used primarily to handle discrepancies that can occur when loading from ini if the line ordering isn't ideal. diff --git a/GUI/GUISound.cpp b/GUI/GUISound.cpp index 79ac23da5..c96a95918 100644 --- a/GUI/GUISound.cpp +++ b/GUI/GUISound.cpp @@ -39,44 +39,44 @@ namespace RTE { void GUISound::Initialize() { // Interface sounds should not be pitched to reinforce the appearance of time decoupling between simulation and UI. - m_SplashSound.Create("Base.rte/Sounds/GUIs/MetaStart.flac", true, false); + m_SplashSound.Create("Base.rte/Sounds/GUIs/MetaStart.flac", true, false, SoundContainer::BusRouting::UI); - m_EnterMenuSound.Create("Base.rte/Sounds/GUIs/MenuEnter.flac", true, false); + m_EnterMenuSound.Create("Base.rte/Sounds/GUIs/MenuEnter.flac", true, false, SoundContainer::BusRouting::UI); - m_ExitMenuSound.Create("Base.rte/Sounds/GUIs/MenuExit1.flac", true, false); + m_ExitMenuSound.Create("Base.rte/Sounds/GUIs/MenuExit1.flac", true, false, SoundContainer::BusRouting::UI); m_ExitMenuSound.GetTopLevelSoundSet().AddSound("Base.rte/Sounds/GUIs/MenuExit2.flac", true); - m_FocusChangeSound.Create("Base.rte/Sounds/GUIs/FocusChange.flac", true, false); + m_FocusChangeSound.Create("Base.rte/Sounds/GUIs/FocusChange.flac", true, false, SoundContainer::BusRouting::UI); - m_SelectionChangeSound.Create("Base.rte/Sounds/GUIs/SelectionChange.flac", true, false); + m_SelectionChangeSound.Create("Base.rte/Sounds/GUIs/SelectionChange.flac", true, false, SoundContainer::BusRouting::UI); - m_ItemChangeSound.Create("Base.rte/Sounds/GUIs/ItemChange.flac", true, false); + m_ItemChangeSound.Create("Base.rte/Sounds/GUIs/ItemChange.flac", true, false, SoundContainer::BusRouting::UI); - m_ButtonPressSound.Create("Base.rte/Sounds/GUIs/ButtonPress.flac", true, false); + m_ButtonPressSound.Create("Base.rte/Sounds/GUIs/ButtonPress.flac", true, false, SoundContainer::BusRouting::UI); - m_BackButtonPressSound.Create("Base.rte/Sounds/GUIs/BackButtonPress.flac", true, false); + m_BackButtonPressSound.Create("Base.rte/Sounds/GUIs/BackButtonPress.flac", true, false, SoundContainer::BusRouting::UI); - m_ConfirmSound.Create("Base.rte/Sounds/GUIs/MenuExit1.flac", true, false); + m_ConfirmSound.Create("Base.rte/Sounds/GUIs/MenuExit1.flac", true, false, SoundContainer::BusRouting::UI); - m_UserErrorSound.Create("Base.rte/Sounds/GUIs/UserError.flac", true, false); + m_UserErrorSound.Create("Base.rte/Sounds/GUIs/UserError.flac", true, false, SoundContainer::BusRouting::UI); - m_TestSound.Create("Base.rte/Sounds/GUIs/Test.flac", true, false); + m_TestSound.Create("Base.rte/Sounds/GUIs/Test.flac", true, false, SoundContainer::BusRouting::UI); - m_PieMenuEnterSound.Create("Base.rte/Sounds/GUIs/PieMenuEnter.flac", true, false); + m_PieMenuEnterSound.Create("Base.rte/Sounds/GUIs/PieMenuEnter.flac", true, false, SoundContainer::BusRouting::UI); - m_PieMenuExitSound.Create("Base.rte/Sounds/GUIs/PieMenuExit.flac", true, false); + m_PieMenuExitSound.Create("Base.rte/Sounds/GUIs/PieMenuExit.flac", true, false, SoundContainer::BusRouting::UI); - // m_HoverChangeSound.Create("Base.rte/Sounds/GUIs/SelectionChange.flac", true, false); + // m_HoverChangeSound.Create("Base.rte/Sounds/GUIs/SelectionChange.flac", true, false, SoundContainer::BusRouting::UI); m_HoverChangeSound = m_SelectionChangeSound; - m_HoverDisabledSound.Create("Base.rte/Sounds/GUIs/PlacementBlip.flac", true, false); + m_HoverDisabledSound.Create("Base.rte/Sounds/GUIs/PlacementBlip.flac", true, false, SoundContainer::BusRouting::UI); - m_SlicePickedSound.Create("Base.rte/Sounds/GUIs/SlicePicked.flac", true, false); + m_SlicePickedSound.Create("Base.rte/Sounds/GUIs/SlicePicked.flac", true, false, SoundContainer::BusRouting::UI); - // m_DisabledPickedSound.Create("Base.rte/Sounds/GUIs/PieMenuExit.flac", true, false); + // m_DisabledPickedSound.Create("Base.rte/Sounds/GUIs/PieMenuExit.flac", true, false, SoundContainer::BusRouting::UI); m_DisabledPickedSound = m_PieMenuExitSound; - m_FundsChangedSound.Create("Base.rte/Sounds/GUIs/FundsChanged1.flac", true, false); + m_FundsChangedSound.Create("Base.rte/Sounds/GUIs/FundsChanged1.flac", true, false, SoundContainer::BusRouting::UI); m_FundsChangedSound.GetTopLevelSoundSet().AddSound("Base.rte/Sounds/GUIs/FundsChanged2.flac", true); m_FundsChangedSound.GetTopLevelSoundSet().AddSound("Base.rte/Sounds/GUIs/FundsChanged3.flac", true); m_FundsChangedSound.GetTopLevelSoundSet().AddSound("Base.rte/Sounds/GUIs/FundsChanged4.flac", true); @@ -84,29 +84,29 @@ namespace RTE { m_FundsChangedSound.GetTopLevelSoundSet().AddSound("Base.rte/Sounds/GUIs/FundsChanged6.flac", true); m_FundsChangedSound.SetSoundOverlapMode(SoundContainer::SoundOverlapMode::RESTART); - m_ActorSwitchSound.Create("Base.rte/Sounds/GUIs/ActorSwitch.flac", true, false); + m_ActorSwitchSound.Create("Base.rte/Sounds/GUIs/ActorSwitch.flac", true, false, SoundContainer::BusRouting::UI); - m_BrainSwitchSound.Create("Base.rte/Sounds/GUIs/BrainSwitch.flac", true, false); + m_BrainSwitchSound.Create("Base.rte/Sounds/GUIs/BrainSwitch.flac", true, false, SoundContainer::BusRouting::UI); - m_CameraTravelSound.Create("Base.rte/Sounds/GUIs/CameraTravel1.flac", true, false); + m_CameraTravelSound.Create("Base.rte/Sounds/GUIs/CameraTravel1.flac", true, false, SoundContainer::BusRouting::UI); m_CameraTravelSound.GetTopLevelSoundSet().AddSound("Base.rte/Sounds/GUIs/CameraTravel2.flac", true); m_CameraTravelSound.GetTopLevelSoundSet().AddSound("Base.rte/Sounds/GUIs/CameraTravel3.flac", true); - // m_AreaPickedSound.Create("Base.rte/Sounds/GUIs/MenuEnter.flac", true, false); + // m_AreaPickedSound.Create("Base.rte/Sounds/GUIs/MenuEnter.flac", true, false, SoundContainer::BusRouting::UI); m_AreaPickedSound = m_ConfirmSound; - // m_ObjectPickedSound.Create("Base.rte/Sounds/GUIs/MenuEnter.flac", true, false); + // m_ObjectPickedSound.Create("Base.rte/Sounds/GUIs/MenuEnter.flac", true, false, SoundContainer::BusRouting::UI); m_ObjectPickedSound = m_ConfirmSound; - // m_PurchaseMadeSound.Create("Base.rte/Sounds/GUIs/MenuEnter.flac", true, false); + // m_PurchaseMadeSound.Create("Base.rte/Sounds/GUIs/MenuEnter.flac", true, false, SoundContainer::BusRouting::UI); m_PurchaseMadeSound = m_ConfirmSound; - m_PlacementBlip.Create("Base.rte/Sounds/GUIs/PlacementBlip.flac", true, false); + m_PlacementBlip.Create("Base.rte/Sounds/GUIs/PlacementBlip.flac", true, false, SoundContainer::BusRouting::UI); - m_PlacementThud.Create("Base.rte/Sounds/GUIs/PlacementThud1.flac", true, false); + m_PlacementThud.Create("Base.rte/Sounds/GUIs/PlacementThud1.flac", true, false, SoundContainer::BusRouting::UI); m_PlacementThud.GetTopLevelSoundSet().AddSound("Base.rte/Sounds/GUIs/PlacementThud2.flac", true); - m_PlacementGravel.Create("Base.rte/Sounds/GUIs/PlacementGravel1.flac", true, false); + m_PlacementGravel.Create("Base.rte/Sounds/GUIs/PlacementGravel1.flac", true, false, SoundContainer::BusRouting::UI); m_PlacementGravel.GetTopLevelSoundSet().AddSound("Base.rte/Sounds/GUIs/PlacementGravel2.flac", true); m_PlacementGravel.GetTopLevelSoundSet().AddSound("Base.rte/Sounds/GUIs/PlacementGravel3.flac", true); m_PlacementGravel.GetTopLevelSoundSet().AddSound("Base.rte/Sounds/GUIs/PlacementGravel4.flac", true); diff --git a/Lua/LuaBindingsEntities.cpp b/Lua/LuaBindingsEntities.cpp index 222a40f4d..bd690b9e5 100644 --- a/Lua/LuaBindingsEntities.cpp +++ b/Lua/LuaBindingsEntities.cpp @@ -1366,8 +1366,10 @@ namespace RTE { .def(luabind::constructor<>()) .property("SoundOverlapMode", &SoundContainer::GetSoundOverlapMode, &SoundContainer::SetSoundOverlapMode) + .property("BusRouting", &SoundContainer::GetBusRouting, &SoundContainer::SetBusRouting) .property("Immobile", &SoundContainer::IsImmobile, &SoundContainer::SetImmobile) .property("AttenuationStartDistance", &SoundContainer::GetAttenuationStartDistance, &SoundContainer::SetAttenuationStartDistance) + .property("PanningStrengthMultiplier", &SoundContainer::GetPanningStrengthMultiplier, &SoundContainer::SetPanningStrengthMultiplier) .property("Loops", &SoundContainer::GetLoopSetting, &SoundContainer::SetLoopSetting) .property("Priority", &SoundContainer::GetPriority, &SoundContainer::SetPriority) .property("AffectedByGlobalPitch", &SoundContainer::IsAffectedByGlobalPitch, &SoundContainer::SetAffectedByGlobalPitch) @@ -1389,7 +1391,13 @@ namespace RTE { .def("Restart", (bool (SoundContainer:: *)()) &SoundContainer::Restart) .def("Restart", (bool (SoundContainer:: *)(int player)) &SoundContainer::Restart) .def("FadeOut", &SoundContainer::FadeOut) - + + .enum_("BusRouting")[ + luabind::value("SFX", SoundContainer::BusRouting::SFX), + luabind::value("UI", SoundContainer::BusRouting::UI), + luabind::value("MUSIC", SoundContainer::BusRouting::MUSIC) + ] + .enum_("SoundOverlapMode")[ luabind::value("OVERLAP", SoundContainer::SoundOverlapMode::OVERLAP), luabind::value("RESTART", SoundContainer::SoundOverlapMode::RESTART), diff --git a/Managers/AudioMan.cpp b/Managers/AudioMan.cpp index eaabbd507..220bd5406 100644 --- a/Managers/AudioMan.cpp +++ b/Managers/AudioMan.cpp @@ -66,31 +66,29 @@ namespace RTE { audioSystemSetupResult = (audioSystemSetupResult == FMOD_OK) ? m_AudioSystem->getMasterChannelGroup(&m_MasterChannelGroup) : audioSystemSetupResult; + audioSystemSetupResult = (audioSystemSetupResult == FMOD_OK) ? m_AudioSystem->createChannelGroup("SFX", &m_SFXChannelGroup) : audioSystemSetupResult; + audioSystemSetupResult = (audioSystemSetupResult == FMOD_OK) ? m_AudioSystem->createChannelGroup("UI", &m_UIChannelGroup) : audioSystemSetupResult; audioSystemSetupResult = (audioSystemSetupResult == FMOD_OK) ? m_AudioSystem->createChannelGroup("Music", &m_MusicChannelGroup) : audioSystemSetupResult; - audioSystemSetupResult = (audioSystemSetupResult == FMOD_OK) ? m_AudioSystem->createChannelGroup("Sounds", &m_SoundChannelGroup) : audioSystemSetupResult; + // Add a safety limiter to the master channel group FMOD::DSP *dsp_limiter; audioSystemSetupResult = (audioSystemSetupResult == FMOD_OK) ? m_AudioSystem->createDSPByType(FMOD_DSP_TYPE_LIMITER, &dsp_limiter) : audioSystemSetupResult; audioSystemSetupResult = (audioSystemSetupResult == FMOD_OK) ? m_MasterChannelGroup->addDSP(0, dsp_limiter) : audioSystemSetupResult; - // Add a compressor to the sound channel group + // Add a compressor to the SFX channel group + // This is pretty heavy-handed, but it sounds great. Might need to be changed once we have sidechaining and fancier things going on. FMOD::DSP* dsp_compressor; audioSystemSetupResult = (audioSystemSetupResult == FMOD_OK) ? m_AudioSystem->createDSPByType(FMOD_DSP_TYPE_COMPRESSOR, &dsp_compressor) : audioSystemSetupResult; audioSystemSetupResult = (audioSystemSetupResult == FMOD_OK) ? dsp_compressor->setParameterFloat(0, -10.0f) : audioSystemSetupResult; // Threshold audioSystemSetupResult = (audioSystemSetupResult == FMOD_OK) ? dsp_compressor->setParameterFloat(1, 4.0f) : audioSystemSetupResult; // Ratio audioSystemSetupResult = (audioSystemSetupResult == FMOD_OK) ? dsp_compressor->setParameterFloat(2, 80.0f) : audioSystemSetupResult; // Attack time audioSystemSetupResult = (audioSystemSetupResult == FMOD_OK) ? dsp_compressor->setParameterFloat(3, 180.0f) : audioSystemSetupResult; // Release time - audioSystemSetupResult = (audioSystemSetupResult == FMOD_OK) ? dsp_compressor->setParameterFloat(4, 10.0f) : audioSystemSetupResult; // Make-up gain - audioSystemSetupResult = (audioSystemSetupResult == FMOD_OK) ? m_SoundChannelGroup->addDSP(0, dsp_compressor) : audioSystemSetupResult; - - audioSystemSetupResult = (audioSystemSetupResult == FMOD_OK) ? m_AudioSystem->createChannelGroup("MobileSounds", &m_MobileSoundChannelGroup) : audioSystemSetupResult; - audioSystemSetupResult = (audioSystemSetupResult == FMOD_OK) ? m_AudioSystem->createChannelGroup("ImmobileSounds", &m_ImmobileSoundChannelGroup) : audioSystemSetupResult; - audioSystemSetupResult = (audioSystemSetupResult == FMOD_OK) ? m_AudioSystem->createChannelGroup("MenuSounds", &m_MenuSoundChannelGroup) : audioSystemSetupResult; + audioSystemSetupResult = (audioSystemSetupResult == FMOD_OK) ? dsp_compressor->setParameterFloat(4, 5.0f) : audioSystemSetupResult; // Make-up gain + audioSystemSetupResult = (audioSystemSetupResult == FMOD_OK) ? m_SFXChannelGroup->addDSP(0, dsp_compressor) : audioSystemSetupResult; + + audioSystemSetupResult = (audioSystemSetupResult == FMOD_OK) ? m_MasterChannelGroup->addGroup(m_SFXChannelGroup) : audioSystemSetupResult; + audioSystemSetupResult = (audioSystemSetupResult == FMOD_OK) ? m_MasterChannelGroup->addGroup(m_UIChannelGroup) : audioSystemSetupResult; audioSystemSetupResult = (audioSystemSetupResult == FMOD_OK) ? m_MasterChannelGroup->addGroup(m_MusicChannelGroup) : audioSystemSetupResult; - audioSystemSetupResult = (audioSystemSetupResult == FMOD_OK) ? m_MasterChannelGroup->addGroup(m_SoundChannelGroup) : audioSystemSetupResult; - audioSystemSetupResult = (audioSystemSetupResult == FMOD_OK) ? m_SoundChannelGroup->addGroup(m_MobileSoundChannelGroup) : audioSystemSetupResult; - audioSystemSetupResult = (audioSystemSetupResult == FMOD_OK) ? m_SoundChannelGroup->addGroup(m_ImmobileSoundChannelGroup) : audioSystemSetupResult; - audioSystemSetupResult = (audioSystemSetupResult == FMOD_OK) ? m_SoundChannelGroup->addGroup(m_MenuSoundChannelGroup) : audioSystemSetupResult; m_AudioEnabled = audioSystemSetupResult == FMOD_OK; @@ -161,7 +159,7 @@ namespace RTE { listenerNumber++; } - Update3DEffectsForMobileSoundChannels(); + Update3DEffectsForSFXChannels(); } else { if (!m_CurrentActivityHumanPlayerPositions.empty()) { m_CurrentActivityHumanPlayerPositions.clear(); @@ -190,9 +188,8 @@ namespace RTE { m_GlobalPitch = std::clamp(pitch, 0.125F, 8.0F); if (includeMusic) { m_MusicChannelGroup->setPitch(m_GlobalPitch); } - - FMOD::ChannelGroup *channelGroupToUse = includeImmobileSounds ? m_SoundChannelGroup : m_MobileSoundChannelGroup; - channelGroupToUse->setPitch(m_GlobalPitch); + + m_SFXChannelGroup->setPitch(m_GlobalPitch); } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -267,35 +264,21 @@ namespace RTE { int numberOfPlayingChannels; FMOD::Channel *soundChannel; - FMOD_RESULT result = m_MobileSoundChannelGroup->getNumChannels(&numberOfPlayingChannels); + FMOD_RESULT result = m_SFXChannelGroup->getNumChannels(&numberOfPlayingChannels); if (result != FMOD_OK) { - g_ConsoleMan.PrintString("ERROR: Failed to get the number of playing mobile sound channels when finishing all looping sounds: " + std::string(FMOD_ErrorString(result))); + g_ConsoleMan.PrintString("ERROR: Failed to get the number of playing SFX sound channels when finishing all looping sounds: " + std::string(FMOD_ErrorString(result))); return; } for (int i = 0; i < numberOfPlayingChannels; i++) { - result = m_MobileSoundChannelGroup->getChannel(i, &soundChannel); + result = m_SFXChannelGroup->getChannel(i, &soundChannel); if (result != FMOD_OK) { - g_ConsoleMan.PrintString("ERROR: Failed to get mobile sound channel when finishing all looping sounds: " + std::string(FMOD_ErrorString(result))); - return; - } - soundChannel->setLoopCount(0); - } - - result = m_ImmobileSoundChannelGroup->getNumChannels(&numberOfPlayingChannels); - if (result != FMOD_OK) { - g_ConsoleMan.PrintString("ERROR: Failed to get the number of playing immobile sound channels when finishing all looping sounds: " + std::string(FMOD_ErrorString(result))); - return; - } - - for (int i = 0; i < numberOfPlayingChannels; i++) { - result = m_ImmobileSoundChannelGroup->getChannel(i, &soundChannel); - if (result != FMOD_OK) { - g_ConsoleMan.PrintString("ERROR: Failed to get immobile sound channel when finishing all looping sounds: " + std::string(FMOD_ErrorString(result))); + g_ConsoleMan.PrintString("ERROR: Failed to get SFX sound channel when finishing all looping sounds: " + std::string(FMOD_ErrorString(result))); return; } soundChannel->setLoopCount(0); } + } } @@ -591,11 +574,18 @@ namespace RTE { } } - FMOD::ChannelGroup *channelGroupToPlayIn; - if (g_ActivityMan.ActivityRunning()) { - channelGroupToPlayIn = soundContainer->IsImmobile() ? m_ImmobileSoundChannelGroup : m_MobileSoundChannelGroup; - } else { - channelGroupToPlayIn = m_MenuSoundChannelGroup; + FMOD::ChannelGroup *channelGroupToPlayIn = m_SFXChannelGroup; + + switch (soundContainer->GetBusRouting()){ + case SoundContainer::UI: + channelGroupToPlayIn = m_UIChannelGroup; + break; + case SoundContainer::SFX: + channelGroupToPlayIn = m_SFXChannelGroup; + break; + case SoundContainer::MUSIC: + channelGroupToPlayIn = m_MusicChannelGroup; + break; } FMOD::Channel *channel; @@ -613,7 +603,7 @@ namespace RTE { float pitchVariationMultiplier = pitchVariationFactor == 1.0F ? 1.0F : RandomNum(1.0F / pitchVariationFactor, 1.0F * pitchVariationFactor); result = (result == FMOD_OK) ? channel->setPitch(soundContainer->GetPitch() * pitchVariationMultiplier) : result; if (soundContainer->IsImmobile()) { - result = (result == FMOD_OK) ? channel->set3DLevel(0.0F) : result; + //result = (result == FMOD_OK) ? channel->set3DLevel(0.0F) : result; result = (result == FMOD_OK) ? channel->setVolume(soundContainer->GetVolume()) : result; } else { @@ -623,7 +613,7 @@ namespace RTE { result = (result == FMOD_OK) ? channel->addDSP(0, dsp_multibandeq) : result; m_SoundChannelMinimumAudibleDistances.insert({ channelIndex, soundData->MinimumAudibleDistance }); - result = (result == FMOD_OK) ? channel->set3DLevel(m_SoundPanningEffectStrength) : result; + result = (result == FMOD_OK) ? channel->set3DLevel(m_SoundPanningEffectStrength * soundContainer->GetPanningStrengthMultiplier()) : result; FMOD_VECTOR soundContainerPosition = GetAsFMODVector(soundContainer->GetPosition() + soundData->Offset); UpdatePositionalEffectsForSoundChannel(channel, &soundContainerPosition); @@ -787,36 +777,39 @@ namespace RTE { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - void AudioMan::Update3DEffectsForMobileSoundChannels() { + void AudioMan::Update3DEffectsForSFXChannels() { int numberOfPlayingChannels; FMOD::Channel *soundChannel; - FMOD_RESULT result = m_MobileSoundChannelGroup->getNumChannels(&numberOfPlayingChannels); + FMOD_RESULT result = m_SFXChannelGroup->getNumChannels(&numberOfPlayingChannels); if (result != FMOD_OK) { g_ConsoleMan.PrintString("ERROR: Failed to get the number of playing channels when updating calculated sound effects for all playing channels: " + std::string(FMOD_ErrorString(result))); return; } for (int i = 0; i < numberOfPlayingChannels; i++) { - result = m_MobileSoundChannelGroup->getChannel(i, &soundChannel); - FMOD_VECTOR channelPosition; - result = result == FMOD_OK ? soundChannel->get3DAttributes(&channelPosition, nullptr) : result; - result = result == FMOD_OK ? UpdatePositionalEffectsForSoundChannel(soundChannel, &channelPosition) : result; - - float channel3dLevel; - result = (result == FMOD_OK) ? soundChannel->get3DLevel(&channel3dLevel) : result; - if (result == FMOD_OK && m_CurrentActivityHumanPlayerPositions.size() == 1) { - float sqrDistanceToPlayer = (*(m_CurrentActivityHumanPlayerPositions[0].get()) - GetAsVector(channelPosition)).GetSqrMagnitude(); - float doubleMinimumDistanceForPanning = m_MinimumDistanceForPanning * 2.0F; - if (sqrDistanceToPlayer < (m_MinimumDistanceForPanning * m_MinimumDistanceForPanning)) { - soundChannel->set3DLevel(0); - } else if (sqrDistanceToPlayer < (doubleMinimumDistanceForPanning * doubleMinimumDistanceForPanning)) { - soundChannel->set3DLevel(LERP(0, 1, 0, m_SoundPanningEffectStrength, channel3dLevel)); - } else { - soundChannel->set3DLevel(m_SoundPanningEffectStrength); + result = m_SFXChannelGroup->getChannel(i, &soundChannel); + FMOD_MODE mode; + result = result == FMOD_OK ? soundChannel->getMode(&mode) : result; + if (mode & FMOD_2D == 0){ + FMOD_VECTOR channelPosition; + result = result == FMOD_OK ? soundChannel->get3DAttributes(&channelPosition, nullptr) : result; + result = result == FMOD_OK ? UpdatePositionalEffectsForSoundChannel(soundChannel, &channelPosition) : result; + float channel3dLevel; + result = (result == FMOD_OK) ? soundChannel->get3DLevel(&channel3dLevel) : result; + if (result == FMOD_OK && m_CurrentActivityHumanPlayerPositions.size() == 1) { + float sqrDistanceToPlayer = (*(m_CurrentActivityHumanPlayerPositions[0].get()) - GetAsVector(channelPosition)).GetSqrMagnitude(); + float doubleMinimumDistanceForPanning = m_MinimumDistanceForPanning * 2.0F; + if (sqrDistanceToPlayer < (m_MinimumDistanceForPanning * m_MinimumDistanceForPanning)) { + soundChannel->set3DLevel(0); + } else if (sqrDistanceToPlayer < (doubleMinimumDistanceForPanning * doubleMinimumDistanceForPanning)) { + soundChannel->set3DLevel(LERP(0, 1, 0, m_SoundPanningEffectStrength, channel3dLevel)); + } else { + soundChannel->set3DLevel(m_SoundPanningEffectStrength); + } } } - + if (result != FMOD_OK) { g_ConsoleMan.PrintString("ERROR: An error occurred updating calculated sound effects for playing channel with index " + std::to_string(i) + ": " + std::string(FMOD_ErrorString(result))); continue; diff --git a/Managers/AudioMan.h b/Managers/AudioMan.h index 83b5550d4..f3eeb35e0 100644 --- a/Managers/AudioMan.h +++ b/Managers/AudioMan.h @@ -271,7 +271,12 @@ namespace RTE { /// Mutes or unmutes all the sound effects channels. /// /// Whether to mute or unmute all the sound effects channels. - void SetSoundsMuted(bool muteOrUnmute = true) { m_MuteSounds = muteOrUnmute; if (m_AudioEnabled) { m_SoundChannelGroup->setMute(m_MuteSounds); } } + void SetSoundsMuted(bool muteOrUnmute = true) { m_MuteSounds = muteOrUnmute; if (m_AudioEnabled) + { + // TODO: We may or may not wanna separate this out and add a UI sound slider + m_SFXChannelGroup->setMute(m_MuteSounds); + m_UIChannelGroup->setMute(m_MuteSounds); + } } /// /// Gets the volume of all sounds. Does not get volume of music. @@ -283,7 +288,11 @@ namespace RTE { /// Sets the volume of all sounds to a specific volume. Does not affect music. /// /// The desired volume scalar. 0.0-1.0. - void SetSoundsVolume(float volume = 1.0F) { m_SoundsVolume = volume; if (m_AudioEnabled) { m_SoundChannelGroup->setVolume(m_SoundsVolume); } } + void SetSoundsVolume(float volume = 1.0F) { m_SoundsVolume = volume; if (m_AudioEnabled) + { + m_SFXChannelGroup->setVolume(m_SoundsVolume); + m_UIChannelGroup->setVolume(m_SoundsVolume); + } } #pragma endregion #pragma region Global Playback and Handling @@ -301,7 +310,7 @@ namespace RTE { /// Pauses all ingame sounds. /// Whether to pause sounds or resume them. /// - void PauseIngameSounds(bool pause = true) { if (m_AudioEnabled) { m_MobileSoundChannelGroup->setPaused(pause); m_ImmobileSoundChannelGroup->setPaused(pause); } } + void PauseIngameSounds(bool pause = true) { if (m_AudioEnabled) { m_SFXChannelGroup->setPaused(pause); } } #pragma endregion #pragma region Music Playback and Handling @@ -434,11 +443,9 @@ namespace RTE { FMOD::System *m_AudioSystem; //!< The FMOD Sound management object. FMOD::ChannelGroup *m_MasterChannelGroup; //!< The top-level FMOD ChannelGroup that holds everything. + FMOD::ChannelGroup *m_SFXChannelGroup; //!< The FMOD ChannelGroup for diegetic gameplay sounds. + FMOD::ChannelGroup *m_UIChannelGroup; //!< The FMOD ChannelGroup for UI sounds. FMOD::ChannelGroup *m_MusicChannelGroup; //!< The FMOD ChannelGroup for music. - FMOD::ChannelGroup *m_SoundChannelGroup; //!< The FMOD ChannelGroup for sounds. - FMOD::ChannelGroup *m_MobileSoundChannelGroup; //!< The FMOD ChannelGroup for mobile sounds. - FMOD::ChannelGroup *m_ImmobileSoundChannelGroup; //!< The FMOD ChannelGroup for immobile sounds. - FMOD::ChannelGroup *m_MenuSoundChannelGroup; //!< The FMOD ChannelGroup for immobile sounds. bool m_AudioEnabled; //!< Bool to tell whether audio is enabled or not. std::vector> m_CurrentActivityHumanPlayerPositions; //!< The stored positions of each human player in the current activity. Only filled when there's an activity running. @@ -523,7 +530,7 @@ namespace RTE { /// /// Updates 3D effects calculations for all sound channels whose SoundContainers isn't immobile. /// - void Update3DEffectsForMobileSoundChannels(); + void Update3DEffectsForSFXChannels(); /// /// Sets or updates the position of the given sound channel so it handles scene wrapping correctly. Also handles volume attenuation and minimum audible distance.