From 81de3b5e884d3d3d89098ccb7e24d332c4e9a1d4 Mon Sep 17 00:00:00 2001 From: Tillman Staffen Date: Wed, 4 Mar 2026 10:28:02 +0100 Subject: [PATCH] Matched with Base --- .../BP_AnimationTesting_Manager.uasset | 3 + .../BP_AnimationTesting_LookAtTarget.uasset | 3 + .../Core/GM_AnimationTesting.uasset | 3 + .../Core/Pawn_AnimationTesting.uasset | 3 + .../Data/E_AnimationTesting_Avatars.uasset | 3 + .../Data/E_AnimationTesting_Cameras.uasset | 3 + .../AS_EmptyAnimation_Body.uasset | 3 + .../AS_EmptyAnimation_Face.uasset | 3 + .../EmptyAnimation/LS_EmptyAnimation.uasset | 3 + .../AnimationTesting/M_Animation_Testing.umap | 3 + .../Materials/MI_AnimationTesting_Pawn.uasset | 3 + .../UI/W_AnimationTesting.uasset | 3 + .../Project/BP/Avatars/Avatar_Ben_BREX.uasset | 4 +- .../Project/BP/Avatars/Avatar_Jasmin.uasset | 2 +- .../Project/BP/BP_Environment_Manager.uasset | 4 +- .../Content/Project/BP/BP_Gameinstance.uasset | 3 + .../Project/BP/BP_Media_Manager.uasset | 4 +- .../Project/BP/BP_Project_Manager.uasset | 4 +- .../Project/BP/Base/BFL_GlobalHelper.uasset | 4 +- .../Content/Project/BP/Base/GM_Project.uasset | 4 +- .../Content/Project/BP/Base/PC_Project.uasset | 4 +- .../EnumsAndStructs/E_ProjectActions.uasset | 4 +- .../EnumsAndStructs/S_ConfigSettings.uasset | 4 +- .../BP/Environments/BP_Environment_2D.uasset | 2 +- .../BP_Environment_FogWithRing.uasset | 4 +- .../BP_Environment_MHC_Fireside.uasset | 4 +- .../BP_Environment_MHC_Moonlight.uasset | 4 +- .../BP_Environment_MHC_Portrait.uasset | 4 +- .../BP_Environment_MHC_Portrait_Dark.uasset | 4 +- .../BP_Environment_MHC_RedLantern.uasset | 4 +- .../BP_Environment_MHC_Split.uasset | 4 +- .../BP_Environment_MHC_Tungsten.uasset | 4 +- .../Project/BP/Modes/DA_Mode_QnA.uasset | 3 + .../States/DT_ProjectStates_LookatTest.uasset | 4 +- .../Modes/States/DT_ProjectStates_QnA.uasset | 3 + Unreal/Content/Project/Maps/M_Startup.umap | 4 +- .../2D_Environment/MM_2D_Environment.uasset | 4 +- .../Environment/SM_BackgroundSphere.uasset | 4 +- .../Meshes/Environment/SM_HDRI_Dome.uasset | 4 +- .../Environment/SM_InvertedSphere.uasset | 4 +- .../Project/Widgets/W_BlendWidget.uasset | 4 +- .../Project/Widgets/W_DialogueBox.uasset | 2 +- .../Project/Widgets/W_LogoWidget.uasset | 2 +- Unreal/Content/Project/Widgets/W_Main.uasset | 4 +- .../Content/Project/Widgets/W_Volume.uasset | 4 +- .../SPIE/Avatars/Avatar_SpieFrau_Shirt.uasset | 4 +- .../BP/Mode/States/DT_SPIE_SpieOne.uasset | 4 +- Unreal/Content/Schema/Spie_Config.schema.json | 3 +- .../AvatarCore_AI/AvatarCore_AI.Build.cs | 1 + .../AvatarCore_AI/Private/AIBaseManager.cpp | 3 +- .../Content/AvatarCoreLogging.uasset | 4 +- .../Content/AvatarCoreManager.uasset | 4 +- .../Content/IdleComponent.uasset | 4 +- .../Miscellaneous/AvatarCoreSavegame.uasset | 4 +- .../EAvatarCoreDebugModules.uasset | 4 +- .../Content/Miscellaneous/MicCheck.uasset | 4 +- .../PlaceableGameplayTagCamera_BP.uasset | 4 +- .../StateManagement/BFL_ProjectHelper.uasset | 4 +- .../StateManagement/BP_StateManager.uasset | 4 +- .../AICommand_TestProceedToOutro.uasset | 4 +- .../StateManagement/DataTable/PDA_Mode.uasset | 2 +- .../S_Prompt_DialogueArray.uasset | 2 +- .../StructsAndEnums/S_StateProcedure.uasset | 4 +- .../Widgets/BP_LookAtTest.uasset | 4 +- .../Widgets/Enum_UiDistanceLayer.uasset | 3 + .../Miscellaneous/E_WidgetPriority.uasset | 4 +- .../Widgets/W_AvatarReactionButton.uasset | 3 + .../Widgets/W_BaseStateWidget.uasset | 4 +- .../W_Base_DynamicAvatarReaction.uasset | 4 +- .../W_TestAvatarLookAtWidgetCollection.uasset | 4 +- .../AICommandTestBigHead.uasset | 4 +- .../AICommand_CurrentLocation.uasset | 4 +- .../AICommand_CurrentTime.uasset | 4 +- .../OpenAI_Websearch_Command.uasset | 4 +- .../Modules/W_AvatarCoreModuleEntry.uasset | 4 +- .../Modules/W_AvatarCoreModuleStates.uasset | 4 +- .../Pages/W_DebugAvatarCoreAnimation.uasset | 4 +- .../Pages/W_DebugAvatarCoreManager.uasset | 4 +- .../Debug/Pages/W_DebugAvatarCoreSTT.uasset | 2 +- .../Debug/Pages/W_DebugAvatarCoreTTS.uasset | 2 +- .../Debug/Pages/W_DebugProjectStates.uasset | 4 +- .../BI_AvatarCoreStartupEntry.uasset | 4 +- .../W_AvatarCoreStartupScreen.uasset | 4 +- .../BI_AvatarCoreSubtitleInterface.uasset | 4 +- .../Subtitles/Typo/FontSubtitleBold.uasset | 4 +- .../Subtitles/Typo/FontSubtitleRegular.uasset | 4 +- .../Subtitles/Typo/FrutigerLTCom-Bold.uasset | 4 +- .../Subtitles/Typo/FrutigerLTCom-Light.uasset | 4 +- .../Widgets/Subtitles/Typo/MSGOTHIC.uasset | 4 +- .../Widgets/Subtitles/Typo/SEGUIEMJ.uasset | 4 +- .../W_AvatarCoreSubtitleContainer.uasset | 4 +- .../W_AvatarCoreSubtitleEntry.uasset | 4 +- .../Content/n8n/BP_N8N_Connector.uasset | 2 +- .../Content/n8n/N8N_DebugPingCommand.uasset | 4 +- .../Content/n8n/N8N_WebResearchCommand.uasset | 4 +- ...AvatarCore_AnimInst_BodyForRetarget.uasset | 4 +- .../AvatarCore_AnimInst_BodyVisible.uasset | 4 +- .../AnimBPs/AvatarCore_AnimInst_Face.uasset | 4 +- .../AnimBPs/Interface/BI_AvatarAnimBP.uasset | 4 +- .../ControlRig/CR_AvatarCore_Body.uasset | 4 +- .../AvatarCore_AnimInst_BodyPresskit.uasset | 3 + .../AvatarCore_AnimInst_FacePresskit.uasset | 3 + .../EUS_MetahumanToAvatar.uasset | 4 +- .../Content/BP/MetaHuman/BaseAvatar.uasset | 4 +- .../Public/AvatarCore_AnimationVariables.h | 3 +- .../Public/AvatarCore_BaseAvatar.h | 15 +- .../AvatarCore_STT/AvatarCore_STT.Build.cs | 10 +- .../Processor/Azure/STTProcessorAzure.cpp | 1 + .../Processor/Parakeet/ParakeetRunnable.cpp | 164 +++++ .../Parakeet/STTParakeetProcessorBase.cpp | 616 ++++++++++++++++++ .../Parakeet/STTParakeetProcessorConfig.cpp | 10 + .../RealtimeAPI/STTProcessorRealtimeAPI.cpp | 2 + .../Processor/Whisper/STTProcessorWhisper.cpp | 3 + .../AvatarCore_STT/Private/STTManagerBase.cpp | 20 +- .../Processor/Parakeet/ParakeetRunnable.h | 47 ++ .../Parakeet/STTParakeetProcessorBase.h | 76 +++ .../Parakeet/STTParakeetProcessorConfig.h | 39 ++ .../AvatarCore_STT/Public/STTManagerBase.h | 8 + .../Source/AvatarCore_STT/Public/STTStructs.h | 1 + .../ThirdParty/Parakeet/ParakeetSTT.bat | 117 ++++ .../Source/ThirdParty/Parakeet/ParakeetSTT.py | 454 +++++++++++++ .../ThirdParty/Parakeet/StartPythonVenv.bat | 4 + .../ThirdParty/Parakeet/requirements.txt | 9 + .../AvatarCore_TTS/AvatarCore_TTS.Build.cs | 6 +- .../AvatarCore_TTS/Private/TTSManagerBase.cpp | 195 +----- .../Private/TTSWebRTCChannel.cpp | 211 ++++++ .../AvatarCore_TTS/Public/TTSBaseConfig.h | 16 - .../AvatarCore_TTS/Public/TTSManagerBase.h | 6 +- .../AvatarCore_TTS/Public/TTSWebRTCChannel.h | 74 +++ .../Source/ThirdParty/fvad/include/fvad.h | 96 --- .../Source/ThirdParty/fvad/lib/fvad.lib | Bin 39698 -> 0 bytes .../Content/DebugWidget/DebugWidget.uasset | 4 +- .../Source/BTools/Private/BToolsBPLibrary.cpp | 48 ++ .../Source/BTools/Public/BToolsBPLibrary.h | 2 + 134 files changed, 2172 insertions(+), 459 deletions(-) create mode 100644 Unreal/Content/Project/AnimationTesting/BP_AnimationTesting_Manager.uasset create mode 100644 Unreal/Content/Project/AnimationTesting/BPs/BP_AnimationTesting_LookAtTarget.uasset create mode 100644 Unreal/Content/Project/AnimationTesting/Core/GM_AnimationTesting.uasset create mode 100644 Unreal/Content/Project/AnimationTesting/Core/Pawn_AnimationTesting.uasset create mode 100644 Unreal/Content/Project/AnimationTesting/Data/E_AnimationTesting_Avatars.uasset create mode 100644 Unreal/Content/Project/AnimationTesting/Data/E_AnimationTesting_Cameras.uasset create mode 100644 Unreal/Content/Project/AnimationTesting/EmptyAnimation/AS_EmptyAnimation_Body.uasset create mode 100644 Unreal/Content/Project/AnimationTesting/EmptyAnimation/AS_EmptyAnimation_Face.uasset create mode 100644 Unreal/Content/Project/AnimationTesting/EmptyAnimation/LS_EmptyAnimation.uasset create mode 100644 Unreal/Content/Project/AnimationTesting/M_Animation_Testing.umap create mode 100644 Unreal/Content/Project/AnimationTesting/Materials/MI_AnimationTesting_Pawn.uasset create mode 100644 Unreal/Content/Project/AnimationTesting/UI/W_AnimationTesting.uasset create mode 100644 Unreal/Content/Project/BP/BP_Gameinstance.uasset create mode 100644 Unreal/Content/Project/BP/Modes/DA_Mode_QnA.uasset create mode 100644 Unreal/Content/Project/BP/Modes/States/DT_ProjectStates_QnA.uasset create mode 100644 Unreal/Plugins/AvatarCore_Manager/Content/StateManagement/Widgets/Enum_UiDistanceLayer.uasset create mode 100644 Unreal/Plugins/AvatarCore_Manager/Content/StateManagement/Widgets/W_AvatarReactionButton.uasset create mode 100644 Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/PressKit/AvatarCore_AnimInst_BodyPresskit.uasset create mode 100644 Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/PressKit/AvatarCore_AnimInst_FacePresskit.uasset create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/AvatarCore_STT/Private/Processor/Parakeet/ParakeetRunnable.cpp create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/AvatarCore_STT/Private/Processor/Parakeet/STTParakeetProcessorBase.cpp create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/AvatarCore_STT/Private/Processor/Parakeet/STTParakeetProcessorConfig.cpp create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/AvatarCore_STT/Public/Processor/Parakeet/ParakeetRunnable.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/AvatarCore_STT/Public/Processor/Parakeet/STTParakeetProcessorBase.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/AvatarCore_STT/Public/Processor/Parakeet/STTParakeetProcessorConfig.h create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/Parakeet/ParakeetSTT.bat create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/Parakeet/ParakeetSTT.py create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/Parakeet/StartPythonVenv.bat create mode 100644 Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/Parakeet/requirements.txt create mode 100644 Unreal/Plugins/AvatarCore_TTS/Source/AvatarCore_TTS/Private/TTSWebRTCChannel.cpp create mode 100644 Unreal/Plugins/AvatarCore_TTS/Source/AvatarCore_TTS/Public/TTSWebRTCChannel.h delete mode 100644 Unreal/Plugins/AvatarCore_TTS/Source/ThirdParty/fvad/include/fvad.h delete mode 100644 Unreal/Plugins/AvatarCore_TTS/Source/ThirdParty/fvad/lib/fvad.lib diff --git a/Unreal/Content/Project/AnimationTesting/BP_AnimationTesting_Manager.uasset b/Unreal/Content/Project/AnimationTesting/BP_AnimationTesting_Manager.uasset new file mode 100644 index 0000000..b8191df --- /dev/null +++ b/Unreal/Content/Project/AnimationTesting/BP_AnimationTesting_Manager.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:46709a7ae020f20fa8ce76e5712f4d1a832e8b3594c8e8093fe75c1bb9d789c9 +size 643441 diff --git a/Unreal/Content/Project/AnimationTesting/BPs/BP_AnimationTesting_LookAtTarget.uasset b/Unreal/Content/Project/AnimationTesting/BPs/BP_AnimationTesting_LookAtTarget.uasset new file mode 100644 index 0000000..4f60ca2 --- /dev/null +++ b/Unreal/Content/Project/AnimationTesting/BPs/BP_AnimationTesting_LookAtTarget.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:964f179c01b4123eb9129a1d4cedc5d16f52f9fbbe60cd8676496bc02d915b3b +size 30202 diff --git a/Unreal/Content/Project/AnimationTesting/Core/GM_AnimationTesting.uasset b/Unreal/Content/Project/AnimationTesting/Core/GM_AnimationTesting.uasset new file mode 100644 index 0000000..43c9669 --- /dev/null +++ b/Unreal/Content/Project/AnimationTesting/Core/GM_AnimationTesting.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9eaf09dad53a8cf437aac6ed5d325c0f33f05142598ffa633dc133540a938592 +size 27878 diff --git a/Unreal/Content/Project/AnimationTesting/Core/Pawn_AnimationTesting.uasset b/Unreal/Content/Project/AnimationTesting/Core/Pawn_AnimationTesting.uasset new file mode 100644 index 0000000..dbd052d --- /dev/null +++ b/Unreal/Content/Project/AnimationTesting/Core/Pawn_AnimationTesting.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:de45c07c60124f0fb0a2fb7857979b03c00f9f88bb46f8e9e79152b965515117 +size 44191 diff --git a/Unreal/Content/Project/AnimationTesting/Data/E_AnimationTesting_Avatars.uasset b/Unreal/Content/Project/AnimationTesting/Data/E_AnimationTesting_Avatars.uasset new file mode 100644 index 0000000..326bfd1 --- /dev/null +++ b/Unreal/Content/Project/AnimationTesting/Data/E_AnimationTesting_Avatars.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:74a4b7c56f65a024fcd0c9aceb88612414854adb71e2abf7294c7f349bea8a44 +size 1900 diff --git a/Unreal/Content/Project/AnimationTesting/Data/E_AnimationTesting_Cameras.uasset b/Unreal/Content/Project/AnimationTesting/Data/E_AnimationTesting_Cameras.uasset new file mode 100644 index 0000000..20c9330 --- /dev/null +++ b/Unreal/Content/Project/AnimationTesting/Data/E_AnimationTesting_Cameras.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:dbd5ca149c349cab9115086c2f828bd94fbc6c2e71f7c3e4b05bbd1e5e79e2cf +size 2198 diff --git a/Unreal/Content/Project/AnimationTesting/EmptyAnimation/AS_EmptyAnimation_Body.uasset b/Unreal/Content/Project/AnimationTesting/EmptyAnimation/AS_EmptyAnimation_Body.uasset new file mode 100644 index 0000000..a4b532b --- /dev/null +++ b/Unreal/Content/Project/AnimationTesting/EmptyAnimation/AS_EmptyAnimation_Body.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:bb8f5bc24aebe76e1573ad6cebf0c0f88041ac0797f6ea583e1c8210132135d2 +size 753268 diff --git a/Unreal/Content/Project/AnimationTesting/EmptyAnimation/AS_EmptyAnimation_Face.uasset b/Unreal/Content/Project/AnimationTesting/EmptyAnimation/AS_EmptyAnimation_Face.uasset new file mode 100644 index 0000000..4830be5 --- /dev/null +++ b/Unreal/Content/Project/AnimationTesting/EmptyAnimation/AS_EmptyAnimation_Face.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4243442d00f3aeb0978c5479fabecda0bb6f901ebbcd6041add8db981531d5a9 +size 126884664 diff --git a/Unreal/Content/Project/AnimationTesting/EmptyAnimation/LS_EmptyAnimation.uasset b/Unreal/Content/Project/AnimationTesting/EmptyAnimation/LS_EmptyAnimation.uasset new file mode 100644 index 0000000..d8f0c9a --- /dev/null +++ b/Unreal/Content/Project/AnimationTesting/EmptyAnimation/LS_EmptyAnimation.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:83c6975e7c8b328d9cdc843ce9be9bfaae5bd8a29ddec3a73c0429504b555149 +size 271180 diff --git a/Unreal/Content/Project/AnimationTesting/M_Animation_Testing.umap b/Unreal/Content/Project/AnimationTesting/M_Animation_Testing.umap new file mode 100644 index 0000000..4ae66e3 --- /dev/null +++ b/Unreal/Content/Project/AnimationTesting/M_Animation_Testing.umap @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a2c7709560d3ed86e7261d8e4c18814fe0d357a946a725dda503ec3a3d8505f1 +size 155174 diff --git a/Unreal/Content/Project/AnimationTesting/Materials/MI_AnimationTesting_Pawn.uasset b/Unreal/Content/Project/AnimationTesting/Materials/MI_AnimationTesting_Pawn.uasset new file mode 100644 index 0000000..7de9e74 --- /dev/null +++ b/Unreal/Content/Project/AnimationTesting/Materials/MI_AnimationTesting_Pawn.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f617dd52953e316b16ad9b4364bbcb6839ca102799aed952cbac015fa2d795f4 +size 7803 diff --git a/Unreal/Content/Project/AnimationTesting/UI/W_AnimationTesting.uasset b/Unreal/Content/Project/AnimationTesting/UI/W_AnimationTesting.uasset new file mode 100644 index 0000000..33828a0 --- /dev/null +++ b/Unreal/Content/Project/AnimationTesting/UI/W_AnimationTesting.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ba22978d848b03254f3f8abe28a08c16589832c5ad80bbd114c1202d13ab6fac +size 751329 diff --git a/Unreal/Content/Project/BP/Avatars/Avatar_Ben_BREX.uasset b/Unreal/Content/Project/BP/Avatars/Avatar_Ben_BREX.uasset index b81b406..a9f83f0 100644 --- a/Unreal/Content/Project/BP/Avatars/Avatar_Ben_BREX.uasset +++ b/Unreal/Content/Project/BP/Avatars/Avatar_Ben_BREX.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8cb1bf8133f28d19f3b35c71434f99f53c12ce03bd9df8540022f48785fa413f -size 58106 +oid sha256:b9d61eb539f5abd4893e35eba616dcc99cb82d2140f206d4c53d6b11d8c99f9b +size 55785 diff --git a/Unreal/Content/Project/BP/Avatars/Avatar_Jasmin.uasset b/Unreal/Content/Project/BP/Avatars/Avatar_Jasmin.uasset index 92ecb0d..dc4c5d6 100644 --- a/Unreal/Content/Project/BP/Avatars/Avatar_Jasmin.uasset +++ b/Unreal/Content/Project/BP/Avatars/Avatar_Jasmin.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:961e499ca8feccef352841c9e22fcdc1e8a4683dd40a20bdf691476ad1f9b515 +oid sha256:590d586789673df3badfadf20ada2abb9765e74144150915ef1946d1476beb8a size 51103 diff --git a/Unreal/Content/Project/BP/BP_Environment_Manager.uasset b/Unreal/Content/Project/BP/BP_Environment_Manager.uasset index b6714fb..1b189cd 100644 --- a/Unreal/Content/Project/BP/BP_Environment_Manager.uasset +++ b/Unreal/Content/Project/BP/BP_Environment_Manager.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5dc6c08162e52dc2f1fdd54286241c15eed6963f8b708b80b50f6b6ea9b8589a -size 160252 +oid sha256:3a3cdf58c8b26664c1ff054f601963a8513ec30875cf5d9a10bbc62fa804cff5 +size 159268 diff --git a/Unreal/Content/Project/BP/BP_Gameinstance.uasset b/Unreal/Content/Project/BP/BP_Gameinstance.uasset new file mode 100644 index 0000000..4d830da --- /dev/null +++ b/Unreal/Content/Project/BP/BP_Gameinstance.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:41c336b82843796813bcc0bb2c87ca7787d4dd19f07fab5bd5e7e872790631c9 +size 5598 diff --git a/Unreal/Content/Project/BP/BP_Media_Manager.uasset b/Unreal/Content/Project/BP/BP_Media_Manager.uasset index 9e36767..c3dc398 100644 --- a/Unreal/Content/Project/BP/BP_Media_Manager.uasset +++ b/Unreal/Content/Project/BP/BP_Media_Manager.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:07669cc3a5cb99088ee5c50477e0176f5412393f48406f0ca5fa0ae2a848ac8c -size 114341 +oid sha256:69041a628d34370dcbc3958118a8e089fcf59c739eecd9e19977ed110e34d4d7 +size 113357 diff --git a/Unreal/Content/Project/BP/BP_Project_Manager.uasset b/Unreal/Content/Project/BP/BP_Project_Manager.uasset index 84c4faa..87d34d8 100644 --- a/Unreal/Content/Project/BP/BP_Project_Manager.uasset +++ b/Unreal/Content/Project/BP/BP_Project_Manager.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:cae83a39d4a73eb0a68a650d4f03f03ad9a1ef78a9233019fc913a2afbfc263d -size 2872253 +oid sha256:9a8eec63a500e0f6a1c612f71bafc4723eb98b98c26d502e0ecefe72f56f6567 +size 2857962 diff --git a/Unreal/Content/Project/BP/Base/BFL_GlobalHelper.uasset b/Unreal/Content/Project/BP/Base/BFL_GlobalHelper.uasset index f0ee4c7..57e02aa 100644 --- a/Unreal/Content/Project/BP/Base/BFL_GlobalHelper.uasset +++ b/Unreal/Content/Project/BP/Base/BFL_GlobalHelper.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bce790c082aeac325a7d74739e8e4107b182581dd2da2e5270629ef79090d061 -size 17739 +oid sha256:bf80be63298a42a947eb56e24fca05103c0bc94ed3f1d6d24a3fe4eed8f882d8 +size 17466 diff --git a/Unreal/Content/Project/BP/Base/GM_Project.uasset b/Unreal/Content/Project/BP/Base/GM_Project.uasset index c431835..fed7b43 100644 --- a/Unreal/Content/Project/BP/Base/GM_Project.uasset +++ b/Unreal/Content/Project/BP/Base/GM_Project.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:78c0b0f49cb29b334607d05da46514ea4b58c005bb89f183f2873bd09572bbd0 -size 27465 +oid sha256:e4601d4639a5ea75e48f69ab80fd77ba415e4148a19cd923c7aaebff051551d7 +size 27464 diff --git a/Unreal/Content/Project/BP/Base/PC_Project.uasset b/Unreal/Content/Project/BP/Base/PC_Project.uasset index 6dfe827..99bc58e 100644 --- a/Unreal/Content/Project/BP/Base/PC_Project.uasset +++ b/Unreal/Content/Project/BP/Base/PC_Project.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:55eb6b2fda654fbf97df0f8358bc19569521eb60d65cf080a24747e7046a9e58 -size 206015 +oid sha256:5442b3132d40d8b428cc089641258e0b9216bf3ab6e241076b47ab53e3dee48e +size 192278 diff --git a/Unreal/Content/Project/BP/EnumsAndStructs/E_ProjectActions.uasset b/Unreal/Content/Project/BP/EnumsAndStructs/E_ProjectActions.uasset index f5198a1..1ea09fb 100644 --- a/Unreal/Content/Project/BP/EnumsAndStructs/E_ProjectActions.uasset +++ b/Unreal/Content/Project/BP/EnumsAndStructs/E_ProjectActions.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:dd2cd6338f0f1d00f4c0979b479e5582877fb8777da274d9b52f74057e436dc4 -size 6514 +oid sha256:a60eb6d1299976f51b2a37e93ca22b06b2796c8275737ccf4ba12264f6d7e42d +size 3969 diff --git a/Unreal/Content/Project/BP/EnumsAndStructs/S_ConfigSettings.uasset b/Unreal/Content/Project/BP/EnumsAndStructs/S_ConfigSettings.uasset index de814f2..e49adf6 100644 --- a/Unreal/Content/Project/BP/EnumsAndStructs/S_ConfigSettings.uasset +++ b/Unreal/Content/Project/BP/EnumsAndStructs/S_ConfigSettings.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2eefdc5266f01878929d61a78a98168c2c0114d17f77dc0bf27993df43222c0f -size 57359 +oid sha256:18ada72ab7bf534263d8461b2eb37bb2450ff58b4390c3e2a5bdc28c464cea4d +size 62455 diff --git a/Unreal/Content/Project/BP/Environments/BP_Environment_2D.uasset b/Unreal/Content/Project/BP/Environments/BP_Environment_2D.uasset index e02da8b..d4d2748 100644 --- a/Unreal/Content/Project/BP/Environments/BP_Environment_2D.uasset +++ b/Unreal/Content/Project/BP/Environments/BP_Environment_2D.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a5396bb17829f9c863e41bea0dffacc1f6814a0d1ca43dece5d0f9f75e937a67 +oid sha256:a73506382d437123f9cfdc60b4bbbd684c4cb3cf8a094c88b24b7753915af22c size 108728 diff --git a/Unreal/Content/Project/BP/Environments/BP_Environment_FogWithRing.uasset b/Unreal/Content/Project/BP/Environments/BP_Environment_FogWithRing.uasset index 1c61b7d..c8c0283 100644 --- a/Unreal/Content/Project/BP/Environments/BP_Environment_FogWithRing.uasset +++ b/Unreal/Content/Project/BP/Environments/BP_Environment_FogWithRing.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7190353537679b1f31a2a50ff66bac74bbd5008e96f52ad5436522df79c4cf1d -size 851811 +oid sha256:0e566031014dbf55b1787cea950369b3b1dcbf44525d7f78ea9e2284ec4bcb79 +size 848852 diff --git a/Unreal/Content/Project/BP/Environments/BP_Environment_MHC_Fireside.uasset b/Unreal/Content/Project/BP/Environments/BP_Environment_MHC_Fireside.uasset index 9228a30..8e6c70f 100644 --- a/Unreal/Content/Project/BP/Environments/BP_Environment_MHC_Fireside.uasset +++ b/Unreal/Content/Project/BP/Environments/BP_Environment_MHC_Fireside.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b4226b90a0daa04a379c934da479f4e5d03544fda99c1c8179278858ab2a3cc7 -size 51012 +oid sha256:9d77231a785393297382a1e45c98f66d00ce22fd4e48a3a8a31e2a2ef19cde2e +size 48422 diff --git a/Unreal/Content/Project/BP/Environments/BP_Environment_MHC_Moonlight.uasset b/Unreal/Content/Project/BP/Environments/BP_Environment_MHC_Moonlight.uasset index dfa9e20..8501f15 100644 --- a/Unreal/Content/Project/BP/Environments/BP_Environment_MHC_Moonlight.uasset +++ b/Unreal/Content/Project/BP/Environments/BP_Environment_MHC_Moonlight.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:73200ca2a3f8cd38533b7cdb08aedd7d7e16235f403c9685180fbd694ee1b249 -size 51050 +oid sha256:c141d8ff5866af3eb245f578708b62db80ee316b7ec8282624b4f705c8e4eec5 +size 48541 diff --git a/Unreal/Content/Project/BP/Environments/BP_Environment_MHC_Portrait.uasset b/Unreal/Content/Project/BP/Environments/BP_Environment_MHC_Portrait.uasset index b7f2844..14b0701 100644 --- a/Unreal/Content/Project/BP/Environments/BP_Environment_MHC_Portrait.uasset +++ b/Unreal/Content/Project/BP/Environments/BP_Environment_MHC_Portrait.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7b12e7b846ced72ca96a7ac761a35d20a995041981106bf9f094c4b20d0e7e69 -size 51568 +oid sha256:c3c6a922bf1fdee6d2261a242ecc353af81526ed4f75dcabf41e2fbbeac1c661 +size 49076 diff --git a/Unreal/Content/Project/BP/Environments/BP_Environment_MHC_Portrait_Dark.uasset b/Unreal/Content/Project/BP/Environments/BP_Environment_MHC_Portrait_Dark.uasset index b647b37..e3b6c04 100644 --- a/Unreal/Content/Project/BP/Environments/BP_Environment_MHC_Portrait_Dark.uasset +++ b/Unreal/Content/Project/BP/Environments/BP_Environment_MHC_Portrait_Dark.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f0ed9d5860db101bb9a51a7a9454cb08238ca1fba5b4d4bba96e7084ec753ce8 -size 51215 +oid sha256:a1038729a695c79d9f6ab41c98df39173638616f63f9bb7eed2ff71c0b6ce0d6 +size 48723 diff --git a/Unreal/Content/Project/BP/Environments/BP_Environment_MHC_RedLantern.uasset b/Unreal/Content/Project/BP/Environments/BP_Environment_MHC_RedLantern.uasset index 96d1a92..8987f21 100644 --- a/Unreal/Content/Project/BP/Environments/BP_Environment_MHC_RedLantern.uasset +++ b/Unreal/Content/Project/BP/Environments/BP_Environment_MHC_RedLantern.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ba4b524d54b42a7d1746fe82ac2692dfa7fd96929436d3451ac4d3ab7b7bf7d9 -size 51598 +oid sha256:1ec7f4a1fadf847b843c2d2a1258465e2f2d71b43fa20e5199631483b97bb201 +size 49089 diff --git a/Unreal/Content/Project/BP/Environments/BP_Environment_MHC_Split.uasset b/Unreal/Content/Project/BP/Environments/BP_Environment_MHC_Split.uasset index da1e64f..6eaa347 100644 --- a/Unreal/Content/Project/BP/Environments/BP_Environment_MHC_Split.uasset +++ b/Unreal/Content/Project/BP/Environments/BP_Environment_MHC_Split.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a806c0af3f5119c0558e54b839abbe89e4fea6ada1b07bf41cb388ceaba12b8a -size 49733 +oid sha256:89d1e15fb9933ad79c15b60e92023a639d60eabf66f1628543bd54c9416e8170 +size 47224 diff --git a/Unreal/Content/Project/BP/Environments/BP_Environment_MHC_Tungsten.uasset b/Unreal/Content/Project/BP/Environments/BP_Environment_MHC_Tungsten.uasset index ae05c41..54ece0f 100644 --- a/Unreal/Content/Project/BP/Environments/BP_Environment_MHC_Tungsten.uasset +++ b/Unreal/Content/Project/BP/Environments/BP_Environment_MHC_Tungsten.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:374d8bb05a3a0ce17a062d6a3dc37fb33e5380f86df19a19a749defcdffb63d3 -size 49907 +oid sha256:460cc6fbc1d44ae3ab7410b8e2a1954f2e7ca1fbe6bc7d808a60173a69330a68 +size 47317 diff --git a/Unreal/Content/Project/BP/Modes/DA_Mode_QnA.uasset b/Unreal/Content/Project/BP/Modes/DA_Mode_QnA.uasset new file mode 100644 index 0000000..4fbc496 --- /dev/null +++ b/Unreal/Content/Project/BP/Modes/DA_Mode_QnA.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6a14383eaa0706aacfbb47fa45b0fc0d916ec59a9e72202534af52a2479cd9af +size 3994 diff --git a/Unreal/Content/Project/BP/Modes/States/DT_ProjectStates_LookatTest.uasset b/Unreal/Content/Project/BP/Modes/States/DT_ProjectStates_LookatTest.uasset index a0e5f9a..56ffa48 100644 --- a/Unreal/Content/Project/BP/Modes/States/DT_ProjectStates_LookatTest.uasset +++ b/Unreal/Content/Project/BP/Modes/States/DT_ProjectStates_LookatTest.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d08d8b90f2073ec5130ce62e7d872403f3013a02af56d23a54eefe7d3eac2cdf -size 4371 +oid sha256:58cf2cc93c554e6d7519326a3f12fca874979b8b732308faa7f587649f593aee +size 3901 diff --git a/Unreal/Content/Project/BP/Modes/States/DT_ProjectStates_QnA.uasset b/Unreal/Content/Project/BP/Modes/States/DT_ProjectStates_QnA.uasset new file mode 100644 index 0000000..0ad8396 --- /dev/null +++ b/Unreal/Content/Project/BP/Modes/States/DT_ProjectStates_QnA.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:507dd26a710b7f7f5f930869dd478eac685cf3551ed04ce9029c5cef065b4a4d +size 3866 diff --git a/Unreal/Content/Project/Maps/M_Startup.umap b/Unreal/Content/Project/Maps/M_Startup.umap index cd8c650..45be4cd 100644 --- a/Unreal/Content/Project/Maps/M_Startup.umap +++ b/Unreal/Content/Project/Maps/M_Startup.umap @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:67660da5c3d40633638f371e1977267fb833c22dde3f14fe345eb28c0fbeb5b4 -size 169531 +oid sha256:1bda2aaf841412e81f102ff9303f99ec9db6e01634874cda789e7220c3c3e30f +size 198194 diff --git a/Unreal/Content/Project/Materials/2D_Environment/MM_2D_Environment.uasset b/Unreal/Content/Project/Materials/2D_Environment/MM_2D_Environment.uasset index 6c7276a..e2d25db 100644 --- a/Unreal/Content/Project/Materials/2D_Environment/MM_2D_Environment.uasset +++ b/Unreal/Content/Project/Materials/2D_Environment/MM_2D_Environment.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:071c4b6a7093b0392d6e6fd6735ed31e77bc3c43cd7e1031ce8a79e44b317062 -size 31918 +oid sha256:6bea808945faa5e21438123b6eb7e666584b73ac6a2e6b844b55b16cee8c51c1 +size 31234 diff --git a/Unreal/Content/Project/Meshes/Environment/SM_BackgroundSphere.uasset b/Unreal/Content/Project/Meshes/Environment/SM_BackgroundSphere.uasset index 7a7eba4..1b483e4 100644 --- a/Unreal/Content/Project/Meshes/Environment/SM_BackgroundSphere.uasset +++ b/Unreal/Content/Project/Meshes/Environment/SM_BackgroundSphere.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d634ad1cc45456d29570aaac63d6e56548df0d40211500a21ef0e0f7173f82a2 -size 103667 +oid sha256:d1b0682dac6e33ead4930a0c02ca1e73e768eb42c8eee26c58ea4161608e3aaa +size 106205 diff --git a/Unreal/Content/Project/Meshes/Environment/SM_HDRI_Dome.uasset b/Unreal/Content/Project/Meshes/Environment/SM_HDRI_Dome.uasset index 66575a1..4264e01 100644 --- a/Unreal/Content/Project/Meshes/Environment/SM_HDRI_Dome.uasset +++ b/Unreal/Content/Project/Meshes/Environment/SM_HDRI_Dome.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:41aab26a536cc098d3c525dc1fe7575531a64771cac9392372f048b650599501 -size 2386128 +oid sha256:cd3de79bf1e4dbab95a7876a8660b3b11dfa0d894d69dbcbce680fa81382b097 +size 2388974 diff --git a/Unreal/Content/Project/Meshes/Environment/SM_InvertedSphere.uasset b/Unreal/Content/Project/Meshes/Environment/SM_InvertedSphere.uasset index 259a5b1..5e5fc71 100644 --- a/Unreal/Content/Project/Meshes/Environment/SM_InvertedSphere.uasset +++ b/Unreal/Content/Project/Meshes/Environment/SM_InvertedSphere.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b987041fb22eadf1e0468566839319766f21cb2260c0db9f964bc71b452b7907 -size 397286 +oid sha256:e9def82b6ca9b15b374634d4064920339e7f9f22b2a95544f5c4c9ad4388b795 +size 399982 diff --git a/Unreal/Content/Project/Widgets/W_BlendWidget.uasset b/Unreal/Content/Project/Widgets/W_BlendWidget.uasset index 89476ac..8d3c124 100644 --- a/Unreal/Content/Project/Widgets/W_BlendWidget.uasset +++ b/Unreal/Content/Project/Widgets/W_BlendWidget.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:03884ca95bb44a9bc01a834adb0f40d16f9277cf1e9002d35c6833994609163b -size 108106 +oid sha256:ee174608ceccfc201e08c0511701560eab986a2e7da3a2651f79fd8984677d7b +size 108837 diff --git a/Unreal/Content/Project/Widgets/W_DialogueBox.uasset b/Unreal/Content/Project/Widgets/W_DialogueBox.uasset index 6ea8b6e..93dd710 100644 --- a/Unreal/Content/Project/Widgets/W_DialogueBox.uasset +++ b/Unreal/Content/Project/Widgets/W_DialogueBox.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d0c8131de375bb9820ab96bb3cfc175aa2afbb954ad67d0edb66401878d8ad00 +oid sha256:02fd852d23fef8ce326ea9127ca95f8cb8b9254d35349099e29393588d567bf5 size 979064 diff --git a/Unreal/Content/Project/Widgets/W_LogoWidget.uasset b/Unreal/Content/Project/Widgets/W_LogoWidget.uasset index 8d8bd60..0bbf389 100644 --- a/Unreal/Content/Project/Widgets/W_LogoWidget.uasset +++ b/Unreal/Content/Project/Widgets/W_LogoWidget.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4f1c51121a8407ab247cf6c6b00bc5fb1a263526e3dc5c49c9e4a567e5deafbb +oid sha256:42c5dc5c45ae64ce53a0828d483154ffb05feb771e16cee597ca6796d861b304 size 103986 diff --git a/Unreal/Content/Project/Widgets/W_Main.uasset b/Unreal/Content/Project/Widgets/W_Main.uasset index 7afd75a..53905f3 100644 --- a/Unreal/Content/Project/Widgets/W_Main.uasset +++ b/Unreal/Content/Project/Widgets/W_Main.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:23df7eb9b594bd608ca062847d66a6e52bba18eae12f2c4d4b56bc43925fb28f -size 206066 +oid sha256:faeb206137936d8ec8fabe4d7c35b134482ff216af16fb6378361878124fd1b7 +size 403920 diff --git a/Unreal/Content/Project/Widgets/W_Volume.uasset b/Unreal/Content/Project/Widgets/W_Volume.uasset index 3305a41..51cd0e5 100644 --- a/Unreal/Content/Project/Widgets/W_Volume.uasset +++ b/Unreal/Content/Project/Widgets/W_Volume.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e62894db8feab6d162e0674a5a84437343745c70925c7955bcbe4587e9615895 -size 232989 +oid sha256:86ce4d32ae94f712237eefde1ea6e9805f8f801f419bd94f58fa278d890aedec +size 231878 diff --git a/Unreal/Content/SPIE/Avatars/Avatar_SpieFrau_Shirt.uasset b/Unreal/Content/SPIE/Avatars/Avatar_SpieFrau_Shirt.uasset index afe7c97..de3b98a 100644 --- a/Unreal/Content/SPIE/Avatars/Avatar_SpieFrau_Shirt.uasset +++ b/Unreal/Content/SPIE/Avatars/Avatar_SpieFrau_Shirt.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:752942d555b5184b77a90d7ffefb77075dd3d535e0fa9866d6667c9a985645c0 -size 185266 +oid sha256:abc9011d2baaf1e749e1441cf0e5e0fc6fb3b80fe4ffe07bc9815d02bcdd5ad9 +size 185582 diff --git a/Unreal/Content/SPIE/BP/Mode/States/DT_SPIE_SpieOne.uasset b/Unreal/Content/SPIE/BP/Mode/States/DT_SPIE_SpieOne.uasset index 2fedce1..2538426 100644 --- a/Unreal/Content/SPIE/BP/Mode/States/DT_SPIE_SpieOne.uasset +++ b/Unreal/Content/SPIE/BP/Mode/States/DT_SPIE_SpieOne.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d4ababdba49557a7e2befc884813e384fa6930fc6137665c3755c2c5a41334e8 -size 6394 +oid sha256:57f470427b8970d7acedb37f6e41506b4b41db1182dd7246eee0342f2843dada +size 6509 diff --git a/Unreal/Content/Schema/Spie_Config.schema.json b/Unreal/Content/Schema/Spie_Config.schema.json index b36aef5..32309b7 100644 --- a/Unreal/Content/Schema/Spie_Config.schema.json +++ b/Unreal/Content/Schema/Spie_Config.schema.json @@ -414,7 +414,8 @@ "type": "enum", "enum": [ "OpenAI Transcription", - "Mircosoft Azure Congnitive Speech Services" + "Mircosoft Azure Congnitive Speech Services", + "nvidia NeMo Parakeet (local transcription)" ] }, "bUsePTT": diff --git a/Unreal/Plugins/AvatarCore_AI/Source/AvatarCore_AI/AvatarCore_AI.Build.cs b/Unreal/Plugins/AvatarCore_AI/Source/AvatarCore_AI/AvatarCore_AI.Build.cs index 47e5388..b833ed7 100644 --- a/Unreal/Plugins/AvatarCore_AI/Source/AvatarCore_AI/AvatarCore_AI.Build.cs +++ b/Unreal/Plugins/AvatarCore_AI/Source/AvatarCore_AI/AvatarCore_AI.Build.cs @@ -34,6 +34,7 @@ public class AvatarCore_AI : ModuleRules "WebSockets", "AudioCapture", "AudioMixer", + "Sockets", "HTTP" // ... add other public dependencies that you statically link with here ... } diff --git a/Unreal/Plugins/AvatarCore_AI/Source/AvatarCore_AI/Private/AIBaseManager.cpp b/Unreal/Plugins/AvatarCore_AI/Source/AvatarCore_AI/Private/AIBaseManager.cpp index 3a8ed4c..f97e532 100644 --- a/Unreal/Plugins/AvatarCore_AI/Source/AvatarCore_AI/Private/AIBaseManager.cpp +++ b/Unreal/Plugins/AvatarCore_AI/Source/AvatarCore_AI/Private/AIBaseManager.cpp @@ -176,7 +176,7 @@ void UAIBaseManager::RepeatText(FString TextToRepeat, bool DoRephrase) ResponseID++; FString Instruction; if (DoRephrase) - Instruction = "Repeat the text in your own words: " + TextToRepeat; + Instruction = "[REPHRASE] " + TextToRepeat; else Instruction = "[REPEAT] " + TextToRepeat; SendResponse(Instruction, false, true); @@ -454,6 +454,7 @@ void UAIBaseManager::ClearAllSystemInstructios() void UAIBaseManager::AddRepeatSystemInstruction() { UAIBaseManager::AddSystemInstruction(TEXT("Repeat Text"), TEXT("If the text starts with [REPEAT], repeat the text exactly word for word."), true); + UAIBaseManager::AddSystemInstruction(TEXT("Rephrase Text"), TEXT("If the text starts with [REPHRASE], repeat the text in your own words without stating that you are rephrasing."), true); } void UAIBaseManager::RemoveSystemInstruction(const FName Name) diff --git a/Unreal/Plugins/AvatarCore_Manager/Content/AvatarCoreLogging.uasset b/Unreal/Plugins/AvatarCore_Manager/Content/AvatarCoreLogging.uasset index 562bc80..45a6676 100644 --- a/Unreal/Plugins/AvatarCore_Manager/Content/AvatarCoreLogging.uasset +++ b/Unreal/Plugins/AvatarCore_Manager/Content/AvatarCoreLogging.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:25e5aa3fc5fb9dadfc9110f0f15318bcc1ce6e4500fcf7cdbcfc481a07d38632 -size 328843 +oid sha256:28f50d8978991777f319ba6a9e1ebaede105c0095d195695f461fd48e45de8fa +size 328745 diff --git a/Unreal/Plugins/AvatarCore_Manager/Content/AvatarCoreManager.uasset b/Unreal/Plugins/AvatarCore_Manager/Content/AvatarCoreManager.uasset index ca98b38..ce8fd5c 100644 --- a/Unreal/Plugins/AvatarCore_Manager/Content/AvatarCoreManager.uasset +++ b/Unreal/Plugins/AvatarCore_Manager/Content/AvatarCoreManager.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3c4f6f94e91fca1d7927efb521902495d6e046b7e427f2b9d8032213ea902bb0 -size 1840387 +oid sha256:e07f653749b3fdbfcf882eb51a0fd60a4bd4e1a26eb4793b911cc1372d7a2591 +size 1847725 diff --git a/Unreal/Plugins/AvatarCore_Manager/Content/IdleComponent.uasset b/Unreal/Plugins/AvatarCore_Manager/Content/IdleComponent.uasset index 4661340..a90de4e 100644 --- a/Unreal/Plugins/AvatarCore_Manager/Content/IdleComponent.uasset +++ b/Unreal/Plugins/AvatarCore_Manager/Content/IdleComponent.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8cb8be396730d755530ba6c3d9474aa51f84cbdcefcd69514c5bb069284ab9af -size 62768 +oid sha256:55a78c2a571f8e53a446868cc1b4e9c77f94529673e4e92ff6fffdbb160fec54 +size 65399 diff --git a/Unreal/Plugins/AvatarCore_Manager/Content/Miscellaneous/AvatarCoreSavegame.uasset b/Unreal/Plugins/AvatarCore_Manager/Content/Miscellaneous/AvatarCoreSavegame.uasset index bc23b79..5cbc169 100644 --- a/Unreal/Plugins/AvatarCore_Manager/Content/Miscellaneous/AvatarCoreSavegame.uasset +++ b/Unreal/Plugins/AvatarCore_Manager/Content/Miscellaneous/AvatarCoreSavegame.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:62579b7e9770ef0c000e54cb75c3d21d4d1d5a1a19d6db82922b5a98215ac3b5 -size 39015 +oid sha256:edda19c903479b3d4b7e59f1cdbc31d4ad761bf2241e4da28e583a95175914a6 +size 40807 diff --git a/Unreal/Plugins/AvatarCore_Manager/Content/Miscellaneous/EAvatarCoreDebugModules.uasset b/Unreal/Plugins/AvatarCore_Manager/Content/Miscellaneous/EAvatarCoreDebugModules.uasset index fbe6127..1209772 100644 --- a/Unreal/Plugins/AvatarCore_Manager/Content/Miscellaneous/EAvatarCoreDebugModules.uasset +++ b/Unreal/Plugins/AvatarCore_Manager/Content/Miscellaneous/EAvatarCoreDebugModules.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:da6ce50f248b5151f038634b276fcdbd1e3146db47a2f559a33431c3c4af1ffb -size 3164 +oid sha256:565d4d9768c24b3f91cdadfa802f570693b6d556d7bb7b256946250e95ca7079 +size 2412 diff --git a/Unreal/Plugins/AvatarCore_Manager/Content/Miscellaneous/MicCheck.uasset b/Unreal/Plugins/AvatarCore_Manager/Content/Miscellaneous/MicCheck.uasset index 7de7c62..a628feb 100644 --- a/Unreal/Plugins/AvatarCore_Manager/Content/Miscellaneous/MicCheck.uasset +++ b/Unreal/Plugins/AvatarCore_Manager/Content/Miscellaneous/MicCheck.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d02ca2c708b0496a5409095d1b90e8101325abb8a0ff8fa382cff4162671eb7e -size 122354 +oid sha256:ef9dfda1ca7bbb894191306b3bac779e8b8a6607281fd6101e131a6ce0eb2999 +size 122286 diff --git a/Unreal/Plugins/AvatarCore_Manager/Content/PlaceableGameplayTagCamera_BP.uasset b/Unreal/Plugins/AvatarCore_Manager/Content/PlaceableGameplayTagCamera_BP.uasset index c17b559..5b320c6 100644 --- a/Unreal/Plugins/AvatarCore_Manager/Content/PlaceableGameplayTagCamera_BP.uasset +++ b/Unreal/Plugins/AvatarCore_Manager/Content/PlaceableGameplayTagCamera_BP.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:dee53cc28813c8f01e1d59243ab8c65fc8cdf3a6ddaf25d66d4860a1fd837757 -size 212555 +oid sha256:811ff456b994d7374638af1f07fe90fdd15028cd86eb2f196c418f143e0a8188 +size 212773 diff --git a/Unreal/Plugins/AvatarCore_Manager/Content/StateManagement/BFL_ProjectHelper.uasset b/Unreal/Plugins/AvatarCore_Manager/Content/StateManagement/BFL_ProjectHelper.uasset index 0732cac..900bf67 100644 --- a/Unreal/Plugins/AvatarCore_Manager/Content/StateManagement/BFL_ProjectHelper.uasset +++ b/Unreal/Plugins/AvatarCore_Manager/Content/StateManagement/BFL_ProjectHelper.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7f69b23eb63a4d07ad9816cc0d94ed37515fd9b26fabaebd51672a6fd3f6b09b -size 77944 +oid sha256:64f8d86ad0d69abb8a33703c1ab8cf3b95e1ce3a0d1a9519eb1dd52aca63bfa2 +size 77016 diff --git a/Unreal/Plugins/AvatarCore_Manager/Content/StateManagement/BP_StateManager.uasset b/Unreal/Plugins/AvatarCore_Manager/Content/StateManagement/BP_StateManager.uasset index c9292aa..2e93d15 100644 --- a/Unreal/Plugins/AvatarCore_Manager/Content/StateManagement/BP_StateManager.uasset +++ b/Unreal/Plugins/AvatarCore_Manager/Content/StateManagement/BP_StateManager.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3513d179e8009e77b8e06a26d24b1333496f853a5e7bff4493e19a5f07074e96 -size 692761 +oid sha256:774148725543297ae271afb3919192283ca905ae6cfc9980b6784fadde8778ff +size 671653 diff --git a/Unreal/Plugins/AvatarCore_Manager/Content/StateManagement/Commands/AICommand_TestProceedToOutro.uasset b/Unreal/Plugins/AvatarCore_Manager/Content/StateManagement/Commands/AICommand_TestProceedToOutro.uasset index 0b785e5..af6fabd 100644 --- a/Unreal/Plugins/AvatarCore_Manager/Content/StateManagement/Commands/AICommand_TestProceedToOutro.uasset +++ b/Unreal/Plugins/AvatarCore_Manager/Content/StateManagement/Commands/AICommand_TestProceedToOutro.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6454914d8aa5720c6833263d9a7dcfbaa8ed88ee30bfc105bbe411d3660d7737 -size 68026 +oid sha256:ca2f2f0dbc6231a484cd3ed35d5c4c7a4f3ee92d8e681ecfa66c4323b628389f +size 68043 diff --git a/Unreal/Plugins/AvatarCore_Manager/Content/StateManagement/DataTable/PDA_Mode.uasset b/Unreal/Plugins/AvatarCore_Manager/Content/StateManagement/DataTable/PDA_Mode.uasset index 4c6f180..6b272ab 100644 --- a/Unreal/Plugins/AvatarCore_Manager/Content/StateManagement/DataTable/PDA_Mode.uasset +++ b/Unreal/Plugins/AvatarCore_Manager/Content/StateManagement/DataTable/PDA_Mode.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:65cf80e749250fcefa24dda965a09e29f23b582d5784c55c8d98025541a3e2db +oid sha256:ea2c8ab78aaddaeccfbd13d9bb6663ba1f9c349930bc23367db0594b18fcf20a size 17369 diff --git a/Unreal/Plugins/AvatarCore_Manager/Content/StateManagement/StructsAndEnums/S_Prompt_DialogueArray.uasset b/Unreal/Plugins/AvatarCore_Manager/Content/StateManagement/StructsAndEnums/S_Prompt_DialogueArray.uasset index 24fa510..465a50a 100644 --- a/Unreal/Plugins/AvatarCore_Manager/Content/StateManagement/StructsAndEnums/S_Prompt_DialogueArray.uasset +++ b/Unreal/Plugins/AvatarCore_Manager/Content/StateManagement/StructsAndEnums/S_Prompt_DialogueArray.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5008337d4fe4263b23f5b4dd74fc6f3b213674e1245073093b3f6eed6fe6e2b5 +oid sha256:b8eb3c9a30a76fcd40288274c01820eba1948f3394620620525fa9f488a18fa0 size 4258 diff --git a/Unreal/Plugins/AvatarCore_Manager/Content/StateManagement/StructsAndEnums/S_StateProcedure.uasset b/Unreal/Plugins/AvatarCore_Manager/Content/StateManagement/StructsAndEnums/S_StateProcedure.uasset index ae36e25..c604dd6 100644 --- a/Unreal/Plugins/AvatarCore_Manager/Content/StateManagement/StructsAndEnums/S_StateProcedure.uasset +++ b/Unreal/Plugins/AvatarCore_Manager/Content/StateManagement/StructsAndEnums/S_StateProcedure.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5a195fcefd7de1fcdab6293607beef1b0d11c8d1bd3e02d28f34477a31d34cc1 -size 17213 +oid sha256:7b8ba154eb4c8498e58503ab3b14bb1c448a12a66f1852b23cc5139d4dfa304d +size 16202 diff --git a/Unreal/Plugins/AvatarCore_Manager/Content/StateManagement/Widgets/BP_LookAtTest.uasset b/Unreal/Plugins/AvatarCore_Manager/Content/StateManagement/Widgets/BP_LookAtTest.uasset index 8b68ba6..7bedc14 100644 --- a/Unreal/Plugins/AvatarCore_Manager/Content/StateManagement/Widgets/BP_LookAtTest.uasset +++ b/Unreal/Plugins/AvatarCore_Manager/Content/StateManagement/Widgets/BP_LookAtTest.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6b134f4a3b9500393e4238f99ff770d4a1b67921742623cf5aea93a1fea5bd5a -size 29971 +oid sha256:922a6c52e331b3f0ea1ca8e7fb8800e091580374a50b70c71a6c24c3aeed7823 +size 41178 diff --git a/Unreal/Plugins/AvatarCore_Manager/Content/StateManagement/Widgets/Enum_UiDistanceLayer.uasset b/Unreal/Plugins/AvatarCore_Manager/Content/StateManagement/Widgets/Enum_UiDistanceLayer.uasset new file mode 100644 index 0000000..efcb1fc --- /dev/null +++ b/Unreal/Plugins/AvatarCore_Manager/Content/StateManagement/Widgets/Enum_UiDistanceLayer.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:14c9ff1f9a22b10805ab85070d1c01a63fc25e55d9be518595ab0346ebc23102 +size 2757 diff --git a/Unreal/Plugins/AvatarCore_Manager/Content/StateManagement/Widgets/Miscellaneous/E_WidgetPriority.uasset b/Unreal/Plugins/AvatarCore_Manager/Content/StateManagement/Widgets/Miscellaneous/E_WidgetPriority.uasset index c674fe5..901bdae 100644 --- a/Unreal/Plugins/AvatarCore_Manager/Content/StateManagement/Widgets/Miscellaneous/E_WidgetPriority.uasset +++ b/Unreal/Plugins/AvatarCore_Manager/Content/StateManagement/Widgets/Miscellaneous/E_WidgetPriority.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:07a8d890a34621fd7e24f8570ba6ed13ddcb212e97da1355fc6bb1e50211c151 -size 2677 +oid sha256:71717f8b325a42930dd8f3e4e648a213dca9c12285fa30c78548894ea90f3ce0 +size 3524 diff --git a/Unreal/Plugins/AvatarCore_Manager/Content/StateManagement/Widgets/W_AvatarReactionButton.uasset b/Unreal/Plugins/AvatarCore_Manager/Content/StateManagement/Widgets/W_AvatarReactionButton.uasset new file mode 100644 index 0000000..baf13eb --- /dev/null +++ b/Unreal/Plugins/AvatarCore_Manager/Content/StateManagement/Widgets/W_AvatarReactionButton.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0407df5ac216e7265d4a3dfa32274ad94f42c3cb86f3e477e1558ad86d0c638e +size 186328 diff --git a/Unreal/Plugins/AvatarCore_Manager/Content/StateManagement/Widgets/W_BaseStateWidget.uasset b/Unreal/Plugins/AvatarCore_Manager/Content/StateManagement/Widgets/W_BaseStateWidget.uasset index abfbbf3..d6b88b8 100644 --- a/Unreal/Plugins/AvatarCore_Manager/Content/StateManagement/Widgets/W_BaseStateWidget.uasset +++ b/Unreal/Plugins/AvatarCore_Manager/Content/StateManagement/Widgets/W_BaseStateWidget.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9347100c9b84e752bfdcb7beeab4c9f4d507a5908c2f07f4f5353a1e2b777f74 -size 189539 +oid sha256:5e254ba11071181f606bc35cd74b1f4e09ccbddac19eabd722cf79ecd81e1ddd +size 189482 diff --git a/Unreal/Plugins/AvatarCore_Manager/Content/StateManagement/Widgets/W_Base_DynamicAvatarReaction.uasset b/Unreal/Plugins/AvatarCore_Manager/Content/StateManagement/Widgets/W_Base_DynamicAvatarReaction.uasset index b90a75d..c24fcf1 100644 --- a/Unreal/Plugins/AvatarCore_Manager/Content/StateManagement/Widgets/W_Base_DynamicAvatarReaction.uasset +++ b/Unreal/Plugins/AvatarCore_Manager/Content/StateManagement/Widgets/W_Base_DynamicAvatarReaction.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ffb3cc7855a17504bec61250901fe1a74deb46a5b34a6b66308c04f0bacc878e -size 110436 +oid sha256:44ecd1477976e5d8b2955292c3e7d1fb2c72eb748950ffc445f5551ad84b36d3 +size 234544 diff --git a/Unreal/Plugins/AvatarCore_Manager/Content/StateManagement/Widgets/W_TestAvatarLookAtWidgetCollection.uasset b/Unreal/Plugins/AvatarCore_Manager/Content/StateManagement/Widgets/W_TestAvatarLookAtWidgetCollection.uasset index d7b772e..9ce5098 100644 --- a/Unreal/Plugins/AvatarCore_Manager/Content/StateManagement/Widgets/W_TestAvatarLookAtWidgetCollection.uasset +++ b/Unreal/Plugins/AvatarCore_Manager/Content/StateManagement/Widgets/W_TestAvatarLookAtWidgetCollection.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:56f0170b0662440eb41fdab5169d0608649dfe28825daa374a9740002bb0ced0 -size 380285 +oid sha256:7ed0405363a236d16d5f0af9df5cd1dded81011a05ef8f17390ea6e746bcd298 +size 467937 diff --git a/Unreal/Plugins/AvatarCore_Manager/Content/UnrealCommands/AICommandTestBigHead.uasset b/Unreal/Plugins/AvatarCore_Manager/Content/UnrealCommands/AICommandTestBigHead.uasset index 0ea8a09..94ac60e 100644 --- a/Unreal/Plugins/AvatarCore_Manager/Content/UnrealCommands/AICommandTestBigHead.uasset +++ b/Unreal/Plugins/AvatarCore_Manager/Content/UnrealCommands/AICommandTestBigHead.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6a8fa99a1c886668b195230e5baf79001e004e38af3e74cf98ecb9ce08caa620 -size 82856 +oid sha256:8939469f77d5f6b28d19e68f32dd453105c51ce8a0713e3c7b1f49e0df26cd3f +size 82775 diff --git a/Unreal/Plugins/AvatarCore_Manager/Content/UnrealCommands/AICommand_CurrentLocation.uasset b/Unreal/Plugins/AvatarCore_Manager/Content/UnrealCommands/AICommand_CurrentLocation.uasset index 25e1164..8783e78 100644 --- a/Unreal/Plugins/AvatarCore_Manager/Content/UnrealCommands/AICommand_CurrentLocation.uasset +++ b/Unreal/Plugins/AvatarCore_Manager/Content/UnrealCommands/AICommand_CurrentLocation.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d92276453a0370fbe8a0fa88e04c8da8040ca84e9e240d31bf8a76cbebc15764 -size 35741 +oid sha256:b1a33d50214a3cfe8a86331ffe371170db7ba6a5ad00c600b85efa7c956ba84f +size 35244 diff --git a/Unreal/Plugins/AvatarCore_Manager/Content/UnrealCommands/AICommand_CurrentTime.uasset b/Unreal/Plugins/AvatarCore_Manager/Content/UnrealCommands/AICommand_CurrentTime.uasset index 4382dc7..c794240 100644 --- a/Unreal/Plugins/AvatarCore_Manager/Content/UnrealCommands/AICommand_CurrentTime.uasset +++ b/Unreal/Plugins/AvatarCore_Manager/Content/UnrealCommands/AICommand_CurrentTime.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:76d8c0a10ba8097afb47a1e62d6164888733408388a255f548b5e2c516d58886 -size 27908 +oid sha256:c109d61b3e8a01f89c28cabdf646db30dbd4845ff3ba2f68a519a1a6ca35a169 +size 27477 diff --git a/Unreal/Plugins/AvatarCore_Manager/Content/UnrealCommands/OpenAI_Websearch_Command.uasset b/Unreal/Plugins/AvatarCore_Manager/Content/UnrealCommands/OpenAI_Websearch_Command.uasset index 19ca7a0..660d01f 100644 --- a/Unreal/Plugins/AvatarCore_Manager/Content/UnrealCommands/OpenAI_Websearch_Command.uasset +++ b/Unreal/Plugins/AvatarCore_Manager/Content/UnrealCommands/OpenAI_Websearch_Command.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ee7d8e8a915c988035d09fc4c0ba4d29e9b001bcf15e1adc04b8554cb62dcb84 -size 81649 +oid sha256:0760e82f009924a82693e2f0788fa7f13315ed4d022436f68962a2cab62d586f +size 79890 diff --git a/Unreal/Plugins/AvatarCore_Manager/Content/Widgets/Debug/Modules/W_AvatarCoreModuleEntry.uasset b/Unreal/Plugins/AvatarCore_Manager/Content/Widgets/Debug/Modules/W_AvatarCoreModuleEntry.uasset index b10d90a..30818e8 100644 --- a/Unreal/Plugins/AvatarCore_Manager/Content/Widgets/Debug/Modules/W_AvatarCoreModuleEntry.uasset +++ b/Unreal/Plugins/AvatarCore_Manager/Content/Widgets/Debug/Modules/W_AvatarCoreModuleEntry.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:96ae408325723997a94a934d8504f8948a143f0c95f5bc8253514aa39382ad68 -size 266052 +oid sha256:a3b0852ef0d33b6723bf048c0f7ed0e57bd42d1da5e504bcebb91806120f9681 +size 272457 diff --git a/Unreal/Plugins/AvatarCore_Manager/Content/Widgets/Debug/Modules/W_AvatarCoreModuleStates.uasset b/Unreal/Plugins/AvatarCore_Manager/Content/Widgets/Debug/Modules/W_AvatarCoreModuleStates.uasset index eb6b8c5..29ac39b 100644 --- a/Unreal/Plugins/AvatarCore_Manager/Content/Widgets/Debug/Modules/W_AvatarCoreModuleStates.uasset +++ b/Unreal/Plugins/AvatarCore_Manager/Content/Widgets/Debug/Modules/W_AvatarCoreModuleStates.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e426285df3ecc0a2ec737f0a1cc641718cafbb9fe69e03065ffcb2b0b747adac -size 166174 +oid sha256:838cb0017a3ddbd888a37a470fc989b5712e55d578193c35ceb9f1541313f12e +size 167363 diff --git a/Unreal/Plugins/AvatarCore_Manager/Content/Widgets/Debug/Pages/W_DebugAvatarCoreAnimation.uasset b/Unreal/Plugins/AvatarCore_Manager/Content/Widgets/Debug/Pages/W_DebugAvatarCoreAnimation.uasset index 1d9cb7d..d65a8a2 100644 --- a/Unreal/Plugins/AvatarCore_Manager/Content/Widgets/Debug/Pages/W_DebugAvatarCoreAnimation.uasset +++ b/Unreal/Plugins/AvatarCore_Manager/Content/Widgets/Debug/Pages/W_DebugAvatarCoreAnimation.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ed0d2138ac9bd7d0881785aa5ab7a30513d4d2ee803831a3b03145374c000864 -size 161090 +oid sha256:826aebc1f48fa4f136a9c1905c199dbf04449a5d09727b32092ab1f40a9c0aea +size 161066 diff --git a/Unreal/Plugins/AvatarCore_Manager/Content/Widgets/Debug/Pages/W_DebugAvatarCoreManager.uasset b/Unreal/Plugins/AvatarCore_Manager/Content/Widgets/Debug/Pages/W_DebugAvatarCoreManager.uasset index bbafc22..3f92410 100644 --- a/Unreal/Plugins/AvatarCore_Manager/Content/Widgets/Debug/Pages/W_DebugAvatarCoreManager.uasset +++ b/Unreal/Plugins/AvatarCore_Manager/Content/Widgets/Debug/Pages/W_DebugAvatarCoreManager.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a8e9e2e0a0338227ecb7f7cfce331989e06906459ce73a111218fc7472261413 -size 151904 +oid sha256:d508d3f664eb5dce283e3695a088c011c69aab6911398de627542ec681fd3584 +size 157553 diff --git a/Unreal/Plugins/AvatarCore_Manager/Content/Widgets/Debug/Pages/W_DebugAvatarCoreSTT.uasset b/Unreal/Plugins/AvatarCore_Manager/Content/Widgets/Debug/Pages/W_DebugAvatarCoreSTT.uasset index 322316e..7c4179c 100644 --- a/Unreal/Plugins/AvatarCore_Manager/Content/Widgets/Debug/Pages/W_DebugAvatarCoreSTT.uasset +++ b/Unreal/Plugins/AvatarCore_Manager/Content/Widgets/Debug/Pages/W_DebugAvatarCoreSTT.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:31cd45c7dd176aadf01c60a9d93d7a4ad7680df4e089697608e03c2508e20f3b +oid sha256:27255cc982492035331b5f199d3db81fb8c043da5e6211ba48e6643e4bca9f5e size 220332 diff --git a/Unreal/Plugins/AvatarCore_Manager/Content/Widgets/Debug/Pages/W_DebugAvatarCoreTTS.uasset b/Unreal/Plugins/AvatarCore_Manager/Content/Widgets/Debug/Pages/W_DebugAvatarCoreTTS.uasset index 9ffdbd9..3f1ce5c 100644 --- a/Unreal/Plugins/AvatarCore_Manager/Content/Widgets/Debug/Pages/W_DebugAvatarCoreTTS.uasset +++ b/Unreal/Plugins/AvatarCore_Manager/Content/Widgets/Debug/Pages/W_DebugAvatarCoreTTS.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:22fd51adbc68b5449ea3d285371be7cc27567208a2fa0d8b72ea860d91042d2a +oid sha256:d1ef777d7b8bfefaefe0ebcffeb3eb14b807802ea715ccaab9e42ac1cda10431 size 268822 diff --git a/Unreal/Plugins/AvatarCore_Manager/Content/Widgets/Debug/Pages/W_DebugProjectStates.uasset b/Unreal/Plugins/AvatarCore_Manager/Content/Widgets/Debug/Pages/W_DebugProjectStates.uasset index 4cdd392..c10307d 100644 --- a/Unreal/Plugins/AvatarCore_Manager/Content/Widgets/Debug/Pages/W_DebugProjectStates.uasset +++ b/Unreal/Plugins/AvatarCore_Manager/Content/Widgets/Debug/Pages/W_DebugProjectStates.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ec19ea547a8890eef09e2b5b7f1949c5c769ffe9ef240407ce3c6fc525159593 -size 214789 +oid sha256:ae31d9c33b82c42819e7814b7a3542a926dc5cfa3a43f9992c4b1142ab9f80da +size 213616 diff --git a/Unreal/Plugins/AvatarCore_Manager/Content/Widgets/Interfaces/BI_AvatarCoreStartupEntry.uasset b/Unreal/Plugins/AvatarCore_Manager/Content/Widgets/Interfaces/BI_AvatarCoreStartupEntry.uasset index a15d814..9711a58 100644 --- a/Unreal/Plugins/AvatarCore_Manager/Content/Widgets/Interfaces/BI_AvatarCoreStartupEntry.uasset +++ b/Unreal/Plugins/AvatarCore_Manager/Content/Widgets/Interfaces/BI_AvatarCoreStartupEntry.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:098605a180a67fa34f806d29d67df510107795f5a1fc42e65d9eb93884bbd282 -size 18439 +oid sha256:1e03bc4c5d62f6f10dd3caf4590e009c10e9c5107a126e1b97aa15321860dc25 +size 19177 diff --git a/Unreal/Plugins/AvatarCore_Manager/Content/Widgets/StartupScreen/W_AvatarCoreStartupScreen.uasset b/Unreal/Plugins/AvatarCore_Manager/Content/Widgets/StartupScreen/W_AvatarCoreStartupScreen.uasset index 5f03436..d124f2c 100644 --- a/Unreal/Plugins/AvatarCore_Manager/Content/Widgets/StartupScreen/W_AvatarCoreStartupScreen.uasset +++ b/Unreal/Plugins/AvatarCore_Manager/Content/Widgets/StartupScreen/W_AvatarCoreStartupScreen.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a2c16aa3c929ee2e02048bbc049961b86e4eaf52a5af4014380f00f68614a30c -size 77856 +oid sha256:a27f87304c545dd5173df8aac4044d93ffece686fd40a8499cdbfe6199bfd2de +size 77758 diff --git a/Unreal/Plugins/AvatarCore_Manager/Content/Widgets/Subtitles/BI_AvatarCoreSubtitleInterface.uasset b/Unreal/Plugins/AvatarCore_Manager/Content/Widgets/Subtitles/BI_AvatarCoreSubtitleInterface.uasset index fc1f295..932d058 100644 --- a/Unreal/Plugins/AvatarCore_Manager/Content/Widgets/Subtitles/BI_AvatarCoreSubtitleInterface.uasset +++ b/Unreal/Plugins/AvatarCore_Manager/Content/Widgets/Subtitles/BI_AvatarCoreSubtitleInterface.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:906d13eaf3c3bc34bee94579e4dfad801c02f1967925daacab23fb81b2af1e34 -size 19277 +oid sha256:f37dd043b7b1bd6122f356baaac742f6cc93f5edd9089edc46268bc53e4d6c95 +size 20334 diff --git a/Unreal/Plugins/AvatarCore_Manager/Content/Widgets/Subtitles/Typo/FontSubtitleBold.uasset b/Unreal/Plugins/AvatarCore_Manager/Content/Widgets/Subtitles/Typo/FontSubtitleBold.uasset index c8e9371..19e65de 100644 --- a/Unreal/Plugins/AvatarCore_Manager/Content/Widgets/Subtitles/Typo/FontSubtitleBold.uasset +++ b/Unreal/Plugins/AvatarCore_Manager/Content/Widgets/Subtitles/Typo/FontSubtitleBold.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:89a9fa8954361250b38350c42309fd9e5b7271cdaf633598a2dac9ea62bb2329 -size 8086 +oid sha256:954e59eb8246f3693e0d210df8fb9b4523db1300aebcc18f940d2c986946af78 +size 8099 diff --git a/Unreal/Plugins/AvatarCore_Manager/Content/Widgets/Subtitles/Typo/FontSubtitleRegular.uasset b/Unreal/Plugins/AvatarCore_Manager/Content/Widgets/Subtitles/Typo/FontSubtitleRegular.uasset index 98f1eaf..9261f15 100644 --- a/Unreal/Plugins/AvatarCore_Manager/Content/Widgets/Subtitles/Typo/FontSubtitleRegular.uasset +++ b/Unreal/Plugins/AvatarCore_Manager/Content/Widgets/Subtitles/Typo/FontSubtitleRegular.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6773a728531678e6f7b3e7cd641f849000c394bad3e879726eedb89dba25147d -size 7873 +oid sha256:7fce45f32feda8186ff533fc51cdab891326f776df988908ffd13ebf2854bd32 +size 7886 diff --git a/Unreal/Plugins/AvatarCore_Manager/Content/Widgets/Subtitles/Typo/FrutigerLTCom-Bold.uasset b/Unreal/Plugins/AvatarCore_Manager/Content/Widgets/Subtitles/Typo/FrutigerLTCom-Bold.uasset index 71845ef..cf5643f 100644 --- a/Unreal/Plugins/AvatarCore_Manager/Content/Widgets/Subtitles/Typo/FrutigerLTCom-Bold.uasset +++ b/Unreal/Plugins/AvatarCore_Manager/Content/Widgets/Subtitles/Typo/FrutigerLTCom-Bold.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9be0c601d5306a7b245aa4184599374f7fa568a9387a1f200828d20fa74f7241 -size 154012 +oid sha256:1e37d02ef48b3f723f22d9e7fcc895646c34ba17f146ded8e4170de3a28c7fd4 +size 153849 diff --git a/Unreal/Plugins/AvatarCore_Manager/Content/Widgets/Subtitles/Typo/FrutigerLTCom-Light.uasset b/Unreal/Plugins/AvatarCore_Manager/Content/Widgets/Subtitles/Typo/FrutigerLTCom-Light.uasset index da5ce95..84cb717 100644 --- a/Unreal/Plugins/AvatarCore_Manager/Content/Widgets/Subtitles/Typo/FrutigerLTCom-Light.uasset +++ b/Unreal/Plugins/AvatarCore_Manager/Content/Widgets/Subtitles/Typo/FrutigerLTCom-Light.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9d2e1031718c0c9362491c9f99d75835732c95cff467454b37e9de60d8a7b587 -size 201483 +oid sha256:3944b37edec810728dae034af3f9da867667cd3ba2cb6555a43b2f764c79a92d +size 201320 diff --git a/Unreal/Plugins/AvatarCore_Manager/Content/Widgets/Subtitles/Typo/MSGOTHIC.uasset b/Unreal/Plugins/AvatarCore_Manager/Content/Widgets/Subtitles/Typo/MSGOTHIC.uasset index 034a8f1..55a3066 100644 --- a/Unreal/Plugins/AvatarCore_Manager/Content/Widgets/Subtitles/Typo/MSGOTHIC.uasset +++ b/Unreal/Plugins/AvatarCore_Manager/Content/Widgets/Subtitles/Typo/MSGOTHIC.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d356fb35e7947b55638961a33a9b5316b277834b09d01c7d1ab320690316ab6e -size 8991698 +oid sha256:7ccc44ee3c9f38898101b55d68a580a2d75e53e32c030dfd440067ff1eaf1b72 +size 8991535 diff --git a/Unreal/Plugins/AvatarCore_Manager/Content/Widgets/Subtitles/Typo/SEGUIEMJ.uasset b/Unreal/Plugins/AvatarCore_Manager/Content/Widgets/Subtitles/Typo/SEGUIEMJ.uasset index 63da532..5d36468 100644 --- a/Unreal/Plugins/AvatarCore_Manager/Content/Widgets/Subtitles/Typo/SEGUIEMJ.uasset +++ b/Unreal/Plugins/AvatarCore_Manager/Content/Widgets/Subtitles/Typo/SEGUIEMJ.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f6c8465e360cdc3e80521b0cef034efe601b47127b402a9d1fb3cea1ff63ec93 -size 12419250 +oid sha256:02cc25d5bcda582f88b0ae5b2ae6ca0c086114939b05e7bd9af158d0d358c1b2 +size 12419087 diff --git a/Unreal/Plugins/AvatarCore_Manager/Content/Widgets/Subtitles/W_AvatarCoreSubtitleContainer.uasset b/Unreal/Plugins/AvatarCore_Manager/Content/Widgets/Subtitles/W_AvatarCoreSubtitleContainer.uasset index 31813dd..178d203 100644 --- a/Unreal/Plugins/AvatarCore_Manager/Content/Widgets/Subtitles/W_AvatarCoreSubtitleContainer.uasset +++ b/Unreal/Plugins/AvatarCore_Manager/Content/Widgets/Subtitles/W_AvatarCoreSubtitleContainer.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ba4ac740d1e2326d5c7fb64331f556fac35ba068ceabf9df45999a46b8dc82be -size 209500 +oid sha256:a87a1b8330e90aa566217e0d05a72517b2ccd6c93a8bef6374e39f1a4bc304c1 +size 219154 diff --git a/Unreal/Plugins/AvatarCore_Manager/Content/Widgets/Subtitles/W_AvatarCoreSubtitleEntry.uasset b/Unreal/Plugins/AvatarCore_Manager/Content/Widgets/Subtitles/W_AvatarCoreSubtitleEntry.uasset index 5548631..20fa13f 100644 --- a/Unreal/Plugins/AvatarCore_Manager/Content/Widgets/Subtitles/W_AvatarCoreSubtitleEntry.uasset +++ b/Unreal/Plugins/AvatarCore_Manager/Content/Widgets/Subtitles/W_AvatarCoreSubtitleEntry.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ac0bfd368f4fae22b41997860db328c8ef6a2063e1f20b18ee3ec09b84379346 -size 91157 +oid sha256:47bdad13fe2f79fcb197977ce75993fbf9d0c8669452b2f7d89aa85bde2cbb95 +size 97849 diff --git a/Unreal/Plugins/AvatarCore_Manager/Content/n8n/BP_N8N_Connector.uasset b/Unreal/Plugins/AvatarCore_Manager/Content/n8n/BP_N8N_Connector.uasset index 3203b7b..397151e 100644 --- a/Unreal/Plugins/AvatarCore_Manager/Content/n8n/BP_N8N_Connector.uasset +++ b/Unreal/Plugins/AvatarCore_Manager/Content/n8n/BP_N8N_Connector.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:dd78e5ceb04c264878d42998909a9957e9e15c3eeb060d317e404092ee8a337d +oid sha256:55dddf504fca34b2462e6ca4f6bf14621204f28ffba8a48ece05bee51dd50f8e size 232521 diff --git a/Unreal/Plugins/AvatarCore_Manager/Content/n8n/N8N_DebugPingCommand.uasset b/Unreal/Plugins/AvatarCore_Manager/Content/n8n/N8N_DebugPingCommand.uasset index 7d280cd..2de891f 100644 --- a/Unreal/Plugins/AvatarCore_Manager/Content/n8n/N8N_DebugPingCommand.uasset +++ b/Unreal/Plugins/AvatarCore_Manager/Content/n8n/N8N_DebugPingCommand.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8a4fedba6c298c3faebab560eb2552b22ef2b2f2a9f2e2ebb3696077b6714ee9 -size 102972 +oid sha256:9fd565d530be8bc2928f2f1c9ef886166e0837b1fa680294247a3007b29ffa71 +size 102270 diff --git a/Unreal/Plugins/AvatarCore_Manager/Content/n8n/N8N_WebResearchCommand.uasset b/Unreal/Plugins/AvatarCore_Manager/Content/n8n/N8N_WebResearchCommand.uasset index d1fb346..e5f99e1 100644 --- a/Unreal/Plugins/AvatarCore_Manager/Content/n8n/N8N_WebResearchCommand.uasset +++ b/Unreal/Plugins/AvatarCore_Manager/Content/n8n/N8N_WebResearchCommand.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9f073ad0c1c11f9890fa8d6119d8d3bd8bcf0fac6a04201ae9e1dac31a6c7f0d -size 124979 +oid sha256:9e9fb160d14f2efa8903abc9645dc8796b022ab5e8a2200927cd130e5239a2dd +size 123267 diff --git a/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/AnimBPs/AvatarCore_AnimInst_BodyForRetarget.uasset b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/AnimBPs/AvatarCore_AnimInst_BodyForRetarget.uasset index 88522ae..d82c0d2 100644 --- a/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/AnimBPs/AvatarCore_AnimInst_BodyForRetarget.uasset +++ b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/AnimBPs/AvatarCore_AnimInst_BodyForRetarget.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2c22123811e6bf24bbaecf6ce686ff091443ca999149ab6f2bc3e45ea973fda6 -size 697077 +oid sha256:cf10bb6ce6f516e3a4f0db5f3518b71d70c595328cc6a9ff00248b366d1497cf +size 698476 diff --git a/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/AnimBPs/AvatarCore_AnimInst_BodyVisible.uasset b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/AnimBPs/AvatarCore_AnimInst_BodyVisible.uasset index 958951a..406e0fb 100644 --- a/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/AnimBPs/AvatarCore_AnimInst_BodyVisible.uasset +++ b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/AnimBPs/AvatarCore_AnimInst_BodyVisible.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:760e778ea67e4893d98470180d78251f5d9893abc87d8f747972a65202b14e63 -size 1861947 +oid sha256:08ad1afe30456c3140083058dbf942f15cd1c18e7e6aee65182305f3d49024d0 +size 1876800 diff --git a/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/AnimBPs/AvatarCore_AnimInst_Face.uasset b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/AnimBPs/AvatarCore_AnimInst_Face.uasset index 7c144fb..ab7e3fb 100644 --- a/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/AnimBPs/AvatarCore_AnimInst_Face.uasset +++ b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/AnimBPs/AvatarCore_AnimInst_Face.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5939e21006429317796d90d8d80defdf5b7df225fc176827b31be6c6d12a8e38 -size 2017099 +oid sha256:aded6fec950125c7f1ce866302ffc36603c424e46f1ab7e2a9960bf51ba1f95d +size 2029779 diff --git a/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/AnimBPs/Interface/BI_AvatarAnimBP.uasset b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/AnimBPs/Interface/BI_AvatarAnimBP.uasset index 5461a1d..84063d2 100644 --- a/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/AnimBPs/Interface/BI_AvatarAnimBP.uasset +++ b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/AnimBPs/Interface/BI_AvatarAnimBP.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9bb90fb9d7fab001c005b85f7bd7651e9aa303f3beaa76d58c679b6c7c311709 -size 64621 +oid sha256:ff1702da230adef473e864ce7146930cc8213fc760bce99356ef706ad7a627e9 +size 65165 diff --git a/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/ControlRig/CR_AvatarCore_Body.uasset b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/ControlRig/CR_AvatarCore_Body.uasset index bb8b750..5dfec45 100644 --- a/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/ControlRig/CR_AvatarCore_Body.uasset +++ b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/ControlRig/CR_AvatarCore_Body.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:48a4f1f472121ceed0d5823ed5658629685476e68e988e67988be28ca5d3e34d -size 5127375 +oid sha256:c3b52b1607a91f7f01c6e29142bc0e4f9cd5c449ac5e59a6a9785a338b2fa364 +size 5177229 diff --git a/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/PressKit/AvatarCore_AnimInst_BodyPresskit.uasset b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/PressKit/AvatarCore_AnimInst_BodyPresskit.uasset new file mode 100644 index 0000000..c968f63 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/PressKit/AvatarCore_AnimInst_BodyPresskit.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c59f80b553777da24dca01f0d67ebb64b0136a626d974d52aef8168455e11ca6 +size 1784501 diff --git a/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/PressKit/AvatarCore_AnimInst_FacePresskit.uasset b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/PressKit/AvatarCore_AnimInst_FacePresskit.uasset new file mode 100644 index 0000000..43d7a6f --- /dev/null +++ b/Unreal/Plugins/AvatarCore_MetaHuman/Content/Animation/PressKit/AvatarCore_AnimInst_FacePresskit.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:56cddecf80158d1f1fc8ad68d0c4ac2b188d46a01ac81b9f7445f0ed92abe52d +size 891079 diff --git a/Unreal/Plugins/AvatarCore_MetaHuman/Content/BP/EditorUtility/EUS_MetahumanToAvatar.uasset b/Unreal/Plugins/AvatarCore_MetaHuman/Content/BP/EditorUtility/EUS_MetahumanToAvatar.uasset index 769d184..4bbe985 100644 --- a/Unreal/Plugins/AvatarCore_MetaHuman/Content/BP/EditorUtility/EUS_MetahumanToAvatar.uasset +++ b/Unreal/Plugins/AvatarCore_MetaHuman/Content/BP/EditorUtility/EUS_MetahumanToAvatar.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f5e72ff11f4d40a7af2b9bc117bce69783b8df4cce2e8e3208732b6e053b2274 -size 406103 +oid sha256:1db8d9db14971ac70c0b250a40ca19ae9bc21f405ce84ca77c1d1eeb86ec0546 +size 399863 diff --git a/Unreal/Plugins/AvatarCore_MetaHuman/Content/BP/MetaHuman/BaseAvatar.uasset b/Unreal/Plugins/AvatarCore_MetaHuman/Content/BP/MetaHuman/BaseAvatar.uasset index 67f1257..b8ccbd7 100644 --- a/Unreal/Plugins/AvatarCore_MetaHuman/Content/BP/MetaHuman/BaseAvatar.uasset +++ b/Unreal/Plugins/AvatarCore_MetaHuman/Content/BP/MetaHuman/BaseAvatar.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:360cc20135619ecccbd9120ff1f26bd4ee2f8db29df7296d6678401980ca221a -size 2084005 +oid sha256:d5d130eb9940b3ad8c6758820db27baf7ecced5abb09fee2636a608148ed4014 +size 2426536 diff --git a/Unreal/Plugins/AvatarCore_MetaHuman/Source/AvatarCore_MetaHuman/Public/AvatarCore_AnimationVariables.h b/Unreal/Plugins/AvatarCore_MetaHuman/Source/AvatarCore_MetaHuman/Public/AvatarCore_AnimationVariables.h index d0a8e72..02ce6ce 100644 --- a/Unreal/Plugins/AvatarCore_MetaHuman/Source/AvatarCore_MetaHuman/Public/AvatarCore_AnimationVariables.h +++ b/Unreal/Plugins/AvatarCore_MetaHuman/Source/AvatarCore_MetaHuman/Public/AvatarCore_AnimationVariables.h @@ -11,7 +11,8 @@ enum class EPressKitPosesBody : uint8 ArmsClosed UMETA(DisplayName = "Arms Closed"), Forward UMETA(DisplayName = "Forward"), FingerUp UMETA(DisplayName = "Finger Up"), - HandsTouching UMETA(DisplayName = "Hands Touching") + HandsTouching UMETA(DisplayName = "Hands Touching"), + Custom UMETA(DisplayName = "Custom") }; UENUM(BlueprintType) diff --git a/Unreal/Plugins/AvatarCore_MetaHuman/Source/AvatarCore_MetaHuman/Public/AvatarCore_BaseAvatar.h b/Unreal/Plugins/AvatarCore_MetaHuman/Source/AvatarCore_MetaHuman/Public/AvatarCore_BaseAvatar.h index d690451..6e9f234 100644 --- a/Unreal/Plugins/AvatarCore_MetaHuman/Source/AvatarCore_MetaHuman/Public/AvatarCore_BaseAvatar.h +++ b/Unreal/Plugins/AvatarCore_MetaHuman/Source/AvatarCore_MetaHuman/Public/AvatarCore_BaseAvatar.h @@ -38,13 +38,22 @@ public: * Modes | PressKit * ========================= */ UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Modes|PressKit", meta = (EditCondition = "bPressKitMode && !bAnimPreviewMode", EditConditionHides)) - TObjectPtr FacePoseCustom = nullptr; + EPressKitPosesFace FacePose_PressKit = EPressKitPosesFace::Neutral; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Modes|PressKit", meta = (EditCondition = "bPressKitMode && !bAnimPreviewMode && FacePose_PressKit == EPressKitPosesFace::Custom", EditConditionHides)) + TObjectPtr FacePoseCustom_PressKit = nullptr; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Modes|PressKit", meta = (EditCondition = "bPressKitMode && !bAnimPreviewMode", EditConditionHides)) + EPressKitPosesBody BodyPose_PressKit = EPressKitPosesBody::ArmsOpen; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Modes|PressKit", meta = (EditCondition = "bPressKitMode && !bAnimPreviewMode && BodyPose_PressKit == EPressKitPosesBody::Custom", EditConditionHides)) + TObjectPtr BodyPoseCustom_PressKit = nullptr; UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Modes|PressKit", meta = (EditCondition = "bPressKitMode && !bAnimPreviewMode", EditConditionHides)) - EPressKitPosesFace FacePose = EPressKitPosesFace::Neutral; + AActor* LookAtTarget_PressKit = nullptr; UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Modes|PressKit", meta = (EditCondition = "bPressKitMode && !bAnimPreviewMode", EditConditionHides)) - EPressKitPosesBody BodyPose = EPressKitPosesBody::ArmsOpen; + ELookAtSetup LookAtSetup_PressKit = ELookAtSetup::BodyHeadEyes; /* ========================= diff --git a/Unreal/Plugins/AvatarCore_STT/Source/AvatarCore_STT/AvatarCore_STT.Build.cs b/Unreal/Plugins/AvatarCore_STT/Source/AvatarCore_STT/AvatarCore_STT.Build.cs index 46b07f7..7cc6ed9 100644 --- a/Unreal/Plugins/AvatarCore_STT/Source/AvatarCore_STT/AvatarCore_STT.Build.cs +++ b/Unreal/Plugins/AvatarCore_STT/Source/AvatarCore_STT/AvatarCore_STT.Build.cs @@ -29,10 +29,6 @@ public class AvatarCore_STT : ModuleRules { // Enable exceptions bEnableExceptions = true; - - // Add definitions - PublicDefinitions.Add("PLATFORM_EXCEPTIONS_DISABLED=0"); - PublicDefinitions.Add("_HAS_EXCEPTIONS=1"); PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs; @@ -43,6 +39,11 @@ public class AvatarCore_STT : ModuleRules } ); + // Ensure ThirdParty/CoquiTTS is packaged in all builds (including shipping) + string CoquiTTSPath = System.IO.Path.Combine(ModuleDirectory, "..", "ThirdParty", "Parakeet"); + RuntimeDependencies.Add(System.IO.Path.Combine(CoquiTTSPath, "*.*")); + RuntimeDependencies.Add(System.IO.Path.Combine(CoquiTTSPath, "**", "*.*")); + PublicIncludePaths.Add(Path.Combine(ModuleDirectory, "..", "ThirdParty", "fvad", "include")); PublicAdditionalLibraries.Add(Path.Combine(ModuleDirectory, "..", "ThirdParty", "fvad", "lib", "fvad.lib")); @@ -96,6 +97,7 @@ public class AvatarCore_STT : ModuleRules "SlateCore", "AvatarCore_AI", "HTTP", + "Sockets", "Json", "JsonUtilities", "WebRTC", diff --git a/Unreal/Plugins/AvatarCore_STT/Source/AvatarCore_STT/Private/Processor/Azure/STTProcessorAzure.cpp b/Unreal/Plugins/AvatarCore_STT/Source/AvatarCore_STT/Private/Processor/Azure/STTProcessorAzure.cpp index 11a5b39..061e4e3 100644 --- a/Unreal/Plugins/AvatarCore_STT/Source/AvatarCore_STT/Private/Processor/Azure/STTProcessorAzure.cpp +++ b/Unreal/Plugins/AvatarCore_STT/Source/AvatarCore_STT/Private/Processor/Azure/STTProcessorAzure.cpp @@ -52,6 +52,7 @@ void USTTProcessorAzure::InitSTTProcessor(USTTManagerBase* BaseSTTManager, USTTB STTManager->OnSTTLog.Broadcast(TEXT("SpeechConfig initialized successfully.")); AzureRunnable = MakeUnique(config, audioConfig, STTManager->GetSpecialWords(), this, true); + BaseSTTManager->OnSTTFullyInitialized(); } void USTTProcessorAzure::ClearSTTProcessor() diff --git a/Unreal/Plugins/AvatarCore_STT/Source/AvatarCore_STT/Private/Processor/Parakeet/ParakeetRunnable.cpp b/Unreal/Plugins/AvatarCore_STT/Source/AvatarCore_STT/Private/Processor/Parakeet/ParakeetRunnable.cpp new file mode 100644 index 0000000..5d5bf2f --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/AvatarCore_STT/Private/Processor/Parakeet/ParakeetRunnable.cpp @@ -0,0 +1,164 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#include "Processor/Parakeet/ParakeetRunnable.h" +#include "Processor/Parakeet/STTParakeetProcessorBase.h" +#include "Sockets.h" +#include "Serialization/JsonReader.h" +#include "Serialization/JsonSerializer.h" +#include "Async/Async.h" + +FParakeetRunnable::FParakeetRunnable(FSocket* InSocket, USTTParakeetProcessorBase* InOwner) + : bIsRunning(false), Thread(nullptr), Owner(InOwner), Socket(InSocket) +{ + Thread = FRunnableThread::Create(this, TEXT("ParakeetRunnable")); +} + +FParakeetRunnable::~FParakeetRunnable() +{ + if (Thread) + { + Owner = nullptr; + Thread->Kill(true); + delete Thread; + } +} + +bool FParakeetRunnable::Init() +{ + return Socket != nullptr; +} + +uint32 FParakeetRunnable::Run() +{ + bIsRunning = true; + + TArray Buffer; + Buffer.Reserve(64 * 1024); + + while (bIsRunning) + { + // --- Send queued outgoing messages --- + FlushOutQueue(); + + // --- Read incoming data from socket --- + uint32 Pending = 0; + if (Socket && Socket->HasPendingData(Pending)) + { + int32 ToRead = FMath::Min((int32)Pending, 16 * 1024); + int32 OldSize = Buffer.Num(); + Buffer.AddUninitialized(ToRead); + int32 Read = 0; + if (!Socket->Recv(Buffer.GetData() + OldSize, ToRead, Read)) + { + // Socket error + break; + } + Buffer.SetNum(OldSize + Read); + + // Extract complete lines + int32 Start = 0; + for (int32 i = 0; i < Buffer.Num(); ++i) + { + if (Buffer[i] == '\n') + { + const int32 Len = i - Start; + FUTF8ToTCHAR Conv((const ANSICHAR*)(Buffer.GetData() + Start), Len); + FString Line = FString(Conv.Length(), Conv.Get()); + HandleLine(Line); + Start = i + 1; + } + } + if (Start > 0) + { + Buffer.RemoveAt(0, Start, EAllowShrinking::No); + } + } + else + { + FPlatformProcess::Sleep(0.005f); + } + } + + return 0; +} + +void FParakeetRunnable::Stop() +{ + bIsRunning = false; +} + +void FParakeetRunnable::SendLine(const FString& Line) +{ + OutQueue.Enqueue(Line); +} + +void FParakeetRunnable::FlushOutQueue() +{ + FString Line; + while (OutQueue.Dequeue(Line)) + { + if (!Socket) break; + FTCHARToUTF8 Conv(*Line); + int32 BytesSent = 0; + Socket->Send((uint8*)Conv.Get(), Conv.Length(), BytesSent); + } +} + +void FParakeetRunnable::HandleLine(const FString& Line) +{ + TSharedPtr Msg; + TSharedRef> Reader = TJsonReaderFactory<>::Create(Line); + if (!FJsonSerializer::Deserialize(Reader, Msg) || !Msg.IsValid()) + { + return; + } + + const FString Type = Msg->GetStringField(TEXT("type")); + + TWeakObjectPtr WeakOwner(Owner); + + if (Type == TEXT("ready")) + { + AsyncTask(ENamedThreads::GameThread, [WeakOwner]() + { + if (WeakOwner.IsValid()) + { + WeakOwner->OnParakeetReady(); + } + }); + } + else if (Type == TEXT("intermediate")) + { + FString Text = Msg->GetStringField(TEXT("text")); + AsyncTask(ENamedThreads::GameThread, [WeakOwner, Text]() + { + if (WeakOwner.IsValid()) + { + WeakOwner->OnParakeetIntermediate(Text); + } + }); + } + else if (Type == TEXT("final")) + { + FString Text = Msg->GetStringField(TEXT("text")); + FString Language = Msg->GetStringField(TEXT("language")); + AsyncTask(ENamedThreads::GameThread, [WeakOwner, Text, Language]() + { + if (WeakOwner.IsValid()) + { + WeakOwner->OnParakeetFinal(Text, Language); + } + }); + } + else if (Type == TEXT("error")) + { + FString Message = Msg->GetStringField(TEXT("message")); + AsyncTask(ENamedThreads::GameThread, [WeakOwner, Message]() + { + if (WeakOwner.IsValid()) + { + WeakOwner->OnParakeetError(Message); + } + }); + } +} diff --git a/Unreal/Plugins/AvatarCore_STT/Source/AvatarCore_STT/Private/Processor/Parakeet/STTParakeetProcessorBase.cpp b/Unreal/Plugins/AvatarCore_STT/Source/AvatarCore_STT/Private/Processor/Parakeet/STTParakeetProcessorBase.cpp new file mode 100644 index 0000000..612ed92 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/AvatarCore_STT/Private/Processor/Parakeet/STTParakeetProcessorBase.cpp @@ -0,0 +1,616 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#include "Processor/Parakeet/STTParakeetProcessorBase.h" +#include "Processor/Parakeet/ParakeetRunnable.h" +#include "STTManagerBase.h" +#include "Sockets.h" +#include "SocketSubsystem.h" +#include "IPAddress.h" +#include "HAL/PlatformProcess.h" +#include "Async/Async.h" +#include "Misc/Base64.h" +#include "Serialization/JsonReader.h" +#include "Serialization/JsonSerializer.h" +#include "Engine/World.h" +#include "TimerManager.h" +#include "Misc/Paths.h" + +namespace +{ + static constexpr int32 ParakeetDefaultPort = 40200; +} + +// --------------------------------------------------------------------------- +// Init / Clear / Destroy +// --------------------------------------------------------------------------- + +void USTTParakeetProcessorBase::InitSTTProcessor(USTTManagerBase* BaseSTTManager, USTTBaseProcessorConfig* InProcessorConfig, bool InDebugMode) +{ + USTTProcessorBase::InitSTTProcessor(BaseSTTManager, InProcessorConfig, InDebugMode); + + ParakeetConfig = Cast(InProcessorConfig); + if (!ParakeetConfig) + { + STTManager->OnSTTError.Broadcast(TEXT("Parakeet Processor Config is invalid.")); + return; + } + +#if WITH_EDITOR + bIsEditor = GIsEditor; +#else + bIsEditor = false; +#endif + + // Resolve batch file path + ParakeetBatPath = FPaths::Combine(FPaths::ProjectPluginsDir(), TEXT("AvatarCore_STT/Source/ThirdParty/Parakeet/ParakeetSTT.bat")); + if (!FPaths::FileExists(ParakeetBatPath)) + { + STTManager->OnSTTError.Broadcast(FString::Printf(TEXT("ParakeetSTT.bat not found: %s"), *ParakeetBatPath)); + } + ParakeetVenvPath = FPaths::Combine(FPlatformMisc::GetEnvironmentVariable(TEXT("LOCALAPPDATA")), TEXT("AvatarCore/ParakeetVenv/")); + + if (bDebugMode) + STTManager->OnSTTLog.Broadcast(TEXT("ParakeetProcessor Init: preparing connection...")); + + // Create TCP socket + ISocketSubsystem* Subsystem = ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM); + if (!Subsystem) + { + STTManager->OnSTTError.Broadcast(TEXT("Socket subsystem not available")); + return; + } + + ParakeetSocket = Subsystem->CreateSocket(NAME_Stream, TEXT("ParakeetClient"), false); + if (!ParakeetSocket) + { + STTManager->OnSTTError.Broadcast(TEXT("Failed to create Parakeet client socket")); + return; + } + + TSharedRef Addr = Subsystem->CreateInternetAddr(); + bool bIsValid = false; + Addr->SetIp(*ParakeetConfig->Host, bIsValid); + Addr->SetPort(ParakeetConfig->Port > 0 ? ParakeetConfig->Port : ParakeetDefaultPort); + if (!bIsValid) + { + STTManager->OnSTTError.Broadcast(TEXT("Invalid address for Parakeet")); + return; + } + + ParakeetAddr = Addr; + ConnectAttempts = 0; + + // Editor: try to connect to existing instance (with brief delay for server to be ready after previous disconnect) + // Non-editor: kill stale processes and launch fresh + if (!bIsEditor) + { + KillExistingParakeetProcesses(); + if (UWorld* World = STTManager->GetWorld()) + { + World->GetTimerManager().SetTimer(ConnectTimerHandle, this, &USTTParakeetProcessorBase::RestartParakeet, 0.25f, false); + } + } + else + { + if (UWorld* World = STTManager->GetWorld()) + { + // Small delay so the Python server has time to loop back to accept() after previous client disconnect + World->GetTimerManager().SetTimer(ConnectTimerHandle, FTimerDelegate::CreateWeakLambda(this, [this]() + { + ConnectToParakeet(true); + }), 0.5f, false); + } + } +} + +void USTTParakeetProcessorBase::ClearSTTProcessor() +{ + if (!STTManager->IsSTTFullyInitialized()) + return; + + USTTProcessorBase::ClearSTTProcessor(); + + // Send clear to Python + TSharedPtr Obj = MakeShared(); + Obj->SetStringField(TEXT("type"), TEXT("clear")); + SendJsonMessage(Obj); + + IntermediateResult.Empty(); +} + +void USTTParakeetProcessorBase::DestroySTTProcessor() +{ + // Stop runnable + delete Runnable; + Runnable = nullptr; + + // Unbind delegate + if (IsValid(STTManager)) + { + STTManager->OnSpeechStateChanged.RemoveDynamic(this, &USTTParakeetProcessorBase::OnSpeechStateChanged); + + if (UWorld* World = STTManager->GetWorld()) + { + World->GetTimerManager().ClearTimer(ConnectTimerHandle); + } + } + + // Close socket + if (ParakeetSocket) + { + ParakeetSocket->Close(); + ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM)->DestroySocket(ParakeetSocket); + ParakeetSocket = nullptr; + } + + // Kill local process if we spawned it (non-editor only) + if (!bIsEditor && bLaunchedLocalServer) + { + if (ParakeetProcHandle.IsValid()) + { + FPlatformProcess::TerminateProc(ParakeetProcHandle, true); + FPlatformProcess::CloseProc(ParakeetProcHandle); + } + bLaunchedLocalServer = false; + } + + if (!bIsEditor) + { + KillExistingParakeetProcesses(); + } + else + { + if (bDebugMode && IsValid(STTManager)) + STTManager->OnSTTLog.Broadcast(TEXT("Editor mode: Parakeet Python server kept alive for reconnect")); + } + + STTManager = nullptr; +} + +// --------------------------------------------------------------------------- +// Audio chunk handling +// --------------------------------------------------------------------------- + +void USTTParakeetProcessorBase::OnChunkReceived(TArray PCMData, FAudioInformation AudioInformation) +{ + if (IsValid(STTManager) && STTManager->IsBlocked()) + { + if (bDebugMode && IsValid(STTManager)) + STTManager->OnSTTLog.Broadcast(TEXT("Parakeet: OnChunkReceived skipped (blocked)")); + return; + } + + if (!Runnable) + { + if (bDebugMode && IsValid(STTManager)) + STTManager->OnSTTLog.Broadcast(TEXT("Parakeet: OnChunkReceived skipped (no runnable)")); + return; + } + + // Encode PCM16 data as base64 + TArray RawBytes; + RawBytes.Append(reinterpret_cast(PCMData.GetData()), PCMData.Num() * sizeof(int16)); + FString B64 = FBase64::Encode(RawBytes); + + // Build JSON message + TSharedPtr Obj = MakeShared(); + Obj->SetStringField(TEXT("type"), TEXT("audio")); + Obj->SetStringField(TEXT("data_b64"), B64); + + SendJsonMessage(Obj); + + // Start transcription tracking if not already running + if (!bTranscriptionRunning) + { + USTTProcessorBase::OnTranscriptionStarted(); + } +} + +// --------------------------------------------------------------------------- +// Speech state handling +// --------------------------------------------------------------------------- + +void USTTParakeetProcessorBase::OnSpeechStateChanged(ESTTTalkingState TalkingState) +{ + if (!STTManager->IsSTTFullyInitialized()) + return; + + if (bDebugMode && IsValid(STTManager)) + STTManager->OnSTTLog.Broadcast(FString::Printf(TEXT("Parakeet: OnSpeechStateChanged -> %d"), (int32)TalkingState)); + + if (TalkingState == ESTTTalkingState::BLOCKED) + { + // Discard everything + TSharedPtr Obj = MakeShared(); + Obj->SetStringField(TEXT("type"), TEXT("clear")); + SendJsonMessage(Obj); + IntermediateResult.Empty(); + bTranscriptionRunning = false; + } + else if (TalkingState == ESTTTalkingState::SILENCE || TalkingState == ESTTTalkingState::TRANSCRIBING) + { + if (bTranscriptionRunning) + { + // Request final transcription + TSharedPtr Obj = MakeShared(); + Obj->SetStringField(TEXT("type"), TEXT("finalize")); + SendJsonMessage(Obj); + } + else + { + // If we have accumulated intermediate text but transcription already ended + if (!IntermediateResult.IsEmpty()) + { + USTTProcessorBase::OnTranscriptionIntermediateResult(TranscriptionCounter, IntermediateResult); + IntermediateResult.Empty(); + } + } + } +} + +// --------------------------------------------------------------------------- +// Callbacks from FParakeetRunnable (game thread) +// --------------------------------------------------------------------------- + +void USTTParakeetProcessorBase::OnParakeetReady() +{ + if (bDebugMode && IsValid(STTManager)) + STTManager->OnSTTLog.Broadcast(TEXT("Parakeet server READY")); + + if (IsValid(STTManager)) + { + STTManager->OnSTTFullyInitialized(); + STTManager->OnSpeechStateChanged.AddUniqueDynamic(this, &USTTParakeetProcessorBase::OnSpeechStateChanged); + } +} + +void USTTParakeetProcessorBase::OnParakeetIntermediate(const FString& Text) +{ + if (IsValid(STTManager) && STTManager->IsBlocked()) + return; + + IntermediateResult = Text; + + if (!Text.IsEmpty()) + { + USTTProcessorBase::OnTranscriptionIntermediateResult(TranscriptionCounter, Text); + } +} + +void USTTParakeetProcessorBase::OnParakeetFinal(const FString& Text, const FString& Language) +{ + if (IsValid(STTManager) && STTManager->IsBlocked()) + return; + + DetectedLanguage = Language.IsEmpty() ? TEXT("LANGUAGE_NOT_DETECTED") : Language; + + if (!Text.IsEmpty()) + { + USTTProcessorBase::OnTranscriptionResult(TranscriptionCounter, Text, DetectedLanguage); + } + else if (!IntermediateResult.IsEmpty()) + { + // Fall back to last intermediate if final is empty + USTTProcessorBase::OnTranscriptionResult(TranscriptionCounter, IntermediateResult, DetectedLanguage); + } + + IntermediateResult.Empty(); +} + +void USTTParakeetProcessorBase::OnParakeetError(const FString& Error) +{ + if (IsValid(STTManager)) + STTManager->OnSTTError.Broadcast(FString::Printf(TEXT("Parakeet ERROR: %s"), *Error)); +} + +// --------------------------------------------------------------------------- +// JSON send helper +// --------------------------------------------------------------------------- + +void USTTParakeetProcessorBase::SendJsonMessage(const TSharedPtr& Obj) +{ + if (!Runnable) + { + UE_LOG(LogTemp, Warning, TEXT("No Runnable")); + return; + } + + FString Payload; + TSharedRef>> Writer = + TJsonWriterFactory>::Create(&Payload); + FJsonSerializer::Serialize(Obj.ToSharedRef(), Writer); + Writer->Close(); + Payload.AppendChar('\n'); + Runnable->SendLine(Payload); +} + +// --------------------------------------------------------------------------- +// Configuration message +// --------------------------------------------------------------------------- + +void USTTParakeetProcessorBase::SendConfiguration() +{ + if (!ParakeetConfig) + { + if (IsValid(STTManager)) + STTManager->OnSTTError.Broadcast(TEXT("Cannot send configuration: ParakeetConfig is null")); + return; + } + + TSharedPtr ConfigObj = MakeShared(); + TSharedPtr ParamsObj = MakeShared(); + + ParamsObj->SetStringField(TEXT("pretrained_model"), ParakeetConfig->PretrainedModel); + ParamsObj->SetStringField(TEXT("device"), ParakeetConfig->Device); + ParamsObj->SetNumberField(TEXT("update_interval"), ParakeetConfig->UpdateIntervalSec); + + ConfigObj->SetStringField(TEXT("type"), TEXT("config")); + ConfigObj->SetObjectField(TEXT("params"), ParamsObj); + + FString Payload; + TSharedRef>> Writer = + TJsonWriterFactory>::Create(&Payload); + FJsonSerializer::Serialize(ConfigObj.ToSharedRef(), Writer); + Writer->Close(); + Payload.AppendChar('\n'); + + Runnable->SendLine(Payload); + + if (bDebugMode && IsValid(STTManager)) + STTManager->OnSTTLog.Broadcast(FString::Printf(TEXT("Sent CONFIG to Parakeet (bytes=%d)"), Payload.Len())); +} + +// --------------------------------------------------------------------------- +// Connection management +// --------------------------------------------------------------------------- + +void USTTParakeetProcessorBase::TryConnect() +{ + ConnectToParakeet(false); +} + +void USTTParakeetProcessorBase::ConnectToParakeet(bool StartIfFailed) +{ + if (!ParakeetSocket || !ParakeetAddr.IsValid()) + return; + + if (ParakeetSocket->GetConnectionState() == SCS_Connected) + { + if (IsValid(STTManager)) + { + if (UWorld* World = STTManager->GetWorld()) + World->GetTimerManager().ClearTimer(ConnectTimerHandle); + } + ConnectAttempts = 0; + return; + } + + // Clear timer to avoid overlapping attempts + if (IsValid(STTManager)) + { + if (UWorld* World = STTManager->GetWorld()) + World->GetTimerManager().ClearTimer(ConnectTimerHandle); + } + + // Connect off the game thread + TWeakObjectPtr WeakThis(this); + Async(EAsyncExecution::Thread, [WeakThis, StartIfFailed]() + { + if (!WeakThis.IsValid()) + return; + + USTTParakeetProcessorBase* Self = WeakThis.Get(); + bool bConnected = false; + if (Self->ParakeetSocket && Self->ParakeetAddr.IsValid()) + { + bConnected = Self->ParakeetSocket->Connect(*Self->ParakeetAddr); + } + + AsyncTask(ENamedThreads::GameThread, [WeakThis, bConnected, StartIfFailed]() + { + if (!WeakThis.IsValid()) + return; + + USTTParakeetProcessorBase* SelfGT = WeakThis.Get(); + if (bConnected) + { + if (SelfGT->bDebugMode && IsValid(SelfGT->STTManager)) + SelfGT->STTManager->OnSTTLog.Broadcast(TEXT("Connected to Parakeet server, sending configuration...")); + + SelfGT->ParakeetSocket->SetNoDelay(true); + SelfGT->Runnable = new FParakeetRunnable(SelfGT->ParakeetSocket, SelfGT); + SelfGT->SendConfiguration(); + } + else + { + if (StartIfFailed) + { + SelfGT->StartParakeetProcess(); + } + SelfGT->ConnectAttempts++; + if (IsValid(SelfGT->STTManager)) + { + if (UWorld* World = SelfGT->STTManager->GetWorld()) + { + World->GetTimerManager().SetTimer(SelfGT->ConnectTimerHandle, SelfGT, &USTTParakeetProcessorBase::TryConnect, 0.25f, false, 0.25f); + } + } + } + }); + }); +} + +// --------------------------------------------------------------------------- +// Process management +// --------------------------------------------------------------------------- + +bool USTTParakeetProcessorBase::StartParakeetProcess() +{ + if (!FPaths::FileExists(ParakeetBatPath)) + { + if (IsValid(STTManager)) + STTManager->OnSTTError.Broadcast(FString::Printf(TEXT("ParakeetSTT.bat not found at: %s"), *ParakeetBatPath)); + return false; + } + + const FString Args = FString::Printf(TEXT("\"%s\" serve-config --host %s --port %d"), + *ParakeetConfig->PythonPath, + *ParakeetConfig->Host, + ParakeetConfig->Port > 0 ? ParakeetConfig->Port : ParakeetDefaultPort); + + uint32 ProcId = 0; + + // In editor mode we skip pipe redirection entirely. + // The Python process has its own console window for log output. + // Redirecting stdout/stderr via pipes causes BrokenPipeError on the + // Python side when the UObject (and its pipe-reader thread) is destroyed + // at the end of a PIE session while the Python process is kept alive for + // reconnect, which stalls the transcription thread on subsequent connects. + if (bIsEditor) + { + ParakeetProcHandle = FPlatformProcess::CreateProc(*ParakeetBatPath, *Args, false, !bDebugMode, !bDebugMode, &ProcId, 0, nullptr, nullptr, nullptr); + if (!ParakeetProcHandle.IsValid()) + { + if (IsValid(STTManager)) + STTManager->OnSTTError.Broadcast(TEXT("Failed to launch ParakeetSTT process")); + return false; + } + + bLaunchedLocalServer = true; + if (bDebugMode && IsValid(STTManager)) + STTManager->OnSTTLog.Broadcast(FString::Printf(TEXT("Launched ParakeetSTT.bat (PID=%u) — editor mode, no pipes"), ProcId)); + + return true; + } + + // Non-editor: use pipes so we can capture Python stdout/stderr into UE logs + void* StdOutReadPipe = nullptr; + void* StdOutWritePipe = nullptr; + void* StdErrReadPipe = nullptr; + void* StdErrWritePipe = nullptr; + + FPlatformProcess::CreatePipe(StdOutReadPipe, StdOutWritePipe); + FPlatformProcess::CreatePipe(StdErrReadPipe, StdErrWritePipe); + + //Removed the piping for DebugMode - especially in edtior build pipes break after first connect + ParakeetProcHandle = FPlatformProcess::CreateProc(*ParakeetBatPath, *Args, false, !(bDebugMode && bIsEditor), !(bDebugMode && bIsEditor), &ProcId, 0, nullptr, (bDebugMode && bIsEditor) ? nullptr : StdOutWritePipe, bDebugMode ? nullptr : StdErrWritePipe); + if (!ParakeetProcHandle.IsValid()) + { + if (IsValid(STTManager)) + STTManager->OnSTTError.Broadcast(TEXT("Failed to launch ParakeetSTT process")); + FPlatformProcess::ClosePipe(StdOutReadPipe, StdOutWritePipe); + FPlatformProcess::ClosePipe(StdErrReadPipe, StdErrWritePipe); + return false; + } + + bLaunchedLocalServer = true; + if (bDebugMode && IsValid(STTManager)) + STTManager->OnSTTLog.Broadcast(FString::Printf(TEXT("Launched ParakeetSTT.bat (PID=%u) with args: %s"), ProcId, *Args)); + + // Async reader for process stdout/stderr + TWeakObjectPtr WeakThis(this); + bool tmpDebugMode = bDebugMode; + Async(EAsyncExecution::Thread, [WeakThis, StdOutReadPipe, StdOutWritePipe, StdErrReadPipe, StdErrWritePipe, tmpDebugMode]() + { + while (WeakThis.IsValid()) + { + bool bHasOutput = false; + + FString StdOutOutput = FPlatformProcess::ReadPipe(StdOutReadPipe); + if (!StdOutOutput.IsEmpty()) + { + bHasOutput = true; + AsyncTask(ENamedThreads::GameThread, [WeakThis, StdOutOutput, tmpDebugMode]() + { + if (WeakThis.IsValid() && IsValid(WeakThis->STTManager)) + { + TArray Lines; + StdOutOutput.ParseIntoArrayLines(Lines); + for (const FString& Line : Lines) + { + if (!Line.IsEmpty()) + { + if (Line.StartsWith("ERROR")) + { + WeakThis->STTManager->OnSTTError.Broadcast(FString::Printf(TEXT("[ParakeetSTT] %s"), *Line.TrimStartAndEnd())); + } + else + { + if(tmpDebugMode) + WeakThis->STTManager->OnSTTLog.Broadcast(FString::Printf(TEXT("[ParakeetSTT] %s"), *Line.TrimStartAndEnd())); + } + //Most likely this is not needed + if (Line.Contains(TEXT("Server ready"))) + { + WeakThis->ConnectToParakeet(false); + } + } + } + } + }); + } + + FString StdErrOutput = FPlatformProcess::ReadPipe(StdErrReadPipe); + if (!StdErrOutput.IsEmpty()) + { + bHasOutput = true; + AsyncTask(ENamedThreads::GameThread, [WeakThis, StdErrOutput]() + { + if (WeakThis.IsValid() && IsValid(WeakThis->STTManager)) + { + TArray Lines; + StdErrOutput.ParseIntoArrayLines(Lines); + for (const FString& Line : Lines) + { + if (!Line.IsEmpty()) + { + WeakThis->STTManager->OnSTTError.Broadcast(FString::Printf(TEXT("[ParakeetSTT ERROR] %s"), *Line.TrimStartAndEnd())); + } + } + } + }); + } + + if (!bHasOutput) + { + FPlatformProcess::Sleep(0.1f); + } + } + + FPlatformProcess::ClosePipe(StdOutReadPipe, nullptr); + FPlatformProcess::ClosePipe(StdErrReadPipe, nullptr); + }); + + return true; +} + +void USTTParakeetProcessorBase::KillExistingParakeetProcesses() +{ +#if PLATFORM_WINDOWS + // Kill by window title set in ParakeetSTT.bat: title "Parakeet STT" + FPlatformProcess::CreateProc(TEXT("cmd.exe"), TEXT("/c taskkill /FI \"WINDOWTITLE eq Parakeet STT\" /F >nul 2>&1"), false, true, true, nullptr, 0, nullptr, nullptr, nullptr); + + // Kill venv python.exe by executable path using WMIC + FString VenvPyPath = FPaths::Combine(ParakeetVenvPath, TEXT("Scripts\\python.exe")); + VenvPyPath.ReplaceInline(TEXT("/"), TEXT("\\")); + FString WmicCmd = FString::Printf(TEXT("/c wmic process where \"ExecutablePath='%s'\" call terminate >nul 2>&1"), *VenvPyPath); + FPlatformProcess::CreateProc(TEXT("cmd.exe"), *WmicCmd, false, true, true, nullptr, 0, nullptr, nullptr, nullptr); +#endif + + if (bDebugMode && IsValid(STTManager)) + STTManager->OnSTTLog.Broadcast(TEXT("Killed existing Parakeet processes")); +} + +void USTTParakeetProcessorBase::RestartParakeet() +{ + if (StartParakeetProcess()) + { + if (IsValid(STTManager)) + { + if (UWorld* World = STTManager->GetWorld()) + { + World->GetTimerManager().SetTimer(ConnectTimerHandle, this, &USTTParakeetProcessorBase::TryConnect, 0.5f, false); + } + } + } +} diff --git a/Unreal/Plugins/AvatarCore_STT/Source/AvatarCore_STT/Private/Processor/Parakeet/STTParakeetProcessorConfig.cpp b/Unreal/Plugins/AvatarCore_STT/Source/AvatarCore_STT/Private/Processor/Parakeet/STTParakeetProcessorConfig.cpp new file mode 100644 index 0000000..14b4c4a --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/AvatarCore_STT/Private/Processor/Parakeet/STTParakeetProcessorConfig.cpp @@ -0,0 +1,10 @@ +// Fill out your copyright notice in the Description page of Project Settings. + + +#include "Processor/Parakeet/STTParakeetProcessorConfig.h" +#include "Processor/Parakeet/STTParakeetProcessorBase.h" + +USTTParakeetProcessorConfig::USTTParakeetProcessorConfig(const FObjectInitializer& ObjectInitializer) +{ + STTProcessorClass = USTTParakeetProcessorBase::StaticClass(); +} diff --git a/Unreal/Plugins/AvatarCore_STT/Source/AvatarCore_STT/Private/Processor/RealtimeAPI/STTProcessorRealtimeAPI.cpp b/Unreal/Plugins/AvatarCore_STT/Source/AvatarCore_STT/Private/Processor/RealtimeAPI/STTProcessorRealtimeAPI.cpp index 328bdde..1c6799d 100644 --- a/Unreal/Plugins/AvatarCore_STT/Source/AvatarCore_STT/Private/Processor/RealtimeAPI/STTProcessorRealtimeAPI.cpp +++ b/Unreal/Plugins/AvatarCore_STT/Source/AvatarCore_STT/Private/Processor/RealtimeAPI/STTProcessorRealtimeAPI.cpp @@ -2,10 +2,12 @@ #include "Processor/RealtimeAPI/STTProcessorRealtimeAPI.h" +#include "STTManagerBase.h" void USTTProcessorRealtimeAPI::InitSTTProcessor(USTTManagerBase* BaseSTTManager, USTTBaseProcessorConfig* InProcessorConfig, bool InDebugMode) { USTTProcessorBase::InitSTTProcessor(BaseSTTManager, InProcessorConfig, InDebugMode); + BaseSTTManager->OnSTTFullyInitialized(); } void USTTProcessorRealtimeAPI::DestroySTTProcessor() diff --git a/Unreal/Plugins/AvatarCore_STT/Source/AvatarCore_STT/Private/Processor/Whisper/STTProcessorWhisper.cpp b/Unreal/Plugins/AvatarCore_STT/Source/AvatarCore_STT/Private/Processor/Whisper/STTProcessorWhisper.cpp index 5dbc608..5622f20 100644 --- a/Unreal/Plugins/AvatarCore_STT/Source/AvatarCore_STT/Private/Processor/Whisper/STTProcessorWhisper.cpp +++ b/Unreal/Plugins/AvatarCore_STT/Source/AvatarCore_STT/Private/Processor/Whisper/STTProcessorWhisper.cpp @@ -340,7 +340,10 @@ void USTTProcessorWhisper::PerformHealthCheck() if (StatusCode == 401) { Manager->OnSTTError.Broadcast(TEXT("Whisper initialization check failed: API key invalid (401).")); + return; } + + Manager->OnSTTFullyInitialized(); }); Request->ProcessRequest(); diff --git a/Unreal/Plugins/AvatarCore_STT/Source/AvatarCore_STT/Private/STTManagerBase.cpp b/Unreal/Plugins/AvatarCore_STT/Source/AvatarCore_STT/Private/STTManagerBase.cpp index 00c89d9..ccb0925 100644 --- a/Unreal/Plugins/AvatarCore_STT/Source/AvatarCore_STT/Private/STTManagerBase.cpp +++ b/Unreal/Plugins/AvatarCore_STT/Source/AvatarCore_STT/Private/STTManagerBase.cpp @@ -75,6 +75,13 @@ void USTTManagerBase::InitSTTManagerModules() AreModulesInitialized = true; } +void USTTManagerBase::OnSTTFullyInitialized() +{ + bIsInitialized = true; + OnReady.Broadcast(); + SetBlocked(false); +} + void USTTManagerBase::DestroySTTManager() { if (STTRecorder != nullptr && STTRecorder->IsValidLowLevel()) { @@ -140,6 +147,9 @@ void USTTManagerBase::ClearSTTManager() void USTTManagerBase::SetBlocked(bool isBlocked) { + if (!bIsInitialized) + return; + if (IsBlocked() != isBlocked) { if (isBlocked) { if (bDebugMode) @@ -240,7 +250,7 @@ void USTTManagerBase::DebugSTTInput(FString DebugSentence, FString Language = "d void USTTManagerBase::PTTStateChanged(bool BtnPressed) { - if (CurrentSpeechState == ESTTTalkingState::BLOCKED) + if (CurrentSpeechState == ESTTTalkingState::BLOCKED || !IsSTTFullyInitialized()) return; ClearPTTPostRollTimer(); @@ -279,6 +289,9 @@ void USTTManagerBase::ClearPTTPostRollTimer() void USTTManagerBase::UserSpeechStateChanged(ESTTTalkingState NewSpeechState) { + if (!IsSTTFullyInitialized()) + return; + if (NewSpeechState != CurrentSpeechState) { if (GetWorld()->GetTimerManager().TimerExists(UISpeechStateTimerHandle)) { @@ -371,6 +384,11 @@ void USTTManagerBase::DebugSetFilePlayback(){ } +bool USTTManagerBase::IsSTTFullyInitialized() +{ + return bIsInitialized; +} + FString USTTManagerBase::CheckForWordReplacement(const FString& InputText) { FString ResultString = InputText; diff --git a/Unreal/Plugins/AvatarCore_STT/Source/AvatarCore_STT/Public/Processor/Parakeet/ParakeetRunnable.h b/Unreal/Plugins/AvatarCore_STT/Source/AvatarCore_STT/Public/Processor/Parakeet/ParakeetRunnable.h new file mode 100644 index 0000000..ad4dee4 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/AvatarCore_STT/Public/Processor/Parakeet/ParakeetRunnable.h @@ -0,0 +1,47 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" +#include "HAL/Runnable.h" +#include "HAL/RunnableThread.h" +#include "Containers/Queue.h" +#include "Async/Async.h" +#include + +class USTTParakeetProcessorBase; +class FSocket; + +/** + * Background thread that manages TCP socket I/O with the ParakeetSTT Python server. + * Reads NDJSON lines from the server and dispatches them to the owning processor. + * Sends audio chunks and control messages to the server. + */ +class AVATARCORE_STT_API FParakeetRunnable : public FRunnable +{ +public: + FParakeetRunnable(FSocket* InSocket, USTTParakeetProcessorBase* InOwner); + virtual ~FParakeetRunnable(); + + virtual bool Init() override; + virtual uint32 Run() override; + virtual void Stop() override; + + // Queue an NDJSON line to be sent to the Python server + void SendLine(const FString& Line); + +private: + std::atomic bIsRunning; + FRunnableThread* Thread; + USTTParakeetProcessorBase* Owner; + FSocket* Socket; + + // Outgoing message queue (game thread → socket thread) + TQueue OutQueue; + + // Process a single NDJSON line received from the server + void HandleLine(const FString& Line); + + // Send all queued outgoing messages + void FlushOutQueue(); +}; diff --git a/Unreal/Plugins/AvatarCore_STT/Source/AvatarCore_STT/Public/Processor/Parakeet/STTParakeetProcessorBase.h b/Unreal/Plugins/AvatarCore_STT/Source/AvatarCore_STT/Public/Processor/Parakeet/STTParakeetProcessorBase.h new file mode 100644 index 0000000..c9709cc --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/AvatarCore_STT/Public/Processor/Parakeet/STTParakeetProcessorBase.h @@ -0,0 +1,76 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" +#include "Processor/STTProcessorBase.h" +#include "Processor/Parakeet/STTParakeetProcessorConfig.h" +#include "HAL/ThreadSafeBool.h" +#include "STTParakeetProcessorBase.generated.h" + +class FSocket; +class FParakeetRunnable; +class FInternetAddr; + +/** + * STT Processor that communicates with a local ParakeetSTT Python server + * over TCP (NDJSON protocol) for speech-to-text transcription. + */ +UCLASS() +class AVATARCORE_STT_API USTTParakeetProcessorBase : public USTTProcessorBase +{ + GENERATED_BODY() + +public: + + void InitSTTProcessor(USTTManagerBase* BaseSTTManager, USTTBaseProcessorConfig* InProcessorConfig, bool InDebugMode = false) override; + void ClearSTTProcessor() override; + void DestroySTTProcessor() override; + + void OnChunkReceived(TArray PCMData, FAudioInformation AudioInformation) override; + + // Callbacks from FParakeetRunnable (called on GameThread via AsyncTask) + void OnParakeetReady(); + void OnParakeetIntermediate(const FString& Text); + void OnParakeetFinal(const FString& Text, const FString& Language); + void OnParakeetError(const FString& Error); + + UFUNCTION() + void OnSpeechStateChanged(ESTTTalkingState TalkingState); + +private: + + USTTParakeetProcessorConfig* ParakeetConfig = nullptr; + + // TCP socket and runnable + FSocket* ParakeetSocket = nullptr; + FParakeetRunnable* Runnable = nullptr; + + // Connection management + FTimerHandle ConnectTimerHandle; + TSharedPtr ParakeetAddr; + int32 ConnectAttempts = 0; + bool bIsEditor = false; + + // Process management + FString ParakeetBatPath; + FString ParakeetVenvPath; + FProcHandle ParakeetProcHandle; + bool bLaunchedLocalServer = false; + + FString IntermediateResult; + + // Helpers + void SendJsonMessage(const TSharedPtr& Obj); + void SendConfiguration(); + bool StartParakeetProcess(); + void KillExistingParakeetProcesses(); + + UFUNCTION() + void TryConnect(); + + void ConnectToParakeet(bool StartIfFailed); + + UFUNCTION() + void RestartParakeet(); +}; diff --git a/Unreal/Plugins/AvatarCore_STT/Source/AvatarCore_STT/Public/Processor/Parakeet/STTParakeetProcessorConfig.h b/Unreal/Plugins/AvatarCore_STT/Source/AvatarCore_STT/Public/Processor/Parakeet/STTParakeetProcessorConfig.h new file mode 100644 index 0000000..7d11e04 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/AvatarCore_STT/Public/Processor/Parakeet/STTParakeetProcessorConfig.h @@ -0,0 +1,39 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" +#include "Processor/STTBaseProcessorConfig.h" +#include "STTParakeetProcessorConfig.generated.h" + +/** + * Configuration for the Parakeet STT Processor (NVIDIA NeMo ASR). + */ +UCLASS(Blueprintable, BlueprintType) +class AVATARCORE_STT_API USTTParakeetProcessorConfig : public USTTBaseProcessorConfig +{ + GENERATED_BODY() + +public: + + USTTParakeetProcessorConfig(const FObjectInitializer& ObjectInitializer); + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "AvatarCoreSTT|Parakeet", meta = (ExposeOnSpawn = "true")) + FString PythonPath = "python"; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "AvatarCoreSTT|Parakeet", meta = (ExposeOnSpawn = "true")) + FString PretrainedModel = "nvidia/parakeet-tdt-0.6b-v3"; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "AvatarCoreSTT|Parakeet", meta = (ExposeOnSpawn = "true")) + FString Host = "127.0.0.1"; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "AvatarCoreSTT|Parakeet", meta = (ExposeOnSpawn = "true")) + int32 Port = 40200; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "AvatarCoreSTT|Parakeet", meta = (ExposeOnSpawn = "true")) + FString Device = "cuda:0"; + + // How often (seconds) the Python server produces intermediate transcription updates + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "AvatarCoreSTT|Parakeet", meta = (ExposeOnSpawn = "true", ClampMin = "0.1", ClampMax = "5.0")) + float UpdateIntervalSec = 0.5f; +}; diff --git a/Unreal/Plugins/AvatarCore_STT/Source/AvatarCore_STT/Public/STTManagerBase.h b/Unreal/Plugins/AvatarCore_STT/Source/AvatarCore_STT/Public/STTManagerBase.h index c8ec57d..7c6da9a 100644 --- a/Unreal/Plugins/AvatarCore_STT/Source/AvatarCore_STT/Public/STTManagerBase.h +++ b/Unreal/Plugins/AvatarCore_STT/Source/AvatarCore_STT/Public/STTManagerBase.h @@ -70,6 +70,8 @@ public: UFUNCTION(BlueprintCallable, Category = "AvatarCoreSTT") void InitSTTManagerModules(); + void OnSTTFullyInitialized(); + UFUNCTION(BlueprintCallable, Category = "AvatarCoreSTT") void DestroySTTManager(); @@ -157,6 +159,9 @@ public: UFUNCTION(BlueprintCallable, Category = "AvatarCoreSTT") void DebugSetFilePlayback(); + + UFUNCTION(BlueprintCallable, Category = "AvatarCoreSTT") + bool IsSTTFullyInitialized(); private: @@ -178,5 +183,8 @@ private: FTimerHandle PTTPostRollTimer; //We record a little bit more, because people like to release button when not really finished float PTTPostRollTime = 0.25f; + //Ready for some trascription action? + bool bIsInitialized = false; + USTTBaseProcessorConfig* ProcessorConfig; }; diff --git a/Unreal/Plugins/AvatarCore_STT/Source/AvatarCore_STT/Public/STTStructs.h b/Unreal/Plugins/AvatarCore_STT/Source/AvatarCore_STT/Public/STTStructs.h index 620d5ff..29ed520 100644 --- a/Unreal/Plugins/AvatarCore_STT/Source/AvatarCore_STT/Public/STTStructs.h +++ b/Unreal/Plugins/AvatarCore_STT/Source/AvatarCore_STT/Public/STTStructs.h @@ -37,6 +37,7 @@ enum class ESTTTranscriptionType : uint8 { OpenAI = 0 UMETA(DisplayName = "OpenAI Transcription"), Azure = 1 UMETA(DisplayName = "Mircosoft Azure Congnitive Speech Services"), + Parakeet = 2 UMETA(DisplayName = "nvidia NeMo Parakeet (local transcription)"), }; UENUM(BlueprintType) diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/Parakeet/ParakeetSTT.bat b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/Parakeet/ParakeetSTT.bat new file mode 100644 index 0000000..ab4f60c --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/Parakeet/ParakeetSTT.bat @@ -0,0 +1,117 @@ +@echo off +setlocal enabledelayedexpansion +title "Parakeet STT" + +REM ====== Config ====== +set "PY_DOWNLOAD_URL=https://www.python.org/ftp/python/3.10.11/python-3.10.11-amd64.exe" +:: Set your required Python version here +set REQUIRED_MAJOR=3 +set REQUIRED_MINOR=10 + +set "VENV_DIR=%LOCALAPPDATA%/AvatarCore/ParakeetVenv" +set "VENV_PY=%VENV_DIR%\Scripts\python.exe" +set "REQ_FILE=%~dp0requirements.txt" +set "TARGET_SCRIPT=%~dp0ParakeetSTT.py" + +REM Work from this scripts directory +cd /d "%~dp0" + +setlocal EnableExtensions + +:checkpython +echo === Checking for Python %REQUIRED_PY% === +set "PY_PATH=%1" +IF [%1] == [] set "PY_PATH=python" + +:: Check if python command exists +%PY_PATH% --version >nul 2>&1 +if %errorlevel% neq 0 ( + echo ERROR: Python is not installed or not in PATH + start "" "%PY_DOWNLOAD_URL%" + pause + exit /b 1 +) + +:: Get Python version +for /f "tokens=2" %%i in ('%PY_PATH% --version 2^>^&1') do set PYTHON_VERSION=%%i + +echo Found Python version: %PYTHON_VERSION% + +:: Parse version numbers +for /f "tokens=1,2 delims=." %%a in ("%PYTHON_VERSION%") do ( + set CURRENT_MAJOR=%%a + set CURRENT_MINOR=%%b +) + +:: Version comparison logic +set VERSION_OK=0 +if %CURRENT_MAJOR% EQU %REQUIRED_MAJOR% ( + if %CURRENT_MINOR% EQU %REQUIRED_MINOR% ( + set VERSION_OK=1 + ) +) + +:: Display result +if %VERSION_OK% equ 1 ( + echo SUCCESS: Python version is compatible %REQUIRED_MAJOR%.%REQUIRED_MINOR%! + goto :PythonReady +) else ( + echo ERROR: Python version does not match! + start "" "%PY_DOWNLOAD_URL%" + pause + exit /b 1 +) + + +:PythonReady +echo Using Python: %PY_PATH% + +REM ====== Virtual environment ====== +if exist "%VENV_PY%" ( + echo Found existing venv: "%VENV_DIR%" +) else ( + echo ERROR: No venv found. Creating venv at "%VENV_DIR%" ... 1>&2 + %PY_PATH% -m venv "%VENV_DIR%" + if %errorlevel% neq 0 ( + echo Failed to create virtual environment. + pause + exit /b 1 + ) + set "FIRST_SETUP=1" +) + +REM Always upgrade pip once in venv +echo Upgrading pip in venv... 1>&2 +"%VENV_PY%" -m pip install --upgrade pip + +REM Install requirements only on first setup (or if requirements.txt exists and user wants a refresh) +if exist "%REQ_FILE%" ( + if defined FIRST_SETUP ( + echo Installing requirements from "%REQ_FILE%" ... 1>&2 + ) + "%VENV_PY%" -m pip install -r "%REQ_FILE%" + if %errorlevel% neq 0 ( + echo Pip install failed. Check your "requirements.txt". 1>&2 + pause + exit /b 1 + ) + ) else ( + echo No requirements.txt found. Skipping dependency install. + ) + +REM ====== Run the target script ====== +if not exist "%TARGET_SCRIPT%" ( + echo ERROR: "%TARGET_SCRIPT%" not found. 1>&2 + echo Make sure %TARGET_SCRIPT% is next to this script, or update TARGET_SCRIPT path. + pause + exit /b 1 +) + +echo Running %TARGET_SCRIPT% ... +echo Server ready +start /B /WAIT "" "%VENV_PY%" "%TARGET_SCRIPT%" %2 %3 %4 %5 %6 %7 %8 %9 +set "RUN_EXIT=%ERRORLEVEL%" +echo. +echo %TARGET_SCRIPT% exited with code %RUN_EXIT%. +pause +exit /b %RUN_EXIT% \ No newline at end of file diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/Parakeet/ParakeetSTT.py b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/Parakeet/ParakeetSTT.py new file mode 100644 index 0000000..7f919c2 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/Parakeet/ParakeetSTT.py @@ -0,0 +1,454 @@ +""" +ParakeetSTT.py — NDJSON TCP server for NVIDIA Parakeet TDT ASR. + +Launched by ParakeetSTT.bat. Communicates with UE via newline-delimited JSON +over a plain TCP socket. + +Protocol (C++ → Python): + {"type":"config","params":{...}} — model name, device, update_interval, etc. + {"type":"audio","data_b64":"..."} — base64-encoded PCM16 mono 16kHz + {"type":"finalize"} — end of speech, produce final result + {"type":"clear"} — discard buffer, cancel transcription + {"type":"ping"} — keepalive + +Protocol (Python → C++): + {"type":"ready","sample_rate":16000,"languages":[...]} + {"type":"intermediate","text":"..."} + {"type":"final","text":"...","language":"..."} + {"type":"error","message":"..."} + {"type":"pong"} +""" + +from __future__ import annotations + +import argparse +import base64 +import json +import os +import socket +import struct +import sys +import threading +import time +import traceback +from typing import Optional + +import numpy as np +import torch + + +# --------------------------------------------------------------------------- +# Globals +# --------------------------------------------------------------------------- +g_model = None +g_model_name = None +g_device = "cpu" +g_sample_rate = 16000 + +SUPPORTED_LANGUAGES = [ + "bg", "hr", "cs", "da", "nl", "en", "et", "fi", "fr", "de", + "el", "hu", "it", "lv", "lt", "mt", "pl", "pt", "ro", "sk", + "sl", "es", "sv", "ru", "uk", +] + + +# --------------------------------------------------------------------------- +# NDJSON helper (same pattern as CoquiTTS) +# --------------------------------------------------------------------------- +class NDJSONConn: + """Wraps a TCP socket for newline-delimited JSON communication.""" + + def __init__(self, sock: socket.socket): + self._sock = sock + self._buf = b"" + + def send_obj(self, obj: dict): + line = json.dumps(obj, ensure_ascii=False) + "\n" + try: + self._sock.sendall(line.encode("utf-8")) + except OSError: + pass + + def recv_lines(self): + """Yield decoded lines (blocking). Yields None on timeout, breaks on disconnect.""" + self._sock.settimeout(0.05) + while True: + # Check for complete lines in buffer first + while b"\n" in self._buf: + line, self._buf = self._buf.split(b"\n", 1) + yield line.decode("utf-8", errors="replace") + try: + data = self._sock.recv(65536) + if not data: + break # disconnected + self._buf += data + except socket.timeout: + yield None + except OSError: + break + + +# --------------------------------------------------------------------------- +# Model loading +# --------------------------------------------------------------------------- +def select_device(device_str: Optional[str] = None): + global g_device + if device_str and device_str != "auto": + g_device = device_str + else: + g_device = "cuda" if torch.cuda.is_available() else "cpu" + print(f"[ParakeetSTT] Selected device: {g_device}") + + +def load_model(pretrained_name: str): + global g_model, g_model_name, g_sample_rate + + if g_model is not None and g_model_name == pretrained_name: + print(f"[ParakeetSTT] Model already loaded: {pretrained_name}, skipping reload.") + return + + print(f"[ParakeetSTT] Loading model: {pretrained_name} on {g_device} ...") + + import nemo.collections.asr as nemo_asr + + g_model = nemo_asr.models.ASRModel.from_pretrained(model_name=pretrained_name) + g_model = g_model.to(g_device) + g_model.eval() + g_model.freeze() + + # Get sample rate from model config + if hasattr(g_model, '_cfg') and 'preprocessor' in g_model._cfg: + g_sample_rate = g_model._cfg.preprocessor.get('sample_rate', 16000) + + g_model_name = pretrained_name + print(f"[ParakeetSTT] Model loaded. Sample rate: {g_sample_rate}") + + +# --------------------------------------------------------------------------- +# Audio buffer + progressive transcription +# --------------------------------------------------------------------------- +class AudioTranscriber: + """Manages an audio buffer and runs periodic transcription.""" + + def __init__(self, update_interval: float = 0.5): + self._lock = threading.Lock() + self._audio_buffer = np.array([], dtype=np.float32) + self._update_interval = update_interval + self._transcribe_thread: Optional[threading.Thread] = None + self._stop_event = threading.Event() + self._new_audio_event = threading.Event() + self._client: Optional[NDJSONConn] = None + self._last_intermediate = "" + self._finalize_event = threading.Event() + self._finalize_done_event = threading.Event() + + def start(self, client: NDJSONConn): + self._client = client + self._stop_event.clear() + self._finalize_event.clear() + self._finalize_done_event.clear() + self._last_intermediate = "" + self._transcribe_thread = threading.Thread(target=self._transcription_loop, daemon=True) + self._transcribe_thread.start() + + def stop(self): + self._stop_event.set() + self._new_audio_event.set() # wake up the loop + if self._transcribe_thread and self._transcribe_thread.is_alive(): + self._transcribe_thread.join(timeout=5.0) + self._transcribe_thread = None + + def add_audio(self, pcm16_bytes: bytes): + """Append PCM16 mono audio bytes to the buffer.""" + samples = np.frombuffer(pcm16_bytes, dtype=np.int16).astype(np.float32) / 32768.0 + with self._lock: + prev_len = len(self._audio_buffer) + self._audio_buffer = np.concatenate([self._audio_buffer, samples]) + new_len = len(self._audio_buffer) + if prev_len == 0: + print(f"[ParakeetSTT] First audio chunk received: {len(samples)} samples") + self._new_audio_event.set() + + def clear(self): + """Discard all buffered audio.""" + with self._lock: + self._audio_buffer = np.array([], dtype=np.float32) + self._last_intermediate = "" + + def finalize(self): + """Request final transcription and wait for it.""" + self._finalize_done_event.clear() + self._finalize_event.set() + self._new_audio_event.set() # wake up the loop + # Wait for the final transcription to complete (up to 30s) + self._finalize_done_event.wait(timeout=30.0) + + def _get_audio_copy(self) -> np.ndarray: + with self._lock: + return self._audio_buffer.copy() + + def _transcribe(self, audio: np.ndarray) -> tuple[str, str]: + """Run transcription on audio array. Returns (text, language).""" + if g_model is None or len(audio) < 1600: # < 0.1s at 16kHz + return "", "" + + # NeMo transcribe expects a list of numpy arrays or file paths + # For in-memory audio, we pass a list of tensors + try: + with torch.no_grad(): + # Create a temporary wav-like input using numpy array + output = g_model.transcribe([audio], batch_size=1) + + text = "" + language = "en" + + if output and len(output) > 0: + result = output[0] + if hasattr(result, 'text'): + text = result.text + elif isinstance(result, str): + text = result + + # Try to get detected language + if hasattr(result, 'langs') and result.langs: + language = result.langs[0] if isinstance(result.langs, list) else str(result.langs) + elif hasattr(result, 'lang') and result.lang: + language = str(result.lang) + + return text.strip(), language + + except Exception as e: + sys.stderr.write(f"[ParakeetSTT] Transcription error: {e}\n") + traceback.print_exc(file=sys.stderr) + return "", "" + + def _transcription_loop(self): + """Background loop: periodically transcribe the growing buffer.""" + print("[ParakeetSTT] Transcription loop started") + while not self._stop_event.is_set(): + # Wait for new audio or finalize signal + self._new_audio_event.wait(timeout=self._update_interval) + self._new_audio_event.clear() + + if self._stop_event.is_set(): + break + + # Check if finalize was requested + is_final = self._finalize_event.is_set() + + audio = self._get_audio_copy() + if len(audio) < 1600: # < 0.1s + if is_final: + # Nothing to transcribe, send empty final + if self._client: + self._client.send_obj({ + "type": "final", + "text": "", + "language": "" + }) + self._finalize_event.clear() + self.clear() + self._finalize_done_event.set() + continue + + print(f"[ParakeetSTT] Transcribing {len(audio)} samples (final={is_final})") + text, language = self._transcribe(audio) + print(f"[ParakeetSTT] Result: text='{text[:80]}' lang='{language}' final={is_final}") + + if is_final: + # Send final result + if self._client: + self._client.send_obj({ + "type": "final", + "text": text, + "language": language + }) + self._finalize_event.clear() + self.clear() + self._finalize_done_event.set() + else: + # Send intermediate only if text changed + if text and text != self._last_intermediate: + self._last_intermediate = text + if self._client: + self._client.send_obj({ + "type": "intermediate", + "text": text + }) + + +# --------------------------------------------------------------------------- +# TCP Server +# --------------------------------------------------------------------------- +def _handle_messages(client: NDJSONConn, transcriber: AudioTranscriber): + """Process audio/finalize/clear messages from a connected client.""" + accum = None + for line in client.recv_lines(): + if line is None: + continue + s = line.strip() + if not s: + continue + + if accum is None: + if s.startswith('{'): + accum = s + else: + continue + else: + accum += "\n" + s + + try: + msg = json.loads(accum) + accum = None + except Exception: + continue + + mtype = msg.get("type") + + if mtype == "ping": + client.send_obj({"type": "pong"}) + + elif mtype == "audio": + data_b64 = msg.get("data_b64", "") + if data_b64: + try: + pcm_bytes = base64.b64decode(data_b64) + transcriber.add_audio(pcm_bytes) + except Exception as e: + print(f"[ParakeetSTT] Audio decode error: {e}") + client.send_obj({"type": "error", "message": f"audio decode error: {e}"}) + else: + print(f"[ParakeetSTT] WARNING: audio message with empty data_b64") + + elif mtype == "finalize": + print(f"[ParakeetSTT] Finalize requested") + transcriber.finalize() + print(f"[ParakeetSTT] Finalize completed") + + elif mtype == "clear": + transcriber.clear() + client.send_obj({"type": "ack", "op": "clear"}) + + else: + client.send_obj({"type": "error", "message": f"unknown type: {mtype}"}) + + +def serve_tcp_with_config_handshake(host: str = "127.0.0.1", port: int = 40200): + """TCP server that survives client disconnects (editor reconnect). + 1. Start server, wait for C++ connection + 2. Receive config, load model (skip if already loaded) + 3. Send ready, handle audio/finalize/clear messages + 4. On disconnect, loop back to accept next client + """ + srv = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + srv.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + srv.bind((host, port)) + srv.listen(1) + print(f"[ParakeetSTT] Server listening on {host}:{port}") + + while True: + print(f"[ParakeetSTT] Waiting for client connection...") + conn, addr = srv.accept() + conn.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) + print(f"[ParakeetSTT] Client connected from {addr}") + client = NDJSONConn(conn) + transcriber = AudioTranscriber() + + try: + accum = None + + for line in client.recv_lines(): + if line is None: + continue + s = line.strip() + if not s: + continue + + if accum is None: + if s.startswith('{'): + accum = s + else: + continue + else: + accum += "\n" + s + + try: + msg = json.loads(accum) + accum = None + except Exception: + continue + + mtype = msg.get("type") + + if mtype == "config": + params = msg.get("params", {}) + + select_device(params.get("device", "auto")) + + try: + pretrained = params.get("pretrained_model", "nvidia/parakeet-tdt-0.6b-v3") + load_model(pretrained) + + update_interval = params.get("update_interval", 0.5) + transcriber = AudioTranscriber(update_interval=update_interval) + + client.send_obj({ + "type": "ready", + "sample_rate": g_sample_rate, + "languages": SUPPORTED_LANGUAGES, + "model_loaded": True + }) + print(f"[ParakeetSTT] Ready. Update interval: {update_interval}s") + + transcriber.start(client) + _handle_messages(client, transcriber) + # Client disconnected — fall through to finally, then loop back + break + + except Exception as e: + client.send_obj({ + "type": "error", + "message": f"Failed to load model: {e}" + }) + sys.stderr.write(f"[ParakeetSTT] Model load error: {e}\n") + traceback.print_exc(file=sys.stderr) + break + + else: + client.send_obj({ + "type": "error", + "message": "Configuration required first" + }) + + except Exception as e: + sys.stderr.write(f"[ParakeetSTT] Client error: {e}\n") + traceback.print_exc(file=sys.stderr) + finally: + transcriber.stop() + try: + conn.shutdown(socket.SHUT_RDWR) + except Exception: + pass + conn.close() + print(f"[ParakeetSTT] Client disconnected, waiting for next connection...") + + +# --------------------------------------------------------------------------- +# CLI entrypoint +# --------------------------------------------------------------------------- +if __name__ == "__main__": + parser = argparse.ArgumentParser(description="ParakeetSTT TCP server") + sub = parser.add_subparsers(dest="cmd") + + srvcfg = sub.add_parser("serve-config", help="Run server with config handshake") + srvcfg.add_argument("--host", default="127.0.0.1") + srvcfg.add_argument("--port", type=int, default=40200) + + args = parser.parse_args() + + if args.cmd == "serve-config": + serve_tcp_with_config_handshake(args.host, args.port) + sys.exit(0) + else: + parser.print_help() \ No newline at end of file diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/Parakeet/StartPythonVenv.bat b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/Parakeet/StartPythonVenv.bat new file mode 100644 index 0000000..ba7d7b3 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/Parakeet/StartPythonVenv.bat @@ -0,0 +1,4 @@ +@echo off +call %localappdata%/AvatarCore/ParakeetVenv/Scripts/Activate.bat +cd /d "%~dp0" +cmd \ No newline at end of file diff --git a/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/Parakeet/requirements.txt b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/Parakeet/requirements.txt new file mode 100644 index 0000000..49063e1 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_STT/Source/ThirdParty/Parakeet/requirements.txt @@ -0,0 +1,9 @@ +--index-url https://pypi.org/simple +--extra-index-url https://download.pytorch.org/whl/cu128 +--trusted-host download.pytorch.org +--prefer-binary + + + +#torch==2.9.0+cu128 +nemo_toolkit[asr]==2.6.2 diff --git a/Unreal/Plugins/AvatarCore_TTS/Source/AvatarCore_TTS/AvatarCore_TTS.Build.cs b/Unreal/Plugins/AvatarCore_TTS/Source/AvatarCore_TTS/AvatarCore_TTS.Build.cs index 69eeaa3..b429d5f 100644 --- a/Unreal/Plugins/AvatarCore_TTS/Source/AvatarCore_TTS/AvatarCore_TTS.Build.cs +++ b/Unreal/Plugins/AvatarCore_TTS/Source/AvatarCore_TTS/AvatarCore_TTS.Build.cs @@ -34,6 +34,7 @@ public class AvatarCore_TTS : ModuleRules "JsonUtilities", "AudioMixer", "AudioExtensions", + "WebRTC", // ... add other public dependencies that you statically link with here ... } ); @@ -47,7 +48,6 @@ public class AvatarCore_TTS : ModuleRules "Slate", "SlateCore", "BTools", - "WebRTC" // ... add private dependencies that you statically link with here ... } ); @@ -59,9 +59,5 @@ public class AvatarCore_TTS : ModuleRules // ... add any modules that your module loads dynamically here ... } ); - - PublicIncludePaths.Add(Path.Combine(ModuleDirectory, "..", "ThirdParty", "fvad", "include")); - PublicAdditionalLibraries.Add(Path.Combine(ModuleDirectory, "..", "ThirdParty", "fvad", "lib", "fvad.lib")); - RuntimeDependencies.Add(Path.Combine(ModuleDirectory, "..", "ThirdParty", "fvad", "lib", "fvad.lib")); } } diff --git a/Unreal/Plugins/AvatarCore_TTS/Source/AvatarCore_TTS/Private/TTSManagerBase.cpp b/Unreal/Plugins/AvatarCore_TTS/Source/AvatarCore_TTS/Private/TTSManagerBase.cpp index 2854062..faccf5f 100644 --- a/Unreal/Plugins/AvatarCore_TTS/Source/AvatarCore_TTS/Private/TTSManagerBase.cpp +++ b/Unreal/Plugins/AvatarCore_TTS/Source/AvatarCore_TTS/Private/TTSManagerBase.cpp @@ -13,7 +13,6 @@ UTTSManagerBase::UTTSManagerBase() , ProceduralSoundWave(nullptr) , CurrentState(ETTSState::UNDEFINED) , bIsRooted(false) - , VadInstance(nullptr) { InitializeDefaultExcludedDelimiters(); @@ -22,11 +21,6 @@ UTTSManagerBase::UTTSManagerBase() UTTSManagerBase::~UTTSManagerBase() { - if (VadInstance) - { - fvad_free(VadInstance); - VadInstance = nullptr; - } } void UTTSManagerBase::InitTTSManager(UTTSBaseConfig* InTSSConfig, bool DebugMode) @@ -41,47 +35,6 @@ void UTTSManagerBase::InitTTSManager(UTTSBaseConfig* InTSSConfig, bool DebugMode } TTSConfig = InTSSConfig; TTSConfig->AddToRoot(); - - // Initialize VAD - if (VadInstance) - { - fvad_free(VadInstance); - VadInstance = nullptr; - } - VadInstance = fvad_new(); - if (VadInstance) - { - int32 Rate = (TTSConfig->ResampleToSampleRate > 0) ? TTSConfig->ResampleToSampleRate : TTSConfig->AudioSampleRate; - if (fvad_set_sample_rate(VadInstance, Rate) < 0) - { - TTSLog(FString::Printf(TEXT("VAD init failed: Sample rate %d Hz not supported (use 8000, 16000, 32000, 48000). VAD will be disabled."), Rate), true); - fvad_free(VadInstance); - VadInstance = nullptr; - } - else - { - fvad_set_mode(VadInstance, 30); - TTSLog(FString::Printf(TEXT("VAD initialized with sample rate %d Hz"), Rate)); - } - } -} - -void UTTSManagerBase::AddSilenceToTask(FTTSTask& Task, float SilenceLength) -{ - - if (SilenceLength > 0) - { - // Create silence buffer - int32 SilenceSamples = static_cast((TTSConfig->ResampleToSampleRate > 0 ? TTSConfig->ResampleToSampleRate : TTSConfig->AudioSampleRate) * SilenceLength); - int32 SilenceBytes = SilenceSamples * TTSConfig->AudioNumChannels * 2; - - TArray SilenceBuffer; - SilenceBuffer.AddZeroed(SilenceBytes); - - Task.AudioData.Append(SilenceBuffer); - - TTSLog(FString::Printf(TEXT("Added %f s of silence to audio queue"), SilenceLength)); - } } void UTTSManagerBase::DeinitTTSManager() @@ -168,7 +121,7 @@ void UTTSManagerBase::PlaybackSoundWave(USoundWave* InSoundWave) int32 NumSamplesToRemove = InSoundWave->GetSampleRateForCurrentPlatform() * InSoundWave->NumChannels * 0.1f; if (PCMData.Num() >= NumSamplesToRemove) { - PCMData.RemoveAt(0, NumSamplesToRemove, /* bAllowShrinking = */ false); + PCMData.RemoveAt(0, NumSamplesToRemove, EAllowShrinking::No); } // Pass the PCM data to the AddTTSAudioQueueChunks function @@ -347,8 +300,6 @@ void UTTSManagerBase::OnTTSManagerFinished() ProceduralSoundWave->Duration = 0.0f; } - bInitialSilenceDone = false; - SetTTSState(ETTSState::Ready); } @@ -400,6 +351,12 @@ void UTTSManagerBase::ClearTTS() QueuedSegments.Empty(); } + // Reset WebRTC channel accumulator so previous utterance state doesn't bleed through + if (WebRTCChannel) + { + WebRTCChannel->Reset(); + } + // Reset state variables bReceivedFinalInput = false; @@ -578,6 +535,16 @@ void UTTSManagerBase::OnGeneratedAudioChunkReceived(FTTSTask& Task, const TArray ProcessedAudio = AudioData; } + // Run through WebRTC APM for chunk-boundary smoothing (NS + HPF) + if (WebRTCChannel && WebRTCChannel->IsInitialized() && ProcessedAudio.Num() > 0) + { + TArray WebRTCProcessed = WebRTCChannel->ProcessTTSAudio(ProcessedAudio); + if (WebRTCProcessed.Num() > 0) + { + ProcessedAudio = MoveTemp(WebRTCProcessed); + } + } + // Append audio data to the owning ActiveTask buffer, not directly to the output queue FTTSTask* TargetTaskPtr = nullptr; { @@ -598,7 +565,7 @@ void UTTSManagerBase::OnGeneratedAudioChunkReceived(FTTSTask& Task, const TArray return; } - // Append first, then compute silence relative to the end + // Append first TargetTaskPtr->AudioData.Append(ProcessedAudio); if(bIsPreCaching) @@ -621,7 +588,6 @@ void UTTSManagerBase::OnGeneratedAudioChunkReceived(FTTSTask& Task, const TArray if (IsLastChunk && TTSConfig->UseCacheSystem && !bIsPreCaching) { SaveTaskAudioToCache(Task); - AddEndSilence(Task); } UTTSManagerBase::CheckPlayback(); } @@ -735,6 +701,11 @@ void UTTSManagerBase::RegisterAudioComponent(UAudioComponent* AudioComponent) // Pre-allocate buffer to reduce memory fragmentation ProceduralSoundWave->ResetAudio(); + + // Initialise WebRTC processing channel + WebRTCChannel = MakeUnique(); + WebRTCChannel->Initialize(SampleRate, TTSConfig->AudioNumChannels); + TTSLog(TEXT("WebRTC audio processing channel initialised")); } void UTTSManagerBase::OnAvatarSoundwaveBufferUnderun() @@ -868,21 +839,6 @@ bool UTTSManagerBase::FlushToAudioQueue() const int32 BytesPerFrame = BytesPerSample * NumChannels; const int32 BytesPerSecond = EffectiveSampleRate * BytesPerFrame; - // VAD Frame Size (20ms) - const int32 VadFrameMs = 30; - const int32 SamplesPerVadFrame = EffectiveSampleRate * VadFrameMs / 1000; - const int32 BytesPerVadFrame = SamplesPerVadFrame * BytesPerFrame; - - // Calculate currently buffered audio - int32 TotalBufferedBytes = 0; - { - FScopeLock SegLock(&SegmentQueueCriticalSection); - for (const FQueuedSegment& Seg : QueuedSegments) - { - TotalBufferedBytes += Seg.BytesRemaining; - } - } - FScopeLock TaskLock(&TaskQueueCriticalSection); // Remove fully generated AND fully played tasks from the front before flushing while (ActiveTasks.Num() > 0) @@ -925,83 +881,9 @@ bool UTTSManagerBase::FlushToAudioQueue() const int32 BytesQueuedSoFar = Curr.QueueCursor; const int32 BytesAvail = BytesTotal - BytesQueuedSoFar; - // Initial silence check - if (!bInitialSilenceDone && BytesAvail < (TTSConfig->BufferSeconds * BytesPerSecond)) - return false; - else - bInitialSilenceDone = true; - if (BytesAvail <= 0) continue; - int32 BytesToQueue = BytesAvail; - - // VAD Logic - bool bStoppedEarly = false; - if (TTSConfig->BufferSeconds > 0 && VadInstance && Curr.bIsGenerating) - { - // Only process full VAD frames - int32 NumFrames = BytesAvail / BytesPerVadFrame; - - // If we have less than a frame, wait for more data - if (NumFrames == 0) - { - bStoppedEarly = true; - BytesToQueue = 0; - } - else - { - int32 ProcessedBytes = 0; - // Temp buffer for VAD (mono) - TArray MonoFrame; - MonoFrame.SetNumUninitialized(SamplesPerVadFrame); - - const uint8* AudioPtr = Curr.AudioData.GetData() + BytesQueuedSoFar; - - for (int32 f = 0; f < NumFrames; ++f) - { - // Extract frame and convert to mono if needed - const int16_t* FrameData = reinterpret_cast(AudioPtr + ProcessedBytes); - - if (NumChannels == 1) - { - FMemory::Memcpy(MonoFrame.GetData(), FrameData, SamplesPerVadFrame * sizeof(int16_t)); - } - else - { - // Average channels to mono - for (int32 s = 0; s < SamplesPerVadFrame; ++s) - { - int32 Sum = 0; - for (int32 c = 0; c < NumChannels; ++c) - { - Sum += FrameData[s * NumChannels + c]; - } - MonoFrame[s] = static_cast(Sum / NumChannels); - } - } - - int IsVoice = fvad_process(VadInstance, MonoFrame.GetData(), SamplesPerVadFrame); - if (IsVoice == 0) // Silence - { - float CurrentBufferSec = (float)TotalBufferedBytes / (float)BytesPerSecond; - if (CurrentBufferSec < TTSConfig->BufferSeconds) - { - UE_LOG(LogTemp, Warning, TEXT("Let's stop here and wait for the buffer")); - // Buffer is low, stop here - bStoppedEarly = true; - break; - } - else - UE_LOG(LogTemp, Warning, TEXT("Buffer says plenty to play back %f"), CurrentBufferSec); - } - - ProcessedBytes += BytesPerVadFrame; - // Virtual update for next frame check (we assume this frame WILL be queued) - TotalBufferedBytes += BytesPerVadFrame; - } - BytesToQueue = ProcessedBytes; - } - } + const int32 BytesToQueue = BytesAvail; if (BytesToQueue > 0) { @@ -1021,12 +903,6 @@ bool UTTSManagerBase::FlushToAudioQueue() FScopeLock SegLock(&SegmentQueueCriticalSection); QueuedSegments.Add({ Curr.TaskID, BytesToQueue }); } - // If we didn't use VAD loop logic to increment TotalBufferedBytes (e.g. bIsGenerating=false), do it now - // Note: If VAD was used, we already incremented TotalBufferedBytes in the loop for the bytes we are queuing. - if (!VadInstance || !Curr.bIsGenerating) - { - TotalBufferedBytes += BytesToQueue; - } bQueuedAny = true; if (bQueuedAny) @@ -1041,10 +917,6 @@ bool UTTSManagerBase::FlushToAudioQueue() } } - if (bStoppedEarly) - { - break; // Stop flushing as we are blocked on this task - } } // Fallback: if nothing was queued now but there are already queued segments, ensure playback is running @@ -1261,10 +1133,6 @@ void UTTSManagerBase::CheckGenerateAudio() ActiveTasks.RemoveAt(ActiveTasks.Num()-1); bLoadedFromCache = true; } - else - AddEndSilence(ActiveTasks.Last()); - // Do not increment CurrentlyGenerating since no generation is in progress - // Kick playback pipeline outside lock } else { @@ -1329,20 +1197,6 @@ void UTTSManagerBase::SetTTSState(ETTSState NewState) } } -void UTTSManagerBase::AddEndSilence(FTTSTask& Task) -{ - if (Task.Text.EndsWith(",")) - { - UTTSManagerBase::AddSilenceToTask(Task, TTSConfig->SilenceAfterComma); - } - else - { - UTTSManagerBase::AddSilenceToTask(Task, TTSConfig->SilenceAfterSentence); - } - if (PendingTasks.IsEmpty() && bReceivedFinalInput) - UTTSManagerBase::AddSilenceToTask(Task, TTSConfig->SilenceFlush); -} - void UTTSManagerBase::AddToRootManager() { if (!bIsRooted) @@ -1545,7 +1399,6 @@ void UTTSManagerBase::ConsumeRenderedBytes(int32 Bytes) { FQueuedSegment& Front = QueuedSegments[0]; const int32 Consume = FMath::Min(Bytes, (Front.BytesRemaining)); - // Handle silence segments (no owning task) if (Front.TaskID < 0) { Front.BytesRemaining -= Consume; diff --git a/Unreal/Plugins/AvatarCore_TTS/Source/AvatarCore_TTS/Private/TTSWebRTCChannel.cpp b/Unreal/Plugins/AvatarCore_TTS/Source/AvatarCore_TTS/Private/TTSWebRTCChannel.cpp new file mode 100644 index 0000000..8dd9a53 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_TTS/Source/AvatarCore_TTS/Private/TTSWebRTCChannel.cpp @@ -0,0 +1,211 @@ +#include "TTSWebRTCChannel.h" + +// --------------------------------------------------------------------------- +// Helper: pick the closest WebRTC-compatible processing rate +// --------------------------------------------------------------------------- +static int32 PickWebRTCRate(int32 DesiredRate) +{ + // webrtc::AudioProcessing supports 8000, 16000, 32000, 48000 + if (DesiredRate <= 8000) return 8000; + if (DesiredRate <= 16000) return 16000; + if (DesiredRate <= 32000) return 32000; + return 48000; +} + +// --------------------------------------------------------------------------- +FTTSWebRTCChannel::FTTSWebRTCChannel() +{ +} + +FTTSWebRTCChannel::~FTTSWebRTCChannel() +{ + AudioProcessing = nullptr; +} + +// --------------------------------------------------------------------------- +void FTTSWebRTCChannel::Initialize(int32 InSampleRate, int32 InNumChannels) +{ + FScopeLock Lock(&CriticalSection); + + SampleRate = InSampleRate; + NumChannels = InNumChannels; + WebRTCRate = PickWebRTCRate(InSampleRate); + bInitialized = false; + Accumulator.Reset(); + + // Frame size: webrtc::AudioProcessing::GetFrameSize returns samples-per-channel for 10 ms + const int32 FrameSizePerChannel = webrtc::AudioProcessing::GetFrameSize(WebRTCRate); + FrameSamplesTotal = FrameSizePerChannel * NumChannels; + + Configure(); + bInitialized = (AudioProcessing != nullptr); +} + +// --------------------------------------------------------------------------- +void FTTSWebRTCChannel::Configure() +{ + webrtc::AudioProcessing::Config Config; + + Config.pipeline.multi_channel_capture = (NumChannels > 1); + Config.pipeline.multi_channel_render = (NumChannels > 1); + Config.pipeline.maximum_internal_processing_rate = WebRTCRate; + + Config.echo_canceller.enabled = false; // AEC handled by AvatarCore_STT + Config.pre_amplifier.enabled = false; + Config.high_pass_filter.enabled = true; // removes DC offset / low-freq rumble + Config.noise_suppression.enabled = true; // smooths inter-chunk artifacts + Config.noise_suppression.level = webrtc::AudioProcessing::Config::NoiseSuppression::kLow; + Config.transient_suppression.enabled = true; // suppresses click artifacts at chunk boundaries + Config.gain_controller1.enabled = false; + Config.gain_controller2.enabled = false; + + auto NewAp = webrtc::AudioProcessingBuilder().Create(); + if (NewAp) + { + NewAp->ApplyConfig(Config); + AudioProcessing = NewAp; + } +} + +// --------------------------------------------------------------------------- +void FTTSWebRTCChannel::Reset() +{ + FScopeLock Lock(&CriticalSection); + Accumulator.Reset(); + // Reinitialise APM state so history from the previous utterance doesn't bleed through + if (AudioProcessing && bInitialized) + { + Configure(); // cheapest way to get a clean APM state + } +} + +// --------------------------------------------------------------------------- +TArray FTTSWebRTCChannel::ProcessTTSAudio(const TArray& InPCM16) +{ + TArray OutPCM16; + + if (InPCM16.Num() == 0) + return OutPCM16; + + FScopeLock Lock(&CriticalSection); + + if (!bInitialized || !AudioProcessing || FrameSamplesTotal <= 0) + { + // Pass-through when not initialised + OutPCM16 = InPCM16; + return OutPCM16; + } + + // 1. Convert incoming bytes → int16 samples + const int32 InSamples = InPCM16.Num() / 2; + TArray InInt16; + InInt16.SetNumUninitialized(InSamples); + FMemory::Memcpy(InInt16.GetData(), InPCM16.GetData(), InPCM16.Num()); + + // 2. Resample to WebRTC processing rate if needed + TArray ResampledIn; + if (SampleRate != WebRTCRate) + { + ResampleInt16Linear(InInt16, SampleRate, WebRTCRate, ResampledIn); + } + else + { + ResampledIn = MoveTemp(InInt16); + } + + // 3. Append to accumulator + Accumulator.Append(ResampledIn); + + // 4. Process all complete 10 ms frames + const int32 NumFrames = Accumulator.Num() / FrameSamplesTotal; + TArray ProcessedInt16; + ProcessedInt16.Reserve(NumFrames * FrameSamplesTotal); + + webrtc::StreamConfig StreamCfg(WebRTCRate, NumChannels); + + for (int32 f = 0; f < NumFrames; ++f) + { + int16* FramePtr = Accumulator.GetData() + f * FrameSamplesTotal; + + TArray FrameBuf; + FrameBuf.SetNumUninitialized(FrameSamplesTotal); + FMemory::Memcpy(FrameBuf.GetData(), FramePtr, FrameSamplesTotal * sizeof(int16)); + + // ProcessStream in-place + AudioProcessing->ProcessStream( + FrameBuf.GetData(), + StreamCfg, + StreamCfg, + FrameBuf.GetData()); + + ProcessedInt16.Append(FrameBuf); + } + + // 5. Keep leftover samples in accumulator + const int32 ConsumedSamples = NumFrames * FrameSamplesTotal; + const int32 RemainingSamples = Accumulator.Num() - ConsumedSamples; + if (RemainingSamples > 0) + { + FMemory::Memmove( + Accumulator.GetData(), + Accumulator.GetData() + ConsumedSamples, + RemainingSamples * sizeof(int16)); + } + Accumulator.SetNum(RemainingSamples, EAllowShrinking::No); + + if (ProcessedInt16.Num() == 0) + return OutPCM16; // no complete frames yet + + // 6. Resample back to original TTS sample rate if needed + TArray FinalInt16; + if (SampleRate != WebRTCRate) + { + ResampleInt16Linear(ProcessedInt16, WebRTCRate, SampleRate, FinalInt16); + } + else + { + FinalInt16 = MoveTemp(ProcessedInt16); + } + + // 7. Convert int16 → bytes + OutPCM16.SetNumUninitialized(FinalInt16.Num() * 2); + FMemory::Memcpy(OutPCM16.GetData(), FinalInt16.GetData(), OutPCM16.Num()); + + return OutPCM16; +} + +// --------------------------------------------------------------------------- +void FTTSWebRTCChannel::ResampleInt16Linear(const TArray& InSamples, + int32 InRate, + int32 OutRate, + TArray& OutSamples) +{ + if (InSamples.Num() == 0 || InRate <= 0 || OutRate <= 0) + { + OutSamples.Reset(); + return; + } + if (InRate == OutRate) + { + OutSamples = InSamples; + return; + } + + const float Ratio = static_cast(OutRate) / static_cast(InRate); + const int32 NewCount = FMath::CeilToInt(InSamples.Num() * Ratio); + OutSamples.SetNumUninitialized(NewCount); + + for (int32 i = 0; i < NewCount; ++i) + { + const float SourceIndex = static_cast(i) / Ratio; + const int32 Index1 = FMath::Clamp(FMath::FloorToInt(SourceIndex), 0, InSamples.Num() - 1); + const int32 Index2 = FMath::Clamp(Index1 + 1, 0, InSamples.Num() - 1); + const float Fraction = SourceIndex - static_cast(Index1); + const float Interpolated = FMath::Lerp( + static_cast(InSamples[Index1]), + static_cast(InSamples[Index2]), + Fraction); + OutSamples[i] = static_cast( + FMath::Clamp(FMath::RoundToInt(Interpolated), -32768, 32767)); + } +} diff --git a/Unreal/Plugins/AvatarCore_TTS/Source/AvatarCore_TTS/Public/TTSBaseConfig.h b/Unreal/Plugins/AvatarCore_TTS/Source/AvatarCore_TTS/Public/TTSBaseConfig.h index 910e0d2..72ecd26 100644 --- a/Unreal/Plugins/AvatarCore_TTS/Source/AvatarCore_TTS/Public/TTSBaseConfig.h +++ b/Unreal/Plugins/AvatarCore_TTS/Source/AvatarCore_TTS/Public/TTSBaseConfig.h @@ -50,22 +50,6 @@ public: UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "AvatarCoreTTS|Base", meta = (ExposeOnSpawn = "true")) int32 ResampleToSampleRate = -1; - //Buffer seconds for the WebRTC Channel - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "AvatarCoreTTS|Base", meta = (ExposeOnSpawn = "true")) - float BufferSeconds = 0.25f; - - //Chunk length in seconds - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "AvatarCoreTTS|Base", meta = (ExposeOnSpawn = "true")) - float SilenceAfterSentence = 0.2f; - - //Chunk length in seconds - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "AvatarCoreTTS|Base", meta = (ExposeOnSpawn = "true")) - float SilenceAfterComma = 0.1f; - - // How many seconds of silence should be send to flush the stream - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "AvatarCoreTTS|Base", meta = (ExposeOnSpawn = "true")) - float SilenceFlush = 0.0f; - //Chunk length in seconds UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "AvatarCoreTTS|Base", meta = (ExposeOnSpawn = "true")) float ChunkLength = 0.01f; diff --git a/Unreal/Plugins/AvatarCore_TTS/Source/AvatarCore_TTS/Public/TTSManagerBase.h b/Unreal/Plugins/AvatarCore_TTS/Source/AvatarCore_TTS/Public/TTSManagerBase.h index cebce8e..78f6777 100644 --- a/Unreal/Plugins/AvatarCore_TTS/Source/AvatarCore_TTS/Public/TTSManagerBase.h +++ b/Unreal/Plugins/AvatarCore_TTS/Source/AvatarCore_TTS/Public/TTSManagerBase.h @@ -5,7 +5,7 @@ #include "AvatarSoundWave.h" #include "Components/AudioComponent.h" #include "TTSBaseConfig.h" -#include "fvad.h" +#include "TTSWebRTCChannel.h" #include "TTSManagerBase.generated.h" UENUM(BlueprintType) @@ -277,6 +277,8 @@ private: TArray PreCacheSentences; + TUniquePtr WebRTCChannel; + // Timer for polling Ready transition after playback finished FTimerHandle ReadyPollTimerHandle; @@ -305,6 +307,4 @@ protected: // Consume bytes actually rendered by the audio device (called from OnAudioSample) void ConsumeRenderedBytes(int32 Bytes); - // VAD Instance - Fvad* VadInstance = nullptr; }; diff --git a/Unreal/Plugins/AvatarCore_TTS/Source/AvatarCore_TTS/Public/TTSWebRTCChannel.h b/Unreal/Plugins/AvatarCore_TTS/Source/AvatarCore_TTS/Public/TTSWebRTCChannel.h new file mode 100644 index 0000000..c8a8776 --- /dev/null +++ b/Unreal/Plugins/AvatarCore_TTS/Source/AvatarCore_TTS/Public/TTSWebRTCChannel.h @@ -0,0 +1,74 @@ +#pragma once + +#include "CoreMinimal.h" +#include "HAL/CriticalSection.h" + +THIRD_PARTY_INCLUDES_START +#include "modules/audio_processing/include/audio_processing.h" +#include "api/scoped_refptr.h" +THIRD_PARTY_INCLUDES_END + +/** + * Lightweight WebRTC APM wrapper for TTS audio output processing. + * + * Normalises variable-size PCM16 chunks from TTS generators into strict + * 10 ms frames required by webrtc::AudioProcessing, applies high-pass + * filtering and noise suppression, then returns the processed audio. + * + * No AEC – echo cancellation is handled by AvatarCore_STT. + * No AGC – TTS output level is already controlled by the TTS service. + * + * Thread-safety: ProcessTTSAudio() may be called from any thread; + * all state is protected by a critical section. + */ +class FTTSWebRTCChannel +{ +public: + FTTSWebRTCChannel(); + ~FTTSWebRTCChannel(); + + /** Must be called before first use. Safe to call again to reinitialise. */ + void Initialize(int32 InSampleRate, int32 InNumChannels); + + /** + * Feed raw PCM16 audio (little-endian, interleaved channels). + * Returns processed PCM16 bytes. The returned array may be smaller than + * the input when incomplete 10 ms frames are accumulated internally; + * any remainder is held until the next call. + */ + TArray ProcessTTSAudio(const TArray& InPCM16); + + /** Flush and discard any accumulated partial frame. */ + void Reset(); + + bool IsInitialized() const { return bInitialized; } + +private: + void Configure(); + + /** Resample int16 samples (all channels interleaved) between two rates. */ + static void ResampleInt16Linear(const TArray& InSamples, + int32 InRate, + int32 OutRate, + TArray& OutSamples); + + FCriticalSection CriticalSection; + + int32 SampleRate = 0; + int32 NumChannels = 0; + bool bInitialized = false; + + rtc::scoped_refptr AudioProcessing; + + /** Accumulator for partial frames (int16 samples, interleaved). */ + TArray Accumulator; + + /** Number of int16 samples (all channels) in a single 10 ms frame. */ + int32 FrameSamplesTotal = 0; + + /** + * WebRTC APM works at one of 8000/16000/32000/48000 Hz. + * If SampleRate is not one of these we resample before/after APM. + */ + int32 WebRTCRate = 48000; +}; diff --git a/Unreal/Plugins/AvatarCore_TTS/Source/ThirdParty/fvad/include/fvad.h b/Unreal/Plugins/AvatarCore_TTS/Source/ThirdParty/fvad/include/fvad.h deleted file mode 100644 index d410d28..0000000 --- a/Unreal/Plugins/AvatarCore_TTS/Source/ThirdParty/fvad/include/fvad.h +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * Copyright (c) 2016 Daniel Pirch. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef FVAD_H_ -#define FVAD_H_ - -#ifdef __cplusplus -extern "C" { -#endif - -#include -#include - -/* - * Type for a VAD instance, an opaque object created using fvad_new(). - */ -typedef struct Fvad Fvad; - - -/* - * Creates and initializes a VAD instance. - * - * On success, returns a pointer to the new VAD instance, which should - * eventually be deleted using fvad_free(). - * - * Returns NULL in case of a memory allocation error. - */ -Fvad *fvad_new(void); - -/* - * Frees the dynamic memory of a specified VAD instance. - */ -void fvad_free(Fvad *inst); - - -/* - * Reinitializes a VAD instance, clearing all state and resetting mode and - * sample rate to defaults. - */ -void fvad_reset(Fvad *inst); - - -/* - * Changes the VAD operating ("aggressiveness") mode of a VAD instance. - * - * A more aggressive (higher mode) VAD is more restrictive in reporting speech. - * Put in other words the probability of being speech when the VAD returns 1 is - * increased with increasing mode. As a consequence also the missed detection - * rate goes up. - * - * Valid modes are 0 ("quality"), 1 ("low bitrate"), 2 ("aggressive"), and 3 - * ("very aggressive"). The default mode is 0. - * - * Returns 0 on success, or -1 if the specified mode is invalid. - */ -int fvad_set_mode(Fvad* inst, int mode); - - -/* - * Sets the input sample rate in Hz for a VAD instance. - * - * Valid values are 8000, 16000, 32000 and 48000. The default is 8000. Note - * that internally all processing will be done 8000 Hz; input data in higher - * sample rates will just be downsampled first. - * - * Returns 0 on success, or -1 if the passed value is invalid. - */ -int fvad_set_sample_rate(Fvad* inst, int sample_rate); - - -/* - * Calculates a VAD decision for an audio frame. - * - * `frame` is an array of `length` signed 16-bit samples. Only frames with a - * length of 10, 20 or 30 ms are supported, so for example at 8 kHz, `length` - * must be either 80, 160 or 240. - * - * Returns : 1 - (active voice), - * 0 - (non-active Voice), - * -1 - (invalid frame length). - */ -int fvad_process(Fvad* inst, const int16_t* frame, size_t length); - -#ifdef __cplusplus -} -#endif - -#endif // FVAD_H_ diff --git a/Unreal/Plugins/AvatarCore_TTS/Source/ThirdParty/fvad/lib/fvad.lib b/Unreal/Plugins/AvatarCore_TTS/Source/ThirdParty/fvad/lib/fvad.lib deleted file mode 100644 index 150f694180df68f689cb9e4245dbbad80676e56b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 39698 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 Gxc>))zcXV1 diff --git a/Unreal/Plugins/BTools/Content/DebugWidget/DebugWidget.uasset b/Unreal/Plugins/BTools/Content/DebugWidget/DebugWidget.uasset index d779372..dc364f4 100644 --- a/Unreal/Plugins/BTools/Content/DebugWidget/DebugWidget.uasset +++ b/Unreal/Plugins/BTools/Content/DebugWidget/DebugWidget.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9f3398570b653523c75a513eaaea5e771887272b1c8dd71b079fb704c9220726 -size 438519 +oid sha256:14a1a30975852552f18695fce9e7a43ab7b93723c596e7001c49bfae6696b7c4 +size 450457 diff --git a/Unreal/Plugins/BTools/Source/BTools/Private/BToolsBPLibrary.cpp b/Unreal/Plugins/BTools/Source/BTools/Private/BToolsBPLibrary.cpp index bac2764..c4eb7a3 100644 --- a/Unreal/Plugins/BTools/Source/BTools/Private/BToolsBPLibrary.cpp +++ b/Unreal/Plugins/BTools/Source/BTools/Private/BToolsBPLibrary.cpp @@ -1300,6 +1300,54 @@ FVector UBToolsBPLibrary::GetWorldLocationFromUIElement(const UUserWidget* Widge return WorldLocation; }; +FVector UBToolsBPLibrary::GetWorldLocationFromUIElementWithRay(const UUserWidget* Widget, float TraceDistance) +{ + FVector Target = FVector::ZeroVector; + if (!Widget) return Target; + + // Get widget screen position + FVector2D WidgetScreenPosition = GetWidgetScreenPosition(Widget); + + UWorld* World = Widget->GetWorld(); + if (!World) return Target; + + APlayerController* PC = World->GetFirstPlayerController(); + if (!PC) return Target; + + // Deproject from screen to world + FVector WorldLocation, WorldDirection; + PC->DeprojectScreenPositionToWorld( + WidgetScreenPosition.X, + WidgetScreenPosition.Y, + WorldLocation, + WorldDirection + ); + + // Now raycast forward from camera + FVector TraceStart = WorldLocation; + FVector TraceEnd = WorldLocation + (WorldDirection * TraceDistance); + + FHitResult HitResult; + FCollisionQueryParams CollisionParams; + CollisionParams.AddIgnoredActor(PC->GetPawn()); + + // Line trace to find world position + if (World->LineTraceSingleByChannel( + HitResult, + TraceStart, + TraceEnd, + ECC_Visibility, + CollisionParams + )) + { + // Hit something - return hit location + return HitResult.Location; + } + + // No hit - return point at fixed distance + return TraceStart + (WorldDirection * TraceDistance); +} + // The following function do not work on pseudo monitors ////////////////////////////////////////////////////////////// diff --git a/Unreal/Plugins/BTools/Source/BTools/Public/BToolsBPLibrary.h b/Unreal/Plugins/BTools/Source/BTools/Public/BToolsBPLibrary.h index 87aa3c4..d62f71b 100644 --- a/Unreal/Plugins/BTools/Source/BTools/Public/BToolsBPLibrary.h +++ b/Unreal/Plugins/BTools/Source/BTools/Public/BToolsBPLibrary.h @@ -345,6 +345,8 @@ public: static FVector2D GetWidgetScreenPosition(const UUserWidget* Widget); UFUNCTION(BlueprintCallable, meta = (WorldContext = "WorldContextObject", DisplayName = "Convert Widget Screen Space To World", Keywords = "Widget World Location", ToolTip = "Convert the 2d Viewport location of a widget to world space location"), Category = "BTools") static FVector GetWorldLocationFromUIElement(const UUserWidget* Widget); + UFUNCTION(BlueprintCallable, meta = (WorldContext = "WorldContextObject", DisplayName = "Convert Widget Screen Space To World from Raycast", Keywords = "Widget World Location", ToolTip = "Convert the 2d Viewport location of a widget to world space location"), Category = "BTools") + static FVector GetWorldLocationFromUIElementWithRay(const UUserWidget* Widget, float TraceDistance = 10000.0f); /** * Brings the Unreal application window to the foreground, overlaying all other windows (Windows only).