fix: ТТС снова выдает правильные голоса

This commit is contained in:
Remuchi
2024-04-17 11:36:02 +07:00
parent fbb957d6d5
commit 44b9c3d723
16 changed files with 147 additions and 144 deletions

View File

@@ -972,7 +972,8 @@ public sealed partial class ChangelingSystem
ClonePerson(polymorphEntity.Value, transformData.AppearanceComponent, polyAppearance);
TransferDna(polymorphEntity.Value, transformData.Dna);
_humanoidAppearance.SetTTSVoice(polymorphEntity.Value, transformData.AppearanceComponent.Voice, polyAppearance);
_humanoidAppearance.SetTTSVoice(polymorphEntity.Value, transformData.AppearanceComponent.Voice,
humanoid: polyAppearance);
if (!TryComp<MetaDataComponent>(polymorphEntity.Value, out var meta))
return null;

View File

@@ -40,15 +40,15 @@ public sealed partial class HumanoidAppearanceSystem : SharedHumanoidAppearanceS
return;
}
targetHumanoid.Species = sourceHumanoid.Species;
targetHumanoid.SkinColor = sourceHumanoid.SkinColor;
SetSpecies(target, sourceHumanoid.Species, false, targetHumanoid);
SetSkinColor(target, sourceHumanoid.SkinColor, false, true, targetHumanoid);
targetHumanoid.EyeColor = sourceHumanoid.EyeColor;
targetHumanoid.Age = sourceHumanoid.Age;
SetSex(target, sourceHumanoid.Sex, false, targetHumanoid);
targetHumanoid.CustomBaseLayers = new(sourceHumanoid.CustomBaseLayers);
targetHumanoid.MarkingSet = new(sourceHumanoid.MarkingSet);
targetHumanoid.BodyType = sourceHumanoid.BodyType;
SetTTSVoice(target, sourceHumanoid.Voice, targetHumanoid);
SetSex(target, sourceHumanoid.Sex, false, targetHumanoid);
SetBodyType(target, sourceHumanoid.BodyType, false, targetHumanoid);
SetTTSVoice(target, sourceHumanoid.Voice, false, targetHumanoid);
targetHumanoid.Gender = sourceHumanoid.Gender;
if (TryComp<GrammarComponent>(target, out var grammar))

View File

@@ -1,17 +0,0 @@
using Content.Server._White.TTS;
using Content.Shared.Humanoid;
namespace Content.Server.Humanoid;
public sealed partial class HumanoidAppearanceSystem
{
// ReSharper disable once InconsistentNaming
public void SetTTSVoice(EntityUid uid, string voiceId, HumanoidAppearanceComponent humanoid)
{
if (!TryComp<TTSComponent>(uid, out var comp))
return;
humanoid.Voice = voiceId;
comp.VoicePrototypeId = voiceId;
}
}

View File

@@ -1,17 +1,11 @@
namespace Content.Server._White.TTS;
public sealed class TTSAnnouncementEvent : EntityEventArgs
// ReSharper disable once InconsistentNaming
public sealed class TTSAnnouncementEvent(string message, string voiceId, EntityUid source, bool global)
: EntityEventArgs
{
public readonly string Message;
public readonly bool Global;
public readonly string VoiceId;
public readonly EntityUid Source;
public TTSAnnouncementEvent(string message, string voiceId, EntityUid source , bool global)
{
Message = message;
Global = global;
VoiceId = voiceId;
Source = source;
}
}
public readonly string Message = message;
public readonly bool Global = global;
public readonly string VoiceId = voiceId;
public readonly EntityUid Source = source;
}

View File

@@ -1,19 +0,0 @@
using Content.Shared._White.TTS;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
namespace Content.Server._White.TTS;
/// <summary>
/// Apply TTS for entity chat say messages
/// </summary>
[RegisterComponent]
// ReSharper disable once InconsistentNaming
public sealed partial class TTSComponent : Component
{
/// <summary>
/// Prototype of used voice for TTS.
/// </summary>
[ViewVariables(VVAccess.ReadWrite)]
[DataField("voice", customTypeSerializer:typeof(PrototypeIdSerializer<TTSVoicePrototype>))]
public string VoicePrototypeId { get; set; } = "Eugene";
}

