using System.IO; using Content.Shared._White; using Content.Shared._White.TTS; using Robust.Client.Audio; using Robust.Shared.Audio; using Robust.Shared.Audio.Components; using Robust.Shared.Configuration; // ReSharper disable InconsistentNaming namespace Content.Client._White.TTS; /// /// Plays TTS audio in world /// public sealed class TTSSystem : EntitySystem { [Dependency] private readonly IAudioManager _audioManager = default!; [Dependency] private readonly IConfigurationManager _cfg = default!; [Dependency] private readonly AudioSystem _audioSystem = default!; private float _volume; private readonly Dictionary _currentlyPlaying = new(); private readonly Dictionary> _enquedStreams = new(); // Same as Server.ChatSystem.VoiceRange private const float VoiceRange = 10; public override void Initialize() { _cfg.OnValueChanged(WhiteCVars.TtsVolume, OnTtsVolumeChanged, true); SubscribeNetworkEvent(OnPlayTTS); } public override void Shutdown() { base.Shutdown(); _cfg.UnsubValueChanged(WhiteCVars.TtsVolume, OnTtsVolumeChanged); ClearQueues(); } public override void FrameUpdate(float frameTime) { foreach (var (uid, audioComponent) in _currentlyPlaying) { if (audioComponent is { Running: true, Playing: true }) { continue; } if (!_enquedStreams.TryGetValue(uid, out var queue)) { continue; } if (!queue.TryDequeue(out var toPlay)) { continue; } var audio = _audioSystem.PlayEntity(toPlay.Stream, uid, toPlay.Params); if (!audio.HasValue) { continue; } _currentlyPlaying[uid] = audio.Value.Component; } } private void OnTtsVolumeChanged(float volume) { _volume = volume; } private void OnPlayTTS(PlayTTSEvent ev) { PlayTTS(GetEntity(ev.Uid), ev.Data, ev.BoostVolume ? _volume + 5 : _volume); } public void PlayTTS(EntityUid uid, byte[] data, float volume) { if (_volume <= -20f) { return; } var stream = CreateAudioStream(data); var audioParams = new AudioParams { Volume = volume, MaxDistance = VoiceRange }; var audioStream = new AudioStreamWithParams(stream, audioParams); EnqueueAudio(uid, audioStream); } public void StopCurrentTTS(EntityUid uid) { if (!_currentlyPlaying.TryGetValue(uid, out var audio)) { return; } _audioSystem.Stop(audio.Owner); } private void EnqueueAudio(EntityUid uid, AudioStreamWithParams audioStream) { if (!_currentlyPlaying.ContainsKey(uid)) { var audio = _audioSystem.PlayEntity(audioStream.Stream, uid, audioStream.Params); if (!audio.HasValue) { return; } _currentlyPlaying[uid] = audio.Value.Component; return; } if (_enquedStreams.TryGetValue(uid, out var queue)) { queue.Enqueue(audioStream); return; } queue = new Queue(); queue.Enqueue(audioStream); _enquedStreams[uid] = queue; } private void ClearQueues() { foreach (var (_, queue) in _enquedStreams) { queue.Clear(); } } private AudioStream CreateAudioStream(byte[] data) { var dataStream = new MemoryStream(data) { Position = 0 }; return _audioManager.LoadAudioOggVorbis(dataStream); } private record AudioStreamWithParams(AudioStream Stream, AudioParams Params); }