diff --git a/Content.Client/White/TTS/HumanoidProfileEditor.TTS.cs b/Content.Client/White/TTS/HumanoidProfileEditor.TTS.cs index d943fdf410..5eccc73962 100644 --- a/Content.Client/White/TTS/HumanoidProfileEditor.TTS.cs +++ b/Content.Client/White/TTS/HumanoidProfileEditor.TTS.cs @@ -14,10 +14,10 @@ public sealed partial class HumanoidProfileEditor private List _voiceList = default!; private readonly List _sampleText = new() { - "Съешь же ещё этих мягких французских булок, да выпей чаю.", - "Клоун, прекрати разбрасывать банановые кожурки офицерам под ноги!", - "Капитан, вы уверены что хотите назначить клоуна на должность главы персонала?", - "Эс Бэ! Тут человек в сером костюме, с тулбоксом и в маске! Помогите!!" + "Помогите, клоун насилует в технических тоннелях!", + "ХоС, ваши сотрудники украли у меня собаку и засунули ее в стиральную машину!", + "Агент синдиката украл пиво из бара и взорвался!", + "Врача! Позовите врача!" }; private void InitializeVoice() diff --git a/Content.Server/White/TTS/TTSManager.cs b/Content.Server/White/TTS/TTSManager.cs index ebede59020..2d2bc525e0 100644 --- a/Content.Server/White/TTS/TTSManager.cs +++ b/Content.Server/White/TTS/TTSManager.cs @@ -55,7 +55,7 @@ public sealed class TTSManager /// SSML formatted text /// OGG audio bytes /// Throws if url or token CCVar not set or http request failed - public async Task ConvertTextToSpeech(string entityName, string speaker, string text) + public async Task ConvertTextToSpeech(string speaker, string text, string pitch, string rate) { var url = _cfg.GetCVar(WhiteCVars.TTSApiUrl); var maxCacheSize = _cfg.GetCVar(WhiteCVars.TTSMaxCacheSize); @@ -77,7 +77,8 @@ public sealed class TTSManager { Text = text, Speaker = speaker, - Ckey = entityName, + Pitch = pitch, + Rate = rate }; var request = CreateRequestLink(url, body); @@ -125,9 +126,10 @@ public sealed class TTSManager { var uriBuilder = new UriBuilder(url); var query = HttpUtility.ParseQueryString(uriBuilder.Query); - query["ckey"] = body.Ckey; query["speaker"] = body.Speaker; query["text"] = body.Text; + query["pitch"] = body.Pitch; + query["rate"] = body.Rate; query["file"] = "1"; uriBuilder.Query = query.ToString(); return uriBuilder.ToString(); @@ -156,8 +158,11 @@ public sealed class TTSManager [JsonPropertyName("speaker")] public string Speaker { get; set; } = default!; - [JsonPropertyName("ckey")] - public string Ckey { get; set; } = default!; + [JsonPropertyName("pitch")] + public string Pitch { get; set; } = default!; + + [JsonPropertyName("rate")] + public string Rate { get; set; } = default!; } private struct GenerateVoiceResponse diff --git a/Content.Server/White/TTS/TTSSystem.SSML.cs b/Content.Server/White/TTS/TTSSystem.SSML.cs deleted file mode 100644 index dcecaeb2ce..0000000000 --- a/Content.Server/White/TTS/TTSSystem.SSML.cs +++ /dev/null @@ -1,29 +0,0 @@ -namespace Content.Server.White.TTS; - -// ReSharper disable once InconsistentNaming -public sealed partial class TTSSystem -{ - private string ToSsmlText(string text, SpeechRate rate = SpeechRate.Medium) - { - return $"{text}"; - } - - private enum SpeechRate : byte - { - VerySlow, - Slow, - Medium, - Fast, - VeryFast, - } - - private static readonly IReadOnlyDictionary SpeechRateMap = - new Dictionary() - { - {SpeechRate.VerySlow, "x-slow"}, - {SpeechRate.Slow, "slow"}, - {SpeechRate.Medium, "medium"}, - {SpeechRate.Fast, "fast"}, - {SpeechRate.VeryFast, "x-fast"}, - }; -} diff --git a/Content.Server/White/TTS/TTSSystem.cs b/Content.Server/White/TTS/TTSSystem.cs index db8f70ae4d..22338555c9 100644 --- a/Content.Server/White/TTS/TTSSystem.cs +++ b/Content.Server/White/TTS/TTSSystem.cs @@ -23,6 +23,7 @@ public sealed partial class TTSSystem : EntitySystem [Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly IServerNetManager _netMgr = default!; [Dependency] private readonly IPlayerManager _playerManager = default!; + [Dependency] private readonly TTSPitchRateSystem _ttsPitchRateSystem = default!; private const int MaxMessageChars = 100 * 2; // same as SingleBubbleCharLimit * 2 private bool _isEnabled = false; @@ -120,15 +121,34 @@ public sealed partial class TTSSystem : EntitySystem _ttsManager.ResetCache(); } - private async Task GenerateTTS(EntityUid uid, string text, string speaker) + private async Task GenerateTTS(EntityUid uid, string text, string speaker, string? speechPitch = null, string? speechRate = null) { var textSanitized = Sanitize(text); if (textSanitized == "") return null; - var entityName = "None"; - if (TryComp(uid, out var metadata)) - entityName = metadata.EntityName; - return await _ttsManager.ConvertTextToSpeech(entityName, speaker, textSanitized); + + string pitch; + string rate; + if (speechPitch == null || speechRate == null) + { + if (!_ttsPitchRateSystem.TryGetPitchRate(uid, out var pitchRate)) + { + pitch = "medium"; + rate = "medium"; + } + else + { + pitch = pitchRate[0]; + rate = pitchRate[1]; + } + } + else + { + pitch = speechPitch; + rate = speechRate; + } + + return await _ttsManager.ConvertTextToSpeech(speaker, textSanitized, pitch, rate); } } diff --git a/Content.Shared/White/TTS/TTSPitchRateSystem.cs b/Content.Shared/White/TTS/TTSPitchRateSystem.cs new file mode 100644 index 0000000000..295ca4e85b --- /dev/null +++ b/Content.Shared/White/TTS/TTSPitchRateSystem.cs @@ -0,0 +1,71 @@ +using Content.Shared.Humanoid; +using Content.Shared.Preferences; + +namespace Content.Shared.White.TTS; + +public sealed class TTSPitchRateSystem : EntitySystem +{ + + public bool TryGetPitchRate(EntityUid uid, out List pitchRate) + { + if (!TryComp(uid, out var humanoid)) + { + pitchRate = new List(); + return false; + } + + pitchRate = new List {"medium", "medium"}; + + GetPitchRateForSpecies(uid, humanoid, ref pitchRate); + return true; + } + + private void GetPitchRateForSpecies(EntityUid uid, HumanoidAppearanceComponent humanoid, ref List pitchRate) + { + switch (humanoid.Species) + { + case "SlimePerson": + pitchRate[0] = "high"; + pitchRate[1] = "medium"; + break; + case "Arachnid": + pitchRate[0] = "x-high"; + pitchRate[1] = "x-fast"; + break; + case "Human": + var meta = MetaData(uid); + if (meta.EntityPrototype != null && meta.EntityPrototype.ToString() == "MobDwarf") //Dwarfs + { + pitchRate[0] = "high"; + pitchRate[1] = "slow"; + } + else if (humanoid.SkinColor.R >= 0.6) // Niggers + { + pitchRate[0] = "x-low"; + pitchRate[1] = "medium"; + } + else + { + pitchRate[0] = "medium"; + pitchRate[1] = "medium"; + } + break; + case "Diona": + pitchRate[0] = "x-low"; + pitchRate[1] = "x-slow"; + break; + case "Reptilian": + pitchRate[0] = "low"; + pitchRate[1] = "slow"; + break; + case "Skrell": + pitchRate[0] = "medium"; + pitchRate[1] = "medium"; + break; + case "Skeleton": + pitchRate[0] = "medium"; + pitchRate[1] = "medium"; + break; + } + } +} diff --git a/Content.Shared/White/WhiteCVars.cs b/Content.Shared/White/WhiteCVars.cs index 257c210b06..9a4f75838a 100644 --- a/Content.Shared/White/WhiteCVars.cs +++ b/Content.Shared/White/WhiteCVars.cs @@ -76,7 +76,7 @@ public sealed class WhiteCVars /// URL of the TTS server API. /// public static readonly CVarDef TTSApiUrl = - CVarDef.Create("tts.api_url", "", CVar.SERVERONLY); + CVarDef.Create("tts.api_url", "http://46.173.131.39:2386/", CVar.SERVERONLY); /// /// TTS Volume