View File

@@ -5,7 +5,6 @@ using System.Text.Json.Serialization;
using System.Threading;
using System.Threading.Tasks;
using System.Web;
using Content.Shared.CCVar;
using Content.Shared._White;
using Prometheus;
using Robust.Shared.Configuration;
@@ -18,9 +17,9 @@ public sealed class TTSManager
private static readonly Histogram RequestTimings = Metrics.CreateHistogram(
"tts_req_timings",
"Timings of TTS API requests",
new HistogramConfiguration()
new HistogramConfiguration
{
LabelNames = new[] {"type"},
LabelNames = new[] { "type" },
Buckets = Histogram.ExponentialBuckets(.1, 1.5, 10),
});
@@ -55,7 +54,12 @@ public sealed class TTSManager
/// <param name="text">SSML formatted text</param>
/// <returns>OGG audio bytes</returns>
/// <exception cref="Exception">Throws if url or token CCVar not set or http request failed</exception>
public async Task<byte[]?> ConvertTextToSpeech(string speaker, string text, string pitch, string rate, string? effect = null)
public async Task<byte[]?> ConvertTextToSpeech(
string speaker,
string text,
string pitch,
string rate,
string? effect = null)
{
var url = _cfg.GetCVar(WhiteCVars.TtsApiUrl);
var maxCacheSize = _cfg.GetCVar(WhiteCVars.TtsMaxCacheSize);
@@ -96,7 +100,7 @@ public sealed class TTSManager
var soundData = await response.Content.ReadAsByteArrayAsync(cts.Token);
if(_cache.Count > maxCacheSize)
if (_cache.Count > maxCacheSize)
{
_cache.Remove(_cache.Last().Key);
}
@@ -104,7 +108,9 @@ public sealed class TTSManager
_cache.Add(cacheKey, soundData);
CachedCount.Inc();
_sawmill.Debug($"Generated new sound for '{text}' speech by '{speaker}' speaker ({soundData.Length} bytes)");
_sawmill.Debug(
$"Generated new sound for '{text}' speech by '{speaker}' speaker ({soundData.Length} bytes)");
RequestTimings.WithLabels("Success").Observe((DateTime.UtcNow - reqTime).TotalSeconds);
return soundData;
@@ -149,7 +155,7 @@ public sealed class TTSManager
private string GenerateCacheKey(string speaker, string text)
{
var key = $"{speaker}/{text}";
byte[] keyData = Encoding.UTF8.GetBytes(key);
var keyData = Encoding.UTF8.GetBytes(key);
var sha256 = System.Security.Cryptography.SHA256.Create();
var bytes = sha256.ComputeHash(keyData);
return Convert.ToHexString(bytes);
@@ -187,4 +193,4 @@ public sealed class TTSManager
[JsonPropertyName("audio")]
public string Audio { get; set; }
}
}
}

View File

