Content audio (#20862)
This commit is contained in:
@@ -1,16 +1,21 @@
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using Content.Shared.Audio;
|
||||
using Content.Shared.CCVar;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.Player;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Physics;
|
||||
using Robust.Shared.Random;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.Utility;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.Audio.Effects;
|
||||
using Robust.Shared.Audio.Systems;
|
||||
using Robust.Shared.Player;
|
||||
|
||||
namespace Content.Client.Audio;
|
||||
//TODO: This is using a incomplete version of the whole "only play nearest sounds" algo, that breaks down a bit should the ambient sound cap get hit.
|
||||
@@ -41,14 +46,18 @@ public sealed class AmbientSoundSystem : SharedAmbientSoundSystem
|
||||
private TimeSpan _targetTime = TimeSpan.Zero;
|
||||
private float _ambienceVolume = 0.0f;
|
||||
|
||||
private static AudioParams _params = AudioParams.Default.WithVariation(0.01f).WithLoop(true).WithAttenuation(Attenuation.LinearDistance);
|
||||
private static AudioParams _params = AudioParams.Default
|
||||
.WithVariation(0.01f)
|
||||
.WithLoop(true)
|
||||
.WithAttenuation(Attenuation.LinearDistance)
|
||||
.WithMaxDistance(7f);
|
||||
|
||||
/// <summary>
|
||||
/// How many times we can be playing 1 particular sound at once.
|
||||
/// </summary>
|
||||
private int MaxSingleSound => (int) (_maxAmbientCount / (16.0f / 6.0f));
|
||||
|
||||
private readonly Dictionary<Entity<AmbientSoundComponent>, (IPlayingAudioStream? Stream, SoundSpecifier Sound, string Path)> _playingSounds = new();
|
||||
private readonly Dictionary<AmbientSoundComponent, (EntityUid? Stream, SoundSpecifier Sound, string Path)> _playingSounds = new();
|
||||
private readonly Dictionary<string, int> _playingCount = new();
|
||||
|
||||
public bool OverlayEnabled
|
||||
@@ -98,10 +107,10 @@ public sealed class AmbientSoundSystem : SharedAmbientSoundSystem
|
||||
|
||||
private void OnShutdown(EntityUid uid, AmbientSoundComponent component, ComponentShutdown args)
|
||||
{
|
||||
if (!_playingSounds.Remove((uid, component), out var sound))
|
||||
if (!_playingSounds.Remove(component, out var sound))
|
||||
return;
|
||||
|
||||
sound.Stream?.Stop();
|
||||
_audio.Stop(sound.Stream);
|
||||
_playingCount[sound.Path] -= 1;
|
||||
if (_playingCount[sound.Path] == 0)
|
||||
_playingCount.Remove(sound.Path);
|
||||
@@ -111,13 +120,13 @@ public sealed class AmbientSoundSystem : SharedAmbientSoundSystem
|
||||
{
|
||||
_ambienceVolume = value;
|
||||
|
||||
foreach (var ((_, comp), values) in _playingSounds)
|
||||
foreach (var (comp, values) in _playingSounds)
|
||||
{
|
||||
if (values.Stream == null)
|
||||
continue;
|
||||
|
||||
var stream = (AudioSystem.PlayingStream) values.Stream;
|
||||
stream.Volume = _params.Volume + comp.Volume + _ambienceVolume;
|
||||
var stream = values.Stream;
|
||||
_audio.SetVolume(stream, _params.Volume + comp.Volume + _ambienceVolume);
|
||||
}
|
||||
}
|
||||
private void SetCooldown(float value) => _cooldown = value;
|
||||
@@ -177,7 +186,7 @@ public sealed class AmbientSoundSystem : SharedAmbientSoundSystem
|
||||
{
|
||||
foreach (var (stream, _, _) in _playingSounds.Values)
|
||||
{
|
||||
stream?.Stop();
|
||||
_audio.Stop(stream);
|
||||
}
|
||||
|
||||
_playingSounds.Clear();
|
||||
@@ -186,7 +195,7 @@ public sealed class AmbientSoundSystem : SharedAmbientSoundSystem
|
||||
|
||||
private readonly struct QueryState
|
||||
{
|
||||
public readonly Dictionary<string, List<(float Importance, Entity<AmbientSoundComponent>)>> SourceDict = new();
|
||||
public readonly Dictionary<string, List<(float Importance, AmbientSoundComponent)>> SourceDict = new();
|
||||
public readonly Vector2 MapPos;
|
||||
public readonly TransformComponent Player;
|
||||
public readonly EntityQuery<TransformComponent> Query;
|
||||
@@ -224,7 +233,7 @@ public sealed class AmbientSoundSystem : SharedAmbientSoundSystem
|
||||
|
||||
// Prioritize far away & loud sounds.
|
||||
var importance = range * (ambientComp.Volume + 32);
|
||||
state.SourceDict.GetOrNew(key).Add((importance, (ambientComp.Owner, ambientComp)));
|
||||
state.SourceDict.GetOrNew(key).Add((importance, ambientComp));
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -238,10 +247,9 @@ public sealed class AmbientSoundSystem : SharedAmbientSoundSystem
|
||||
var mapPos = playerXform.MapPosition;
|
||||
|
||||
// Remove out-of-range ambiences
|
||||
foreach (var (ent, sound) in _playingSounds)
|
||||
foreach (var (comp, sound) in _playingSounds)
|
||||
{
|
||||
var entity = ent.Owner;
|
||||
var comp = ent.Comp;
|
||||
var entity = comp.Owner;
|
||||
|
||||
if (comp.Enabled &&
|
||||
// Don't keep playing sounds that have changed since.
|
||||
@@ -258,8 +266,8 @@ public sealed class AmbientSoundSystem : SharedAmbientSoundSystem
|
||||
continue;
|
||||
}
|
||||
|
||||
sound.Stream?.Stop();
|
||||
_playingSounds.Remove((entity, comp));
|
||||
_audio.Stop(sound.Stream);
|
||||
_playingSounds.Remove(comp);
|
||||
_playingCount[sound.Path] -= 1;
|
||||
if (_playingCount[sound.Path] == 0)
|
||||
_playingCount.Remove(sound.Path);
|
||||
@@ -284,12 +292,11 @@ public sealed class AmbientSoundSystem : SharedAmbientSoundSystem
|
||||
|
||||
sources.Sort(static (a, b) => b.Importance.CompareTo(a.Importance));
|
||||
|
||||
foreach (var (_, ent) in sources)
|
||||
foreach (var (_, comp) in sources)
|
||||
{
|
||||
var uid = ent.Owner;
|
||||
var comp = ent.Comp;
|
||||
var uid = comp.Owner;
|
||||
|
||||
if (_playingSounds.ContainsKey(ent) ||
|
||||
if (_playingSounds.ContainsKey(comp) ||
|
||||
metaQuery.GetComponent(uid).EntityPaused)
|
||||
continue;
|
||||
|
||||
@@ -299,11 +306,8 @@ public sealed class AmbientSoundSystem : SharedAmbientSoundSystem
|
||||
.WithPlayOffset(_random.NextFloat(0.0f, 100.0f))
|
||||
.WithMaxDistance(comp.Range);
|
||||
|
||||
var stream = _audio.PlayPvs(comp.Sound, uid, audioParams);
|
||||
if (stream == null)
|
||||
continue;
|
||||
|
||||
_playingSounds[ent] = (stream, comp.Sound, key);
|
||||
var stream = _audio.PlayEntity(comp.Sound, Filter.Local(), uid, false, audioParams);
|
||||
_playingSounds[comp] = (stream.Value.Entity, comp.Sound, key);
|
||||
playingCount++;
|
||||
|
||||
if (_playingSounds.Count >= _maxAmbientCount)
|
||||
|
||||
@@ -5,6 +5,7 @@ using JetBrains.Annotations;
|
||||
using Robust.Client;
|
||||
using Robust.Client.State;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.Audio.Systems;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Player;
|
||||
|
||||
@@ -21,7 +22,7 @@ public sealed class BackgroundAudioSystem : EntitySystem
|
||||
|
||||
private readonly AudioParams _lobbyParams = new(-5f, 1, "Master", 0, 0, 0, true, 0f);
|
||||
|
||||
private IPlayingAudioStream? _lobbyStream;
|
||||
private EntityUid? _lobbyStream;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
@@ -118,12 +119,11 @@ public sealed class BackgroundAudioSystem : EntitySystem
|
||||
}
|
||||
|
||||
_lobbyStream = _audio.PlayGlobal(file, Filter.Local(), false,
|
||||
_lobbyParams.WithVolume(_lobbyParams.Volume + _configManager.GetCVar(CCVars.LobbyMusicVolume)));
|
||||
_lobbyParams.WithVolume(_lobbyParams.Volume + _configManager.GetCVar(CCVars.LobbyMusicVolume)))?.Entity;
|
||||
}
|
||||
|
||||
private void EndLobbyMusic()
|
||||
{
|
||||
_lobbyStream?.Stop();
|
||||
_lobbyStream = null;
|
||||
_lobbyStream = _audio.Stop(_lobbyStream);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using Content.Shared.CCVar;
|
||||
using Content.Shared.GameTicking;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.Audio.Systems;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Player;
|
||||
|
||||
@@ -14,11 +15,11 @@ public sealed class ClientGlobalSoundSystem : SharedGlobalSoundSystem
|
||||
|
||||
// Admin music
|
||||
private bool _adminAudioEnabled = true;
|
||||
private List<IPlayingAudioStream?> _adminAudio = new(1);
|
||||
private List<EntityUid?> _adminAudio = new(1);
|
||||
|
||||
// Event sounds (e.g. nuke timer)
|
||||
private bool _eventAudioEnabled = true;
|
||||
private Dictionary<StationEventMusicType, IPlayingAudioStream?> _eventAudio = new(1);
|
||||
private Dictionary<StationEventMusicType, EntityUid?> _eventAudio = new(1);
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
@@ -49,13 +50,13 @@ public sealed class ClientGlobalSoundSystem : SharedGlobalSoundSystem
|
||||
{
|
||||
foreach (var stream in _adminAudio)
|
||||
{
|
||||
stream?.Stop();
|
||||
_audio.Stop(stream);
|
||||
}
|
||||
_adminAudio.Clear();
|
||||
|
||||
foreach (var (_, stream) in _eventAudio)
|
||||
foreach (var stream in _eventAudio.Values)
|
||||
{
|
||||
stream?.Stop();
|
||||
_audio.Stop(stream);
|
||||
}
|
||||
|
||||
_eventAudio.Clear();
|
||||
@@ -66,7 +67,7 @@ public sealed class ClientGlobalSoundSystem : SharedGlobalSoundSystem
|
||||
if(!_adminAudioEnabled) return;
|
||||
|
||||
var stream = _audio.PlayGlobal(soundEvent.Filename, Filter.Local(), false, soundEvent.AudioParams);
|
||||
_adminAudio.Add(stream);
|
||||
_adminAudio.Add(stream.Value.Entity);
|
||||
}
|
||||
|
||||
private void PlayStationEventMusic(StationEventMusicEvent soundEvent)
|
||||
@@ -75,7 +76,7 @@ public sealed class ClientGlobalSoundSystem : SharedGlobalSoundSystem
|
||||
if(!_eventAudioEnabled || _eventAudio.ContainsKey(soundEvent.Type)) return;
|
||||
|
||||
var stream = _audio.PlayGlobal(soundEvent.Filename, Filter.Local(), false, soundEvent.AudioParams);
|
||||
_eventAudio.Add(soundEvent.Type, stream);
|
||||
_eventAudio.Add(soundEvent.Type, stream.Value.Entity);
|
||||
}
|
||||
|
||||
private void PlayGameSound(GameGlobalSoundEvent soundEvent)
|
||||
@@ -85,8 +86,10 @@ public sealed class ClientGlobalSoundSystem : SharedGlobalSoundSystem
|
||||
|
||||
private void StopStationEventMusic(StopStationEventMusic soundEvent)
|
||||
{
|
||||
if (!_eventAudio.TryGetValue(soundEvent.Type, out var stream)) return;
|
||||
stream?.Stop();
|
||||
if (!_eventAudio.TryGetValue(soundEvent.Type, out var stream))
|
||||
return;
|
||||
|
||||
_audio.Stop(stream);
|
||||
_eventAudio.Remove(soundEvent.Type);
|
||||
}
|
||||
|
||||
@@ -96,7 +99,7 @@ public sealed class ClientGlobalSoundSystem : SharedGlobalSoundSystem
|
||||
if (_adminAudioEnabled) return;
|
||||
foreach (var stream in _adminAudio)
|
||||
{
|
||||
stream?.Stop();
|
||||
_audio.Stop(stream);
|
||||
}
|
||||
_adminAudio.Clear();
|
||||
}
|
||||
@@ -107,7 +110,7 @@ public sealed class ClientGlobalSoundSystem : SharedGlobalSoundSystem
|
||||
if (_eventAudioEnabled) return;
|
||||
foreach (var stream in _eventAudio)
|
||||
{
|
||||
stream.Value?.Stop();
|
||||
_audio.Stop(stream.Value);
|
||||
}
|
||||
_eventAudio.Clear();
|
||||
}
|
||||
|
||||
@@ -9,10 +9,12 @@ using Robust.Client.Player;
|
||||
using Robust.Client.ResourceManagement;
|
||||
using Robust.Client.State;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.Audio.Systems;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Random;
|
||||
using Robust.Shared.ResourceManagement.ResourceTypes;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
@@ -24,7 +26,7 @@ public sealed partial class ContentAudioSystem
|
||||
[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 IClientResourceCache _resource = default!;
|
||||
[Dependency] private readonly IRobustRandom _random = default!;
|
||||
[Dependency] private readonly IStateManager _state = default!;
|
||||
[Dependency] private readonly RulesSystem _rules = default!;
|
||||
@@ -39,7 +41,7 @@ public sealed partial class ContentAudioSystem
|
||||
// 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 EntityUid? _ambientMusicStream;
|
||||
private AmbientMusicPrototype? _musicProto;
|
||||
|
||||
/// <summary>
|
||||
@@ -83,7 +85,7 @@ public sealed partial class ContentAudioSystem
|
||||
|
||||
if (_ambientMusicStream != null && _musicProto != null)
|
||||
{
|
||||
_ambientMusicStream.Volume = _musicProto.Sound.Params.Volume + _volumeSlider;
|
||||
_audio.SetVolume(_ambientMusicStream, _musicProto.Sound.Params.Volume + _volumeSlider);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,7 +94,7 @@ public sealed partial class ContentAudioSystem
|
||||
_configManager.UnsubValueChanged(CCVars.AmbientMusicVolume, AmbienceCVarChanged);
|
||||
_proto.PrototypesReloaded -= OnProtoReload;
|
||||
_state.OnStateChanged -= OnStateChange;
|
||||
_ambientMusicStream?.Stop();
|
||||
_ambientMusicStream = _audio.Stop(_ambientMusicStream);
|
||||
}
|
||||
|
||||
private void OnProtoReload(PrototypesReloadedEventArgs obj)
|
||||
@@ -129,8 +131,7 @@ public sealed partial class ContentAudioSystem
|
||||
private void OnRoundEndMessage(RoundEndMessageEvent ev)
|
||||
{
|
||||
// If scoreboard shows then just stop the music
|
||||
_ambientMusicStream?.Stop();
|
||||
_ambientMusicStream = null;
|
||||
_ambientMusicStream = _audio.Stop(_ambientMusicStream);
|
||||
_nextAudio = TimeSpan.FromMinutes(3);
|
||||
}
|
||||
|
||||
@@ -170,7 +171,7 @@ public sealed partial class ContentAudioSystem
|
||||
return;
|
||||
}
|
||||
|
||||
var isDone = _ambientMusicStream?.Done;
|
||||
var isDone = !Exists(_ambientMusicStream);
|
||||
|
||||
if (_interruptable)
|
||||
{
|
||||
@@ -178,7 +179,7 @@ public sealed partial class ContentAudioSystem
|
||||
|
||||
if (player == null || _musicProto == null || !_rules.IsTrue(player.Value, _proto.Index<RulesPrototype>(_musicProto.Rules)))
|
||||
{
|
||||
FadeOut(_ambientMusicStream, AmbientMusicFadeTime);
|
||||
FadeOut(_ambientMusicStream, duration: AmbientMusicFadeTime);
|
||||
_musicProto = null;
|
||||
_interruptable = false;
|
||||
isDone = true;
|
||||
@@ -221,14 +222,11 @@ public sealed partial class ContentAudioSystem
|
||||
false,
|
||||
AudioParams.Default.WithVolume(_musicProto.Sound.Params.Volume + _volumeSlider));
|
||||
|
||||
if (strim != null)
|
||||
{
|
||||
_ambientMusicStream = (AudioSystem.PlayingStream) strim;
|
||||
_ambientMusicStream = strim.Value.Entity;
|
||||
|
||||
if (_musicProto.FadeIn)
|
||||
{
|
||||
FadeIn(_ambientMusicStream, AmbientMusicFadeTime);
|
||||
}
|
||||
if (_musicProto.FadeIn)
|
||||
{
|
||||
FadeIn(_ambientMusicStream, strim.Value.Component, AmbientMusicFadeTime);
|
||||
}
|
||||
|
||||
// Refresh the list
|
||||
|
||||
@@ -1,17 +1,19 @@
|
||||
using Content.Shared.Audio;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.Audio;
|
||||
using AudioComponent = Robust.Shared.Audio.Components.AudioComponent;
|
||||
|
||||
namespace Content.Client.Audio;
|
||||
|
||||
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();
|
||||
private readonly Dictionary<EntityUid, float> _fadingOut = new();
|
||||
|
||||
// Need volume change per tick + target volume.
|
||||
private readonly Dictionary<AudioSystem.PlayingStream, (float VolumeChange, float TargetVolume)> _fadingIn = new();
|
||||
private readonly Dictionary<EntityUid, (float VolumeChange, float TargetVolume)> _fadingIn = new();
|
||||
|
||||
private readonly List<AudioSystem.PlayingStream> _fadeToRemove = new();
|
||||
private readonly List<EntityUid> _fadeToRemove = new();
|
||||
|
||||
private const float MinVolume = -32f;
|
||||
private const float DefaultDuration = 2f;
|
||||
@@ -42,28 +44,28 @@ public sealed partial class ContentAudioSystem : SharedContentAudioSystem
|
||||
|
||||
#region Fades
|
||||
|
||||
public void FadeOut(AudioSystem.PlayingStream? stream, float duration = DefaultDuration)
|
||||
public void FadeOut(EntityUid? stream, AudioComponent? component = null, float duration = DefaultDuration)
|
||||
{
|
||||
if (stream == null || duration <= 0f)
|
||||
if (stream == null || duration <= 0f || !Resolve(stream.Value, ref component))
|
||||
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);
|
||||
_fadingIn.Remove(stream.Value);
|
||||
var diff = component.Volume - MinVolume;
|
||||
_fadingOut.Add(stream.Value, diff / duration);
|
||||
}
|
||||
|
||||
public void FadeIn(AudioSystem.PlayingStream? stream, float duration = DefaultDuration)
|
||||
public void FadeIn(EntityUid? stream, AudioComponent? component = null, float duration = DefaultDuration)
|
||||
{
|
||||
if (stream == null || duration <= 0f || stream.Volume < MinVolume)
|
||||
if (stream == null || duration <= 0f || !Resolve(stream.Value, ref component) || component.Volume < MinVolume)
|
||||
return;
|
||||
|
||||
_fadingOut.Remove(stream);
|
||||
var curVolume = stream.Volume;
|
||||
_fadingOut.Remove(stream.Value);
|
||||
var curVolume = component.Volume;
|
||||
var change = (curVolume - MinVolume) / duration;
|
||||
_fadingIn.Add(stream, (change, stream.Volume));
|
||||
stream.Volume = MinVolume;
|
||||
_fadingIn.Add(stream.Value, (change, component.Volume));
|
||||
component.Volume = MinVolume;
|
||||
}
|
||||
|
||||
private void UpdateFades(float frameTime)
|
||||
@@ -72,19 +74,18 @@ public sealed partial class ContentAudioSystem : SharedContentAudioSystem
|
||||
|
||||
foreach (var (stream, change) in _fadingOut)
|
||||
{
|
||||
// Cancelled elsewhere
|
||||
if (stream.Done)
|
||||
if (!TryComp(stream, out AudioComponent? component))
|
||||
{
|
||||
_fadeToRemove.Add(stream);
|
||||
continue;
|
||||
}
|
||||
|
||||
var volume = stream.Volume - change * frameTime;
|
||||
stream.Volume = MathF.Max(MinVolume, volume);
|
||||
var volume = component.Volume - change * frameTime;
|
||||
component.Volume = MathF.Max(MinVolume, volume);
|
||||
|
||||
if (stream.Volume.Equals(MinVolume))
|
||||
if (component.Volume.Equals(MinVolume))
|
||||
{
|
||||
stream.Stop();
|
||||
_audio.Stop(stream);
|
||||
_fadeToRemove.Add(stream);
|
||||
}
|
||||
}
|
||||
@@ -99,16 +100,16 @@ public sealed partial class ContentAudioSystem : SharedContentAudioSystem
|
||||
foreach (var (stream, (change, target)) in _fadingIn)
|
||||
{
|
||||
// Cancelled elsewhere
|
||||
if (stream.Done)
|
||||
if (!TryComp(stream, out AudioComponent? component))
|
||||
{
|
||||
_fadeToRemove.Add(stream);
|
||||
continue;
|
||||
}
|
||||
|
||||
var volume = stream.Volume + change * frameTime;
|
||||
stream.Volume = MathF.Min(target, volume);
|
||||
var volume = component.Volume + change * frameTime;
|
||||
component.Volume = MathF.Min(target, volume);
|
||||
|
||||
if (stream.Volume.Equals(target))
|
||||
if (component.Volume.Equals(target))
|
||||
{
|
||||
_fadeToRemove.Add(stream);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user