1
0

ContentAudioSystem.cs 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  1. using Content.Shared.Audio;
  2. using Content.Shared.GameTicking;
  3. using AudioComponent = Robust.Shared.Audio.Components.AudioComponent;
  4. namespace Content.Client.Audio;
  5. public sealed partial class ContentAudioSystem : SharedContentAudioSystem
  6. {
  7. // Need how much volume to change per tick and just remove it when it drops below "0"
  8. private readonly Dictionary<EntityUid, float> _fadingOut = new();
  9. // Need volume change per tick + target volume.
  10. private readonly Dictionary<EntityUid, (float VolumeChange, float TargetVolume)> _fadingIn = new();
  11. private readonly List<EntityUid> _fadeToRemove = new();
  12. private const float MinVolume = -32f;
  13. private const float DefaultDuration = 2f;
  14. /*
  15. * Gain multipliers for specific audio sliders.
  16. * The float value will get multiplied by this when setting
  17. * i.e. a gain of 0.5f x 3 will equal 1.5f which is supported in OpenAL.
  18. */
  19. public const float MasterVolumeMultiplier = 3f;
  20. public const float MidiVolumeMultiplier = 0.25f;
  21. public const float AmbienceMultiplier = 3f;
  22. public const float AmbientMusicMultiplier = 3f;
  23. public const float LobbyMultiplier = 3f;
  24. public const float InterfaceMultiplier = 2f;
  25. public override void Initialize()
  26. {
  27. base.Initialize();
  28. UpdatesOutsidePrediction = true;
  29. InitializeAmbientMusic();
  30. InitializeLobbyMusic();
  31. SubscribeNetworkEvent<RoundRestartCleanupEvent>(OnRoundCleanup);
  32. }
  33. private void OnRoundCleanup(RoundRestartCleanupEvent ev)
  34. {
  35. _fadingOut.Clear();
  36. // Preserve lobby music but everything else should get dumped.
  37. var lobbyMusic = _lobbySoundtrackInfo?.MusicStreamEntityUid;
  38. TryComp(lobbyMusic, out AudioComponent? lobbyMusicComp);
  39. var oldMusicGain = lobbyMusicComp?.Gain;
  40. var restartAudio = _lobbyRoundRestartAudioStream;
  41. TryComp(restartAudio, out AudioComponent? restartComp);
  42. var oldAudioGain = restartComp?.Gain;
  43. SilenceAudio();
  44. if (oldMusicGain != null)
  45. {
  46. Audio.SetGain(lobbyMusic, oldMusicGain.Value, lobbyMusicComp);
  47. }
  48. if (oldAudioGain != null)
  49. {
  50. Audio.SetGain(restartAudio, oldAudioGain.Value, restartComp);
  51. }
  52. PlayRestartSound(ev);
  53. }
  54. public override void Shutdown()
  55. {
  56. base.Shutdown();
  57. ShutdownAmbientMusic();
  58. ShutdownLobbyMusic();
  59. }
  60. public override void Update(float frameTime)
  61. {
  62. base.Update(frameTime);
  63. if (!_timing.IsFirstTimePredicted)
  64. return;
  65. UpdateAmbientMusic();
  66. UpdateLobbyMusic();
  67. UpdateFades(frameTime);
  68. }
  69. #region Fades
  70. public void FadeOut(EntityUid? stream, AudioComponent? component = null, float duration = DefaultDuration)
  71. {
  72. if (stream == null || duration <= 0f || !Resolve(stream.Value, ref component))
  73. return;
  74. // Just in case
  75. // TODO: Maybe handle the removals by making it seamless?
  76. _fadingIn.Remove(stream.Value);
  77. var diff = component.Volume - MinVolume;
  78. _fadingOut.Add(stream.Value, diff / duration);
  79. }
  80. public void FadeIn(EntityUid? stream, AudioComponent? component = null, float duration = DefaultDuration)
  81. {
  82. if (stream == null || duration <= 0f || !Resolve(stream.Value, ref component) || component.Volume < MinVolume)
  83. return;
  84. _fadingOut.Remove(stream.Value);
  85. var curVolume = component.Volume;
  86. var change = (MinVolume - curVolume) / duration;
  87. _fadingIn.Add(stream.Value, (change, component.Volume));
  88. component.Volume = MinVolume;
  89. }
  90. private void UpdateFades(float frameTime)
  91. {
  92. _fadeToRemove.Clear();
  93. foreach (var (stream, change) in _fadingOut)
  94. {
  95. if (!TryComp(stream, out AudioComponent? component))
  96. {
  97. _fadeToRemove.Add(stream);
  98. continue;
  99. }
  100. var volume = component.Volume - change * frameTime;
  101. volume = MathF.Max(MinVolume, volume);
  102. _audio.SetVolume(stream, volume, component);
  103. if (component.Volume.Equals(MinVolume))
  104. {
  105. _audio.Stop(stream);
  106. _fadeToRemove.Add(stream);
  107. }
  108. }
  109. foreach (var stream in _fadeToRemove)
  110. {
  111. _fadingOut.Remove(stream);
  112. }
  113. _fadeToRemove.Clear();
  114. foreach (var (stream, (change, target)) in _fadingIn)
  115. {
  116. // Cancelled elsewhere
  117. if (!TryComp(stream, out AudioComponent? component))
  118. {
  119. _fadeToRemove.Add(stream);
  120. continue;
  121. }
  122. var volume = component.Volume - change * frameTime;
  123. volume = MathF.Min(target, volume);
  124. _audio.SetVolume(stream, volume, component);
  125. if (component.Volume.Equals(target))
  126. {
  127. _fadeToRemove.Add(stream);
  128. }
  129. }
  130. foreach (var stream in _fadeToRemove)
  131. {
  132. _fadingIn.Remove(stream);
  133. }
  134. }
  135. #endregion
  136. }
  137. /// <summary>
  138. /// Raised whenever ambient music tries to play.
  139. /// </summary>
  140. [ByRefEvent]
  141. public record struct PlayAmbientMusicEvent(bool Cancelled = false);