@@ -8,7 +8,6 @@ using Content.Server.Station.Systems;
using Content.Shared._White.TTS;
using Content.Shared.GameTicking;
using Content.Shared._White;
using Robust.Server.Audio;
using Robust.Server.Player;
using Robust.Shared.Configuration;
using Robust.Shared.Network;
@@ -31,7 +30,6 @@ public sealed partial class TTSSystem : EntitySystem
[Dependency] private readonly IPlayerManager _playerManager = default!;
[Dependency] private readonly TTSPitchRateSystem _ttsPitchRateSystem = default!;
[Dependency] private readonly StationSystem _stationSystem = default!;
[Dependency] private readonly AudioSystem _audio = default!;
[Dependency] private readonly EntityLookupSystem _lookup = default!;
private const int MaxMessageChars = 100 * 2; // same as SingleBubbleCharLimit * 2
@@ -44,7 +42,7 @@ public sealed partial class TTSSystem : EntitySystem
_cfg.OnValueChanged(WhiteCVars.TtsApiUrl, url => _apiUrl = url, true);
SubscribeLocalEvent<TransformSpeechEvent>(OnTransformSpeech);
SubscribeLocalEvent<TTSComponent, EntitySpokeEvent>(OnEntitySpoke);
SubscribeLocalEvent<SharedTTSComponent, EntitySpokeEvent>(OnEntitySpoke);
SubscribeLocalEvent<RoundRestartCleanupEvent>(OnRoundRestartCleanup);
SubscribeLocalEvent<TTSAnnouncementEvent>(OnAnnounceRequest);
@@ -80,37 +78,37 @@ public sealed partial class TTSSystem : EntitySystem
foreach (var player in filter.Recipients)
{
if (player.AttachedEntity != null)
if (player.AttachedEntity == null)
continue;
// Get emergency lights in range to broadcast from
var entities = _lookup.GetEntitiesInRange(player.AttachedEntity.Value, 30f)
.Where(HasComp<EmergencyLightComponent>)
.ToList();
if (entities.Count == 0)
return;
// Get closest emergency light
var entity = entities.First();
var range = new Vector2(100f);
foreach (var item in entities)
{
// Get emergency lights in range to broadcast from
var entities = _lookup.GetEntitiesInRange(player.AttachedEntity.Value, 30f)
.Where(HasComp<EmergencyLightComponent>)
.ToList();
var itemSource = _xforms.GetWorldPosition(Transform(item));
var playerSource = _xforms.GetWorldPosition(Transform(player.AttachedEntity.Value));
if (entities.Count == 0)
return;
var distance = playerSource - itemSource;
// Get closest emergency light
var entity = entities.First();
var range = new Vector2(100f);
foreach (var item in entities)
if (range.Length() > distance.Length())
{
var itemSource = _xforms.GetWorldPosition(Transform(item));
var playerSource = _xforms.GetWorldPosition(Transform(player.AttachedEntity.Value));
var distance = playerSource - itemSource;
if (range.Length() > distance.Length())
{
range = distance;
entity = item;
}
range = distance;
entity = item;
}
RaiseNetworkEvent(new PlayTTSEvent(GetNetEntity(entity), soundData, true), Filter.SinglePlayer(player),
false);
}
RaiseNetworkEvent(new PlayTTSEvent(GetNetEntity(entity), soundData, true), Filter.SinglePlayer(player),
false);
}
}
@@ -121,7 +119,7 @@ public sealed partial class TTSSystem : EntitySystem
return;
if (!_playerManager.TryGetSessionByChannel(ev.MsgChannel, out var session) ||
!_prototypeManager.TryIndex<TTSVoicePrototype>(ev.VoiceId, out var protoVoice))
!_prototypeManager.TryIndex(ev.VoiceId, out var protoVoice))
return;
var soundData = await GenerateTTS(ev.Uid, ev.Text, protoVoice.Speaker);
@@ -129,7 +127,7 @@ public sealed partial class TTSSystem : EntitySystem
RaiseNetworkEvent(new PlayTTSEvent(GetNetEntity(ev.Uid), soundData, false), Filter.SinglePlayer(session), false);
}
private async void OnEntitySpoke(EntityUid uid, TTSComponent component, EntitySpokeEvent args)
private async void OnEntitySpoke(EntityUid uid, SharedTTSComponent component, EntitySpokeEvent args)
{
if (!_isEnabled ||
args.Message.Length > MaxMessageChars)
@@ -145,7 +143,7 @@ public sealed partial class TTSSystem : EntitySystem
RaiseLocalEvent(uid, voiceEv);
voiceId = voiceEv.VoiceId;
if (!_prototypeManager.TryIndex<TTSVoicePrototype>(voiceId, out var protoVoice))
if (!_prototypeManager.TryIndex(voiceId, out var protoVoice))
return;
var message = FormattedMessage.RemoveMarkup(args.Message);