From 6f84b041918e3b53c75584b081be919b169edc82 Mon Sep 17 00:00:00 2001 From: Janet Blackquill Date: Wed, 27 Nov 2024 15:58:38 -0500 Subject: [PATCH] Refactor audio system to send collection IDs over the network This is important groundwork for future features such as captioning, as a caption and other data can be associated with the collection prototype instead of passing extra data everywhere with the sound. --- .../Animations/AnimationTrackPlaySound.cs | 12 +-- Robust.Client/Audio/AudioSystem.cs | 84 +++++++++------- Robust.Server/Audio/AudioSystem.cs | 42 ++++---- Robust.Shared/Audio/ResolvedSoundSpecifier.cs | 98 +++++++++++++++++++ .../Audio/Systems/SharedAudioSystem.cs | 54 ++++++---- 5 files changed, 205 insertions(+), 85 deletions(-) create mode 100644 Robust.Shared/Audio/ResolvedSoundSpecifier.cs diff --git a/Robust.Client/Animations/AnimationTrackPlaySound.cs b/Robust.Client/Animations/AnimationTrackPlaySound.cs index be5051a4d8f0..11f6a6e74340 100644 --- a/Robust.Client/Animations/AnimationTrackPlaySound.cs +++ b/Robust.Client/Animations/AnimationTrackPlaySound.cs @@ -40,11 +40,7 @@ public override (int KeyFrameIndex, float FramePlayingTime) var keyFrame = KeyFrames[keyFrameIndex]; var audioParams = keyFrame.AudioParamsFunc.Invoke(); - var audio = new SoundPathSpecifier(keyFrame.Resource) - { - Params = audioParams - }; - IoCManager.Resolve().GetEntitySystem().PlayEntity(audio, Filter.Local(), entity, true); + IoCManager.Resolve().GetEntitySystem().PlayEntity(keyFrame.Specifier, Filter.Local(), entity, true, audioParams); } return (keyFrameIndex, playingTime); @@ -55,7 +51,7 @@ public struct KeyFrame /// /// The RSI state to play when this keyframe gets triggered. /// - public readonly string Resource; + public readonly ResolvedSoundSpecifier Specifier; /// /// A function that returns the audio parameter to be used. @@ -69,9 +65,9 @@ public struct KeyFrame /// public readonly float KeyTime; - public KeyFrame(string resource, float keyTime, Func? audioParams = null) + public KeyFrame(ResolvedSoundSpecifier specifier, float keyTime, Func? audioParams = null) { - Resource = resource; + Specifier = specifier; KeyTime = keyTime; AudioParamsFunc = audioParams ?? (() => AudioParams.Default); } diff --git a/Robust.Client/Audio/AudioSystem.cs b/Robust.Client/Audio/AudioSystem.cs index 5dfd954bb4c9..314ff30a275f 100644 --- a/Robust.Client/Audio/AudioSystem.cs +++ b/Robust.Client/Audio/AudioSystem.cs @@ -415,6 +415,16 @@ public float GetOcclusion(MapCoordinates listener, Vector2 delta, float distance return occlusion; } + private bool TryGetAudio(ResolvedSoundSpecifier specifier, [NotNullWhen(true)] out AudioResource? audio) + { + var filename = GetAudioPath(specifier) ?? string.Empty; + if (_resourceCache.TryGetResource(new ResPath(filename), out audio)) + return true; + + Log.Error($"Server tried to play audio file {filename} which does not exist."); + return false; + } + private bool TryGetAudio(string filename, [NotNullWhen(true)] out AudioResource? audio) { if (_resourceCache.TryGetResource(new ResPath(filename), out audio)) @@ -433,15 +443,15 @@ private bool TryGetAudio(AudioStream stream, [NotNullWhen(true)] out AudioResour return false; } - public override (EntityUid Entity, AudioComponent Component)? PlayPvs(string? filename, EntityCoordinates coordinates, + public override (EntityUid Entity, AudioComponent Component)? PlayPvs(ResolvedSoundSpecifier? specifier, EntityCoordinates coordinates, AudioParams? audioParams = null) { - return PlayStatic(filename, Filter.Local(), coordinates, true, audioParams); + return PlayStatic(specifier, Filter.Local(), coordinates, true, audioParams); } - public override (EntityUid Entity, AudioComponent Component)? PlayPvs(string? filename, EntityUid uid, AudioParams? audioParams = null) + public override (EntityUid Entity, AudioComponent Component)? PlayPvs(ResolvedSoundSpecifier? specifier, EntityUid uid, AudioParams? audioParams = null) { - return PlayEntity(filename, Filter.Local(), uid, true, audioParams); + return PlayEntity(specifier, Filter.Local(), uid, true, audioParams); } /// @@ -477,21 +487,21 @@ public override (EntityUid Entity, AudioComponent Component)? PlayPredicted(Soun /// /// The resource path to the OGG Vorbis file to play. /// - private (EntityUid Entity, AudioComponent Component)? PlayGlobal(string? filename, AudioParams? audioParams = null, bool recordReplay = true) + private (EntityUid Entity, AudioComponent Component)? PlayGlobal(ResolvedSoundSpecifier? specifier, AudioParams? audioParams = null, bool recordReplay = true) { - if (string.IsNullOrEmpty(filename)) + if (specifier is null) return null; if (recordReplay && _replayRecording.IsRecording) { _replayRecording.RecordReplayMessage(new PlayAudioGlobalMessage { - FileName = filename, + Specifier = specifier, AudioParams = audioParams ?? AudioParams.Default }); } - return TryGetAudio(filename, out var audio) ? PlayGlobal(audio, audioParams) : default; + return TryGetAudio(specifier, out var audio) ? PlayGlobal(audio, audioParams) : default; } /// @@ -513,22 +523,22 @@ public override (EntityUid Entity, AudioComponent Component)? PlayPredicted(Soun /// /// The resource path to the OGG Vorbis file to play. /// The entity "emitting" the audio. - private (EntityUid Entity, AudioComponent Component)? PlayEntity(string? filename, EntityUid entity, AudioParams? audioParams = null, bool recordReplay = true) + private (EntityUid Entity, AudioComponent Component)? PlayEntity(ResolvedSoundSpecifier? specifier, EntityUid entity, AudioParams? audioParams = null, bool recordReplay = true) { - if (string.IsNullOrEmpty(filename)) + if (specifier is null) return null; if (recordReplay && _replayRecording.IsRecording) { _replayRecording.RecordReplayMessage(new PlayAudioEntityMessage { - FileName = filename, + Specifier = specifier, NetEntity = GetNetEntity(entity), AudioParams = audioParams ?? AudioParams.Default }); } - return TryGetAudio(filename, out var audio) ? PlayEntity(audio, entity, audioParams) : default; + return TryGetAudio(specifier, out var audio) ? PlayEntity(audio, entity, audioParams) : default; } /// @@ -557,22 +567,22 @@ public override (EntityUid Entity, AudioComponent Component)? PlayPredicted(Soun /// The resource path to the OGG Vorbis file to play. /// The coordinates at which to play the audio. /// - private (EntityUid Entity, AudioComponent Component)? PlayStatic(string? filename, EntityCoordinates coordinates, AudioParams? audioParams = null, bool recordReplay = true) + private (EntityUid Entity, AudioComponent Component)? PlayStatic(ResolvedSoundSpecifier? specifier, EntityCoordinates coordinates, AudioParams? audioParams = null, bool recordReplay = true) { - if (string.IsNullOrEmpty(filename)) + if (specifier is null) return null; if (recordReplay && _replayRecording.IsRecording) { _replayRecording.RecordReplayMessage(new PlayAudioPositionalMessage { - FileName = filename, + Specifier = specifier, Coordinates = GetNetCoordinates(coordinates), AudioParams = audioParams ?? AudioParams.Default }); } - return TryGetAudio(filename, out var audio) ? PlayStatic(audio, coordinates, audioParams) : default; + return TryGetAudio(specifier, out var audio) ? PlayStatic(audio, coordinates, audioParams) : default; } /// @@ -595,27 +605,27 @@ public override (EntityUid Entity, AudioComponent Component)? PlayPredicted(Soun } /// - public override (EntityUid Entity, AudioComponent Component)? PlayGlobal(string? filename, Filter playerFilter, bool recordReplay, AudioParams? audioParams = null) + public override (EntityUid Entity, AudioComponent Component)? PlayGlobal(ResolvedSoundSpecifier? specifier, Filter playerFilter, bool recordReplay, AudioParams? audioParams = null) { - return PlayGlobal(filename, audioParams); + return PlayGlobal(specifier, audioParams); } /// - public override (EntityUid Entity, AudioComponent Component)? PlayEntity(string? filename, Filter playerFilter, EntityUid entity, bool recordReplay, AudioParams? audioParams = null) + public override (EntityUid Entity, AudioComponent Component)? PlayEntity(ResolvedSoundSpecifier? specifier, Filter playerFilter, EntityUid entity, bool recordReplay, AudioParams? audioParams = null) { - return PlayEntity(filename, entity, audioParams); + return PlayEntity(specifier, entity, audioParams); } /// - public override (EntityUid Entity, AudioComponent Component)? PlayStatic(string? filename, Filter playerFilter, EntityCoordinates coordinates, bool recordReplay, AudioParams? audioParams = null) + public override (EntityUid Entity, AudioComponent Component)? PlayStatic(ResolvedSoundSpecifier? specifier, Filter playerFilter, EntityCoordinates coordinates, bool recordReplay, AudioParams? audioParams = null) { - return PlayStatic(filename, coordinates, audioParams); + return PlayStatic(specifier, coordinates, audioParams); } /// - public override (EntityUid Entity, AudioComponent Component)? PlayGlobal(string? filename, ICommonSession recipient, AudioParams? audioParams = null) + public override (EntityUid Entity, AudioComponent Component)? PlayGlobal(ResolvedSoundSpecifier? specifier, ICommonSession recipient, AudioParams? audioParams = null) { - return PlayGlobal(filename, audioParams); + return PlayGlobal(specifier, audioParams); } public override void LoadStream(Entity entity, T stream) @@ -629,33 +639,33 @@ public override void LoadStream(Entity entity, T stream) } /// - public override (EntityUid Entity, AudioComponent Component)? PlayGlobal(string? filename, EntityUid recipient, AudioParams? audioParams = null) + public override (EntityUid Entity, AudioComponent Component)? PlayGlobal(ResolvedSoundSpecifier? specifier, EntityUid recipient, AudioParams? audioParams = null) { - return PlayGlobal(filename, audioParams); + return PlayGlobal(specifier, audioParams); } /// - public override (EntityUid Entity, AudioComponent Component)? PlayEntity(string? filename, ICommonSession recipient, EntityUid uid, AudioParams? audioParams = null) + public override (EntityUid Entity, AudioComponent Component)? PlayEntity(ResolvedSoundSpecifier? specifier, ICommonSession recipient, EntityUid uid, AudioParams? audioParams = null) { - return PlayEntity(filename, uid, audioParams); + return PlayEntity(specifier, uid, audioParams); } /// - public override (EntityUid Entity, AudioComponent Component)? PlayEntity(string? filename, EntityUid recipient, EntityUid uid, AudioParams? audioParams = null) + public override (EntityUid Entity, AudioComponent Component)? PlayEntity(ResolvedSoundSpecifier? specifier, EntityUid recipient, EntityUid uid, AudioParams? audioParams = null) { - return PlayEntity(filename, uid, audioParams); + return PlayEntity(specifier, uid, audioParams); } /// - public override (EntityUid Entity, AudioComponent Component)? PlayStatic(string? filename, ICommonSession recipient, EntityCoordinates coordinates, AudioParams? audioParams = null) + public override (EntityUid Entity, AudioComponent Component)? PlayStatic(ResolvedSoundSpecifier? specifier, ICommonSession recipient, EntityCoordinates coordinates, AudioParams? audioParams = null) { - return PlayStatic(filename, coordinates, audioParams); + return PlayStatic(specifier, coordinates, audioParams); } /// - public override (EntityUid Entity, AudioComponent Component)? PlayStatic(string? filename, EntityUid recipient, EntityCoordinates coordinates, AudioParams? audioParams = null) + public override (EntityUid Entity, AudioComponent Component)? PlayStatic(ResolvedSoundSpecifier? specifier, EntityUid recipient, EntityCoordinates coordinates, AudioParams? audioParams = null) { - return PlayStatic(filename, coordinates, audioParams); + return PlayStatic(specifier, coordinates, audioParams); } private (EntityUid Entity, AudioComponent Component) CreateAndStartPlayingStream(AudioParams? audioParams, AudioStream stream) @@ -694,17 +704,17 @@ private void ApplyAudioParams(AudioParams audioParams, IAudioSource source) private void OnEntityCoordinates(PlayAudioPositionalMessage ev) { - PlayStatic(ev.FileName, GetCoordinates(ev.Coordinates), ev.AudioParams, false); + PlayStatic(ev.Specifier, GetCoordinates(ev.Coordinates), ev.AudioParams, false); } private void OnEntityAudio(PlayAudioEntityMessage ev) { - PlayEntity(ev.FileName, GetEntity(ev.NetEntity), ev.AudioParams, false); + PlayEntity(ev.Specifier, GetEntity(ev.NetEntity), ev.AudioParams, false); } private void OnGlobalAudio(PlayAudioGlobalMessage ev) { - PlayGlobal(ev.FileName, ev.AudioParams, false); + PlayGlobal(ev.Specifier, ev.AudioParams, false); } protected override TimeSpan GetAudioLengthImpl(string filename) diff --git a/Robust.Server/Audio/AudioSystem.cs b/Robust.Server/Audio/AudioSystem.cs index 981c01e770e0..bc91a0466db1 100644 --- a/Robust.Server/Audio/AudioSystem.cs +++ b/Robust.Server/Audio/AudioSystem.cs @@ -81,27 +81,27 @@ private void AddAudioFilter(EntityUid uid, AudioComponent component, Filter filt } /// - public override (EntityUid Entity, AudioComponent Component)? PlayGlobal(string? filename, Filter playerFilter, bool recordReplay, AudioParams? audioParams = null) + public override (EntityUid Entity, AudioComponent Component)? PlayGlobal(ResolvedSoundSpecifier? specifier, Filter playerFilter, bool recordReplay, AudioParams? audioParams = null) { - if (string.IsNullOrEmpty(filename)) + if (specifier is null) return null; - var entity = SetupAudio(filename, audioParams); + var entity = SetupAudio(specifier, audioParams); AddAudioFilter(entity, entity.Comp, playerFilter); entity.Comp.Global = true; return (entity, entity.Comp); } /// - public override (EntityUid Entity, AudioComponent Component)? PlayEntity(string? filename, Filter playerFilter, EntityUid uid, bool recordReplay, AudioParams? audioParams = null) + public override (EntityUid Entity, AudioComponent Component)? PlayEntity(ResolvedSoundSpecifier? specifier, Filter playerFilter, EntityUid uid, bool recordReplay, AudioParams? audioParams = null) { - if (string.IsNullOrEmpty(filename)) + if (specifier is null) return null; if (TerminatingOrDeleted(uid)) return null; - var entity = SetupAudio(filename, audioParams); + var entity = SetupAudio(specifier, audioParams); // Move it after setting it up XformSystem.SetCoordinates(entity, new EntityCoordinates(uid, Vector2.Zero)); AddAudioFilter(entity, entity.Comp, playerFilter); @@ -110,24 +110,24 @@ public override (EntityUid Entity, AudioComponent Component)? PlayEntity(string? } /// - public override (EntityUid Entity, AudioComponent Component)? PlayPvs(string? filename, EntityUid uid, AudioParams? audioParams = null) + public override (EntityUid Entity, AudioComponent Component)? PlayPvs(ResolvedSoundSpecifier? specifier, EntityUid uid, AudioParams? audioParams = null) { - if (string.IsNullOrEmpty(filename)) + if (specifier is null) return null; if (TerminatingOrDeleted(uid)) return null; - var entity = SetupAudio(filename, audioParams); + var entity = SetupAudio(specifier, audioParams); XformSystem.SetCoordinates(entity, new EntityCoordinates(uid, Vector2.Zero)); return (entity, entity.Comp); } /// - public override (EntityUid Entity, AudioComponent Component)? PlayStatic(string? filename, Filter playerFilter, EntityCoordinates coordinates, bool recordReplay, AudioParams? audioParams = null) + public override (EntityUid Entity, AudioComponent Component)? PlayStatic(ResolvedSoundSpecifier? specifier, Filter playerFilter, EntityCoordinates coordinates, bool recordReplay, AudioParams? audioParams = null) { - if (string.IsNullOrEmpty(filename)) + if (specifier is null) return null; if (TerminatingOrDeleted(coordinates.EntityId)) @@ -139,7 +139,7 @@ public override (EntityUid Entity, AudioComponent Component)? PlayStatic(string? if (!coordinates.IsValid(EntityManager)) return null; - var entity = SetupAudio(filename, audioParams); + var entity = SetupAudio(specifier, audioParams); XformSystem.SetCoordinates(entity, coordinates); AddAudioFilter(entity, entity.Comp, playerFilter); @@ -147,10 +147,10 @@ public override (EntityUid Entity, AudioComponent Component)? PlayStatic(string? } /// - public override (EntityUid Entity, AudioComponent Component)? PlayPvs(string? filename, EntityCoordinates coordinates, + public override (EntityUid Entity, AudioComponent Component)? PlayPvs(ResolvedSoundSpecifier? specifier, EntityCoordinates coordinates, AudioParams? audioParams = null) { - if (string.IsNullOrEmpty(filename)) + if (specifier is null) return null; if (TerminatingOrDeleted(coordinates.EntityId)) @@ -163,7 +163,7 @@ public override (EntityUid Entity, AudioComponent Component)? PlayPvs(string? fi return null; // TODO: Transform TryFindGridAt mess + optimisation required. - var entity = SetupAudio(filename, audioParams); + var entity = SetupAudio(specifier, audioParams); XformSystem.SetCoordinates(entity, coordinates); return (entity, entity.Comp); @@ -210,12 +210,12 @@ public override (EntityUid Entity, AudioComponent Component)? PlayPredicted(Soun return audio; } - public override (EntityUid Entity, AudioComponent Component)? PlayGlobal(string? filename, ICommonSession recipient, AudioParams? audioParams = null) + public override (EntityUid Entity, AudioComponent Component)? PlayGlobal(ResolvedSoundSpecifier? filename, ICommonSession recipient, AudioParams? audioParams = null) { return PlayGlobal(filename, Filter.SinglePlayer(recipient), false, audioParams); } - public override (EntityUid Entity, AudioComponent Component)? PlayGlobal(string? filename, EntityUid recipient, AudioParams? audioParams = null) + public override (EntityUid Entity, AudioComponent Component)? PlayGlobal(ResolvedSoundSpecifier? filename, EntityUid recipient, AudioParams? audioParams = null) { if (TryComp(recipient, out ActorComponent? actor)) return PlayGlobal(filename, actor.PlayerSession, audioParams); @@ -223,12 +223,12 @@ public override (EntityUid Entity, AudioComponent Component)? PlayGlobal(string? return null; } - public override (EntityUid Entity, AudioComponent Component)? PlayEntity(string? filename, ICommonSession recipient, EntityUid uid, AudioParams? audioParams = null) + public override (EntityUid Entity, AudioComponent Component)? PlayEntity(ResolvedSoundSpecifier? filename, ICommonSession recipient, EntityUid uid, AudioParams? audioParams = null) { return PlayEntity(filename, Filter.SinglePlayer(recipient), uid, false, audioParams); } - public override (EntityUid Entity, AudioComponent Component)? PlayEntity(string? filename, EntityUid recipient, EntityUid uid, AudioParams? audioParams = null) + public override (EntityUid Entity, AudioComponent Component)? PlayEntity(ResolvedSoundSpecifier? filename, EntityUid recipient, EntityUid uid, AudioParams? audioParams = null) { if (TryComp(recipient, out ActorComponent? actor)) return PlayEntity(filename, actor.PlayerSession, uid, audioParams); @@ -236,12 +236,12 @@ public override (EntityUid Entity, AudioComponent Component)? PlayEntity(string? return null; } - public override (EntityUid Entity, AudioComponent Component)? PlayStatic(string? filename, ICommonSession recipient, EntityCoordinates coordinates, AudioParams? audioParams = null) + public override (EntityUid Entity, AudioComponent Component)? PlayStatic(ResolvedSoundSpecifier? filename, ICommonSession recipient, EntityCoordinates coordinates, AudioParams? audioParams = null) { return PlayStatic(filename, Filter.SinglePlayer(recipient), coordinates, false, audioParams); } - public override (EntityUid Entity, AudioComponent Component)? PlayStatic(string? filename, EntityUid recipient, EntityCoordinates coordinates, AudioParams? audioParams = null) + public override (EntityUid Entity, AudioComponent Component)? PlayStatic(ResolvedSoundSpecifier? filename, EntityUid recipient, EntityCoordinates coordinates, AudioParams? audioParams = null) { if (TryComp(recipient, out ActorComponent? actor)) return PlayStatic(filename, actor.PlayerSession, coordinates, audioParams); diff --git a/Robust.Shared/Audio/ResolvedSoundSpecifier.cs b/Robust.Shared/Audio/ResolvedSoundSpecifier.cs new file mode 100644 index 000000000000..49adabcd1eac --- /dev/null +++ b/Robust.Shared/Audio/ResolvedSoundSpecifier.cs @@ -0,0 +1,98 @@ +using System; +using JetBrains.Annotations; +using Robust.Shared.Utility; +using Robust.Shared.Serialization; +using Robust.Shared.Serialization.Manager.Attributes; + +namespace Robust.Shared.Audio; + +/// +/// Represents a path to a sound resource, either as a literal path or as a collection ID and index. +/// +/// +/// +[Serializable, NetSerializable] +public abstract partial class ResolvedSoundSpecifier { + [Obsolete("String literals for sounds are deprecated, use a SoundSpecifier or ResolvedSoundSpecifier as appropriate instead")] + public static implicit operator ResolvedSoundSpecifier(string s) => new ResolvedPathSpecifier(s); + [Obsolete("String literals for sounds are deprecated, use a SoundSpecifier or ResolvedSoundSpecifier as appropriate instead")] + public static implicit operator ResolvedSoundSpecifier(ResPath s) => new ResolvedPathSpecifier(s); + + [Obsolete("Implicit string conversion for resolved specifiers are deprecated, store the ResolvedSoundSpecifier instead")] + public static implicit operator string(ResolvedSoundSpecifier specifier) => + specifier switch { + ResolvedPathSpecifier path => path.Path.ToString(), + ResolvedCollectionSpecifier collection => collection.Path.ToString(), + }; + + /// + /// Returns whether s is null, or if it contains an empty path/collection ID. + /// + public static bool IsNullOrEmpty(ResolvedSoundSpecifier? s) { + return s switch { + null => true, + ResolvedPathSpecifier path => path.Path.ToString() == "", + ResolvedCollectionSpecifier collection => string.IsNullOrEmpty(collection.Collection), + }; + } +} + +/// +/// Represents a path to a sound resource as a literal path. +/// +/// +[Serializable, NetSerializable] +public sealed partial class ResolvedPathSpecifier : ResolvedSoundSpecifier { + /// + /// The resource path of the sound. + /// + public ResPath Path { get; private set; } + + [UsedImplicitly] + private ResolvedPathSpecifier() + { + } + public ResolvedPathSpecifier(ResPath path) + { + Path = path; + } + public ResolvedPathSpecifier(string path) : this(new ResPath(path)) + { + } +} + +/// +/// Represents a path to a sound resource as a collection ID and index. +/// +/// +[Serializable, NetSerializable] +public sealed partial class ResolvedCollectionSpecifier : ResolvedSoundSpecifier { + /// + /// The ID of the sound collection to look up. + /// + public string? Collection { get; private set; } + /// + /// The index of the file in the associated sound collection to play. + /// + public int Index { get; private set; } + /// + /// The resource path of the sound. + /// + [Obsolete("Path for ResolvedCollectionSpecifier is deprecated, look up the collection prototype instead")] + public ResPath Path { get; private set; } + + [UsedImplicitly] + private ResolvedCollectionSpecifier() + { + } + + public ResolvedCollectionSpecifier(string collection, int index, ResPath path) + { + Collection = collection; + Index = index; + Path = path; + } + public ResolvedCollectionSpecifier(string collection, int index, string path) : this(collection, index, new ResPath(path)) + { + } +} diff --git a/Robust.Shared/Audio/Systems/SharedAudioSystem.cs b/Robust.Shared/Audio/Systems/SharedAudioSystem.cs index 4d79ff0e3d07..7d753134564e 100644 --- a/Robust.Shared/Audio/Systems/SharedAudioSystem.cs +++ b/Robust.Shared/Audio/Systems/SharedAudioSystem.cs @@ -273,31 +273,46 @@ public float GetAudioDistance(float length) /// /// Resolves the filepath to a sound file. /// - public string GetSound(SoundSpecifier specifier) + public ResolvedSoundSpecifier GetSound(SoundSpecifier specifier) { switch (specifier) { case SoundPathSpecifier path: - return path.Path == default ? string.Empty : path.Path.ToString(); + return new ResolvedPathSpecifier(path.Path == default ? string.Empty : path.Path.ToString()); case SoundCollectionSpecifier collection: { if (collection.Collection == null) - return string.Empty; + return new ResolvedPathSpecifier(string.Empty); var soundCollection = ProtoMan.Index(collection.Collection); - return RandMan.Pick(soundCollection.PickFiles).ToString(); + var index = RandMan.Next(soundCollection.PickFiles.Count); + return new ResolvedCollectionSpecifier(collection.Collection, index, soundCollection.PickFiles[index]); } } - return string.Empty; + return new ResolvedPathSpecifier(string.Empty); } #region AudioParams - protected Entity SetupAudio(string? fileName, AudioParams? audioParams, bool initialize = true, TimeSpan? length = null) + public string? GetAudioPath(ResolvedSoundSpecifier? specifier) + { + return specifier switch { + ResolvedPathSpecifier path => + path.Path.ToString(), + ResolvedCollectionSpecifier collection => + collection.Collection is null ? + string.Empty : + ProtoMan.Index(collection.Collection).PickFiles[collection.Index].ToString(), + null => null, + }; + } + + protected Entity SetupAudio(ResolvedSoundSpecifier? specifier, AudioParams? audioParams, bool initialize = true, TimeSpan? length = null) { var uid = EntityManager.CreateEntityUninitialized("Audio", MapCoordinates.Nullspace); + var fileName = GetAudioPath(specifier); DebugTools.Assert(!string.IsNullOrEmpty(fileName) || length is not null); MetadataSys.SetEntityName(uid, $"Audio ({fileName})", raiseEvents: false); audioParams ??= AudioParams.Default; @@ -383,8 +398,9 @@ public void SetVolume(EntityUid? entity, float value, AudioComponent? component /// /// Gets the timespan of the specified audio. /// - public TimeSpan GetAudioLength(string filename) + public TimeSpan GetAudioLength(ResolvedSoundSpecifier specifier) { + var filename = GetAudioPath(specifier) ?? string.Empty; if (!filename.StartsWith("/")) throw new ArgumentException("Path must be rooted"); @@ -417,7 +433,7 @@ public TimeSpan GetAudioLength(string filename) /// /// The resource path to the OGG Vorbis file to play. /// The set of players that will hear the sound. - public abstract (EntityUid Entity, Components.AudioComponent Component)? PlayGlobal(string? filename, Filter playerFilter, bool recordReplay, AudioParams? audioParams = null); + public abstract (EntityUid Entity, Components.AudioComponent Component)? PlayGlobal(ResolvedSoundSpecifier? filename, Filter playerFilter, bool recordReplay, AudioParams? audioParams = null); /// /// Play an audio file globally, without position. @@ -434,7 +450,7 @@ public TimeSpan GetAudioLength(string filename) /// /// The resource path to the OGG Vorbis file to play. /// The player that will hear the sound. - public abstract (EntityUid Entity, Components.AudioComponent Component)? PlayGlobal(string? filename, ICommonSession recipient, AudioParams? audioParams = null); + public abstract (EntityUid Entity, Components.AudioComponent Component)? PlayGlobal(ResolvedSoundSpecifier? filename, ICommonSession recipient, AudioParams? audioParams = null); /// /// Play an audio file globally, without position. @@ -453,7 +469,7 @@ public TimeSpan GetAudioLength(string filename) /// /// The resource path to the OGG Vorbis file to play. /// The player that will hear the sound. - public abstract (EntityUid Entity, Components.AudioComponent Component)? PlayGlobal(string? filename, EntityUid recipient, AudioParams? audioParams = null); + public abstract (EntityUid Entity, Components.AudioComponent Component)? PlayGlobal(ResolvedSoundSpecifier? filename, EntityUid recipient, AudioParams? audioParams = null); /// /// Play an audio file globally, without position. @@ -471,7 +487,7 @@ public TimeSpan GetAudioLength(string filename) /// The resource path to the OGG Vorbis file to play. /// The set of players that will hear the sound. /// The UID of the entity "emitting" the audio. - public abstract (EntityUid Entity, Components.AudioComponent Component)? PlayEntity(string? filename, Filter playerFilter, EntityUid uid, bool recordReplay, AudioParams? audioParams = null); + public abstract (EntityUid Entity, Components.AudioComponent Component)? PlayEntity(ResolvedSoundSpecifier? filename, Filter playerFilter, EntityUid uid, bool recordReplay, AudioParams? audioParams = null); /// /// Play an audio file following an entity. @@ -479,7 +495,7 @@ public TimeSpan GetAudioLength(string filename) /// The resource path to the OGG Vorbis file to play. /// The player that will hear the sound. /// The UID of the entity "emitting" the audio. - public abstract (EntityUid Entity, Components.AudioComponent Component)? PlayEntity(string? filename, ICommonSession recipient, EntityUid uid, AudioParams? audioParams = null); + public abstract (EntityUid Entity, Components.AudioComponent Component)? PlayEntity(ResolvedSoundSpecifier? filename, ICommonSession recipient, EntityUid uid, AudioParams? audioParams = null); /// /// Play an audio file following an entity. @@ -487,7 +503,7 @@ public TimeSpan GetAudioLength(string filename) /// The resource path to the OGG Vorbis file to play. /// The player that will hear the sound. /// The UID of the entity "emitting" the audio. - public abstract (EntityUid Entity, Components.AudioComponent Component)? PlayEntity(string? filename, EntityUid recipient, EntityUid uid, AudioParams? audioParams = null); + public abstract (EntityUid Entity, Components.AudioComponent Component)? PlayEntity(ResolvedSoundSpecifier? filename, EntityUid recipient, EntityUid uid, AudioParams? audioParams = null); /// /// Play an audio file following an entity. @@ -547,7 +563,7 @@ public TimeSpan GetAudioLength(string filename) /// /// The sound specifier that points the audio file(s) that should be played. /// The EntityCoordinates to attach the audio source to. - public abstract (EntityUid Entity, Components.AudioComponent Component)? PlayPvs(string? filename, + public abstract (EntityUid Entity, Components.AudioComponent Component)? PlayPvs(ResolvedSoundSpecifier? filename, EntityCoordinates coordinates, AudioParams? audioParams = null); /// @@ -555,7 +571,7 @@ public abstract (EntityUid Entity, Components.AudioComponent Component)? PlayPvs /// /// The resource path to the OGG Vorbis file to play. /// The UID of the entity "emitting" the audio. - public abstract (EntityUid Entity, Components.AudioComponent Component)? PlayPvs(string? filename, EntityUid uid, + public abstract (EntityUid Entity, Components.AudioComponent Component)? PlayPvs(ResolvedSoundSpecifier? filename, EntityUid uid, AudioParams? audioParams = null); /// @@ -592,7 +608,7 @@ public abstract (EntityUid Entity, Components.AudioComponent Component)? PlayPvs /// The resource path to the OGG Vorbis file to play. /// The set of players that will hear the sound. /// The coordinates at which to play the audio. - public abstract (EntityUid Entity, Components.AudioComponent Component)? PlayStatic(string? filename, Filter playerFilter, EntityCoordinates coordinates, bool recordReplay, AudioParams? audioParams = null); + public abstract (EntityUid Entity, Components.AudioComponent Component)? PlayStatic(ResolvedSoundSpecifier? filename, Filter playerFilter, EntityCoordinates coordinates, bool recordReplay, AudioParams? audioParams = null); /// /// Play an audio file at a static position. @@ -600,7 +616,7 @@ public abstract (EntityUid Entity, Components.AudioComponent Component)? PlayPvs /// The resource path to the OGG Vorbis file to play. /// The player that will hear the sound. /// The coordinates at which to play the audio. - public abstract (EntityUid Entity, Components.AudioComponent Component)? PlayStatic(string? filename, ICommonSession recipient, EntityCoordinates coordinates, AudioParams? audioParams = null); + public abstract (EntityUid Entity, Components.AudioComponent Component)? PlayStatic(ResolvedSoundSpecifier? filename, ICommonSession recipient, EntityCoordinates coordinates, AudioParams? audioParams = null); /// /// Play an audio file at a static position. @@ -608,7 +624,7 @@ public abstract (EntityUid Entity, Components.AudioComponent Component)? PlayPvs /// The resource path to the OGG Vorbis file to play. /// The player that will hear the sound. /// The coordinates at which to play the audio. - public abstract (EntityUid Entity, Components.AudioComponent Component)? PlayStatic(string? filename, EntityUid recipient, EntityCoordinates coordinates, AudioParams? audioParams = null); + public abstract (EntityUid Entity, Components.AudioComponent Component)? PlayStatic(ResolvedSoundSpecifier? filename, EntityUid recipient, EntityCoordinates coordinates, AudioParams? audioParams = null); /// /// Play an audio file at a static position. @@ -653,7 +669,7 @@ public abstract (EntityUid Entity, Components.AudioComponent Component)? PlayPvs [NetSerializable, Serializable] protected abstract class AudioMessage : EntityEventArgs { - public string FileName = string.Empty; + public ResolvedSoundSpecifier Specifier = new ResolvedPathSpecifier(string.Empty); public AudioParams AudioParams; }