[Fix] Потенциальный фикс ТТС (#413)
* fix: possible tts fix?? * style: не втыкай
This commit is contained in:
@@ -87,7 +87,7 @@ public sealed partial class HumanoidProfileEditor
|
|||||||
if (_previewDummy is null || Profile is null)
|
if (_previewDummy is null || Profile is null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
_ttsSys.StopAllStreams();
|
_ttsSys.StopCurrentTTS(_previewDummy.Value);
|
||||||
_ttsMgr.RequestTTS(_previewDummy.Value, IoCManager.Resolve<IRobustRandom>().Pick(_sampleText), Profile.Voice);
|
_ttsMgr.RequestTTS(_previewDummy.Value, IoCManager.Resolve<IRobustRandom>().Pick(_sampleText), Profile.Voice);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,38 +1,29 @@
|
|||||||
using System.Diagnostics.CodeAnalysis;
|
using System.IO;
|
||||||
using System.IO;
|
|
||||||
using System.Linq;
|
|
||||||
using Content.Shared.Physics;
|
|
||||||
using Content.Shared._White;
|
using Content.Shared._White;
|
||||||
using Content.Shared._White.TTS;
|
using Content.Shared._White.TTS;
|
||||||
using Robust.Client.Audio;
|
using Robust.Client.Audio;
|
||||||
using Robust.Client.GameObjects;
|
using Robust.Shared.Audio;
|
||||||
using Robust.Client.Graphics;
|
using Robust.Shared.Audio.Components;
|
||||||
using Robust.Shared.Audio.Sources;
|
|
||||||
using Robust.Shared.Configuration;
|
using Robust.Shared.Configuration;
|
||||||
using Robust.Shared.Map;
|
// ReSharper disable InconsistentNaming
|
||||||
using Robust.Shared.Physics;
|
|
||||||
using Robust.Shared.Physics.Systems;
|
|
||||||
|
|
||||||
namespace Content.Client._White.TTS;
|
namespace Content.Client._White.TTS;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Plays TTS audio in world
|
/// Plays TTS audio in world
|
||||||
/// </summary>
|
/// </summary>
|
||||||
// ReSharper disable once InconsistentNaming
|
|
||||||
public sealed class TTSSystem : EntitySystem
|
public sealed class TTSSystem : EntitySystem
|
||||||
{
|
{
|
||||||
[Dependency] private readonly IAudioManager _audioSystem = default!;
|
[Dependency] private readonly IAudioManager _audioManager = default!;
|
||||||
[Dependency] private readonly IEntityManager _entity = default!;
|
|
||||||
[Dependency] private readonly IEyeManager _eye = default!;
|
|
||||||
[Dependency] private readonly IConfigurationManager _cfg = default!;
|
[Dependency] private readonly IConfigurationManager _cfg = default!;
|
||||||
[Dependency] private readonly SharedPhysicsSystem _broadPhase = default!;
|
[Dependency] private readonly AudioSystem _audioSystem = default!;
|
||||||
[Dependency] private readonly TransformSystem _transform = default!;
|
|
||||||
|
|
||||||
private float _volume;
|
private float _volume;
|
||||||
private const int TTSCollisionMask = (int)CollisionGroup.Impassable;
|
private readonly Dictionary<EntityUid, AudioComponent> _currentlyPlaying = new();
|
||||||
|
private readonly Dictionary<EntityUid, Queue<AudioStreamWithParams>> _enquedStreams = new();
|
||||||
private readonly HashSet<AudioStream> _currentStreams = new();
|
|
||||||
private readonly Dictionary<EntityUid, Queue<AudioStream>> _entityQueues = new();
|
// Same as Server.ChatSystem.VoiceRange
|
||||||
|
private const float VoiceRange = 10;
|
||||||
|
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
@@ -44,54 +35,35 @@ public sealed class TTSSystem : EntitySystem
|
|||||||
{
|
{
|
||||||
base.Shutdown();
|
base.Shutdown();
|
||||||
_cfg.UnsubValueChanged(WhiteCVars.TtsVolume, OnTtsVolumeChanged);
|
_cfg.UnsubValueChanged(WhiteCVars.TtsVolume, OnTtsVolumeChanged);
|
||||||
EndStreams();
|
ClearQueues();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Little bit of duplication logic from AudioSystem
|
|
||||||
public override void FrameUpdate(float frameTime)
|
public override void FrameUpdate(float frameTime)
|
||||||
{
|
{
|
||||||
var streamToRemove = new HashSet<AudioStream>();
|
foreach (var (uid, audioComponent) in _currentlyPlaying)
|
||||||
|
|
||||||
var ourPos = _eye.CurrentEye.Position.Position;
|
|
||||||
foreach (var stream in _currentStreams)
|
|
||||||
{
|
{
|
||||||
if (!stream.Source.Playing ||
|
if (audioComponent is { Running: true, Playing: true })
|
||||||
!_entity.TryGetComponent<MetaDataComponent>(stream.Uid, out var meta) ||
|
|
||||||
Deleted(stream.Uid, meta) ||
|
|
||||||
!_entity.TryGetComponent<TransformComponent>(stream.Uid, out var xform))
|
|
||||||
{
|
|
||||||
stream.Source.Dispose();
|
|
||||||
streamToRemove.Add(stream);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
var mapPos = _transform.GetMapCoordinates(xform);
|
|
||||||
if (mapPos.MapId != MapId.Nullspace)
|
|
||||||
{
|
|
||||||
stream.Source.Position = mapPos.Position;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mapPos.MapId != _eye.CurrentMap)
|
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
var sourceRelative = ourPos - mapPos.Position;
|
if (!_enquedStreams.TryGetValue(uid, out var queue))
|
||||||
var occlusion = 0f;
|
|
||||||
if (sourceRelative.Length() > 0)
|
|
||||||
{
|
{
|
||||||
occlusion = _broadPhase.IntersectRayPenetration(mapPos.MapId,
|
continue;
|
||||||
new CollisionRay(mapPos.Position, sourceRelative.Normalized(), TTSCollisionMask),
|
|
||||||
sourceRelative.Length(), stream.Uid);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
stream.Source.Occlusion = occlusion;
|
if (!queue.TryDequeue(out var toPlay))
|
||||||
}
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
foreach (var audioStream in streamToRemove)
|
var audio = _audioSystem.PlayEntity(toPlay.Stream, uid, toPlay.Params);
|
||||||
{
|
if (!audio.HasValue)
|
||||||
_currentStreams.Remove(audioStream);
|
{
|
||||||
ProcessEntityQueue(audioStream.Uid);
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
_currentlyPlaying[uid] = audio.Value.Component;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -102,114 +74,76 @@ public sealed class TTSSystem : EntitySystem
|
|||||||
|
|
||||||
private void OnPlayTTS(PlayTTSEvent ev)
|
private void OnPlayTTS(PlayTTSEvent ev)
|
||||||
{
|
{
|
||||||
if (_volume <= -20f)
|
PlayTTS(GetEntity(ev.Uid), ev.Data, ev.BoostVolume ? _volume + 5 : _volume);
|
||||||
return;
|
|
||||||
|
|
||||||
var volume = _volume;
|
|
||||||
if (ev.BoostVolume)
|
|
||||||
volume += 5f;
|
|
||||||
if (!TryCreateAudioSource(ev.Data, out var source, volume))
|
|
||||||
return;
|
|
||||||
|
|
||||||
var stream = new AudioStream(GetEntity(ev.Uid), source);
|
|
||||||
AddEntityStreamToQueue(stream);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void PlayCustomText(string text)
|
public void PlayTTS(EntityUid uid, byte[] data, float volume)
|
||||||
{
|
{
|
||||||
RaiseNetworkEvent(new RequestTTSEvent(text));
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool TryCreateAudioSource(byte[] data, [NotNullWhen(true)] out IAudioSource? source, float volume = 0f)
|
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<AudioStreamWithParams>();
|
||||||
|
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 };
|
var dataStream = new MemoryStream(data) { Position = 0 };
|
||||||
var audioStream = _audioSystem.LoadAudioOggVorbis(dataStream);
|
return _audioManager.LoadAudioOggVorbis(dataStream);
|
||||||
source = _audioSystem.CreateAudioSource(audioStream);
|
|
||||||
if (source == null)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
source.Volume = volume == 0f ? _volume : volume;
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void AddEntityStreamToQueue(AudioStream stream)
|
private record AudioStreamWithParams(AudioStream Stream, AudioParams Params);
|
||||||
{
|
|
||||||
if (_entityQueues.TryGetValue(stream.Uid, out var queue))
|
|
||||||
{
|
|
||||||
queue.Enqueue(stream);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_entityQueues.Add(stream.Uid, new Queue<AudioStream>(new[] { stream }));
|
|
||||||
|
|
||||||
if (!IsEntityCurrentlyPlayStream(stream.Uid))
|
|
||||||
ProcessEntityQueue(stream.Uid);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool IsEntityCurrentlyPlayStream(EntityUid uid)
|
|
||||||
{
|
|
||||||
return _currentStreams.Any(s => s.Uid == uid);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ProcessEntityQueue(EntityUid uid)
|
|
||||||
{
|
|
||||||
if (TryTakeEntityStreamFromQueue(uid, out var stream))
|
|
||||||
PlayEntity(stream);
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool TryTakeEntityStreamFromQueue(EntityUid uid, [NotNullWhen(true)] out AudioStream? stream)
|
|
||||||
{
|
|
||||||
if (_entityQueues.TryGetValue(uid, out var queue))
|
|
||||||
{
|
|
||||||
stream = queue.Dequeue();
|
|
||||||
if (queue.Count == 0)
|
|
||||||
_entityQueues.Remove(uid);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
stream = null;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void PlayEntity(AudioStream stream)
|
|
||||||
{
|
|
||||||
if (!_entity.TryGetComponent<TransformComponent>(stream.Uid, out var xform))
|
|
||||||
return;
|
|
||||||
|
|
||||||
stream.Source.Position = _transform.GetWorldPosition(xform);
|
|
||||||
stream.Source.StartPlaying();
|
|
||||||
_currentStreams.Add(stream);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void StopAllStreams()
|
|
||||||
{
|
|
||||||
foreach (var stream in _currentStreams)
|
|
||||||
{
|
|
||||||
stream.Source.StopPlaying();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void EndStreams()
|
|
||||||
{
|
|
||||||
foreach (var stream in _currentStreams)
|
|
||||||
{
|
|
||||||
stream.Source.StopPlaying();
|
|
||||||
stream.Source.Dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
_currentStreams.Clear();
|
|
||||||
_entityQueues.Clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReSharper disable once InconsistentNaming
|
|
||||||
private sealed class AudioStream(EntityUid uid, IAudioSource source)
|
|
||||||
{
|
|
||||||
public EntityUid Uid { get; } = uid;
|
|
||||||
|
|
||||||
public IAudioSource Source { get; } = source;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -63,7 +63,9 @@ public sealed partial class TTSSystem : EntitySystem
|
|||||||
|
|
||||||
Filter filter;
|
Filter filter;
|
||||||
if (ev.Global)
|
if (ev.Global)
|
||||||
|
{
|
||||||
filter = Filter.Broadcast();
|
filter = Filter.Broadcast();
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var station = _stationSystem.GetOwningStation(ev.Source);
|
var station = _stationSystem.GetOwningStation(ev.Source);
|
||||||
@@ -132,11 +134,7 @@ public sealed partial class TTSSystem : EntitySystem
|
|||||||
|
|
||||||
private async void OnEntitySpoke(EntityUid uid, SharedTTSComponent component, EntitySpokeEvent args)
|
private async void OnEntitySpoke(EntityUid uid, SharedTTSComponent component, EntitySpokeEvent args)
|
||||||
{
|
{
|
||||||
if (!_isEnabled ||
|
if (!_isEnabled || string.IsNullOrEmpty(_apiUrl) || args.Message.Length > MaxMessageChars)
|
||||||
args.Message.Length > MaxMessageChars)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(_apiUrl))
|
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -154,6 +152,7 @@ public sealed partial class TTSSystem : EntitySystem
|
|||||||
var soundData = await GenerateTTS(uid, message, protoVoice.Speaker);
|
var soundData = await GenerateTTS(uid, message, protoVoice.Speaker);
|
||||||
if (soundData is null)
|
if (soundData is null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var ttsEvent = new PlayTTSEvent(GetNetEntity(uid), soundData, false);
|
var ttsEvent = new PlayTTSEvent(GetNetEntity(uid), soundData, false);
|
||||||
|
|
||||||
// Say
|
// Say
|
||||||
@@ -182,7 +181,6 @@ public sealed partial class TTSSystem : EntitySystem
|
|||||||
var sourcePos = _xforms.GetWorldPosition(xformQuery.GetComponent(uid), xformQuery);
|
var sourcePos = _xforms.GetWorldPosition(xformQuery.GetComponent(uid), xformQuery);
|
||||||
var receptions = Filter.Pvs(uid).Recipients;
|
var receptions = Filter.Pvs(uid).Recipients;
|
||||||
|
|
||||||
|
|
||||||
foreach (var session in receptions)
|
foreach (var session in receptions)
|
||||||
{
|
{
|
||||||
if (!session.AttachedEntity.HasValue)
|
if (!session.AttachedEntity.HasValue)
|
||||||
|
|||||||
Reference in New Issue
Block a user