Add ambient music (#16829)

This commit is contained in:
metalgearsloth
2023-05-29 10:44:11 +10:00
committed by GitHub
parent f35fcff23f
commit 0c83642c5a
84 changed files with 1252 additions and 338 deletions

View File

@@ -11,6 +11,7 @@ using Robust.Shared.Random;
using Robust.Shared.Timing;
using Robust.Shared.Utility;
using System.Linq;
using Robust.Client.GameObjects;
namespace Content.Client.Audio
{
@@ -106,7 +107,19 @@ namespace Content.Client.Audio
_playingCount.Remove(sound.Sound);
}
private void SetAmbienceVolume(float value) => _ambienceVolume = value;
private void SetAmbienceVolume(float value)
{
_ambienceVolume = value;
foreach (var (comp, values) in _playingSounds)
{
if (values.Stream == null)
continue;
var stream = (AudioSystem.PlayingStream) values.Stream;
stream.Volume = _params.Volume + comp.Volume + _ambienceVolume;
}
}
private void SetCooldown(float value) => _cooldown = value;
private void SetAmbientCount(int value) => _maxAmbientCount = value;
private void SetAmbientRange(float value) => _maxAmbientRange = value;

View File

@@ -1,21 +1,12 @@
using System.Threading;
using Content.Client.Gameplay;
using Content.Client.GameTicking.Managers;
using Content.Client.Lobby;
using Content.Shared.CCVar;
using JetBrains.Annotations;
using Robust.Client;
using Robust.Client.GameObjects;
using Robust.Client.Player;
using Robust.Client.ResourceManagement;
using Robust.Client.State;
using Robust.Shared.Audio;
using Robust.Shared.Configuration;
using Robust.Shared.Player;
using Robust.Shared.Prototypes;
using Robust.Shared.Random;
using Robust.Shared.Timing;
using Timer = Robust.Shared.Timing.Timer;
namespace Content.Client.Audio;
@@ -26,57 +17,18 @@ public sealed class BackgroundAudioSystem : EntitySystem
[Dependency] private readonly IBaseClient _client = default!;
[Dependency] private readonly IConfigurationManager _configManager = default!;
[Dependency] private readonly ClientGameTicker _gameTicker = default!;
[Dependency] private readonly IPlayerManager _playMan = default!;
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly IRobustRandom _robustRandom = default!;
[Dependency] private readonly IStateManager _stateManager = default!;
[Dependency] private readonly IGameTiming _timing = default!;
private readonly AudioParams _ambientParams = new(-10f, 1, "Master", 0, 0, 0, true, 0f);
private readonly AudioParams _lobbyParams = new(-5f, 1, "Master", 0, 0, 0, true, 0f);
private IPlayingAudioStream? _ambientStream;
private IPlayingAudioStream? _lobbyStream;
/// <summary>
/// What is currently playing.
/// </summary>
private SoundCollectionPrototype? _playingCollection;
/// <summary>
/// What the ambience has been set to.
/// </summary>
private SoundCollectionPrototype? _currentCollection;
private CancellationTokenSource _timerCancelTokenSource = new();
private SoundCollectionPrototype _spaceAmbience = default!;
private SoundCollectionPrototype _stationAmbience = default!;
public override void Initialize()
{
base.Initialize();
_stationAmbience = _prototypeManager.Index<SoundCollectionPrototype>("StationAmbienceBase");
_spaceAmbience = _prototypeManager.Index<SoundCollectionPrototype>("SpaceAmbienceBase");
_currentCollection = _stationAmbience;
// TODO: Ideally audio loading streamed better / we have more robust audio but this is quite annoying
var cache = IoCManager.Resolve<IResourceCache>();
foreach (var audio in _spaceAmbience.PickFiles)
{
cache.GetResource<AudioResource>(audio.ToString());
}
_configManager.OnValueChanged(CCVars.AmbienceVolume, AmbienceCVarChanged);
_configManager.OnValueChanged(CCVars.LobbyMusicEnabled, LobbyMusicCVarChanged);
_configManager.OnValueChanged(CCVars.LobbyMusicVolume, LobbyMusicVolumeCVarChanged);
_configManager.OnValueChanged(CCVars.StationAmbienceEnabled, StationAmbienceCVarChanged);
_configManager.OnValueChanged(CCVars.SpaceAmbienceEnabled, SpaceAmbienceCVarChanged);
SubscribeLocalEvent<PlayerAttachedEvent>(OnPlayerAttached);
SubscribeLocalEvent<EntParentChangedMessage>(EntParentChanged);
SubscribeLocalEvent<PlayerDetachedEvent>(OnPlayerDetached);
_stateManager.OnStateChanged += StateManagerOnStateChanged;
@@ -85,28 +37,12 @@ public sealed class BackgroundAudioSystem : EntitySystem
_gameTicker.LobbyStatusUpdated += LobbySongReceived;
}
private void OnPlayerAttached(PlayerAttachedEvent ev)
{
if (!TryComp<TransformComponent>(ev.Entity, out var xform))
return;
CheckAmbience(xform);
}
private void OnPlayerDetached(PlayerDetachedEvent ev)
{
EndAmbience();
}
public override void Shutdown()
{
base.Shutdown();
_configManager.UnsubValueChanged(CCVars.AmbienceVolume, AmbienceCVarChanged);
_configManager.UnsubValueChanged(CCVars.LobbyMusicEnabled, LobbyMusicCVarChanged);
_configManager.UnsubValueChanged(CCVars.LobbyMusicVolume, LobbyMusicVolumeCVarChanged);
_configManager.UnsubValueChanged(CCVars.StationAmbienceEnabled, StationAmbienceCVarChanged);
_configManager.UnsubValueChanged(CCVars.SpaceAmbienceEnabled, SpaceAmbienceCVarChanged);
_stateManager.OnStateChanged -= StateManagerOnStateChanged;
@@ -114,61 +50,17 @@ public sealed class BackgroundAudioSystem : EntitySystem
_gameTicker.LobbyStatusUpdated -= LobbySongReceived;
EndAmbience();
EndLobbyMusic();
}
private void CheckAmbience(TransformComponent xform)
{
if (xform.GridUid != null)
ChangeAmbience(_stationAmbience);
else
ChangeAmbience(_spaceAmbience);
}
private void EntParentChanged(ref EntParentChangedMessage message)
{
if (_playMan.LocalPlayer is null
|| _playMan.LocalPlayer.ControlledEntity != message.Entity
|| !(_timing.IsFirstTimePredicted || _timing.ApplyingState))
return;
// Check if we traversed to grid.
CheckAmbience(message.Transform);
}
private void ChangeAmbience(SoundCollectionPrototype newAmbience)
{
if (_currentCollection == newAmbience)
return;
_timerCancelTokenSource.Cancel();
_currentCollection = newAmbience;
_timerCancelTokenSource = new CancellationTokenSource();
Timer.Spawn(1500, () =>
{
// If we traverse a few times then don't interrupt an existing song.
// If we are not in gameplay, don't call StartAmbience because of player movement
if (_playingCollection == _currentCollection || _stateManager.CurrentState is not GameplayState)
return;
StartAmbience();
}, _timerCancelTokenSource.Token);
}
private void StateManagerOnStateChanged(StateChangedEventArgs args)
{
switch (args.NewState)
{
case LobbyState:
EndAmbience();
StartLobbyMusic();
break;
case GameplayState:
EndLobbyMusic();
StartAmbience();
break;
default:
EndAmbience();
EndLobbyMusic();
break;
}
@@ -176,80 +68,9 @@ public sealed class BackgroundAudioSystem : EntitySystem
private void OnLeave(object? sender, PlayerEventArgs args)
{
EndAmbience();
EndLobbyMusic();
}
private void AmbienceCVarChanged(float volume)
{
if (_stateManager.CurrentState is GameplayState)
{
StartAmbience();
}
else
{
EndAmbience();
}
}
private void StartAmbience()
{
EndAmbience();
if (_currentCollection == null || !CanPlayCollection(_currentCollection))
return;
_playingCollection = _currentCollection;
var file = _robustRandom.Pick(_currentCollection.PickFiles).ToString();
_ambientStream = _audio.PlayGlobal(file, Filter.Local(), false,
_ambientParams.WithVolume(_ambientParams.Volume + _configManager.GetCVar(CCVars.AmbienceVolume)));
}
private void EndAmbience()
{
_playingCollection = null;
_ambientStream?.Stop();
_ambientStream = null;
}
private bool CanPlayCollection(SoundCollectionPrototype collection)
{
if (collection.ID == _spaceAmbience.ID)
return _configManager.GetCVar(CCVars.SpaceAmbienceEnabled);
if (collection.ID == _stationAmbience.ID)
return _configManager.GetCVar(CCVars.StationAmbienceEnabled);
return true;
}
private void StationAmbienceCVarChanged(bool enabled)
{
if (_currentCollection == null)
return;
if (enabled && _stateManager.CurrentState is GameplayState && _currentCollection.ID == _stationAmbience.ID)
{
StartAmbience();
}
else if (_currentCollection.ID == _stationAmbience.ID)
{
EndAmbience();
}
}
private void SpaceAmbienceCVarChanged(bool enabled)
{
if (_currentCollection == null)
return;
if (enabled && _stateManager.CurrentState is GameplayState && _currentCollection.ID == _spaceAmbience.ID)
{
StartAmbience();
}
else if (_currentCollection.ID == _spaceAmbience.ID)
{
EndAmbience();
}
}
private void LobbyMusicVolumeCVarChanged(float volume)
{
if (_stateManager.CurrentState is LobbyState)

View File

@@ -0,0 +1,262 @@
using System.Linq;
using Content.Client.Gameplay;
using Content.Shared.Audio;
using Content.Shared.CCVar;
using Content.Shared.GameTicking;
using Content.Shared.Random;
using Robust.Client.GameObjects;
using Robust.Client.Player;
using Robust.Client.ResourceManagement;
using Robust.Client.State;
using Robust.Shared.Audio;
using Robust.Shared.Configuration;
using Robust.Shared.Player;
using Robust.Shared.Prototypes;
using Robust.Shared.Random;
using Robust.Shared.Timing;
using Robust.Shared.Utility;
namespace Content.Client.Audio;
public sealed partial class ContentAudioSystem
{
[Dependency] private readonly IConfigurationManager _configManager = default!;
[Dependency] private readonly IGameTiming _timing = default!;
[Dependency] private readonly IPlayerManager _player = default!;
[Dependency] private readonly IPrototypeManager _proto = default!;
[Dependency] private readonly IResourceCache _resource = default!;
[Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly IStateManager _state = default!;
[Dependency] private readonly RulesSystem _rules = default!;
[Dependency] private readonly SharedAudioSystem _audio = default!;
private readonly TimeSpan _minAmbienceTime = TimeSpan.FromSeconds(30);
private readonly TimeSpan _maxAmbienceTime = TimeSpan.FromSeconds(60);
private const float AmbientMusicFadeTime = 10f;
private static float _volumeSlider;
// Don't need to worry about this being serializable or pauseable as it doesn't affect the sim.
private TimeSpan _nextAudio;
private AudioSystem.PlayingStream? _ambientMusicStream;
private AmbientMusicPrototype? _musicProto;
/// <summary>
/// If we find a better ambient music proto can we interrupt this one.
/// </summary>
private bool _interruptable;
/// <summary>
/// Track what ambient sounds we've played. This is so they all get played an even
/// number of times.
/// When we get to the end of the list we'll re-shuffle
/// </summary>
private readonly Dictionary<string, List<ResPath>> _ambientSounds = new();
private ISawmill _sawmill = default!;
private void InitializeAmbientMusic()
{
// TODO: Shitty preload
foreach (var audio in _proto.Index<SoundCollectionPrototype>("AmbienceSpace").PickFiles)
{
_resource.GetResource<AudioResource>(audio.ToString());
}
_configManager.OnValueChanged(CCVars.AmbientMusicVolume, AmbienceCVarChanged, true);
_sawmill = IoCManager.Resolve<ILogManager>().GetSawmill("audio.ambience");
// Reset audio
_nextAudio = TimeSpan.MaxValue;
SetupAmbientSounds();
_proto.PrototypesReloaded += OnProtoReload;
_state.OnStateChanged += OnStateChange;
// On round end summary OR lobby cut audio.
SubscribeNetworkEvent<RoundEndMessageEvent>(OnRoundEndMessage);
}
private void AmbienceCVarChanged(float obj)
{
_volumeSlider = obj;
if (_ambientMusicStream != null && _musicProto != null)
{
_ambientMusicStream.Volume = _musicProto.Sound.Params.Volume + _volumeSlider;
}
}
private void ShutdownAmbientMusic()
{
_configManager.UnsubValueChanged(CCVars.AmbientMusicVolume, AmbienceCVarChanged);
_proto.PrototypesReloaded -= OnProtoReload;
_state.OnStateChanged -= OnStateChange;
_ambientMusicStream?.Stop();
}
private void OnProtoReload(PrototypesReloadedEventArgs obj)
{
if (!obj.ByType.ContainsKey(typeof(AmbientMusicPrototype)) &&
!obj.ByType.ContainsKey(typeof(RulesPrototype)))
{
return;
}
_ambientSounds.Clear();
SetupAmbientSounds();
}
private void OnStateChange(StateChangedEventArgs obj)
{
if (obj.NewState is not GameplayState)
return;
// If they go to game then reset the ambience timer.
_nextAudio = _timing.CurTime + _random.Next(_minAmbienceTime, _maxAmbienceTime);
}
private void SetupAmbientSounds()
{
foreach (var ambience in _proto.EnumeratePrototypes<AmbientMusicPrototype>())
{
var tracks = _ambientSounds.GetOrNew(ambience.ID);
RefreshTracks(ambience.Sound, tracks, null);
_random.Shuffle(tracks);
}
}
private void OnRoundEndMessage(RoundEndMessageEvent ev)
{
// If scoreboard shows then just stop the music
_ambientMusicStream?.Stop();
_ambientMusicStream = null;
_nextAudio = TimeSpan.FromMinutes(3);
}
private void RefreshTracks(SoundSpecifier sound, List<ResPath> tracks, ResPath? lastPlayed)
{
DebugTools.Assert(tracks.Count == 0);
switch (sound)
{
case SoundCollectionSpecifier collection:
if (collection.Collection == null)
break;
var slothCud = _proto.Index<SoundCollectionPrototype>(collection.Collection);
tracks.AddRange(slothCud.PickFiles);
break;
case SoundPathSpecifier path:
tracks.Add(path.Path);
break;
}
// Just so the same track doesn't play twice
if (tracks.Count > 1 && tracks[^1] == lastPlayed)
{
(tracks[0], tracks[^1]) = (tracks[^1], tracks[0]);
}
}
private void UpdateAmbientMusic()
{
// Update still runs in lobby so just ignore it.
if (_state.CurrentState is not GameplayState)
{
FadeOut(_ambientMusicStream);
_ambientMusicStream = null;
_musicProto = null;
return;
}
var isDone = _ambientMusicStream?.Done;
if (_interruptable)
{
var player = _player.LocalPlayer?.ControlledEntity;
if (player == null || _musicProto == null || !_rules.IsTrue(player.Value, _proto.Index<RulesPrototype>(_musicProto.Rules)))
{
FadeOut(_ambientMusicStream, AmbientMusicFadeTime);
_musicProto = null;
_interruptable = false;
isDone = true;
}
}
// Still running existing ambience
if (isDone == false)
return;
// If ambience finished reset the CD (this also means if we have long ambience it won't clip)
if (isDone == true)
{
// Also don't need to worry about rounding here as it doesn't affect the sim
_nextAudio = _timing.CurTime + _random.Next(_minAmbienceTime, _maxAmbienceTime);
}
_ambientMusicStream = null;
if (_nextAudio > _timing.CurTime)
return;
_musicProto = GetAmbience();
if (_musicProto == null)
{
_interruptable = false;
return;
}
_interruptable = _musicProto.Interruptable;
var tracks = _ambientSounds[_musicProto.ID];
var track = tracks[^1];
tracks.RemoveAt(tracks.Count - 1);
var strim = _audio.PlayGlobal(
track.ToString(),
Filter.Local(),
false,
AudioParams.Default.WithVolume(_musicProto.Sound.Params.Volume + _volumeSlider));
if (strim != null)
{
_ambientMusicStream = (AudioSystem.PlayingStream) strim;
if (_musicProto.FadeIn)
{
FadeIn(_ambientMusicStream, AmbientMusicFadeTime);
}
}
// Refresh the list
if (tracks.Count == 0)
{
RefreshTracks(_musicProto.Sound, tracks, track);
}
}
private AmbientMusicPrototype? GetAmbience()
{
var player = _player.LocalPlayer?.ControlledEntity;
if (player == null)
return null;
var ambiences = _proto.EnumeratePrototypes<AmbientMusicPrototype>().ToList();
ambiences.Sort((x, y) => y.Priority.CompareTo(x.Priority));
foreach (var amb in ambiences)
{
if (!_rules.IsTrue(player.Value, _proto.Index<RulesPrototype>(amb.Rules)))
continue;
return amb;
}
_sawmill.Warning($"Unable to find fallback ambience track");
return null;
}
}

View File

@@ -1,8 +1,124 @@
using Content.Shared.Audio;
using Robust.Client.GameObjects;
namespace Content.Client.Audio;
public sealed class ContentAudioSystem : SharedContentAudioSystem
public sealed partial class ContentAudioSystem : SharedContentAudioSystem
{
// Need how much volume to change per tick and just remove it when it drops below "0"
private readonly Dictionary<AudioSystem.PlayingStream, float> _fadingOut = new();
// Need volume change per tick + target volume.
private readonly Dictionary<AudioSystem.PlayingStream, (float VolumeChange, float TargetVolume)> _fadingIn = new();
private readonly List<AudioSystem.PlayingStream> _fadeToRemove = new();
private const float MinVolume = -32f;
private const float DefaultDuration = 2f;
public override void Initialize()
{
base.Initialize();
UpdatesOutsidePrediction = true;
InitializeAmbientMusic();
}
public override void Shutdown()
{
base.Shutdown();
ShutdownAmbientMusic();
}
public override void Update(float frameTime)
{
base.Update(frameTime);
if (!_timing.IsFirstTimePredicted)
return;
UpdateAmbientMusic();
UpdateFades(frameTime);
}
#region Fades
public void FadeOut(AudioSystem.PlayingStream? stream, float duration = DefaultDuration)
{
if (stream == null || duration <= 0f)
return;
// Just in case
// TODO: Maybe handle the removals by making it seamless?
_fadingIn.Remove(stream);
var diff = stream.Volume - MinVolume;
_fadingOut.Add(stream, diff / duration);
}
public void FadeIn(AudioSystem.PlayingStream? stream, float duration = DefaultDuration)
{
if (stream == null || duration <= 0f || stream.Volume < MinVolume)
return;
_fadingOut.Remove(stream);
var curVolume = stream.Volume;
var change = (curVolume - MinVolume) / duration;
_fadingIn.Add(stream, (change, stream.Volume));
stream.Volume = MinVolume;
}
private void UpdateFades(float frameTime)
{
_fadeToRemove.Clear();
foreach (var (stream, change) in _fadingOut)
{
// Cancelled elsewhere
if (stream.Done)
{
_fadeToRemove.Add(stream);
continue;
}
var volume = stream.Volume - change * frameTime;
stream.Volume = MathF.Max(MinVolume, volume);
if (stream.Volume.Equals(MinVolume))
{
stream.Stop();
_fadeToRemove.Add(stream);
}
}
foreach (var stream in _fadeToRemove)
{
_fadingOut.Remove(stream);
}
_fadeToRemove.Clear();
foreach (var (stream, (change, target)) in _fadingIn)
{
// Cancelled elsewhere
if (stream.Done)
{
_fadeToRemove.Add(stream);
continue;
}
var volume = stream.Volume + change * frameTime;
stream.Volume = MathF.Min(target, volume);
if (stream.Volume.Equals(target))
{
_fadeToRemove.Add(stream);
}
}
foreach (var stream in _fadeToRemove)
{
_fadingIn.Remove(stream);
}
}
#endregion
}

View File

@@ -27,7 +27,7 @@
<Control MinSize="8 0" />
<Slider Name="MidiVolumeSlider"
MinValue="0"
MaxValue="200"
MaxValue="100"
HorizontalExpand="True"
MinSize="80 0"
Rounded="True" />
@@ -35,12 +35,25 @@
<Label Name="MidiVolumeLabel" MinSize="48 0" Align="Right" />
<Control MinSize="4 0"/>
</BoxContainer>
<BoxContainer Orientation="Horizontal" Margin="5 0 0 0">
<Label Text="{Loc 'ui-options-ambient-music-volume'}" HorizontalExpand="True" />
<Control MinSize="8 0" />
<Slider Name="AmbientMusicVolumeSlider"
MinValue="0"
MaxValue="100"
HorizontalExpand="True"
MinSize="80 0"
Rounded="True" />
<Control MinSize="8 0" />
<Label Name="AmbientMusicVolumeLabel" MinSize="48 0" Align="Right" />
<Control MinSize="4 0"/>
</BoxContainer>
<BoxContainer Orientation="Horizontal" Margin="5 0 0 0">
<Label Text="{Loc 'ui-options-ambience-volume'}" HorizontalExpand="True" />
<Control MinSize="8 0" />
<Slider Name="AmbienceVolumeSlider"
MinValue="0"
MaxValue="300"
MaxValue="100"
HorizontalExpand="True"
MinSize="80 0"
Rounded="True" />
@@ -53,7 +66,7 @@
<Control MinSize="8 0" />
<Slider Name="LobbyVolumeSlider"
MinValue="0"
MaxValue="200"
MaxValue="100"
HorizontalExpand="True"
MinSize="80 0"
Rounded="True" />
@@ -79,8 +92,6 @@
<CheckBox Name="RestartSoundsCheckBox" Text="{Loc 'ui-options-restart-sounds'}" />
<CheckBox Name="EventMusicCheckBox" Text="{Loc 'ui-options-event-music'}" />
<CheckBox Name="AdminSoundsCheckBox" Text="{Loc 'ui-options-admin-sounds'}" />
<CheckBox Name="StationAmbienceCheckBox" Text="{Loc 'ui-options-station-ambience'}" />
<CheckBox Name="SpaceAmbienceCheckBox" Text="{Loc 'ui-options-space-ambience'}" />
</BoxContainer>
</BoxContainer>
<controls:StripeBack HasBottomEdge="False" HasMargins="False">

View File

@@ -25,13 +25,12 @@ namespace Content.Client.Options.UI.Tabs
RestartSoundsCheckBox.Pressed = _cfg.GetCVar(CCVars.RestartSoundsEnabled);
EventMusicCheckBox.Pressed = _cfg.GetCVar(CCVars.EventMusicEnabled);
AdminSoundsCheckBox.Pressed = _cfg.GetCVar(CCVars.AdminSoundsEnabled);
StationAmbienceCheckBox.Pressed = _cfg.GetCVar(CCVars.StationAmbienceEnabled);
SpaceAmbienceCheckBox.Pressed = _cfg.GetCVar(CCVars.SpaceAmbienceEnabled);
ApplyButton.OnPressed += OnApplyButtonPressed;
ResetButton.OnPressed += OnResetButtonPressed;
MasterVolumeSlider.OnValueChanged += OnMasterVolumeSliderChanged;
MidiVolumeSlider.OnValueChanged += OnMidiVolumeSliderChanged;
AmbientMusicVolumeSlider.OnValueChanged += OnAmbientMusicVolumeSliderChanged;
AmbienceVolumeSlider.OnValueChanged += OnAmbienceVolumeSliderChanged;
AmbienceSoundsSlider.OnValueChanged += OnAmbienceSoundsSliderChanged;
LobbyVolumeSlider.OnValueChanged += OnLobbyVolumeSliderChanged;
@@ -39,8 +38,6 @@ namespace Content.Client.Options.UI.Tabs
RestartSoundsCheckBox.OnToggled += OnRestartSoundsCheckToggled;
EventMusicCheckBox.OnToggled += OnEventMusicCheckToggled;
AdminSoundsCheckBox.OnToggled += OnAdminSoundsCheckToggled;
StationAmbienceCheckBox.OnToggled += OnStationAmbienceCheckToggled;
SpaceAmbienceCheckBox.OnToggled += OnSpaceAmbienceCheckToggled;
AmbienceSoundsSlider.MinValue = _cfg.GetCVar(CCVars.MinMaxAmbientSourcesConfigured);
AmbienceSoundsSlider.MaxValue = _cfg.GetCVar(CCVars.MaxMaxAmbientSourcesConfigured);
@@ -54,6 +51,7 @@ namespace Content.Client.Options.UI.Tabs
ResetButton.OnPressed -= OnResetButtonPressed;
MasterVolumeSlider.OnValueChanged -= OnMasterVolumeSliderChanged;
MidiVolumeSlider.OnValueChanged -= OnMidiVolumeSliderChanged;
AmbientMusicVolumeSlider.OnValueChanged -= OnAmbientMusicVolumeSliderChanged;
AmbienceVolumeSlider.OnValueChanged -= OnAmbienceVolumeSliderChanged;
LobbyVolumeSlider.OnValueChanged -= OnLobbyVolumeSliderChanged;
base.Dispose(disposing);
@@ -64,6 +62,11 @@ namespace Content.Client.Options.UI.Tabs
UpdateChanges();
}
private void OnAmbientMusicVolumeSliderChanged(Range obj)
{
UpdateChanges();
}
private void OnAmbienceVolumeSliderChanged(Range obj)
{
UpdateChanges();
@@ -103,29 +106,21 @@ namespace Content.Client.Options.UI.Tabs
UpdateChanges();
}
private void OnStationAmbienceCheckToggled(BaseButton.ButtonEventArgs args)
{
UpdateChanges();
}
private void OnSpaceAmbienceCheckToggled(BaseButton.ButtonEventArgs args)
{
UpdateChanges();
}
private void OnApplyButtonPressed(BaseButton.ButtonEventArgs args)
{
_cfg.SetCVar(CVars.AudioMasterVolume, MasterVolumeSlider.Value / 100);
_cfg.SetCVar(CVars.MidiVolume, LV100ToDB(MidiVolumeSlider.Value));
_cfg.SetCVar(CCVars.AmbienceVolume, LV100ToDB(AmbienceVolumeSlider.Value));
// Want the CVar updated values to have the multiplier applied
// For the UI we just display 0-100 still elsewhere
_cfg.SetCVar(CVars.MidiVolume, LV100ToDB(MidiVolumeSlider.Value, CCVars.MidiMultiplier));
_cfg.SetCVar(CCVars.AmbienceVolume, LV100ToDB(AmbienceVolumeSlider.Value, CCVars.AmbienceMultiplier));
_cfg.SetCVar(CCVars.AmbientMusicVolume, LV100ToDB(AmbientMusicVolumeSlider.Value, CCVars.AmbientMusicMultiplier));
_cfg.SetCVar(CCVars.LobbyMusicVolume, LV100ToDB(LobbyVolumeSlider.Value));
_cfg.SetCVar(CCVars.MaxAmbientSources, (int)AmbienceSoundsSlider.Value);
_cfg.SetCVar(CCVars.LobbyMusicEnabled, LobbyMusicCheckBox.Pressed);
_cfg.SetCVar(CCVars.RestartSoundsEnabled, RestartSoundsCheckBox.Pressed);
_cfg.SetCVar(CCVars.EventMusicEnabled, EventMusicCheckBox.Pressed);
_cfg.SetCVar(CCVars.AdminSoundsEnabled, AdminSoundsCheckBox.Pressed);
_cfg.SetCVar(CCVars.StationAmbienceEnabled, StationAmbienceCheckBox.Pressed);
_cfg.SetCVar(CCVars.SpaceAmbienceEnabled, SpaceAmbienceCheckBox.Pressed);
_cfg.SaveToFile();
UpdateChanges();
}
@@ -138,30 +133,32 @@ namespace Content.Client.Options.UI.Tabs
private void Reset()
{
MasterVolumeSlider.Value = _cfg.GetCVar(CVars.AudioMasterVolume) * 100;
MidiVolumeSlider.Value = DBToLV100(_cfg.GetCVar(CVars.MidiVolume));
AmbienceVolumeSlider.Value = DBToLV100(_cfg.GetCVar(CCVars.AmbienceVolume));
MidiVolumeSlider.Value = DBToLV100(_cfg.GetCVar(CVars.MidiVolume), CCVars.MidiMultiplier);
AmbienceVolumeSlider.Value = DBToLV100(_cfg.GetCVar(CCVars.AmbienceVolume), CCVars.AmbienceMultiplier);
AmbientMusicVolumeSlider.Value =
DBToLV100(_cfg.GetCVar(CCVars.AmbientMusicVolume), CCVars.AmbientMusicMultiplier);
LobbyVolumeSlider.Value = DBToLV100(_cfg.GetCVar(CCVars.LobbyMusicVolume));
AmbienceSoundsSlider.Value = _cfg.GetCVar(CCVars.MaxAmbientSources);
LobbyMusicCheckBox.Pressed = _cfg.GetCVar(CCVars.LobbyMusicEnabled);
RestartSoundsCheckBox.Pressed = _cfg.GetCVar(CCVars.RestartSoundsEnabled);
EventMusicCheckBox.Pressed = _cfg.GetCVar(CCVars.EventMusicEnabled);
AdminSoundsCheckBox.Pressed = _cfg.GetCVar(CCVars.AdminSoundsEnabled);
StationAmbienceCheckBox.Pressed = _cfg.GetCVar(CCVars.StationAmbienceEnabled);
SpaceAmbienceCheckBox.Pressed = _cfg.GetCVar(CCVars.SpaceAmbienceEnabled);
UpdateChanges();
}
// Note: Rather than moving these functions somewhere, instead switch MidiManager to using linear units rather than dB
// Do be sure to rename the setting though
private float DBToLV100(float db)
private float DBToLV100(float db, float multiplier = 1f)
{
return (MathF.Pow(10, (db / 10)) * 100);
var weh = (float) (Math.Pow(10, db / 10) * 100 / multiplier);
return weh;
}
private float LV100ToDB(float lv100)
private float LV100ToDB(float lv100, float multiplier = 1f)
{
// Saving negative infinity doesn't work, so use -10000000 instead (MidiManager does it)
return MathF.Max(-10000000, MathF.Log(lv100 / 100, 10) * 10);
var weh = MathF.Max(-10000000, (float) (Math.Log(lv100 * multiplier / 100, 10) * 10));
return weh;
}
private void UpdateChanges()
@@ -169,9 +166,11 @@ namespace Content.Client.Options.UI.Tabs
var isMasterVolumeSame =
Math.Abs(MasterVolumeSlider.Value - _cfg.GetCVar(CVars.AudioMasterVolume) * 100) < 0.01f;
var isMidiVolumeSame =
Math.Abs(MidiVolumeSlider.Value - DBToLV100(_cfg.GetCVar(CVars.MidiVolume))) < 0.01f;
Math.Abs(MidiVolumeSlider.Value - DBToLV100(_cfg.GetCVar(CVars.MidiVolume), CCVars.MidiMultiplier)) < 0.01f;
var isAmbientVolumeSame =
Math.Abs(AmbienceVolumeSlider.Value - DBToLV100(_cfg.GetCVar(CCVars.AmbienceVolume))) < 0.01f;
Math.Abs(AmbienceVolumeSlider.Value - DBToLV100(_cfg.GetCVar(CCVars.AmbienceVolume), CCVars.AmbienceMultiplier)) < 0.01f;
var isAmbientMusicVolumeSame =
Math.Abs(AmbientMusicVolumeSlider.Value - DBToLV100(_cfg.GetCVar(CCVars.AmbientMusicVolume), CCVars.AmbientMusicMultiplier)) < 0.01f;
var isLobbyVolumeSame =
Math.Abs(LobbyVolumeSlider.Value - DBToLV100(_cfg.GetCVar(CCVars.LobbyMusicVolume))) < 0.01f;
var isAmbientSoundsSame = (int)AmbienceSoundsSlider.Value == _cfg.GetCVar(CCVars.MaxAmbientSources);
@@ -179,16 +178,16 @@ namespace Content.Client.Options.UI.Tabs
var isRestartSoundsSame = RestartSoundsCheckBox.Pressed == _cfg.GetCVar(CCVars.RestartSoundsEnabled);
var isEventSame = EventMusicCheckBox.Pressed == _cfg.GetCVar(CCVars.EventMusicEnabled);
var isAdminSoundsSame = AdminSoundsCheckBox.Pressed == _cfg.GetCVar(CCVars.AdminSoundsEnabled);
var isStationAmbienceSame = StationAmbienceCheckBox.Pressed == _cfg.GetCVar(CCVars.StationAmbienceEnabled);
var isSpaceAmbienceSame = SpaceAmbienceCheckBox.Pressed == _cfg.GetCVar(CCVars.SpaceAmbienceEnabled);
var isEverythingSame = isMasterVolumeSame && isMidiVolumeSame && isAmbientVolumeSame && isAmbientSoundsSame && isLobbySame && isRestartSoundsSame && isEventSame
&& isAdminSoundsSame && isStationAmbienceSame && isSpaceAmbienceSame && isLobbyVolumeSame;
var isEverythingSame = isMasterVolumeSame && isMidiVolumeSame && isAmbientVolumeSame && isAmbientMusicVolumeSame && isAmbientSoundsSame && isLobbySame && isRestartSoundsSame && isEventSame
&& isAdminSoundsSame && isLobbyVolumeSame;
ApplyButton.Disabled = isEverythingSame;
ResetButton.Disabled = isEverythingSame;
MasterVolumeLabel.Text =
Loc.GetString("ui-options-volume-percent", ("volume", MasterVolumeSlider.Value / 100));
MidiVolumeLabel.Text =
Loc.GetString("ui-options-volume-percent", ("volume", MidiVolumeSlider.Value / 100));
AmbientMusicVolumeLabel.Text =
Loc.GetString("ui-options-volume-percent", ("volume", AmbientMusicVolumeSlider.Value / 100));
AmbienceVolumeLabel.Text =
Loc.GetString("ui-options-volume-percent", ("volume", AmbienceVolumeSlider.Value / 100));
LobbyVolumeLabel.Text =