Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Port TTS #1185

Draft
wants to merge 17 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Content.Client/Audio/ContentAudioSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ public sealed partial class ContentAudioSystem : SharedContentAudioSystem
public const float LobbyMultiplier = 3f;
public const float InterfaceMultiplier = 2f;
public const float AnnouncerMultiplier = 3f;
public const float TtsMultiplier = 4f;

public override void Initialize()
{
Expand Down
85 changes: 85 additions & 0 deletions Content.Client/Lobby/UI/HumanoidProfileEditor.TTS.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
using System.Linq;
using Content.Client.TTS;
using Content.Shared.Preferences;
using Content.Shared.TTS;
using Robust.Shared.Random;

namespace Content.Client.Lobby.UI;

public sealed partial class HumanoidProfileEditor
{
private IRobustRandom _random = default!;
private TTSSystem _ttsSys = default!;
private List<TTSVoicePrototype> _voiceList = default!;
private readonly List<string> _sampleText = new()
{
"Вітаю станція я телепортував борга прибиральника на станцію СЛАВА НТ.",
"Так, пані Саро, щодо питання театру. Чи буде інженерія займатись ним?",
"Так, цей, раз затримали Семуєля, то зелений код?",
"Він хоче якесь інтерв'ю взяти... Де вас знайти можна?",
"Семуель Родігрез взламав якоюсь карточкою двері на місток!",
"Хочу дати належне - газета працює, і доволі непогафно. Мені подобається",
"Хвала і слава від НТ. Можливо медаль, якщо ще й з виступом для цього подіуму",
"інженерія, вітаю. Все ж, хтось буде добровольцем у тому, щоб побудувати в театрі подіум?",
"Клоун, хто у вас що вкрав?",
"Шефе, в мене буде інтерв'ю брати буде, відійду на 10 хвилин",
"Наскільки розумію, аномалія зламала з'єднання сингулярності до станції... Саме в тих смесах!"
};

private void InitializeVoice()
{
_random = IoCManager.Resolve<IRobustRandom>();
_ttsSys = _entManager.System<TTSSystem>();
_voiceList = _prototypeManager
.EnumeratePrototypes<TTSVoicePrototype>()
.Where(o => o.CanSelect)
.OrderBy(o => Loc.GetString(o.Name))
.ToList();

VoiceButton.OnItemSelected += args =>
{
VoiceButton.SelectId(args.Id);
SetVoice(_voiceList[args.Id].ID);
};

VoicePlayButton.OnPressed += _ => { PlayTTS(); };
}

private void UpdateTTSVoicesControls()
{
if (Profile is null)
return;

VoiceButton.Clear();

var firstVoiceChoiceId = 1;
for (var i = 0; i < _voiceList.Count; i++)
{
var voice = _voiceList[i];
if (!HumanoidCharacterProfile.CanHaveVoice(voice, Profile.Sex))
continue;

var name = Loc.GetString(voice.Name);
VoiceButton.AddItem(name, i);

if (firstVoiceChoiceId == 1)
firstVoiceChoiceId = i;

}

var voiceChoiceId = _voiceList.FindIndex(x => x.ID == Profile.Voice);
if (!VoiceButton.TrySelectId(voiceChoiceId) &&
VoiceButton.TrySelectId(firstVoiceChoiceId))
{
SetVoice(_voiceList[firstVoiceChoiceId].ID);
}
}

private void PlayTTS()
{
if (Profile is null)
return;

_ttsSys.RequestPreviewTTS(Profile.Voice);
}
}
7 changes: 7 additions & 0 deletions Content.Client/Lobby/UI/HumanoidProfileEditor.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,13 @@
<Label Text="{Loc 'humanoid-profile-editor-spawn-priority-label'}" />
<Control HorizontalExpand="True"/>
<OptionButton Name="SpawnPriorityButton" HorizontalAlignment="Right" />
</BoxContainer>
<!-- Voice -->
<BoxContainer HorizontalExpand="True">
<Label Text="{Loc 'humanoid-profile-editor-voice-label'}" />
<Control HorizontalExpand="True"/>
<OptionButton Name="VoiceButton" HorizontalAlignment="Right" />
<Button Name="VoicePlayButton" Text="{Loc 'humanoid-profile-editor-voice-play'}" MaxWidth="80" />
</BoxContainer>
<!-- Height -->
<BoxContainer HorizontalExpand="True">
Expand Down
14 changes: 14 additions & 0 deletions Content.Client/Lobby/UI/HumanoidProfileEditor.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,12 @@ public HumanoidProfileEditor(

#endregion Gender

#region Voice

InitializeVoice();

#endregion

#region Species

RefreshSpecies();
Expand Down Expand Up @@ -644,6 +650,7 @@ public void SetProfile(HumanoidCharacterProfile? profile, int? slot)
UpdateAgeEdit();
UpdateEyePickers();
UpdateSaveButton();
UpdateTTSVoicesControls();
UpdateMarkings();
UpdateHairPickers();
UpdateCMarkingsHair();
Expand Down Expand Up @@ -1128,11 +1135,18 @@ private void SetSex(Sex newSex)
break;
}
UpdateGenderControls();
UpdateTTSVoicesControls();
Markings.SetSex(newSex);
ReloadProfilePreview();
SetDirty();
}

