From dcd42679c177f52273053c3d7302b4c7f336a6aa Mon Sep 17 00:00:00 2001 From: lucoiso Date: Mon, 14 Aug 2023 19:18:42 +0100 Subject: [PATCH 1/3] make shareable -> make shared --- AzSpeech.uplugin | 4 ++-- Source/AzSpeech/Private/AzSpeech/AzSpeechHelper.cpp | 2 +- Source/AzSpeechEditor/Private/SAzSpeechAudioGenerator.cpp | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/AzSpeech.uplugin b/AzSpeech.uplugin index f485da8..6caaf75 100644 --- a/AzSpeech.uplugin +++ b/AzSpeech.uplugin @@ -1,7 +1,7 @@ { "FileVersion": 3, - "Version": 27, - "VersionName": "1.6.10", + "Version": 28, + "VersionName": "1.6.11", "FriendlyName": "AzSpeech - Voice and Text", "Description": "Integrates Azure Speech Cognitive Services into the Engine by adding functions to perform recognition and synthesis via asynchronous tasks.", "Category": "Game Features", diff --git a/Source/AzSpeech/Private/AzSpeech/AzSpeechHelper.cpp b/Source/AzSpeech/Private/AzSpeech/AzSpeechHelper.cpp index 5ef85a3..2aa8ba0 100644 --- a/Source/AzSpeech/Private/AzSpeech/AzSpeechHelper.cpp +++ b/Source/AzSpeech/Private/AzSpeech/AzSpeechHelper.cpp @@ -525,7 +525,7 @@ const FAzSpeechAnimationData UAzSpeechHelper::ExtractAnimationDataFromVisemeData } const TSharedRef> Reader = TJsonReaderFactory<>::Create(VisemeData.Animation); - TSharedPtr JsonObject = MakeShareable(new FJsonObject); + TSharedPtr JsonObject = MakeShared(); FJsonSerializer::Deserialize(Reader, JsonObject); if (!JsonObject.IsValid()) diff --git a/Source/AzSpeechEditor/Private/SAzSpeechAudioGenerator.cpp b/Source/AzSpeechEditor/Private/SAzSpeechAudioGenerator.cpp index e02726a..387351d 100644 --- a/Source/AzSpeechEditor/Private/SAzSpeechAudioGenerator.cpp +++ b/Source/AzSpeechEditor/Private/SAzSpeechAudioGenerator.cpp @@ -98,11 +98,11 @@ void SAzSpeechAudioGenerator::Construct([[maybe_unused]] const FArguments&) }; TextTypes = GetStringArrayAsSharedPtr({ "Text", "SSML" }); - SelectVoice = MakeShareable(new FString("Select a Voice (Set Locale to Update the List)")); + SelectVoice = MakeShared("Select a Voice (Set Locale to Update the List)"); AvailableVoices = { SelectVoice }; - GameModule = MakeShareable(new FString("Game")); + GameModule = MakeShared("Game"); Module = *GameModule.Get(); AvailableModules = { GameModule }; @@ -337,7 +337,7 @@ TArray> SAzSpeechAudioGenerator::GetStringArrayAsSharedPtr(c for (const auto& String : Input) { - Output.Add(MakeShareable(new FString(String))); + Output.Add(MakeShared(String)); } return Output; From 82b720020f6b4f25d75866a230f4977277a1a77b Mon Sep 17 00:00:00 2001 From: lucoiso Date: Tue, 15 Aug 2023 16:04:37 +0100 Subject: [PATCH 2/3] Slate Adjustments --- Source/AzSpeech/Private/AzSpeech.cpp | 2 +- .../Private/AzSpeech/AzSpeechSettings.cpp | 2 +- .../Runnables/AzSpeechRecognitionRunnable.h | 2 +- .../Runnables/AzSpeechSynthesisRunnable.h | 2 +- .../AzSpeechEditor/Private/AzSpeechEditor.cpp | 6 +- .../Generator/AzSpeechPropertiesGetter.cpp | 41 ++ .../Generator/AzSpeechPropertiesGetter.h | 35 ++ .../Generator/SAzSpeechAudioGenerator.cpp | 320 ++++++++++++++++ .../{ => Generator}/SAzSpeechAudioGenerator.h | 43 +-- .../Private/SAzSpeechAudioGenerator.cpp | 359 ------------------ 10 files changed, 411 insertions(+), 401 deletions(-) create mode 100644 Source/AzSpeechEditor/Private/Generator/AzSpeechPropertiesGetter.cpp create mode 100644 Source/AzSpeechEditor/Private/Generator/AzSpeechPropertiesGetter.h create mode 100644 Source/AzSpeechEditor/Private/Generator/SAzSpeechAudioGenerator.cpp rename Source/AzSpeechEditor/Private/{ => Generator}/SAzSpeechAudioGenerator.h (54%) delete mode 100644 Source/AzSpeechEditor/Private/SAzSpeechAudioGenerator.cpp diff --git a/Source/AzSpeech/Private/AzSpeech.cpp b/Source/AzSpeech/Private/AzSpeech.cpp index cb207c1..3851d02 100644 --- a/Source/AzSpeech/Private/AzSpeech.cpp +++ b/Source/AzSpeech/Private/AzSpeech.cpp @@ -151,7 +151,7 @@ void FAzSpeechModule::StartupModule() #endif #if WITH_EDITOR && !AZSPEECH_SUPPORTED_PLATFORM - FMessageDialog::Open(EAppMsgType::Ok, FText::FromString("Currently, AzSpeech does not officially support the platform you're using/targeting. If you encounter any issue and can/want to contribute, get in touch! :)\n\nRepository Link: github.com/lucoiso/UEAzSpeech")); + FMessageDialog::Open(EAppMsgType::Ok, FText::FromString(TEXT("Currently, AzSpeech does not officially support the platform you're using/targeting. If you encounter any issue and can/want to contribute, get in touch! :)\n\nRepository Link: github.com/lucoiso/UEAzSpeech"))); #endif } diff --git a/Source/AzSpeech/Private/AzSpeech/AzSpeechSettings.cpp b/Source/AzSpeech/Private/AzSpeech/AzSpeechSettings.cpp index 69b11b3..639dd93 100644 --- a/Source/AzSpeech/Private/AzSpeech/AzSpeechSettings.cpp +++ b/Source/AzSpeech/Private/AzSpeech/AzSpeechSettings.cpp @@ -81,7 +81,7 @@ void UAzSpeechSettings::PostEditChangeProperty(FPropertyChangedEvent& PropertyCh if (DefaultOptions.RecognitionOptions.CandidateLanguages.Num() > MaxCandidateLanguages) { - FMessageDialog::Open(EAppMsgType::Ok, FText::FromString("You can only include up to 4 languages for at-start LID and up to 10 languages for continuous LID.")); + FMessageDialog::Open(EAppMsgType::Ok, FText::FromString(TEXT("You can only include up to 4 languages for at-start LID and up to 10 languages for continuous LID."))); DefaultOptions.RecognitionOptions.CandidateLanguages.RemoveAtSwap(MaxCandidateLanguages, DefaultOptions.RecognitionOptions.CandidateLanguages.Num() - MaxCandidateLanguages, true); } diff --git a/Source/AzSpeech/Public/AzSpeech/Runnables/AzSpeechRecognitionRunnable.h b/Source/AzSpeech/Public/AzSpeech/Runnables/AzSpeechRecognitionRunnable.h index 6b71571..5bff37b 100644 --- a/Source/AzSpeech/Public/AzSpeech/Runnables/AzSpeechRecognitionRunnable.h +++ b/Source/AzSpeech/Public/AzSpeech/Runnables/AzSpeechRecognitionRunnable.h @@ -16,7 +16,7 @@ THIRD_PARTY_INCLUDES_END /** * */ -class FAzSpeechRecognitionRunnable : public FAzSpeechRunnableBase + class FAzSpeechRecognitionRunnable : public FAzSpeechRunnableBase { public: FAzSpeechRecognitionRunnable() = delete; diff --git a/Source/AzSpeech/Public/AzSpeech/Runnables/AzSpeechSynthesisRunnable.h b/Source/AzSpeech/Public/AzSpeech/Runnables/AzSpeechSynthesisRunnable.h index 0a4b3c1..5a793e0 100644 --- a/Source/AzSpeech/Public/AzSpeech/Runnables/AzSpeechSynthesisRunnable.h +++ b/Source/AzSpeech/Public/AzSpeech/Runnables/AzSpeechSynthesisRunnable.h @@ -14,7 +14,7 @@ THIRD_PARTY_INCLUDES_END /** * */ -class FAzSpeechSynthesisRunnable : public FAzSpeechRunnableBase + class FAzSpeechSynthesisRunnable : public FAzSpeechRunnableBase { public: FAzSpeechSynthesisRunnable() = delete; diff --git a/Source/AzSpeechEditor/Private/AzSpeechEditor.cpp b/Source/AzSpeechEditor/Private/AzSpeechEditor.cpp index 66787c3..17d8eae 100644 --- a/Source/AzSpeechEditor/Private/AzSpeechEditor.cpp +++ b/Source/AzSpeechEditor/Private/AzSpeechEditor.cpp @@ -3,7 +3,7 @@ // Repo: https://github.com/lucoiso/UEAzSpeech #include "AzSpeechEditor.h" -#include "SAzSpeechAudioGenerator.h" +#include "Generator/SAzSpeechAudioGenerator.h" #include #include #include @@ -50,8 +50,8 @@ void FAzSpeechEditorModule::RegisterMenus() const TSharedPtr Menu = WorkspaceMenu::GetMenuStructure().GetToolsCategory()->AddGroup(LOCTEXT("AzSpeechCategory", "AzSpeech"), LOCTEXT("AzSpeechCategoryTooltip", "AzSpeech Plugin Tabs"), FSlateIcon(AppStyleName, "Icons.Package")); FGlobalTabmanager::Get()->RegisterNomadTabSpawner(AzSpeechEditorTabName, EditorTabSpawnerDelegate) - .SetDisplayName(FText::FromString("AzSpeech Audio Generator")) - .SetTooltipText(FText::FromString("Open AzSpeech Audio Generator")) + .SetDisplayName(FText::FromString(TEXT("AzSpeech Audio Generator"))) + .SetTooltipText(FText::FromString(TEXT("Open AzSpeech Audio Generator"))) .SetIcon(FSlateIcon(AppStyleName, "Icons.Plus")) .SetGroup(Menu.ToSharedRef()); } diff --git a/Source/AzSpeechEditor/Private/Generator/AzSpeechPropertiesGetter.cpp b/Source/AzSpeechEditor/Private/Generator/AzSpeechPropertiesGetter.cpp new file mode 100644 index 0000000..f95db20 --- /dev/null +++ b/Source/AzSpeechEditor/Private/Generator/AzSpeechPropertiesGetter.cpp @@ -0,0 +1,41 @@ +// Author: Lucas Vilas-Boas +// Year: 2023 +// Repo: https://github.com/lucoiso/UEAzSpeech + +#include "AzSpeechPropertiesGetter.h" + +#ifdef UE_INLINE_GENERATED_CPP_BY_NAME +#include UE_INLINE_GENERATED_CPP_BY_NAME(AzSpeechPropertiesGetter) +#endif + +UAzSpeechPropertiesGetter::UAzSpeechPropertiesGetter(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) +{ +} + +void UAzSpeechPropertiesGetter::OnAvailableVoicesChanged(const TArray& Voices) +{ + OnAvailableVoicesUpdated.ExecuteIfBound(Voices); + Destroy(); +} + +void UAzSpeechPropertiesGetter::SynthesisCompleted(const TArray& AudioData) +{ + OnAudioDataGenerated.ExecuteIfBound(AudioData); + Destroy(); +} + +void UAzSpeechPropertiesGetter::TaskFail() +{ + Destroy(); +} + +void UAzSpeechPropertiesGetter::Destroy() +{ + ClearFlags(RF_Standalone); + +#if ENGINE_MAJOR_VERSION >= 5 + MarkAsGarbage(); +#else + MarkPendingKill(); +#endif +} \ No newline at end of file diff --git a/Source/AzSpeechEditor/Private/Generator/AzSpeechPropertiesGetter.h b/Source/AzSpeechEditor/Private/Generator/AzSpeechPropertiesGetter.h new file mode 100644 index 0000000..30764eb --- /dev/null +++ b/Source/AzSpeechEditor/Private/Generator/AzSpeechPropertiesGetter.h @@ -0,0 +1,35 @@ +// Author: Lucas Vilas-Boas +// Year: 2023 +// Repo: https://github.com/lucoiso/UEAzSpeech + +#pragma once + +#include +#include +#include "AzSpeechPropertiesGetter.generated.h" + +DECLARE_DELEGATE_OneParam(FAvailableVoicesUpdated, TArray); +DECLARE_DELEGATE_OneParam(FAudioDataGenerated, TArray); + +UCLASS(MinimalAPI, NotBlueprintable, NotPlaceable, Category = "Implementation") +class UAzSpeechPropertiesGetter : public UObject +{ + GENERATED_BODY() + +public: + explicit UAzSpeechPropertiesGetter(const FObjectInitializer& ObjectInitializer = FObjectInitializer::Get()); + + FAvailableVoicesUpdated OnAvailableVoicesUpdated; + FAudioDataGenerated OnAudioDataGenerated; + + UFUNCTION() + void OnAvailableVoicesChanged(const TArray& Voices); + + UFUNCTION() + void SynthesisCompleted(const TArray& AudioData); + + UFUNCTION() + void TaskFail(); + + void Destroy(); +}; \ No newline at end of file diff --git a/Source/AzSpeechEditor/Private/Generator/SAzSpeechAudioGenerator.cpp b/Source/AzSpeechEditor/Private/Generator/SAzSpeechAudioGenerator.cpp new file mode 100644 index 0000000..a68bca9 --- /dev/null +++ b/Source/AzSpeechEditor/Private/Generator/SAzSpeechAudioGenerator.cpp @@ -0,0 +1,320 @@ +// Author: Lucas Vilas-Boas +// Year: 2023 +// Repo: https://github.com/lucoiso/UEAzSpeech + +#include "SAzSpeechAudioGenerator.h" +#include "AzSpeechPropertiesGetter.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +void SAzSpeechAudioGenerator::Construct([[maybe_unused]] const FArguments&) +{ + TextTypes = GetStringArrayAsSharedPtr({ "Text", "SSML" }); + SelectVoice = MakeShared("Select a Voice (Set Locale to Update the List)"); + + AvailableVoices = { SelectVoice }; + + GameModule = MakeShared("Game"); + Module = *GameModule.Get(); + + AvailableModules = { GameModule }; + AvailableModules.Append(GetAvailableContentModules()); + + ChildSlot + [ + ConstructContent() + ]; +} + +TSharedRef SAzSpeechAudioGenerator::ConstructContent() +{ + constexpr float MarginSpacing = 4.f; + +#if ENGINE_MAJOR_VERSION < 5 + using FAppStyle = FEditorStyle; +#endif + + return SNew(SScrollBox) + + SScrollBox::Slot() + .Padding(MarginSpacing) + [ + SNew(SGridPanel) + .FillColumn(1, 1.f) + + SGridPanel::Slot(0, 0) + .Padding(MarginSpacing) + [ + SNew(STextBlock) + .Text(FText::FromString(TEXT("Synthesis Text/SSML"))) + .TextStyle(FAppStyle::Get(), "PropertyEditor.AssetClass") + .Font(FAppStyle::Get().GetFontStyle("PropertyWindow.NormalFont")) + ] + + SGridPanel::Slot(1, 0) + .Padding(MarginSpacing) + [ + SNew(SMultiLineEditableTextBox) + .OnTextChanged_Lambda( + [this](const FText& InText) + { + SynthesisText = InText; + } + ) + ] + + SGridPanel::Slot(0, 1) + .Padding(MarginSpacing) + [ + SNew(STextBlock) + .Text(FText::FromString(TEXT("Is SSML"))) + .TextStyle(FAppStyle::Get(), "PropertyEditor.AssetClass") + .Font(FAppStyle::Get().GetFontStyle("PropertyWindow.NormalFont")) + ] + + SGridPanel::Slot(1, 1) + .Padding(MarginSpacing) + [ + SNew(SCheckBox) + .IsChecked(ECheckBoxState::Unchecked) + .OnCheckStateChanged_Lambda( + [this](const ECheckBoxState InState) + { + bIsSSMLBased = InState == ECheckBoxState::Checked; + } + ) + ] + + SGridPanel::Slot(0, 2) + .Padding(MarginSpacing) + [ + SNew(STextBlock) + .Text(FText::FromString(TEXT("Locale"))) + .TextStyle(FAppStyle::Get(), "PropertyEditor.AssetClass") + .Font(FAppStyle::Get().GetFontStyle("PropertyWindow.NormalFont")) + ] + + SGridPanel::Slot(1, 2) + .Padding(MarginSpacing) + [ + SNew(SEditableTextBox) + .OnTextCommitted_Lambda( + [this](const FText& InText, [[maybe_unused]] ETextCommit::Type InCommitType) + { + const FString NewValue = FText::TrimPrecedingAndTrailing(InText).ToString(); + + if (Locale.Equals(NewValue, ESearchCase::IgnoreCase)) + { + return; + } + + Locale = NewValue; + VoiceComboBox->SetSelectedItem(SelectVoice); + + if (Locale.Equals("Auto", ESearchCase::IgnoreCase)) + { + AvailableVoices = { SelectVoice }; + } + else if (!AzSpeech::Internal::HasEmptyParam(Locale)) + { + UpdateAvailableVoices(); + } + } + ) + ] + + SGridPanel::Slot(0, 3) + .Padding(MarginSpacing) + [ + SNew(STextBlock) + .Text(FText::FromString(TEXT("Voice"))) + .TextStyle(FAppStyle::Get(), "PropertyEditor.AssetClass") + .Font(FAppStyle::Get().GetFontStyle("PropertyWindow.NormalFont")) + ] + + SGridPanel::Slot(1, 3) + .Padding(MarginSpacing) + [ + SAssignNew(VoiceComboBox, STextComboBox) + .OptionsSource(&AvailableVoices) + .InitiallySelectedItem(SelectVoice) + .OnSelectionChanged_Lambda( + [this](const TSharedPtr& InStr, [[maybe_unused]] ESelectInfo::Type) + { + Voice = InStr->TrimStartAndEnd(); + } + ) + ] + + SGridPanel::Slot(0, 4) + .Padding(MarginSpacing) + [ + SNew(STextBlock) + .Text(FText::FromString(TEXT("Module"))) + .TextStyle(FAppStyle::Get(), "PropertyEditor.AssetClass") + .Font(FAppStyle::Get().GetFontStyle("PropertyWindow.NormalFont")) + ] + + SGridPanel::Slot(1, 4) + .Padding(MarginSpacing) + [ + SNew(STextComboBox) + .OptionsSource(&AvailableModules) + .InitiallySelectedItem(GameModule) + .OnSelectionChanged_Lambda( + [this](const TSharedPtr& InStr, [[maybe_unused]] ESelectInfo::Type) + { + Module = InStr->TrimStartAndEnd(); + } + ) + ] + + SGridPanel::Slot(0, 5) + .Padding(MarginSpacing) + [ + SNew(STextBlock) + .Text(FText::FromString(TEXT("Path (Relative to Module Root)"))) + .TextStyle(FAppStyle::Get(), "PropertyEditor.AssetClass") + .Font(FAppStyle::Get().GetFontStyle("PropertyWindow.NormalFont")) + ] + + SGridPanel::Slot(1, 5) + .Padding(MarginSpacing) + [ + SAssignNew(PathInput, SEditableTextBox) + .OnTextCommitted_Lambda( + [this](const FText& InText, [[maybe_unused]] ETextCommit::Type InCommitType) + { + OnFileInfoCommited(InText, RelativePath, PathInput); + } + ) + ] + + SGridPanel::Slot(0, 6) + .Padding(MarginSpacing) + [ + SNew(STextBlock) + .Text(FText::FromString(TEXT("Asset Name"))) + .TextStyle(FAppStyle::Get(), "PropertyEditor.AssetClass") + .Font(FAppStyle::Get().GetFontStyle("PropertyWindow.NormalFont")) + ] + + SGridPanel::Slot(1, 6) + .Padding(MarginSpacing) + [ + SAssignNew(AssetNameInput, SEditableTextBox) + .OnTextCommitted_Lambda( + [this](const FText& InText, [[maybe_unused]] ETextCommit::Type InCommitType) + { + OnFileInfoCommited(InText, AssetName, AssetNameInput); + } + ) + ] + + SGridPanel::Slot(1, 7) + .Padding(MarginSpacing) + .HAlign(HAlign_Left) + [ + SNew(SButton) + .HAlign(HAlign_Center) + .Text(FText::FromString(TEXT("Generate Audio"))) + .OnClicked(this, &SAzSpeechAudioGenerator::HandleGenerateAudioButtonClicked) + .IsEnabled(this, &SAzSpeechAudioGenerator::IsGenerationEnabled) + ] + ]; +} + +void SAzSpeechAudioGenerator::UpdateAvailableVoices() +{ + UAzSpeechPropertiesGetter* const InternalGetter = NewObject(); + InternalGetter->SetFlags(RF_Standalone); + + InternalGetter->OnAvailableVoicesUpdated.BindLambda( + [this](TArray Voices) + { + OnAvailableVoicesChanged(Voices); + } + ); + + UGetAvailableVoicesAsync* const Task = UGetAvailableVoicesAsync::GetAvailableVoicesAsync(GEditor->GetEditorWorldContext().World(), Locale); + Task->Success.AddDynamic(InternalGetter, &UAzSpeechPropertiesGetter::OnAvailableVoicesChanged); + Task->Fail.AddDynamic(InternalGetter, &UAzSpeechPropertiesGetter::TaskFail); + Task->Activate(); +} + +FReply SAzSpeechAudioGenerator::HandleGenerateAudioButtonClicked() +{ + if (!UAzSpeechSettings::CheckAzSpeechSettings()) + { + FReply::Handled(); + } + + UAzSpeechPropertiesGetter* const InternalGetter = NewObject(); + InternalGetter->SetFlags(RF_Standalone); + + InternalGetter->OnAudioDataGenerated.BindLambda( + [this](TArray AudioData) + { + if (USoundWave* const SoundWave = UAzSpeechHelper::ConvertAudioDataToSoundWave(AudioData, Module, RelativePath, AssetName)) + { + UGameplayStatics::PlaySound2D(GEditor->GetEditorWorldContext().World(), SoundWave); + } + } + ); + + UAzSpeechAudioDataSynthesisBase* Task = nullptr; + if (bIsSSMLBased) + { + Task = USSMLToAudioDataAsync::EditorTask(SynthesisText.ToString()); + Cast(Task)->SynthesisCompleted.AddDynamic(InternalGetter, &UAzSpeechPropertiesGetter::SynthesisCompleted); + } + else + { + Task = UTextToAudioDataAsync::EditorTask(SynthesisText.ToString(), Voice, Locale); + Cast(Task)->SynthesisCompleted.AddDynamic(InternalGetter, &UAzSpeechPropertiesGetter::SynthesisCompleted); + } + + Task->Activate(); + + return FReply::Handled(); +} + +bool SAzSpeechAudioGenerator::IsGenerationEnabled() const +{ + if (bIsSSMLBased) + { + return !AzSpeech::Internal::HasEmptyParam(Module, AssetName, SynthesisText.ToString()); + } + + return !Voice.Equals(*SelectVoice.Get()) && !AzSpeech::Internal::HasEmptyParam(Voice, Module, AssetName, SynthesisText.ToString()); +} + +TArray> SAzSpeechAudioGenerator::GetStringArrayAsSharedPtr(const TArray& Input) const +{ + TArray> Output; + + for (const auto& String : Input) + { + Output.Add(MakeShared(String)); + } + + return Output; +} + +TArray> SAzSpeechAudioGenerator::GetAvailableContentModules() const +{ + TArray Output = UAzSpeechHelper::GetAvailableContentModules(); + Output.Remove("Game"); + + return GetStringArrayAsSharedPtr(Output); +} + +void SAzSpeechAudioGenerator::OnAvailableVoicesChanged(const TArray& Voices) +{ + VoiceComboBox->SetSelectedItem(SelectVoice); + AvailableVoices = { SelectVoice }; + AvailableVoices.Append(GetStringArrayAsSharedPtr(Voices)); +} + +void SAzSpeechAudioGenerator::OnFileInfoCommited(const FText& InText, FString& Member, TSharedPtr& InputRef) +{ + const FString NewValue = FText::TrimPrecedingAndTrailing(InText).ToString(); + Member = FPaths::MakeValidFileName(NewValue); + + InputRef->SetText(FText::FromString(Member)); +} \ No newline at end of file diff --git a/Source/AzSpeechEditor/Private/SAzSpeechAudioGenerator.h b/Source/AzSpeechEditor/Private/Generator/SAzSpeechAudioGenerator.h similarity index 54% rename from Source/AzSpeechEditor/Private/SAzSpeechAudioGenerator.h rename to Source/AzSpeechEditor/Private/Generator/SAzSpeechAudioGenerator.h index 84aa16d..8948993 100644 --- a/Source/AzSpeechEditor/Private/SAzSpeechAudioGenerator.h +++ b/Source/AzSpeechEditor/Private/Generator/SAzSpeechAudioGenerator.h @@ -5,43 +5,14 @@ #pragma once #include -#include "SAzSpeechAudioGenerator.generated.h" - -DECLARE_DELEGATE_OneParam(FAvailableVoicesUpdated, TArray); -DECLARE_DELEGATE_OneParam(FAudioDataGenerated, TArray); - -UCLASS(MinimalAPI, NotBlueprintable, NotPlaceable, Category = "Implementation") -class UAzSpeechPropertiesGetter : public UObject -{ - GENERATED_BODY() - -public: - explicit UAzSpeechPropertiesGetter(const FObjectInitializer& ObjectInitializer = FObjectInitializer::Get()); - - FAvailableVoicesUpdated OnAvailableVoicesUpdated; - FAudioDataGenerated OnAudioDataGenerated; - - UFUNCTION() - void OnAvailableVoicesChanged(const TArray& Voices); - - UFUNCTION() - void SynthesisCompleted(const TArray& AudioData); - - UFUNCTION() - void TaskFail(); - - virtual void Destroy(); -}; +#include class SAzSpeechAudioGenerator final : public SCompoundWidget { - friend class UAzSpeechPropertiesGetter; - public: SLATE_USER_ARGS(SAzSpeechAudioGenerator) { } - SLATE_END_ARGS() void Construct(const FArguments& InArgs); @@ -66,13 +37,15 @@ class SAzSpeechAudioGenerator final : public SCompoundWidget TArray> TextTypes; TArray> AvailableModules; - void OnAvailableVoicesChanged(const TArray& Voices); + TSharedPtr VoiceComboBox; + TSharedPtr PathInput; + TSharedPtr AssetNameInput; + + TSharedRef ConstructContent(); + TArray> GetStringArrayAsSharedPtr(const TArray& Input) const; TArray> GetAvailableContentModules() const; + void OnAvailableVoicesChanged(const TArray& Voices); void OnFileInfoCommited(const FText& InText, FString& Member, TSharedPtr& InputRef); - - TSharedPtr VoiceComboBox; - TSharedPtr PathInput; - TSharedPtr AssetNameInput; }; diff --git a/Source/AzSpeechEditor/Private/SAzSpeechAudioGenerator.cpp b/Source/AzSpeechEditor/Private/SAzSpeechAudioGenerator.cpp deleted file mode 100644 index 387351d..0000000 --- a/Source/AzSpeechEditor/Private/SAzSpeechAudioGenerator.cpp +++ /dev/null @@ -1,359 +0,0 @@ -// Author: Lucas Vilas-Boas -// Year: 2023 -// Repo: https://github.com/lucoiso/UEAzSpeech - -#include "SAzSpeechAudioGenerator.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef UE_INLINE_GENERATED_CPP_BY_NAME -#include UE_INLINE_GENERATED_CPP_BY_NAME(SAzSpeechAudioGenerator) -#endif - -UAzSpeechPropertiesGetter::UAzSpeechPropertiesGetter(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) -{ -} - -void UAzSpeechPropertiesGetter::OnAvailableVoicesChanged(const TArray& Voices) -{ - OnAvailableVoicesUpdated.ExecuteIfBound(Voices); - Destroy(); -} - -void UAzSpeechPropertiesGetter::SynthesisCompleted(const TArray& AudioData) -{ - OnAudioDataGenerated.ExecuteIfBound(AudioData); - Destroy(); -} - -void UAzSpeechPropertiesGetter::TaskFail() -{ - Destroy(); -} - -void UAzSpeechPropertiesGetter::Destroy() -{ - ClearFlags(RF_Standalone); - -#if ENGINE_MAJOR_VERSION >= 5 - MarkAsGarbage(); -#else - MarkPendingKill(); -#endif -} - -void SAzSpeechAudioGenerator::Construct([[maybe_unused]] const FArguments&) -{ - constexpr float Slot_Padding = 1.f; - constexpr float Margin_Spacing = Slot_Padding * 4.f; - -#if ENGINE_MAJOR_VERSION < 5 - using FAppStyle = FEditorStyle; -#endif - - const ISlateStyle& AppStyle = FAppStyle::Get(); - - const auto CenterTextCreator_Lambda = [&AppStyle, &Margin_Spacing](const FString& InStr) -> const TSharedRef - { - return SNew(STextBlock) - .Text(FText::FromString(InStr)) - .TextStyle(AppStyle, "PropertyEditor.AssetClass") - .Font(AppStyle.GetFontStyle("PropertyWindow.NormalFont")) - .Justification(ETextJustify::Left) - .Margin(Margin_Spacing); - }; - - const auto ContentPairCreator_Lambda = [this, &AppStyle, &Slot_Padding](const TSharedRef Content1, const TSharedRef Content2) -> const TSharedRef - { - return SNew(SBorder) - .BorderImage(AppStyle.GetBrush("ToolPanel.GroupBorder")) - [ - SNew(SHorizontalBox) - + SHorizontalBox::Slot() - .FillWidth(0.4f) - .HAlign(HAlign_Fill) - .VAlign(VAlign_Center) - [ - Content1 - ] - + SHorizontalBox::Slot() - .FillWidth(0.6f) - .MaxWidth(400) - .HAlign(HAlign_Fill) - .VAlign(VAlign_Center) - [ - Content2 - ] - ]; - }; - - TextTypes = GetStringArrayAsSharedPtr({ "Text", "SSML" }); - SelectVoice = MakeShared("Select a Voice (Set Locale to Update the List)"); - - AvailableVoices = { SelectVoice }; - - GameModule = MakeShared("Game"); - Module = *GameModule.Get(); - - AvailableModules = { GameModule }; - AvailableModules.Append(GetAvailableContentModules()); - - ChildSlot - [ - SNew(SBorder) - .Padding(Margin_Spacing) - [ - SNew(SScrollBox) - + SScrollBox::Slot() - [ - SNew(SVerticalBox) - + SVerticalBox::Slot() - .Padding(Slot_Padding) - .AutoHeight() - [ - ContentPairCreator_Lambda( - CenterTextCreator_Lambda("Synthesis Text/SSML"), - SNew(SMultiLineEditableTextBox) - .OnTextChanged_Lambda( - [this](const FText& InText) - { - SynthesisText = InText; - } - ) - ) - ] - + SVerticalBox::Slot() - .Padding(Slot_Padding) - .AutoHeight() - [ - ContentPairCreator_Lambda( - CenterTextCreator_Lambda("SSML"), - SNew(SCheckBox) - .IsChecked(ECheckBoxState::Unchecked) - .OnCheckStateChanged_Lambda( - [this](const ECheckBoxState InState) - { - bIsSSMLBased = InState == ECheckBoxState::Checked; - } - ) - ) - ] - + SVerticalBox::Slot() - .Padding(Slot_Padding) - .AutoHeight() - [ - ContentPairCreator_Lambda( - CenterTextCreator_Lambda("Locale"), - SNew(SEditableTextBox) - .OnTextCommitted_Lambda( - [this](const FText& InText, [[maybe_unused]] ETextCommit::Type InCommitType) - { - const FString NewValue = FText::TrimPrecedingAndTrailing(InText).ToString(); - - if (Locale.Equals(NewValue, ESearchCase::IgnoreCase)) - { - return; - } - - Locale = NewValue; - VoiceComboBox->SetSelectedItem(SelectVoice); - - if (Locale.Equals("Auto", ESearchCase::IgnoreCase)) - { - AvailableVoices = { SelectVoice }; - } - else if (!AzSpeech::Internal::HasEmptyParam(Locale)) - { - UpdateAvailableVoices(); - } - } - ) - ) - ] - + SVerticalBox::Slot() - .Padding(Slot_Padding) - .AutoHeight() - [ - ContentPairCreator_Lambda( - CenterTextCreator_Lambda("Voice"), - SAssignNew(VoiceComboBox, STextComboBox) - .OptionsSource(&AvailableVoices) - .InitiallySelectedItem(SelectVoice) - .OnSelectionChanged_Lambda( - [this](const TSharedPtr& InStr, [[maybe_unused]] ESelectInfo::Type) - { - Voice = InStr->TrimStartAndEnd(); - } - ) - ) - ] - + SVerticalBox::Slot() - .Padding(Slot_Padding) - .AutoHeight() - [ - ContentPairCreator_Lambda( - CenterTextCreator_Lambda("Module"), - SNew(STextComboBox) - .OptionsSource(&AvailableModules) - .InitiallySelectedItem(GameModule) - .OnSelectionChanged_Lambda( - [this](const TSharedPtr& InStr, [[maybe_unused]] ESelectInfo::Type) - { - Module = InStr->TrimStartAndEnd(); - } - ) - ) - ] - + SVerticalBox::Slot() - .Padding(Slot_Padding) - .AutoHeight() - [ - ContentPairCreator_Lambda( - CenterTextCreator_Lambda("Relative Path"), - SAssignNew(PathInput, SEditableTextBox) - .OnTextCommitted_Lambda( - [this](const FText& InText, [[maybe_unused]] ETextCommit::Type InCommitType) - { - OnFileInfoCommited(InText, RelativePath, PathInput); - } - ) - ) - ] - + SVerticalBox::Slot() - .Padding(Slot_Padding) - .AutoHeight() - [ - ContentPairCreator_Lambda( - CenterTextCreator_Lambda("Asset Name"), - SAssignNew(AssetNameInput, SEditableTextBox) - .OnTextCommitted_Lambda( - [this](const FText& InText, [[maybe_unused]] ETextCommit::Type InCommitType) - { - OnFileInfoCommited(InText, AssetName, AssetNameInput); - } - ) - ) - ] - + SVerticalBox::Slot() - .Padding(Margin_Spacing) - .HAlign(HAlign_Center) - .AutoHeight() - [ - SNew(SButton) - .Text(FText::FromString("Generate Audio")) - .OnClicked(this, &SAzSpeechAudioGenerator::HandleGenerateAudioButtonClicked) - .IsEnabled(this, &SAzSpeechAudioGenerator::IsGenerationEnabled) - ] - ] - ] - ]; -} - -void SAzSpeechAudioGenerator::UpdateAvailableVoices() -{ - UAzSpeechPropertiesGetter* const InternalGetter = NewObject(); - InternalGetter->SetFlags(RF_Standalone); - - InternalGetter->OnAvailableVoicesUpdated.BindLambda( - [this](TArray Voices) - { - OnAvailableVoicesChanged(Voices); - } - ); - - UGetAvailableVoicesAsync* const Task = UGetAvailableVoicesAsync::GetAvailableVoicesAsync(GEditor->GetEditorWorldContext().World(), Locale); - Task->Success.AddDynamic(InternalGetter, &UAzSpeechPropertiesGetter::OnAvailableVoicesChanged); - Task->Fail.AddDynamic(InternalGetter, &UAzSpeechPropertiesGetter::TaskFail); - Task->Activate(); -} - -FReply SAzSpeechAudioGenerator::HandleGenerateAudioButtonClicked() -{ - if (!UAzSpeechSettings::CheckAzSpeechSettings()) - { - FReply::Handled(); - } - - UAzSpeechPropertiesGetter* const InternalGetter = NewObject(); - InternalGetter->SetFlags(RF_Standalone); - - InternalGetter->OnAudioDataGenerated.BindLambda( - [this](TArray AudioData) - { - if (USoundWave* const SoundWave = UAzSpeechHelper::ConvertAudioDataToSoundWave(AudioData, Module, RelativePath, AssetName)) - { - UGameplayStatics::PlaySound2D(GEditor->GetEditorWorldContext().World(), SoundWave); - } - } - ); - - UAzSpeechAudioDataSynthesisBase* Task = nullptr; - if (bIsSSMLBased) - { - Task = USSMLToAudioDataAsync::EditorTask(SynthesisText.ToString()); - Cast(Task)->SynthesisCompleted.AddDynamic(InternalGetter, &UAzSpeechPropertiesGetter::SynthesisCompleted); - } - else - { - Task = UTextToAudioDataAsync::EditorTask(SynthesisText.ToString(), Voice, Locale); - Cast(Task)->SynthesisCompleted.AddDynamic(InternalGetter, &UAzSpeechPropertiesGetter::SynthesisCompleted); - } - - Task->Activate(); - - return FReply::Handled(); -} - -bool SAzSpeechAudioGenerator::IsGenerationEnabled() const -{ - if (bIsSSMLBased) - { - return !AzSpeech::Internal::HasEmptyParam(Module, AssetName, SynthesisText.ToString()); - } - - return !Voice.Equals(*SelectVoice.Get()) && !AzSpeech::Internal::HasEmptyParam(Voice, Module, AssetName, SynthesisText.ToString()); -} - -void SAzSpeechAudioGenerator::OnAvailableVoicesChanged(const TArray& Voices) -{ - VoiceComboBox->SetSelectedItem(SelectVoice); - AvailableVoices = { SelectVoice }; - AvailableVoices.Append(GetStringArrayAsSharedPtr(Voices)); -} - -TArray> SAzSpeechAudioGenerator::GetStringArrayAsSharedPtr(const TArray& Input) const -{ - TArray> Output; - - for (const auto& String : Input) - { - Output.Add(MakeShared(String)); - } - - return Output; -} - -TArray> SAzSpeechAudioGenerator::GetAvailableContentModules() const -{ - TArray Output = UAzSpeechHelper::GetAvailableContentModules(); - Output.Remove("Game"); - return GetStringArrayAsSharedPtr(Output); -} - -void SAzSpeechAudioGenerator::OnFileInfoCommited(const FText& InText, FString& Member, TSharedPtr& InputRef) -{ - const FString NewValue = FText::TrimPrecedingAndTrailing(InText).ToString(); - Member = FPaths::MakeValidFileName(NewValue); - - InputRef->SetText(FText::FromString(Member)); -} \ No newline at end of file From f57e57995221ca7e6b8e4ce3ec8433ab35ad12f3 Mon Sep 17 00:00:00 2001 From: lucoiso Date: Tue, 15 Aug 2023 16:40:31 +0100 Subject: [PATCH 3/3] Slate Adjustments --- .../Private/Generator/SAzSpeechAudioGenerator.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/AzSpeechEditor/Private/Generator/SAzSpeechAudioGenerator.cpp b/Source/AzSpeechEditor/Private/Generator/SAzSpeechAudioGenerator.cpp index a68bca9..700399a 100644 --- a/Source/AzSpeechEditor/Private/Generator/SAzSpeechAudioGenerator.cpp +++ b/Source/AzSpeechEditor/Private/Generator/SAzSpeechAudioGenerator.cpp @@ -50,7 +50,7 @@ TSharedRef SAzSpeechAudioGenerator::ConstructContent() .Padding(MarginSpacing) [ SNew(SGridPanel) - .FillColumn(1, 1.f) + .FillColumn(1, 1.f) + SGridPanel::Slot(0, 0) .Padding(MarginSpacing) [