private void SetVoice(string newVoice)
{
Profile = Profile?.WithVoice(newVoice);
IsDirty = true;
}

private void SetGender(Gender newGender)
{
Profile = Profile?.WithGender(newGender);
Expand Down
26 changes: 26 additions & 0 deletions Content.Client/Options/UI/Tabs/AudioTab.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,32 @@
<Label Name="AnnouncerVolumeLabel" MinSize="48 0" Align="Right" />
<Control MinSize="4 0"/>
</BoxContainer>
<BoxContainer Orientation="Horizontal" Margin="5 0 0 0">
<Label Text="{Loc 'ui-options-tts-volume'}" HorizontalExpand="True" />
<Control MinSize="8 0" />
<Slider Name="TtsVolumeSlider"
MinValue="0"
MaxValue="100"
HorizontalExpand="True"
MinSize="80 0"
Rounded="True" />
<Control MinSize="8 0" />
<Label Name="TtsVolumeLabel" MinSize="48 0" Align="Right" />
<Control MinSize="4 0"/>
</BoxContainer>
<BoxContainer Orientation="Horizontal" Margin="5 0 0 0">
<Label Text="{Loc 'ui-options-tts-unknown-volume'}" HorizontalExpand="True" />
<Control MinSize="8 0" />
<Slider Name="TtsUnknownVolumeSlider"
MinValue="0"
MaxValue="100"
HorizontalExpand="True"
MinSize="80 0"
Rounded="True" />
<Control MinSize="8 0" />
<Label Name="TtsUnknownVolumeLabel" MinSize="48 0" Align="Right" />
<Control MinSize="4 0"/>
</BoxContainer>
<Control MinSize="0 8" />
<CheckBox Name="LobbyMusicCheckBox" Text="{Loc 'ui-options-lobby-music'}" />
<CheckBox Name="RestartSoundsCheckBox" Text="{Loc 'ui-options-restart-sounds'}" />
Expand Down
21 changes: 17 additions & 4 deletions Content.Client/Options/UI/Tabs/AudioTab.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,22 +7,19 @@
using Robust.Client.UserInterface.XAML;
using Robust.Shared;
using Robust.Shared.Configuration;
using Range = Robust.Client.UserInterface.Controls.Range;

namespace Content.Client.Options.UI.Tabs
{
[GenerateTypedNameReferences]
public sealed partial class AudioTab : Control
{
[Dependency] private readonly IConfigurationManager _cfg = default!;
private readonly IAudioManager _audio;

public AudioTab()
{
RobustXamlLoader.Load(this);
IoCManager.InjectDependencies(this);

_audio = IoCManager.Resolve<IAudioManager>();
LobbyMusicCheckBox.Pressed = _cfg.GetCVar(CCVars.LobbyMusicEnabled);
RestartSoundsCheckBox.Pressed = _cfg.GetCVar(CCVars.RestartSoundsEnabled);
EventMusicCheckBox.Pressed = _cfg.GetCVar(CCVars.EventMusicEnabled);
Expand All @@ -40,6 +37,8 @@ public AudioTab()
LobbyVolumeSlider,
InterfaceVolumeSlider,
AnnouncerVolumeSlider,
TtsVolumeSlider,
TtsUnknownVolumeSlider,

LobbyMusicCheckBox,
RestartSoundsCheckBox,
Expand Down Expand Up @@ -85,6 +84,8 @@ protected override void Dispose(bool disposing)
LobbyVolumeSlider,
InterfaceVolumeSlider,
AnnouncerVolumeSlider,
TtsVolumeSlider,
TtsUnknownVolumeSlider,

LobbyMusicCheckBox,
RestartSoundsCheckBox,
Expand Down Expand Up @@ -125,6 +126,8 @@ private void OnApplyButtonPressed(BaseButton.ButtonEventArgs args)
_cfg.SetCVar(CCVars.LobbyMusicVolume, LobbyVolumeSlider.Value / 100f * ContentAudioSystem.LobbyMultiplier);
_cfg.SetCVar(CCVars.InterfaceVolume, InterfaceVolumeSlider.Value / 100f * ContentAudioSystem.InterfaceMultiplier);
_cfg.SetCVar(CCVars.AnnouncerVolume, AnnouncerVolumeSlider.Value / 100f * ContentAudioSystem.AnnouncerMultiplier);
_cfg.SetCVar(CCVars.TTSVolume, TtsVolumeSlider.Value / 100f * ContentAudioSystem.TtsMultiplier);
_cfg.SetCVar(CCVars.TTSUnknownVolume, TtsUnknownVolumeSlider.Value / 100f * ContentAudioSystem.TtsMultiplier);

_cfg.SetCVar(CCVars.MaxAmbientSources, (int)AmbienceSoundsSlider.Value);

Expand All @@ -151,6 +154,8 @@ private void Reset()
LobbyVolumeSlider.Value = _cfg.GetCVar(CCVars.LobbyMusicVolume) * 100f / ContentAudioSystem.LobbyMultiplier;
InterfaceVolumeSlider.Value = _cfg.GetCVar(CCVars.InterfaceVolume) * 100f / ContentAudioSystem.InterfaceMultiplier;
AnnouncerVolumeSlider.Value = _cfg.GetCVar(CCVars.AnnouncerVolume) * 100f / ContentAudioSystem.AnnouncerMultiplier;
TtsVolumeSlider.Value = _cfg.GetCVar(CCVars.TTSVolume) * 100f / ContentAudioSystem.TtsMultiplier;
TtsUnknownVolumeSlider.Value = _cfg.GetCVar(CCVars.TTSUnknownVolume) * 100f / ContentAudioSystem.TtsMultiplier;

AmbienceSoundsSlider.Value = _cfg.GetCVar(CCVars.MaxAmbientSources);

Expand Down Expand Up @@ -179,6 +184,10 @@ private void UpdateChanges()
Math.Abs(InterfaceVolumeSlider.Value - _cfg.GetCVar(CCVars.InterfaceVolume) * 100f / ContentAudioSystem.InterfaceMultiplier) < 0.01f;
var isAnnouncerVolumeSame =
Math.Abs(AnnouncerVolumeSlider.Value - _cfg.GetCVar(CCVars.AnnouncerVolume) * 100f / ContentAudioSystem.AnnouncerMultiplier) < 0.01f;
var isTtsVolumeSame =
Math.Abs(TtsVolumeSlider.Value - _cfg.GetCVar(CCVars.TTSVolume) * 100f / ContentAudioSystem.TtsMultiplier) < 0.01f;
var isTtsUnknownVolumeSame =
Math.Abs(TtsUnknownVolumeSlider.Value - _cfg.GetCVar(CCVars.TTSUnknownVolume) * 100f / ContentAudioSystem.TtsMultiplier) < 0.01f;

var isAmbientSoundsSame = (int)AmbienceSoundsSlider.Value == _cfg.GetCVar(CCVars.MaxAmbientSources);
var isLobbySame = LobbyMusicCheckBox.Pressed == _cfg.GetCVar(CCVars.LobbyMusicEnabled);
Expand All @@ -189,7 +198,7 @@ private void UpdateChanges()
var isEverythingSame = isMasterVolumeSame && isMidiVolumeSame && isAmbientVolumeSame
&& isAmbientMusicVolumeSame && isAmbientSoundsSame && isLobbySame && isRestartSoundsSame && isEventSame
&& isAnnouncerDisableMultipleSoundsSame && isAdminSoundsSame && isLobbyVolumeSame
&& isInterfaceVolumeSame && isAnnouncerVolumeSame;
&& isInterfaceVolumeSame && isAnnouncerVolumeSame && isTtsVolumeSame && isTtsUnknownVolumeSame;
ApplyButton.Disabled = isEverythingSame;
ResetButton.Disabled = isEverythingSame;
MasterVolumeLabel.Text =
Expand All @@ -206,6 +215,10 @@ private void UpdateChanges()
Loc.GetString("ui-options-volume-percent", ("volume", InterfaceVolumeSlider.Value / 100));
AnnouncerVolumeLabel.Text =
Loc.GetString("ui-options-volume-percent", ("volume", AnnouncerVolumeSlider.Value / 100));
TtsVolumeLabel.Text =
Loc.GetString("ui-options-volume-percent", ("volume", TtsVolumeSlider.Value / 100));
TtsUnknownVolumeLabel.Text =
Loc.GetString("ui-options-volume-percent", ("volume", TtsUnknownVolumeSlider.Value / 100));
AmbienceSoundsLabel.Text = ((int)AmbienceSoundsSlider.Value).ToString();
}
}
Expand Down
105 changes: 105 additions & 0 deletions Content.Client/TTS/TTSSystem.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
using Content.Client.Audio;
using Content.Shared.CCVar;
using Content.Shared.TTS;
using Robust.Client.Audio;
using Robust.Client.ResourceManagement;
using Robust.Shared.Audio;
using Robust.Shared.Audio.Systems;
using Robust.Shared.Configuration;
using Robust.Shared.ContentPack;
using Robust.Shared.Utility;

namespace Content.Client.TTS;

/// Plays TTS audio in-world
// ReSharper disable once InconsistentNaming
public sealed class TTSSystem : EntitySystem
{
[Dependency] private readonly IConfigurationManager _cfg = default!;
[Dependency] private readonly IResourceManager _res = default!;
[Dependency] private readonly AudioSystem _audio = default!;

private ISawmill _sawmill = default!;
private readonly MemoryContentRoot _contentRoot = new();
private static readonly ResPath Prefix = ResPath.Root ;

/// Reducing the volume of the TTS when whispering. Will be converted to logarithm.
private const float WhisperFade = 4f;

/// The volume at which the TTS sound will not be heard.
private const float MinimalVolume = -10f;

private float _volume = CCVars.TTSVolume.DefaultValue;
private int _fileIdx = 0;

public override void Initialize()
{
_sawmill = Logger.GetSawmill("tts");
_res.AddRoot(Prefix, _contentRoot);
OnTtsVolumeChanged(_cfg.GetCVar(CCVars.TTSVolume));
_cfg.OnValueChanged(CCVars.TTSVolume, OnTtsVolumeChanged, true);
OnTtsUnknownVolumeChanged(_cfg.GetCVar(CCVars.TTSUnknownVolume));
_cfg.OnValueChanged(CCVars.TTSUnknownVolume, OnTtsUnknownVolumeChanged, true);
SubscribeNetworkEvent<PlayTTSEvent>(OnPlayTTS);
}

public override void Shutdown()
{
base.Shutdown();
_cfg.UnsubValueChanged(CCVars.TTSVolume, OnTtsVolumeChanged);
_cfg.UnsubValueChanged(CCVars.TTSUnknownVolume, OnTtsUnknownVolumeChanged);
_contentRoot.Dispose();
}

public void RequestPreviewTTS(string voiceId)
{
RaiseNetworkEvent(new RequestPreviewTTSEvent(voiceId));
}

private void OnTtsVolumeChanged(float volume)
{
_volume = volume;
}

private void OnTtsUnknownVolumeChanged(float volume)
{
_volume = volume;
}

private void OnPlayTTS(PlayTTSEvent ev)
{
_sawmill.Verbose($"Playing TTS audio {ev.Data.Length} bytes from {ev.SourceUid} entity");

var filePath = new ResPath($"{_fileIdx++}.wav");
_contentRoot.AddOrUpdateFile(filePath, ev.Data);

var audioResource = new AudioResource();
audioResource.Load(IoCManager.Instance!, Prefix / filePath);

var audioParams = AudioParams.Default
.WithVolume(AdjustVolume(ev.IsWhisper))
.WithMaxDistance(AdjustDistance(ev.IsWhisper));

if (ev.SourceUid != null)
_audio.PlayEntity(audioResource.AudioStream, GetEntity(ev.SourceUid.Value), audioParams);
else
_audio.PlayGlobal(audioResource.AudioStream, audioParams);

_contentRoot.RemoveFile(filePath);
}

private float AdjustVolume(bool isWhisper)
{
var volume = MinimalVolume + SharedAudioSystem.GainToVolume(_volume);

if (isWhisper)
volume -= SharedAudioSystem.GainToVolume(WhisperFade);

return volume;
}

private float AdjustDistance(bool isWhisper)
{
return isWhisper ? 5 : 10;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ private static HumanoidCharacterProfile CharlieCharlieson()
FlavorText = "The biggest boy around.",
Species = "Human",
Customspeciename = "",
Voice = "TEST",
Age = 21,
Appearance = new(
"Afro",
Expand Down
Loading
Loading