Add convAI plugin
This commit is contained in:
193
ConvAI/Convai/Source/Convai/Convai.Build.cs
Normal file
193
ConvAI/Convai/Source/Convai/Convai.Build.cs
Normal file
@@ -0,0 +1,193 @@
|
||||
// Copyright 2022 Convai Inc. All Rights Reserved.
|
||||
|
||||
using UnrealBuildTool;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
// using Tools.DotNETCommon;
|
||||
|
||||
|
||||
|
||||
public class Convai : ModuleRules
|
||||
{
|
||||
private static ConvaiPlatform ConvaiPlatformInstance;
|
||||
|
||||
private string ModulePath
|
||||
{
|
||||
get { return ModuleDirectory; }
|
||||
}
|
||||
|
||||
private string ThirdPartyPath
|
||||
{
|
||||
get { return Path.GetFullPath(Path.Combine(ModulePath, "../ThirdParty/")); }
|
||||
}
|
||||
|
||||
private ConvaiPlatform GetConvaiPlatformInstance(ReadOnlyTargetRules Target)
|
||||
{
|
||||
var ConvaiPlatformType = System.Type.GetType("ConvaiPlatform_" + Target.Platform.ToString());
|
||||
if (ConvaiPlatformType == null)
|
||||
{
|
||||
throw new BuildException("Convai does not support platform " + Target.Platform.ToString());
|
||||
}
|
||||
|
||||
var PlatformInstance = Activator.CreateInstance(ConvaiPlatformType) as ConvaiPlatform;
|
||||
if (PlatformInstance == null)
|
||||
{
|
||||
throw new BuildException("Convai could not instantiate platform " + Target.Platform.ToString());
|
||||
}
|
||||
|
||||
return PlatformInstance;
|
||||
}
|
||||
|
||||
private bool ConfigurePlatform(ReadOnlyTargetRules Target, UnrealTargetConfiguration Configuration)
|
||||
{
|
||||
//Convai thirdparty libraries root path
|
||||
string root = ThirdPartyPath;
|
||||
foreach (var arch in ConvaiPlatformInstance.Architectures())
|
||||
{
|
||||
string grpcPath = root + "gRPC/" + "lib/" + ConvaiPlatformInstance.LibrariesPath + arch;
|
||||
|
||||
// Add files that end with .lib
|
||||
PublicAdditionalLibraries.AddRange(Directory.GetFiles(grpcPath, "*.lib"));
|
||||
|
||||
// Add files that end with .a
|
||||
PublicAdditionalLibraries.AddRange(Directory.GetFiles(grpcPath, "*.a"));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public Convai(ReadOnlyTargetRules Target) : base(Target)
|
||||
{
|
||||
// Common Settings
|
||||
DefaultBuildSettings = BuildSettingsVersion.Latest;
|
||||
PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;
|
||||
PrecompileForTargets = PrecompileTargetsType.Any;
|
||||
ConvaiPlatformInstance = GetConvaiPlatformInstance(Target);
|
||||
|
||||
PrivateIncludePaths.Add("Convai/Private");
|
||||
|
||||
if (Target.Platform == UnrealTargetPlatform.Mac || Target.Platform == UnrealTargetPlatform.IOS)
|
||||
{
|
||||
// Include .mm file only for Mac
|
||||
PrivateIncludePaths.Add("Convai/Private/Mac");
|
||||
}
|
||||
|
||||
const bool bEnableConvaiHTTP = false;
|
||||
PublicDefinitions.AddRange(new string[] { "USE_CONVAI_HTTP=0" + (bEnableConvaiHTTP ? "1" : "0")});
|
||||
if (bEnableConvaiHTTP)
|
||||
{
|
||||
PublicDependencyModuleNames.AddRange(new string[] { "CONVAIHTTP", "HTTP" });
|
||||
}
|
||||
else
|
||||
{
|
||||
PublicDependencyModuleNames.AddRange(new string[] { "HTTP" });
|
||||
}
|
||||
|
||||
PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore", "Json", "JsonUtilities", "AudioMixer", "AudioCaptureCore", "AudioCapture", "Voice", "SignalProcessing", "libOpus", "OpenSSL", "zlib", "SSL" });
|
||||
PrivateDependencyModuleNames.AddRange(new string[] {"Projects"});
|
||||
PublicDefinitions.AddRange(new string[] { "ConvaiDebugMode=1", "GOOGLE_PROTOBUF_NO_RTTI", "GPR_FORBID_UNREACHABLE_CODE", "GRPC_ALLOW_EXCEPTIONS=0" });
|
||||
|
||||
// Target Platform Specific Settings
|
||||
if (Target.Platform == UnrealTargetPlatform.Win64)
|
||||
{
|
||||
bUsePrecompiled = false;
|
||||
}
|
||||
|
||||
if (Target.Platform == UnrealTargetPlatform.Android)
|
||||
{
|
||||
PublicDependencyModuleNames.AddRange(new string[] { "ApplicationCore", "AndroidPermission" });
|
||||
string BuildPath = Utils.MakePathRelativeTo(ModuleDirectory, Target.RelativeEnginePath);
|
||||
AdditionalPropertiesForReceipt.Add("AndroidPlugin", Path.Combine(BuildPath, "Convai_AndroidAPL.xml"));
|
||||
}
|
||||
|
||||
if (Target.Platform == UnrealTargetPlatform.Mac || Target.Platform == UnrealTargetPlatform.IOS || Target.Platform == UnrealTargetPlatform.Linux)
|
||||
{
|
||||
PublicDefinitions.AddRange(new string[] { "GOOGLE_PROTOBUF_INTERNAL_DONATE_STEAL_INLINE=0", "GOOGLE_PROTOBUF_USE_UNALIGNED=0", "PROTOBUF_ENABLE_DEBUG_LOGGING_MAY_LEAK_PII=0" });
|
||||
PrivateIncludePaths.AddRange(new string[] { Path.Combine(ThirdPartyPath, "gRPC", "Include_1.50.x") });
|
||||
}
|
||||
else
|
||||
{
|
||||
PrivateIncludePaths.AddRange(new string[] { Path.Combine(ThirdPartyPath, "gRPC", "Include") });
|
||||
}
|
||||
|
||||
// ThirdParty Libraries
|
||||
AddEngineThirdPartyPrivateStaticDependencies(Target, "libOpus");
|
||||
AddEngineThirdPartyPrivateStaticDependencies(Target, "OpenSSL");
|
||||
AddEngineThirdPartyPrivateStaticDependencies(Target, "zlib");
|
||||
|
||||
ConfigurePlatform(Target, Target.Configuration);
|
||||
}
|
||||
}
|
||||
|
||||
public abstract class ConvaiPlatform
|
||||
{
|
||||
public virtual string ConfigurationDir(UnrealTargetConfiguration Configuration)
|
||||
{
|
||||
if (Configuration == UnrealTargetConfiguration.Debug || Configuration == UnrealTargetConfiguration.DebugGame)
|
||||
{
|
||||
return "Debug/";
|
||||
}
|
||||
else
|
||||
{
|
||||
return "Release/";
|
||||
}
|
||||
}
|
||||
public abstract string LibrariesPath { get; }
|
||||
public abstract List<string> Architectures();
|
||||
public abstract string LibraryPrefixName { get; }
|
||||
public abstract string LibraryPostfixName { get; }
|
||||
}
|
||||
|
||||
public class ConvaiPlatform_Win64 : ConvaiPlatform
|
||||
{
|
||||
public override string LibrariesPath { get { return "win64/"; } }
|
||||
public override List<string> Architectures() { return new List<string> { "" }; }
|
||||
public override string LibraryPrefixName { get { return ""; } }
|
||||
public override string LibraryPostfixName { get { return ".lib"; } }
|
||||
}
|
||||
|
||||
public class ConvaiPlatform_Android : ConvaiPlatform
|
||||
{
|
||||
public override string LibrariesPath { get { return "android/"; } }
|
||||
public override List<string> Architectures() { return new List<string> { "armeabi-v7a/", "arm64-v8a/", "x86_64/" }; }
|
||||
public override string LibraryPrefixName { get { return "lib"; } }
|
||||
public override string LibraryPostfixName { get { return ".a"; } }
|
||||
}
|
||||
|
||||
public class ConvaiPlatform_Mac : ConvaiPlatform
|
||||
{
|
||||
public override string LibrariesPath { get { return "mac/"; } }
|
||||
public override List<string> Architectures() { return new List<string> { "" }; }
|
||||
public override string LibraryPrefixName { get { return "lib"; } }
|
||||
public override string LibraryPostfixName { get { return ".a"; } }
|
||||
}
|
||||
|
||||
|
||||
public class ConvaiPlatform_Linux : ConvaiPlatform
|
||||
{
|
||||
public override string LibrariesPath { get { return "linux/"; } }
|
||||
public override List<string> Architectures() { return new List<string> { "" }; }
|
||||
public override string LibraryPrefixName { get { return "lib"; } }
|
||||
public override string LibraryPostfixName { get { return ".a"; } }
|
||||
}
|
||||
|
||||
//public class ConvaiPlatform_PS5 : ConvaiPlatform
|
||||
//{
|
||||
// public override string LibrariesPath { get { return "ps5/"; } }
|
||||
// public override List<string> Architectures() { return new List<string> { "" }; }
|
||||
// public override string LibraryPrefixName { get { return "lib"; } }
|
||||
// public override string LibraryPostfixName { get { return ".a"; } }
|
||||
//}
|
||||
|
||||
//public class ConvaiPlatform_IOS : ConvaiPlatform
|
||||
//{
|
||||
// public override string ConfigurationDir(UnrealTargetConfiguration Configuration)
|
||||
// {
|
||||
// return "";
|
||||
// }
|
||||
// public override string LibrariesPath { get { return "ios/"; } }
|
||||
// public override List<string> Architectures() { return new List<string> { "" }; }
|
||||
// public override string LibraryPrefixName { get { return "lib"; } }
|
||||
// public override string LibraryPostfixName { get { return ".a"; } }
|
||||
//}
|
||||
50
ConvAI/Convai/Source/Convai/Convai.cpp
Normal file
50
ConvAI/Convai/Source/Convai/Convai.cpp
Normal file
@@ -0,0 +1,50 @@
|
||||
// Copyright 2022 Convai Inc. All Rights Reserved.
|
||||
|
||||
#include "Convai.h"
|
||||
#include "Developer/Settings/Public/ISettingsModule.h"
|
||||
#include "UObject/UObjectGlobals.h"
|
||||
#include "UObject/Package.h"
|
||||
|
||||
IMPLEMENT_MODULE(Convai, Convai);
|
||||
|
||||
#define LOCTEXT_NAMESPACE "Convai"
|
||||
|
||||
void Convai::StartupModule()
|
||||
{
|
||||
ConvaiSettings = NewObject<UConvaiSettings>(GetTransientPackage(), "ConvaiSettings", RF_Standalone);
|
||||
ConvaiSettings->AddToRoot();
|
||||
|
||||
// Register settings
|
||||
if (ISettingsModule* SettingsModule = FModuleManager::GetModulePtr<ISettingsModule>("Settings"))
|
||||
{
|
||||
SettingsModule->RegisterSettings("Project", "Plugins", "Convai",
|
||||
LOCTEXT("RuntimeSettingsName", "Convai"),
|
||||
LOCTEXT("RuntimeSettingsDescription", "Configure Convai settings"),
|
||||
ConvaiSettings);
|
||||
}
|
||||
}
|
||||
|
||||
void Convai::ShutdownModule()
|
||||
{
|
||||
if (ISettingsModule* SettingsModule = FModuleManager::GetModulePtr<ISettingsModule>("Settings"))
|
||||
{
|
||||
SettingsModule->UnregisterSettings("Project", "Plugins", "Convai");
|
||||
}
|
||||
|
||||
if (!GExitPurge)
|
||||
{
|
||||
ConvaiSettings->RemoveFromRoot();
|
||||
}
|
||||
else
|
||||
{
|
||||
ConvaiSettings = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
UConvaiSettings* Convai::GetConvaiSettings() const
|
||||
{
|
||||
check(ConvaiSettings);
|
||||
return ConvaiSettings;
|
||||
}
|
||||
|
||||
#undef LOCTEXT_NAMESPACE
|
||||
108
ConvAI/Convai/Source/Convai/Convai.h
Normal file
108
ConvAI/Convai/Source/Convai/Convai.h
Normal file
@@ -0,0 +1,108 @@
|
||||
// Copyright 2022 Convai Inc. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Modules/ModuleManager.h"
|
||||
#include "Public/ConvaiDefinitions.h"
|
||||
#include "Convai.generated.h"
|
||||
|
||||
UCLASS(config = Engine, defaultconfig)
|
||||
class CONVAI_API UConvaiSettings : public UObject
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UConvaiSettings(const FObjectInitializer& ObjectInitializer)
|
||||
: Super(ObjectInitializer)
|
||||
{
|
||||
API_Key = "";
|
||||
EnableNewActionSystem = false;
|
||||
}
|
||||
/* API Key Issued from the website */
|
||||
UPROPERTY(Config, EditAnywhere, Category = "Convai API")
|
||||
FString API_Key;
|
||||
|
||||
/* Enable new actions system */
|
||||
UPROPERTY(Config, EditAnywhere, Category = "Convai API", meta = (DisplayName = "Enable New Action System (Experimental)"))
|
||||
bool EnableNewActionSystem;
|
||||
|
||||
/* Authentication token used for Convai Connect */
|
||||
UPROPERTY(Config, EditAnywhere, AdvancedDisplay, Category = "Convai API")
|
||||
FString AuthToken;
|
||||
|
||||
/* Custom Server URL (Used for debugging) */
|
||||
UPROPERTY(Config, EditAnywhere, AdvancedDisplay, Category = "Convai API")
|
||||
FString CustomURL;
|
||||
|
||||
/* Custom Beta API URL (Used for debugging) */
|
||||
UPROPERTY(Config, EditAnywhere, AdvancedDisplay, Category = "Convai API")
|
||||
FString CustomBetaURL;
|
||||
|
||||
/* Custom Production API URL (Used for debugging) */
|
||||
UPROPERTY(Config, EditAnywhere, AdvancedDisplay, Category = "Convai API")
|
||||
FString CustomProdURL;
|
||||
|
||||
/* Test Character ID (Used for debugging) */
|
||||
UPROPERTY(Config, EditAnywhere, AdvancedDisplay, Category = "Convai API")
|
||||
FString TestCharacterID;
|
||||
|
||||
UPROPERTY(Config, EditAnywhere, AdvancedDisplay, Category = "Convai API")
|
||||
bool AllowInsecureConnection;
|
||||
|
||||
/* Use both hardcoded and system SSL certificates on Windows */
|
||||
UPROPERTY(Config, EditAnywhere, AdvancedDisplay, Category = "Convai API")
|
||||
bool UseSystemCertificates;
|
||||
|
||||
/* Extra Parameters (Used for debugging) */
|
||||
UPROPERTY(Config, EditAnywhere, AdvancedDisplay, Category = "Convai API")
|
||||
FString ExtraParams;
|
||||
|
||||
UPROPERTY(Config, VisibleAnywhere, Category = "Long Term Memory")
|
||||
TArray<FConvaiSpeakerInfo> SpeakerIDs;
|
||||
};
|
||||
|
||||
|
||||
class CONVAI_API Convai : public IModuleInterface
|
||||
{
|
||||
public:
|
||||
|
||||
/** IModuleInterface implementation */
|
||||
void StartupModule();
|
||||
void ShutdownModule();
|
||||
|
||||
|
||||
/**
|
||||
* Singleton-like access to this module's interface. This is just for convenience!
|
||||
* Beware of calling this during the shutdown phase, though. Your module might have been unloaded already.
|
||||
*
|
||||
* @return Returns singleton instance, loading the module on demand if needed
|
||||
*/
|
||||
static inline Convai& Get()
|
||||
{
|
||||
return FModuleManager::LoadModuleChecked<Convai>("Convai");
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks to see if this module is loaded and ready. It is only valid to call Get() if IsAvailable() returns true.
|
||||
*
|
||||
* @return True if the module is loaded and ready to use
|
||||
*/
|
||||
static inline bool IsAvailable()
|
||||
{
|
||||
return FModuleManager::Get().IsModuleLoaded("Convai");
|
||||
}
|
||||
|
||||
virtual bool IsGameModule() const override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/** Getter for internal settings object to support runtime configuration changes */
|
||||
UConvaiSettings* GetConvaiSettings() const;
|
||||
|
||||
|
||||
protected:
|
||||
/** Module settings */
|
||||
UConvaiSettings* ConvaiSettings;
|
||||
};
|
||||
301
ConvAI/Convai/Source/Convai/Convai_AndroidAPL.xml
Normal file
301
ConvAI/Convai/Source/Convai/Convai_AndroidAPL.xml
Normal file
@@ -0,0 +1,301 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- // Copyright 2022 Convai Inc. All Rights Reserved. -->
|
||||
<root xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<!-- init section is always evaluated once per architecture -->
|
||||
<trace enable="true"/>
|
||||
<init>
|
||||
<log text="AndroidAPITemplate init"/>
|
||||
</init>
|
||||
|
||||
<androidManifestUpdates>
|
||||
|
||||
<addPermission android:name="android.permission.RECORD_AUDIO" />
|
||||
|
||||
</androidManifestUpdates>
|
||||
|
||||
<!-- optional additions to proguard -->
|
||||
<proguardAdditions>
|
||||
<insert><![CDATA[
|
||||
-keepattributes Signature
|
||||
-dontskipnonpubliclibraryclassmembers
|
||||
|
||||
-keepclassmembers class com.epicgames.ue4.GameActivity {
|
||||
public <methods>;
|
||||
public <fields>;
|
||||
}
|
||||
]]></insert>
|
||||
</proguardAdditions>
|
||||
|
||||
<gradleProperties>
|
||||
<insert>
|
||||
android.useAndroidX=true
|
||||
android.enableJetifier=true
|
||||
</insert>
|
||||
</gradleProperties>
|
||||
|
||||
<buildscriptGradleAdditions>
|
||||
<insert>
|
||||
dependencies
|
||||
{
|
||||
repositories {
|
||||
// Check that you have the following line (if not, add it):
|
||||
google() // Google's Maven repository
|
||||
}
|
||||
classpath 'com.google.gms:google-services:4.3.3' // google-services plugin
|
||||
}
|
||||
</insert>
|
||||
</buildscriptGradleAdditions>
|
||||
|
||||
<buildGradleAdditions>
|
||||
<!-- Needs to be same version number as play-services -->
|
||||
<insert>
|
||||
|
||||
dependencies
|
||||
{
|
||||
implementation 'com.google.android.play:app-update:2.1.0'
|
||||
implementation 'com.google.android.play:review:2.0.1'
|
||||
}
|
||||
|
||||
allprojects {
|
||||
def mappings = [
|
||||
'android.support.annotation': 'androidx.annotation',
|
||||
'android.arch.lifecycle': 'androidx.lifecycle',
|
||||
'android.support.v4.app.NotificationCompat': 'androidx.core.app.NotificationCompat',
|
||||
'android.support.v4.app.ActivityCompat': 'androidx.core.app.ActivityCompat',
|
||||
'android.support.v4.content.ContextCompat': 'androidx.core.content.ContextCompat',
|
||||
'android.support.v13.app.FragmentCompat': 'androidx.legacy.app.FragmentCompat',
|
||||
'android.arch.lifecycle.Lifecycle': 'androidx.lifecycle.Lifecycle',
|
||||
'android.arch.lifecycle.LifecycleObserver': 'androidx.lifecycle.LifecycleObserver',
|
||||
'android.arch.lifecycle.OnLifecycleEvent': 'androidx.lifecycle.OnLifecycleEvent',
|
||||
'android.arch.lifecycle.ProcessLifecycleOwner': 'androidx.lifecycle.ProcessLifecycleOwner',
|
||||
]
|
||||
|
||||
beforeEvaluate { project ->
|
||||
project.rootProject.projectDir.traverse(type: groovy.io.FileType.FILES, nameFilter: ~/.*\.java$/) { f ->
|
||||
mappings.each { entry ->
|
||||
if (f.getText('UTF-8').contains(entry.key)) {
|
||||
println "Updating ${entry.key} to ${entry.value} in file ${f}"
|
||||
ant.replace(file: f, token: entry.key, value: entry.value)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
</insert>
|
||||
</buildGradleAdditions>
|
||||
|
||||
<resourceCopies>
|
||||
<!-- Copy the generated resource file to be packaged -->
|
||||
</resourceCopies>
|
||||
|
||||
<AARImports>
|
||||
</AARImports>
|
||||
|
||||
<!-- optional additions to the GameActivity imports in GameActivity.java -->
|
||||
<gameActivityImportAdditions>
|
||||
<insert>
|
||||
import java.util.HashSet;
|
||||
import java.util.Arrays;
|
||||
import android.os.Handler;
|
||||
|
||||
import android.util.Log;
|
||||
import android.media.AudioRecord;
|
||||
import android.media.AudioFormat;
|
||||
import android.media.MediaRecorder;
|
||||
import android.media.AudioTrack;
|
||||
|
||||
import androidx.core.app.ActivityCompat;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import android.Manifest;
|
||||
import android.media.audiofx.AcousticEchoCanceler;
|
||||
</insert>
|
||||
</gameActivityImportAdditions>
|
||||
|
||||
|
||||
<!-- optional additions to the GameActivity class in GameActivity.java -->
|
||||
<gameActivityClassAdditions>
|
||||
<insert>
|
||||
<![CDATA[
|
||||
|
||||
/* JNI UE4 Native */
|
||||
private static native void dataMicPayload(short[] dataMic, int readSize);
|
||||
|
||||
/* VAR */
|
||||
AudioRecord microphone = null;
|
||||
Thread threadMicro = null;
|
||||
AudioTrack audioplayback;
|
||||
RunnableVoice runableVoice;
|
||||
|
||||
private boolean isPermissionAllowedMic = false;
|
||||
|
||||
|
||||
/* FUNC */
|
||||
|
||||
public void AndroidThunkJava_AndroidAskPermissionMicrophone(){
|
||||
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.RECORD_AUDIO}, 1);
|
||||
}
|
||||
|
||||
public boolean AndroidThunkJava_AndroidHasPermissionMicrophone(){
|
||||
boolean res = false;
|
||||
res = ContextCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO) == PackageManager.PERMISSION_GRANTED;
|
||||
android.util.Log.d("AndroidRateApp","UPL permission " + res);
|
||||
return res;
|
||||
}
|
||||
|
||||
public boolean AndroidThunkJava_AndroidStartMicrophone(int sampleRateToUse) {
|
||||
|
||||
android.util.Log.d("AndroidRateApp","UPL microphone sampleRateToUse " + sampleRateToUse);
|
||||
|
||||
if(!isPermissionAllowedMic){
|
||||
isPermissionAllowedMic = AndroidThunkJava_AndroidHasPermissionMicrophone();
|
||||
if(!isPermissionAllowedMic) return false;
|
||||
}
|
||||
|
||||
|
||||
if(microphone == null){
|
||||
final int sampleRate = sampleRateToUse;
|
||||
final int channelConfig = AudioFormat.CHANNEL_IN_MONO;
|
||||
final int audioFormat = AudioFormat.ENCODING_PCM_16BIT;
|
||||
|
||||
final int minBufferSize = AudioRecord.getMinBufferSize(sampleRate, channelConfig, audioFormat);
|
||||
microphone = new AudioRecord(MediaRecorder.AudioSource.VOICE_COMMUNICATION, sampleRate, channelConfig, audioFormat, minBufferSize );
|
||||
|
||||
if(AcousticEchoCanceler.isAvailable()){
|
||||
AcousticEchoCanceler aec = AcousticEchoCanceler.create(microphone.getAudioSessionId());
|
||||
if (aec != null) {
|
||||
aec.setEnabled(true);
|
||||
android.util.Log.d("AndroidRateApp","echo cancelation ok VOICE_COMMUNICATION");
|
||||
} else {
|
||||
android.util.Log.d("AndroidRateApp","echo cancelation not ok VOICE_COMMUNICATION");
|
||||
}
|
||||
}
|
||||
microphone.startRecording();
|
||||
|
||||
runableVoice = new RunnableVoice(minBufferSize);
|
||||
runableVoice.microphoneThread = microphone;
|
||||
|
||||
threadMicro = new Thread(runableVoice);
|
||||
|
||||
|
||||
threadMicro.start();
|
||||
}
|
||||
|
||||
microphone.startRecording();
|
||||
runableVoice.setthreadMicroRunning(true);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void AndroidThunkJava_AndroidStopMicrophone() {
|
||||
android.util.Log.d("AndroidRateApp","UPL AndroidThunkJava_AndroidStopMicrophone setthreadMicroRunning");
|
||||
runableVoice.setthreadMicroRunning(false);
|
||||
//threadMicro.stop();
|
||||
microphone.stop();
|
||||
runableVoice.setthreadMicroRunning(false);
|
||||
//microphone.release();
|
||||
}
|
||||
|
||||
|
||||
public class RunnableVoice implements Runnable {
|
||||
public volatile boolean threadMicroRunning = false;
|
||||
public AudioRecord microphoneThread = null;
|
||||
int minBufferSize;
|
||||
|
||||
public RunnableVoice(int minBufferSize){
|
||||
this.minBufferSize = minBufferSize;
|
||||
}
|
||||
|
||||
public void setthreadMicroRunning(boolean threadMicroRunning){
|
||||
this.threadMicroRunning = threadMicroRunning;
|
||||
android.util.Log.d("AndroidRateApp","UPL setthreadMicroRunning " + this.threadMicroRunning);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
short[] buffer = new short[minBufferSize / 2];
|
||||
while (true) {
|
||||
if(threadMicroRunning){
|
||||
int readSize = microphoneThread.read(buffer, 0, buffer.length);
|
||||
android.util.Log.d("AndroidRateApp","UPL microphone data / 2 " + readSize +" "+buffer[0]);
|
||||
dataMicPayload(buffer, readSize);
|
||||
}
|
||||
}
|
||||
} catch(Exception e){
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
]]>
|
||||
</insert>
|
||||
</gameActivityClassAdditions>
|
||||
|
||||
<!-- optional additions to GameActivity onCreate metadata reading in GameActivity.java -->
|
||||
<gameActivityReadMetadataAdditions>
|
||||
<insert>
|
||||
|
||||
</insert>
|
||||
</gameActivityReadMetadataAdditions>
|
||||
|
||||
<!-- optional additions to GameActivity onCreate in GameActivity.java -->
|
||||
<gameActivityOnCreateAdditions>
|
||||
<insert>
|
||||
<![CDATA[
|
||||
|
||||
]]>
|
||||
</insert>
|
||||
</gameActivityOnCreateAdditions>
|
||||
|
||||
<!-- optional additions to GameActivity onDestroy in GameActivity.java -->
|
||||
<gameActivityOnDestroyAdditions>
|
||||
<insert>
|
||||
|
||||
</insert>
|
||||
</gameActivityOnDestroyAdditions>
|
||||
|
||||
|
||||
<!-- optional additions to GameActivity onStart in GameActivity.java -->
|
||||
<gameActivityOnStartAdditions>
|
||||
<insert>
|
||||
|
||||
</insert>
|
||||
</gameActivityOnStartAdditions>
|
||||
|
||||
<!-- optional additions to GameActivity onStop in GameActivity.java -->
|
||||
<gameActivityOnStopAdditions>
|
||||
<insert>
|
||||
|
||||
</insert>
|
||||
</gameActivityOnStopAdditions>
|
||||
|
||||
|
||||
<!-- optional additions to GameActivity onPause in GameActivity.java -->
|
||||
<gameActivityOnPauseAdditions>
|
||||
<insert>
|
||||
<![CDATA[
|
||||
]]>
|
||||
</insert>
|
||||
</gameActivityOnPauseAdditions>
|
||||
|
||||
|
||||
<!-- optional additions to GameActivity onResume in GameActivity.java -->
|
||||
<gameActivityOnResumeAdditions>
|
||||
<insert>
|
||||
</insert>
|
||||
</gameActivityOnResumeAdditions>
|
||||
|
||||
|
||||
<!-- optional additions to GameActivity onActivityResult in GameActivity.java -->
|
||||
<gameActivityOnActivityResultAdditions>
|
||||
<insert>
|
||||
</insert>
|
||||
</gameActivityOnActivityResultAdditions>
|
||||
|
||||
|
||||
<!-- optional libraries to load in GameActivity.java before libUE4.so -->
|
||||
<soLoadLibrary>
|
||||
<!-- need this if plugin enabled and supported architecture, even if not packaged for GearVR -->
|
||||
</soLoadLibrary>
|
||||
</root>
|
||||
445
ConvAI/Convai/Source/Convai/Private/ConvaiActionUtils.cpp
Normal file
445
ConvAI/Convai/Source/Convai/Private/ConvaiActionUtils.cpp
Normal file
@@ -0,0 +1,445 @@
|
||||
// Copyright 2022 Convai Inc. All Rights Reserved.
|
||||
|
||||
|
||||
#include "ConvaiActionUtils.h"
|
||||
#include "ConvaiUtils.h"
|
||||
#include "Internationalization/Regex.h"
|
||||
#include "ConvaiDefinitions.h"
|
||||
|
||||
DEFINE_LOG_CATEGORY(ConvaiActionUtilsLog);
|
||||
|
||||
namespace
|
||||
{
|
||||
bool FindCharAfterIndex(const FString& SearchString, const char Search, int32& OutIndex, const int32& AfterIndex)
|
||||
{
|
||||
return SearchString.Left(AfterIndex).FindChar(Search, OutIndex);
|
||||
}
|
||||
|
||||
FString RemoveQuotedWords(const FString& Input)
|
||||
{
|
||||
|
||||
int32 QuoteStartIndex = -1;
|
||||
if (!Input.FindChar('"', QuoteStartIndex))
|
||||
{
|
||||
return Input;
|
||||
}
|
||||
|
||||
FString Result = Input;
|
||||
|
||||
while (Result.FindChar('"', QuoteStartIndex))
|
||||
{
|
||||
int32 QuoteEndIndex = -1;
|
||||
|
||||
// Start searching for the end quote after the start quote
|
||||
if (Result.Left(QuoteStartIndex + 1).FindChar('"', QuoteEndIndex) && QuoteEndIndex > QuoteStartIndex)
|
||||
{
|
||||
// Remove the quoted string, including the quotes
|
||||
Result.RemoveAt(QuoteStartIndex, QuoteEndIndex - QuoteStartIndex + 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
// No closing quote found after this point, break the loop
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
int32 CountWords(const FString& str)
|
||||
{
|
||||
if (str.Len() == 0) return 0;
|
||||
|
||||
TArray<FString> words;
|
||||
str.ParseIntoArray(words, TEXT(" "), true);
|
||||
|
||||
return words.Num();
|
||||
}
|
||||
|
||||
FString KeepNWords(const FString& StringToBeParsed, const FString& CandidateString)
|
||||
{
|
||||
TArray<FString> words;
|
||||
StringToBeParsed.ParseIntoArray(words, TEXT(" "), true);
|
||||
|
||||
int32 N = CountWords(CandidateString);
|
||||
|
||||
FString result;
|
||||
for (int32 i = 0; i < FMath::Min(N, words.Num()); i++)
|
||||
{
|
||||
result += words[i] + " ";
|
||||
}
|
||||
|
||||
// Trim trailing space
|
||||
result = result.LeftChop(1);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
FString FindClosestString(FString Input, const TArray<FString>& StringArray)
|
||||
{
|
||||
FString ClosestString;
|
||||
int32 MinDistance = MAX_int32;
|
||||
|
||||
for (auto& String : StringArray)
|
||||
{
|
||||
int32 Distance = UConvaiUtils::LevenshteinDistance(Input, String);
|
||||
if (Distance < MinDistance)
|
||||
{
|
||||
MinDistance = Distance;
|
||||
ClosestString = String;
|
||||
}
|
||||
}
|
||||
|
||||
return ClosestString;
|
||||
}
|
||||
|
||||
// Helper function to check if a substring is found outside of quotes.
|
||||
bool FindSubstringOutsideQuotes(const FString& SearchString, const FString& SubstringToFind)
|
||||
{
|
||||
bool bInsideQuotes = false;
|
||||
int32 SubstringLength = SubstringToFind.Len();
|
||||
int32 SearchStringLength = SearchString.Len();
|
||||
|
||||
if (SubstringLength > SearchStringLength || SubstringLength == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int32 i = 0; i <= SearchStringLength - SubstringLength; ++i)
|
||||
{
|
||||
// Toggle the inside quotes flag if a quote is found
|
||||
if (SearchString[i] == '"')
|
||||
{
|
||||
bInsideQuotes = !bInsideQuotes;
|
||||
continue;
|
||||
}
|
||||
|
||||
// If we are not inside quotes, perform the search
|
||||
if (!bInsideQuotes && SearchString.Mid(i, SubstringLength).Equals(SubstringToFind, ESearchCase::IgnoreCase))
|
||||
{
|
||||
// Make sure the match is not part of a larger string
|
||||
if ((i == 0 || !FChar::IsAlnum(SearchString[i - 1])) &&
|
||||
(i + SubstringLength == SearchStringLength || !FChar::IsAlnum(SearchString[i + SubstringLength])))
|
||||
{
|
||||
return true; // Substring found outside of quotes
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false; // Substring not found or is within quotes
|
||||
}
|
||||
|
||||
bool FindClosePhraseOutsideQuotes(const FString& SearchString, const FString& PhraseToFind, int& OutBestDistance, int NumWordsToSkip = 0)
|
||||
{
|
||||
if (PhraseToFind.IsEmpty())
|
||||
{
|
||||
return false; // Can't find an empty phrase.
|
||||
}
|
||||
|
||||
int32 MaxLevenshteinDistance = FMath::Clamp(PhraseToFind.Len() / 2, 2, 4);
|
||||
TArray<FString> Words;
|
||||
FString CurrentWord;
|
||||
bool bInsideQuotes = false;
|
||||
OutBestDistance = MaxLevenshteinDistance + 1; // Initialize with a value above the limit
|
||||
|
||||
// Collect words outside quotes
|
||||
for (TCHAR Char : SearchString)
|
||||
{
|
||||
if (Char == '"')
|
||||
{
|
||||
bInsideQuotes = !bInsideQuotes;
|
||||
if (!CurrentWord.IsEmpty())
|
||||
{
|
||||
Words.Add(CurrentWord);
|
||||
CurrentWord.Empty();
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!bInsideQuotes && (Char == ' ' || Char == '\t'))
|
||||
{
|
||||
if (!CurrentWord.IsEmpty())
|
||||
{
|
||||
Words.Add(CurrentWord);
|
||||
CurrentWord.Empty();
|
||||
}
|
||||
}
|
||||
else if (!bInsideQuotes)
|
||||
{
|
||||
CurrentWord.AppendChar(Char);
|
||||
}
|
||||
}
|
||||
|
||||
// Add the last word if there is one
|
||||
if (!CurrentWord.IsEmpty())
|
||||
{
|
||||
Words.Add(CurrentWord);
|
||||
}
|
||||
|
||||
// Number of words in the phrase to find
|
||||
TArray<FString> PhraseWords;
|
||||
PhraseToFind.ParseIntoArray(PhraseWords, TEXT(" "), true);
|
||||
int32 NumWordsInPhrase = PhraseWords.Num();
|
||||
|
||||
// Sliding window of words to check for the phrase
|
||||
for (int32 i = NumWordsToSkip; i <= Words.Num() - NumWordsInPhrase; ++i)
|
||||
{
|
||||
FString WindowString;
|
||||
for (int32 j = 0; j < NumWordsInPhrase; ++j)
|
||||
{
|
||||
WindowString += (j > 0 ? TEXT(" ") : TEXT("")) + Words[i + j];
|
||||
}
|
||||
|
||||
if (WindowString.Len() - PhraseToFind.Len() >= MaxLevenshteinDistance || PhraseToFind.Len() - WindowString.Len() >= MaxLevenshteinDistance)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
int32 Distance = UConvaiUtils::LevenshteinDistance(WindowString, PhraseToFind);
|
||||
if (Distance <= MaxLevenshteinDistance && Distance < OutBestDistance)
|
||||
{
|
||||
OutBestDistance = Distance;
|
||||
}
|
||||
}
|
||||
|
||||
// The function returns true if we found a match within the acceptable Levenshtein distance
|
||||
return OutBestDistance <= MaxLevenshteinDistance;
|
||||
}
|
||||
|
||||
static bool FindNearestObjectByName(FString SearchString, TArray<FConvaiObjectEntry> Objects, FConvaiObjectEntry& ObjectMatch)
|
||||
{
|
||||
FString SearchStringLower = SearchString.ToLower();
|
||||
bool Found = false;
|
||||
int BestDistance = 100;
|
||||
for (auto o : Objects)
|
||||
{
|
||||
FString ObjectNameLower = o.Name.ToLower();
|
||||
int Distance = 0;
|
||||
if (FindClosePhraseOutsideQuotes(SearchStringLower, ObjectNameLower, Distance))
|
||||
{
|
||||
if (Distance < BestDistance)
|
||||
{
|
||||
ObjectMatch = o;
|
||||
BestDistance = Distance;
|
||||
Found = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Try to search in objects using each word in the SearchString
|
||||
if (!Found)
|
||||
{
|
||||
TArray<FString> words;
|
||||
FString SearchStringWithoutQuotes = RemoveQuotedWords(SearchStringLower);
|
||||
SearchStringWithoutQuotes.ParseIntoArray(words, TEXT(" "), true);
|
||||
bool breakFromLoop = false;
|
||||
for (auto o : Objects)
|
||||
{
|
||||
if (breakFromLoop)
|
||||
break;
|
||||
if (CountWords(o.Name) <= 1)
|
||||
continue; // consider only names consisting of 1+ words
|
||||
|
||||
FString ObjectNameLower = o.Name.ToLower();
|
||||
for (auto w : words)
|
||||
{
|
||||
if (w.Len() <= 3)
|
||||
continue; // consider only words greater than 3 letters
|
||||
|
||||
int Distance = 0;
|
||||
if (FindClosePhraseOutsideQuotes(ObjectNameLower, w, Distance))
|
||||
{
|
||||
ObjectMatch = o;
|
||||
BestDistance = Distance;
|
||||
Found = true;
|
||||
breakFromLoop = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Found;
|
||||
}
|
||||
|
||||
TArray<FConvaiObjectEntry> BubbleSortEntriesByNumberOfWords(TArray<FConvaiObjectEntry> Entries)
|
||||
{
|
||||
bool swapped;
|
||||
int n = Entries.Num();
|
||||
for (int i = 0; i < n - 1; i++) {
|
||||
swapped = false;
|
||||
for (int j = 0; j < n - i - 1; j++) {
|
||||
if (CountWords(Entries[j].Name) > CountWords(Entries[j+1].Name)) {
|
||||
Entries.Swap(j, j + 1);
|
||||
swapped = true;
|
||||
}
|
||||
}
|
||||
// If no two elements were swapped by inner loop, then break
|
||||
if (!swapped) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return Entries;
|
||||
}
|
||||
};
|
||||
|
||||
TArray<FString> UConvaiActions::SmartSplit(const FString& SequenceString)
|
||||
{
|
||||
TArray<FString> Result;
|
||||
FString CurrentString = "";
|
||||
bool InQuotes = false;
|
||||
|
||||
for (int i = 0; i < SequenceString.Len(); ++i)
|
||||
{
|
||||
TCHAR CurrentChar = SequenceString[i];
|
||||
|
||||
if (CurrentChar == '\"')
|
||||
{
|
||||
InQuotes = !InQuotes;
|
||||
}
|
||||
|
||||
if (CurrentChar == ',' && !InQuotes)
|
||||
{
|
||||
Result.Add(CurrentString.TrimStartAndEnd());
|
||||
CurrentString = "";
|
||||
}
|
||||
else
|
||||
{
|
||||
CurrentString += CurrentChar;
|
||||
}
|
||||
}
|
||||
|
||||
if (!CurrentString.IsEmpty())
|
||||
{
|
||||
Result.Add(CurrentString.TrimStartAndEnd());
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
FString UConvaiActions::ExtractText(FString Action, FString ActionResult)
|
||||
{
|
||||
FString ExtraText = "";
|
||||
FRegexPattern TextPattern(TEXT("\".*\""));
|
||||
FRegexMatcher StringMatcher = FRegexMatcher(TextPattern, ActionResult);
|
||||
if (StringMatcher.FindNext())
|
||||
{
|
||||
//CONVAI_LOG(ConvaiUtilsLog, Warning, TEXT("*StringMatcher.GetCaptureGroup(0):%s"), *StringMatcher.GetCaptureGroup(0));
|
||||
ExtraText = *StringMatcher.GetCaptureGroup(0).LeftChop(1).RightChop(1);
|
||||
}
|
||||
else
|
||||
{
|
||||
int index = ActionResult.Find(Action);
|
||||
index += Action.Len();
|
||||
ExtraText = ActionResult.RightChop(index + 1);
|
||||
//ExtraText = ActionResult;
|
||||
}
|
||||
return ExtraText;
|
||||
}
|
||||
|
||||
float UConvaiActions::ExtractNumber(FString ActionResult)
|
||||
{
|
||||
float ExtraNumber = 0;
|
||||
FRegexPattern NumericPattern(TEXT("\\d+"));
|
||||
FRegexMatcher NumberMatcher = FRegexMatcher(NumericPattern, ActionResult);
|
||||
if (NumberMatcher.FindNext())
|
||||
{
|
||||
//CONVAI_LOG(ConvaiGetActionHttpLog, Warning, TEXT("*NumberMatcher.GetCaptureGroup(0):%s"), *NumberMatcher.GetCaptureGroup(0));
|
||||
ExtraNumber = FCString::Atof(*NumberMatcher.GetCaptureGroup(0));
|
||||
}
|
||||
return ExtraNumber;
|
||||
}
|
||||
|
||||
FString UConvaiActions::RemoveDesc(FString str)
|
||||
{
|
||||
FRegexPattern DescPattern(TEXT("<.*>"));
|
||||
FRegexMatcher DescMatcher = FRegexMatcher(DescPattern, str);
|
||||
int i = 0;
|
||||
while (DescMatcher.FindNext())
|
||||
{
|
||||
int start = DescMatcher.GetMatchBeginning();
|
||||
str.ReplaceInline(*DescMatcher.GetCaptureGroup(i++), *FString(""));
|
||||
str.TrimEndInline();
|
||||
str.TrimStartInline();
|
||||
}
|
||||
//CONVAI_LOG(ConvaiActionUtilsLog, Warning, TEXT("str:%s"), *str);
|
||||
return str;
|
||||
}
|
||||
|
||||
FString UConvaiActions::FindAction(FString ActionToBeParsed, TArray<FString> Actions)
|
||||
{
|
||||
FString ClosestAction = "None";
|
||||
int32 MinDistance = 4;
|
||||
for (auto a : Actions)
|
||||
{
|
||||
FString TrimmedAction = ActionToBeParsed;
|
||||
a = RemoveDesc(a);
|
||||
TrimmedAction = KeepNWords(ActionToBeParsed, a);
|
||||
int32 Distance = UConvaiUtils::LevenshteinDistance(TrimmedAction, a);
|
||||
if (Distance < MinDistance)
|
||||
{
|
||||
MinDistance = Distance;
|
||||
ClosestAction = a;
|
||||
}
|
||||
}
|
||||
return ClosestAction;
|
||||
}
|
||||
|
||||
bool UConvaiActions::ParseAction(UConvaiEnvironment* Environment, FString ActionToBeParsed, FConvaiResultAction& ConvaiResultAction)
|
||||
{
|
||||
FString ActionToAdd;
|
||||
FConvaiObjectEntry RelatedObjOrChar;
|
||||
ConvaiResultAction.ActionString = ActionToBeParsed;
|
||||
|
||||
// find actions
|
||||
ActionToAdd = FindAction(ActionToBeParsed, Environment->Actions);
|
||||
|
||||
TArray<FConvaiObjectEntry> AllCharsAndObjs;
|
||||
AllCharsAndObjs.Append(Environment->Characters);
|
||||
AllCharsAndObjs.Append(Environment->Objects);
|
||||
|
||||
// find objects or characters
|
||||
FindNearestObjectByName(ActionToBeParsed, AllCharsAndObjs, RelatedObjOrChar);
|
||||
|
||||
// find objects
|
||||
//FindNearestObjectByName(ActionToBeParsed, Environment->Objects, RelatedObjOrChar);
|
||||
|
||||
// Find extra numeric param
|
||||
float ExtraNumber = ExtractNumber(ActionToBeParsed);
|
||||
|
||||
// Find extra textual param
|
||||
FString ExtraText = ExtractText(ActionToAdd, ActionToBeParsed);
|
||||
|
||||
// Add to result action sequence array
|
||||
if (ActionToAdd != "")
|
||||
{
|
||||
ConvaiResultAction.Action = ActionToAdd;
|
||||
ConvaiResultAction.RelatedObjectOrCharacter = RelatedObjOrChar;
|
||||
ConvaiResultAction.ConvaiExtraParams.Number = ExtraNumber;
|
||||
ConvaiResultAction.ConvaiExtraParams.Text = ExtraText;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool UConvaiActions::ValidateEnvironment(UConvaiEnvironment* Environment, FString& Error)
|
||||
{
|
||||
if (!IsValid(Environment))
|
||||
{
|
||||
Error = "ConvaiActions: Environment object is invalid or not set! You can create it using \"Create Environemnt Object\" function";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Environment->MainCharacter.Ref.IsValid())
|
||||
{
|
||||
Error = "ConvaiActions: Main Character in the Environment object is invalid or not set! Please set it using \"Set Main Character\" function and use the player pawn as input";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Environment->Actions.Num() == 0)
|
||||
{
|
||||
Error = "ConvaiActions: The Environment object does not have any actions added, please add some actions using \"Add Action\" or \"Add Actions\" function";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
31
ConvAI/Convai/Source/Convai/Private/ConvaiAndroid.cpp
Normal file
31
ConvAI/Convai/Source/Convai/Private/ConvaiAndroid.cpp
Normal file
@@ -0,0 +1,31 @@
|
||||
// Copyright 2022 Convai Inc. All Rights Reserved.
|
||||
|
||||
#include "ConvaiAndroid.h"
|
||||
|
||||
//#define PLATFORM_ANDROID 1
|
||||
|
||||
#if PLATFORM_ANDROID
|
||||
#include "AndroidPermissionFunctionLibrary.h"
|
||||
#endif
|
||||
|
||||
DEFINE_LOG_CATEGORY(ConvaiAndroidLog);
|
||||
|
||||
|
||||
void UConvaiAndroid::ConvaiAndroidAskMicrophonePermission()
|
||||
{
|
||||
#if PLATFORM_ANDROID
|
||||
TArray<FString> Permissions;
|
||||
Permissions.Add((FString("android.permission.RECORD_AUDIO")));
|
||||
UAndroidPermissionFunctionLibrary::AcquirePermissions(Permissions);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool UConvaiAndroid::ConvaiAndroidHasMicrophonePermission()
|
||||
{
|
||||
#if PLATFORM_ANDROID
|
||||
return UAndroidPermissionFunctionLibrary::CheckPermission("android.permission.RECORD_AUDIO");
|
||||
#else
|
||||
return true;
|
||||
#endif
|
||||
|
||||
}
|
||||
@@ -0,0 +1,454 @@
|
||||
// Copyright 2022 Convai Inc. All Rights Reserved.
|
||||
|
||||
|
||||
#include "ConvaiAudioCaptureComponent.h"
|
||||
#include "ConvaiDefinitions.h"
|
||||
#include "Utility/Log/ConvaiLogger.h"
|
||||
|
||||
DEFINE_LOG_CATEGORY(ConvaiAudioLog);
|
||||
|
||||
|
||||
namespace Audio
|
||||
{
|
||||
FConvaiAudioCaptureSynth::FConvaiAudioCaptureSynth()
|
||||
: NumSamplesEnqueued(0)
|
||||
, bInitialized(false)
|
||||
, bIsCapturing(false)
|
||||
{
|
||||
}
|
||||
|
||||
FConvaiAudioCaptureSynth::~FConvaiAudioCaptureSynth()
|
||||
{
|
||||
}
|
||||
|
||||
TArray<FCaptureDeviceInfo> FConvaiAudioCaptureSynth::GetCaptureDevicesAvailable()
|
||||
{
|
||||
TArray<FCaptureDeviceInfo> CaptureDevicesInfo;
|
||||
AudioCapture.GetCaptureDevicesAvailable(CaptureDevicesInfo);
|
||||
for (int i = CaptureDevicesInfo.Num()-1; i>=0; i--)
|
||||
{
|
||||
if (CaptureDevicesInfo[i].InputChannels <= 0)
|
||||
CaptureDevicesInfo.RemoveAt(i);
|
||||
}
|
||||
return CaptureDevicesInfo;
|
||||
}
|
||||
|
||||
int32 FConvaiAudioCaptureSynth::MapInputDeviceIDToDeviceID(int32 InputDeviceID)
|
||||
{
|
||||
uint32 NumInputDevices = 0;
|
||||
TArray<FCaptureDeviceInfo> CaptureDevicesInfo;
|
||||
AudioCapture.GetCaptureDevicesAvailable(CaptureDevicesInfo);
|
||||
uint32 NumDevices = CaptureDevicesInfo.Num();
|
||||
|
||||
Audio::FCaptureDeviceInfo DeviceInfo;
|
||||
for (uint32 DeviceIndex = 0; AudioCapture.GetCaptureDeviceInfo(DeviceInfo, DeviceIndex); DeviceIndex++)
|
||||
{
|
||||
//CONVAI_LOG(ConvaiAudioLog, Log, TEXT("DeviceIndex: %d, InputDeviceID: %d - name: %s - channels:%d - NumDevices:%d"), DeviceIndex, InputDeviceID, *DeviceInfo.DeviceName, DeviceInfo.InputChannels, NumDevices);
|
||||
bool IsInput = DeviceInfo.InputChannels > 0;
|
||||
if (!IsInput)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
//CONVAI_LOG(ConvaiAudioLog, Log, TEXT("DeviceIndex: %d, is an input - name: %s"), DeviceIndex, *DeviceInfo.DeviceName);
|
||||
if (NumInputDevices == InputDeviceID)
|
||||
{
|
||||
return DeviceIndex;
|
||||
}
|
||||
NumInputDevices++;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool FConvaiAudioCaptureSynth::GetDefaultCaptureDeviceInfo(FCaptureDeviceInfo& OutInfo)
|
||||
{
|
||||
return AudioCapture.GetCaptureDeviceInfo(OutInfo);
|
||||
}
|
||||
|
||||
bool FConvaiAudioCaptureSynth::GetCaptureDeviceInfo(FCaptureDeviceInfo& OutInfo, int32 DeviceIndex)
|
||||
{
|
||||
DeviceIndex = MapInputDeviceIDToDeviceID(DeviceIndex);
|
||||
return AudioCapture.GetCaptureDeviceInfo(OutInfo, DeviceIndex);
|
||||
}
|
||||
|
||||
bool FConvaiAudioCaptureSynth::OpenDefaultStream()
|
||||
{
|
||||
bool bSuccess = true;
|
||||
if (!AudioCapture.IsStreamOpen())
|
||||
{
|
||||
FOnCaptureFunction OnCapture = [this](const float* AudioData, int32 NumFrames, int32 NumChannels, int32 SampleRate, double StreamTime, bool bOverFlow)
|
||||
{
|
||||
int32 NumSamples = NumChannels * NumFrames;
|
||||
|
||||
FScopeLock Lock(&CaptureCriticalSection);
|
||||
|
||||
if (bIsCapturing)
|
||||
{
|
||||
// Append the audio memory to the capture data buffer
|
||||
int32 Index = AudioCaptureData.AddUninitialized(NumSamples);
|
||||
float* AudioCaptureDataPtr = AudioCaptureData.GetData();
|
||||
FMemory::Memcpy(&AudioCaptureDataPtr[Index], AudioData, NumSamples * sizeof(float));
|
||||
}
|
||||
};
|
||||
|
||||
// Prepare the audio buffer memory for 2 seconds of stereo audio at 48k SR to reduce chance for allocation in callbacks
|
||||
AudioCaptureData.Reserve(2 * 2 * 48000);
|
||||
|
||||
FAudioCaptureDeviceParams Params = FAudioCaptureDeviceParams();
|
||||
|
||||
// Start the stream here to avoid hitching the audio render thread.
|
||||
if (AudioCapture.OpenCaptureStream(Params, MoveTemp(OnCapture), 1024))
|
||||
{
|
||||
AudioCapture.StartStream();
|
||||
}
|
||||
else
|
||||
{
|
||||
bSuccess = false;
|
||||
}
|
||||
}
|
||||
return bSuccess;
|
||||
}
|
||||
|
||||
bool FConvaiAudioCaptureSynth::OpenStream(int32 DeviceIndex)
|
||||
{
|
||||
bool bSuccess = true;
|
||||
if (!AudioCapture.IsStreamOpen())
|
||||
{
|
||||
FOnCaptureFunction OnCapture = [this](const float* AudioData, int32 NumFrames, int32 NumChannels, int32 SampleRate, double StreamTime, bool bOverFlow)
|
||||
{
|
||||
int32 NumSamples = NumChannels * NumFrames;
|
||||
|
||||
FScopeLock Lock(&CaptureCriticalSection);
|
||||
|
||||
if (bIsCapturing)
|
||||
{
|
||||
// Append the audio memory to the capture data buffer
|
||||
int32 Index = AudioCaptureData.AddUninitialized(NumSamples);
|
||||
float* AudioCaptureDataPtr = AudioCaptureData.GetData();
|
||||
FMemory::Memcpy(&AudioCaptureDataPtr[Index], AudioData, NumSamples * sizeof(float));
|
||||
}
|
||||
};
|
||||
|
||||
// Prepare the audio buffer memory for 2 seconds of stereo audio at 48k SR to reduce chance for allocation in callbacks
|
||||
AudioCaptureData.Reserve(2 * 2 * 48000);
|
||||
|
||||
FAudioCaptureDeviceParams Params = FAudioCaptureDeviceParams();
|
||||
Params.DeviceIndex = DeviceIndex;
|
||||
// Start the stream here to avoid hitching the audio render thread.
|
||||
if (AudioCapture.OpenCaptureStream(Params, MoveTemp(OnCapture), 1024))
|
||||
{
|
||||
AudioCapture.StartStream();
|
||||
}
|
||||
else
|
||||
{
|
||||
bSuccess = false;
|
||||
}
|
||||
}
|
||||
return bSuccess;
|
||||
}
|
||||
|
||||
void FConvaiAudioCaptureSynth::CloseStream()
|
||||
{
|
||||
if (AudioCapture.IsStreamOpen())
|
||||
{
|
||||
AudioCapture.CloseStream();
|
||||
}
|
||||
}
|
||||
|
||||
bool FConvaiAudioCaptureSynth::StartCapturing()
|
||||
{
|
||||
FScopeLock Lock(&CaptureCriticalSection);
|
||||
|
||||
AudioCaptureData.Reset();
|
||||
|
||||
check(AudioCapture.IsStreamOpen());
|
||||
|
||||
bIsCapturing = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
void FConvaiAudioCaptureSynth::StopCapturing()
|
||||
{
|
||||
check(AudioCapture.IsStreamOpen());
|
||||
check(AudioCapture.IsCapturing());
|
||||
FScopeLock Lock(&CaptureCriticalSection);
|
||||
bIsCapturing = false;
|
||||
}
|
||||
|
||||
void FConvaiAudioCaptureSynth::AbortCapturing()
|
||||
{
|
||||
AudioCapture.AbortStream();
|
||||
AudioCapture.CloseStream();
|
||||
}
|
||||
|
||||
bool FConvaiAudioCaptureSynth::IsStreamOpen() const
|
||||
{
|
||||
return AudioCapture.IsStreamOpen();
|
||||
}
|
||||
|
||||
bool FConvaiAudioCaptureSynth::IsCapturing() const
|
||||
{
|
||||
return bIsCapturing;
|
||||
}
|
||||
|
||||
int32 FConvaiAudioCaptureSynth::GetNumSamplesEnqueued()
|
||||
{
|
||||
FScopeLock Lock(&CaptureCriticalSection);
|
||||
return AudioCaptureData.Num();
|
||||
}
|
||||
|
||||
FAudioCapture* FConvaiAudioCaptureSynth::GetAudioCapture()
|
||||
{
|
||||
return &AudioCapture;
|
||||
}
|
||||
|
||||
bool FConvaiAudioCaptureSynth::GetAudioData(TArray<float>& OutAudioData)
|
||||
{
|
||||
FScopeLock Lock(&CaptureCriticalSection);
|
||||
|
||||
int32 CaptureDataSamples = AudioCaptureData.Num();
|
||||
if (CaptureDataSamples > 0)
|
||||
{
|
||||
// Append the capture audio to the output buffer
|
||||
int32 OutIndex = OutAudioData.AddUninitialized(CaptureDataSamples);
|
||||
float* OutDataPtr = OutAudioData.GetData();
|
||||
FMemory::Memcpy(&OutDataPtr[OutIndex], AudioCaptureData.GetData(), CaptureDataSamples * sizeof(float));
|
||||
|
||||
// Reset the capture data buffer since we copied the audio out
|
||||
AudioCaptureData.Reset();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
UConvaiAudioCaptureComponent::UConvaiAudioCaptureComponent(const FObjectInitializer& ObjectInitializer)
|
||||
: Super(ObjectInitializer)
|
||||
{
|
||||
bSuccessfullyInitialized = false;
|
||||
bIsCapturing = false;
|
||||
CapturedAudioDataSamples = 0;
|
||||
ReadSampleIndex = 0;
|
||||
bIsDestroying = false;
|
||||
bIsNotReadyForForFinishDestroy = false;
|
||||
bIsStreamOpen = false;
|
||||
CaptureAudioData.Reserve(2 * 48000 * 5);
|
||||
SelectedDeviceIndex = -1;
|
||||
}
|
||||
|
||||
bool UConvaiAudioCaptureComponent::Init(int32& SampleRate)
|
||||
{
|
||||
|
||||
Audio::FCaptureDeviceInfo DeviceInfo;
|
||||
bool FoundDevice = false;
|
||||
if (SelectedDeviceIndex == -1)
|
||||
{
|
||||
FoundDevice = CaptureSynth.GetDefaultCaptureDeviceInfo(DeviceInfo);
|
||||
}
|
||||
else
|
||||
{
|
||||
FoundDevice = CaptureSynth.GetCaptureDeviceInfo(DeviceInfo, SelectedDeviceIndex);
|
||||
if (!FoundDevice)
|
||||
{
|
||||
FoundDevice = CaptureSynth.GetDefaultCaptureDeviceInfo(DeviceInfo);
|
||||
}
|
||||
}
|
||||
|
||||
CONVAI_LOG(ConvaiAudioLog, Log, TEXT("Using %s as Audio capture device with NumChannels:%d and SampleRate:%d"), *DeviceInfo.DeviceName, DeviceInfo.InputChannels, DeviceInfo.PreferredSampleRate);
|
||||
|
||||
if (FoundDevice)
|
||||
{
|
||||
SampleRate = DeviceInfo.PreferredSampleRate;
|
||||
NumChannels = DeviceInfo.InputChannels;
|
||||
|
||||
// Only support mono and stereo mic inputs for now...
|
||||
if (NumChannels == 1 || NumChannels == 2)
|
||||
{
|
||||
// This may fail if capture synths aren't supported on a given platform or if something went wrong with the capture device
|
||||
bIsStreamOpen = CaptureSynth.OpenStream(SelectedDeviceIndex);
|
||||
if (!bIsStreamOpen)
|
||||
{
|
||||
CONVAI_LOG(ConvaiAudioLog, Warning, TEXT("OpenStream returned false."));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
CONVAI_LOG(ConvaiAudioLog, Warning, TEXT("Audio capture components only support mono and stereo mic input - Audio might be mangeled."));
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void UConvaiAudioCaptureComponent::BeginDestroy()
|
||||
{
|
||||
Super::BeginDestroy();
|
||||
|
||||
// Flag that we're beginning to be destroyed
|
||||
// This is so that if a mic capture is open, we shut it down on the render thread
|
||||
bIsDestroying = true;
|
||||
|
||||
// Make sure stop is kicked off
|
||||
Stop();
|
||||
}
|
||||
|
||||
bool UConvaiAudioCaptureComponent::IsReadyForFinishDestroy()
|
||||
{
|
||||
return !bIsNotReadyForForFinishDestroy;
|
||||
}
|
||||
|
||||
void UConvaiAudioCaptureComponent::FinishDestroy()
|
||||
{
|
||||
if (CaptureSynth.IsStreamOpen())
|
||||
{
|
||||
CaptureSynth.AbortCapturing();
|
||||
}
|
||||
|
||||
check(!CaptureSynth.IsStreamOpen());
|
||||
|
||||
Super::FinishDestroy();
|
||||
bSuccessfullyInitialized = false;
|
||||
bIsCapturing = false;
|
||||
bIsDestroying = false;
|
||||
bIsStreamOpen = false;
|
||||
}
|
||||
|
||||
bool UConvaiAudioCaptureComponent::GetDefaultCaptureDeviceInfo(Audio::FCaptureDeviceInfo& OutInfo)
|
||||
{
|
||||
return CaptureSynth.GetDefaultCaptureDeviceInfo(OutInfo);
|
||||
}
|
||||
|
||||
bool UConvaiAudioCaptureComponent::GetCaptureDeviceInfo(Audio::FCaptureDeviceInfo& OutInfo, int32 DeviceIndex)
|
||||
{
|
||||
return CaptureSynth.GetCaptureDeviceInfo(OutInfo, DeviceIndex);
|
||||
}
|
||||
|
||||
TArray<Audio::FCaptureDeviceInfo> UConvaiAudioCaptureComponent::GetCaptureDevicesAvailable()
|
||||
{
|
||||
return CaptureSynth.GetCaptureDevicesAvailable();
|
||||
}
|
||||
|
||||
int32 UConvaiAudioCaptureComponent::GetActiveCaptureDevice(Audio::FCaptureDeviceInfo& OutInfo)
|
||||
{
|
||||
if (SelectedDeviceIndex == -1)
|
||||
GetDefaultCaptureDeviceInfo(OutInfo);
|
||||
else
|
||||
GetCaptureDeviceInfo(OutInfo, SelectedDeviceIndex);
|
||||
return SelectedDeviceIndex;
|
||||
}
|
||||
|
||||
bool UConvaiAudioCaptureComponent::SetCaptureDevice(int32 DeviceIndex)
|
||||
{
|
||||
Audio::FCaptureDeviceInfo OutInfo;
|
||||
if (GetCaptureDeviceInfo(OutInfo, DeviceIndex))
|
||||
{
|
||||
CaptureSynth.CloseStream();
|
||||
SelectedDeviceIndex = DeviceIndex;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Audio::FConvaiAudioCaptureSynth* UConvaiAudioCaptureComponent::GetCaptureSynth()
|
||||
{
|
||||
return &CaptureSynth;
|
||||
}
|
||||
|
||||
void UConvaiAudioCaptureComponent::OnBeginGenerate()
|
||||
{
|
||||
CapturedAudioDataSamples = 0;
|
||||
ReadSampleIndex = 0;
|
||||
CaptureAudioData.Reset();
|
||||
|
||||
if (!bIsStreamOpen)
|
||||
{
|
||||
bIsStreamOpen = CaptureSynth.OpenDefaultStream();
|
||||
}
|
||||
|
||||
if (bIsStreamOpen)
|
||||
{
|
||||
CaptureSynth.StartCapturing();
|
||||
check(CaptureSynth.IsCapturing());
|
||||
|
||||
// Don't allow this component to be destroyed until the stream is closed again
|
||||
bIsNotReadyForForFinishDestroy = true;
|
||||
FramesSinceStarting = 0;
|
||||
ReadSampleIndex = 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void UConvaiAudioCaptureComponent::OnEndGenerate()
|
||||
{
|
||||
if (bIsStreamOpen)
|
||||
{
|
||||
check(CaptureSynth.IsStreamOpen());
|
||||
CaptureSynth.StopCapturing();
|
||||
bIsStreamOpen = false;
|
||||
|
||||
bIsNotReadyForForFinishDestroy = false;
|
||||
}
|
||||
}
|
||||
|
||||
int32 UConvaiAudioCaptureComponent::OnGenerateAudio(float* OutAudio, int32 NumSamples)
|
||||
{
|
||||
// Don't do anything if the stream isn't open
|
||||
if (!bIsStreamOpen || !CaptureSynth.IsStreamOpen() || !CaptureSynth.IsCapturing())
|
||||
{
|
||||
// Just return NumSamples, which uses zero'd buffer
|
||||
return NumSamples;
|
||||
}
|
||||
|
||||
int32 OutputSamplesGenerated = 0;
|
||||
|
||||
if (CapturedAudioDataSamples > 0 || CaptureSynth.GetNumSamplesEnqueued() > 1024)
|
||||
{
|
||||
// Check if we need to read more audio data from capture synth
|
||||
if (ReadSampleIndex + NumSamples > CaptureAudioData.Num())
|
||||
{
|
||||
// but before we do, copy off the remainder of the capture audio data buffer if there's data in it
|
||||
int32 SamplesLeft = FMath::Max(0, CaptureAudioData.Num() - ReadSampleIndex);
|
||||
if (SamplesLeft > 0)
|
||||
{
|
||||
float* CaptureDataPtr = CaptureAudioData.GetData();
|
||||
FMemory::Memcpy(OutAudio, &CaptureDataPtr[ReadSampleIndex], SamplesLeft * sizeof(float));
|
||||
|
||||
// Track samples generated
|
||||
OutputSamplesGenerated += SamplesLeft;
|
||||
}
|
||||
|
||||
// Get another block of audio from the capture synth
|
||||
CaptureAudioData.Reset();
|
||||
CaptureSynth.GetAudioData(CaptureAudioData);
|
||||
|
||||
// Reset the read sample index since we got a new buffer of audio data
|
||||
ReadSampleIndex = 0;
|
||||
}
|
||||
|
||||
// note it's possible we didn't get any more audio in our last attempt to get it
|
||||
if (CaptureAudioData.Num() > 0)
|
||||
{
|
||||
// Compute samples to copy
|
||||
int32 NumSamplesToCopy = FMath::Min(NumSamples - OutputSamplesGenerated, CaptureAudioData.Num() - ReadSampleIndex);
|
||||
|
||||
float* CaptureDataPtr = CaptureAudioData.GetData();
|
||||
FMemory::Memcpy(&OutAudio[OutputSamplesGenerated], &CaptureDataPtr[ReadSampleIndex], NumSamplesToCopy * sizeof(float));
|
||||
ReadSampleIndex += NumSamplesToCopy;
|
||||
OutputSamplesGenerated += NumSamplesToCopy;
|
||||
}
|
||||
|
||||
CapturedAudioDataSamples += OutputSamplesGenerated;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Say we generated the full samples, this will result in silence
|
||||
OutputSamplesGenerated = NumSamples;
|
||||
}
|
||||
|
||||
return OutputSamplesGenerated;
|
||||
}
|
||||
|
||||
1679
ConvAI/Convai/Source/Convai/Private/ConvaiAudioStreamer.cpp
Normal file
1679
ConvAI/Convai/Source/Convai/Private/ConvaiAudioStreamer.cpp
Normal file
File diff suppressed because it is too large
Load Diff
1764
ConvAI/Convai/Source/Convai/Private/ConvaiChatBotProxy.cpp
Normal file
1764
ConvAI/Convai/Source/Convai/Private/ConvaiChatBotProxy.cpp
Normal file
File diff suppressed because it is too large
Load Diff
1357
ConvAI/Convai/Source/Convai/Private/ConvaiChatbotComponent.cpp
Normal file
1357
ConvAI/Convai/Source/Convai/Private/ConvaiChatbotComponent.cpp
Normal file
File diff suppressed because it is too large
Load Diff
12
ConvAI/Convai/Source/Convai/Private/ConvaiDefinitions.cpp
Normal file
12
ConvAI/Convai/Source/Convai/Private/ConvaiDefinitions.cpp
Normal file
@@ -0,0 +1,12 @@
|
||||
// Copyright 2022 Convai Inc. All Rights Reserved.
|
||||
|
||||
|
||||
#include "ConvaiDefinitions.h"
|
||||
|
||||
const TMap<EEmotionIntensity, float> FConvaiEmotionState::ScoreMultipliers =
|
||||
{
|
||||
{EEmotionIntensity::None, 0.0},
|
||||
{EEmotionIntensity::LessIntense, 0.25},
|
||||
{EEmotionIntensity::Basic, 0.6},
|
||||
{EEmotionIntensity::MoreIntense, 1}
|
||||
};
|
||||
346
ConvAI/Convai/Source/Convai/Private/ConvaiFaceSync.cpp
Normal file
346
ConvAI/Convai/Source/Convai/Private/ConvaiFaceSync.cpp
Normal file
@@ -0,0 +1,346 @@
|
||||
// Copyright 2022 Convai Inc. All Rights Reserved.
|
||||
|
||||
#include "ConvaiFaceSync.h"
|
||||
#include "Misc/ScopeLock.h"
|
||||
#include "ConvaiUtils.h"
|
||||
|
||||
DEFINE_LOG_CATEGORY(ConvaiFaceSyncLog);
|
||||
|
||||
namespace
|
||||
{
|
||||
// Helper function: Creates a zero blendshapes map
|
||||
TMap<FName, float> CreateZeroBlendshapes()
|
||||
{
|
||||
TMap<FName, float> ZeroBlendshapes;
|
||||
for (const auto& BlendShapeName : ConvaiConstants::BlendShapesNames)
|
||||
{
|
||||
ZeroBlendshapes.Add(*BlendShapeName, 0.0);
|
||||
}
|
||||
return ZeroBlendshapes;
|
||||
}
|
||||
|
||||
// Helper function: Creates a zero visemes map
|
||||
TMap<FName, float> CreateZeroVisemes()
|
||||
{
|
||||
TMap<FName, float> ZeroVisemes;
|
||||
for (const auto& VisemeName : ConvaiConstants::VisemeNames)
|
||||
{
|
||||
ZeroVisemes.Add(*VisemeName, 0.0);
|
||||
}
|
||||
ZeroVisemes["sil"] = 1;
|
||||
return ZeroVisemes;
|
||||
}
|
||||
|
||||
|
||||
float Calculate1DBezierCurve(float t, float P0, float P1, float P2, float P3)
|
||||
{
|
||||
// Make sure t is in the range [0, 1]
|
||||
t = FMath::Clamp(t, 0.0f, 1.0f);
|
||||
|
||||
// Calculate the 1D Bezier curve value at time t
|
||||
float result = FMath::Pow(1.0f - t, 3.0f) * P0 + 3.0f * FMath::Pow(1.0f - t, 2.0f) * t * P1 +
|
||||
3.0f * (1.0f - t) * FMath::Pow(t, 2.0f) * P2 + FMath::Pow(t, 3.0f) * P3;
|
||||
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
const TMap<FName, float> UConvaiFaceSyncComponent::ZeroBlendshapeFrame = CreateZeroBlendshapes();
|
||||
const TMap<FName, float> UConvaiFaceSyncComponent::ZeroVisemeFrame = CreateZeroVisemes();
|
||||
|
||||
UConvaiFaceSyncComponent::UConvaiFaceSyncComponent()
|
||||
{
|
||||
PrimaryComponentTick.bCanEverTick = true;
|
||||
CurrentSequenceTimePassed = 0;
|
||||
bAutoActivate = true;
|
||||
//CurrentBlendShapesMap = ZeroBlendshapeFrame;
|
||||
}
|
||||
|
||||
UConvaiFaceSyncComponent::~UConvaiFaceSyncComponent() // Implement destructor
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void UConvaiFaceSyncComponent::BeginPlay()
|
||||
{
|
||||
Super::BeginPlay();
|
||||
CurrentBlendShapesMap = GenerateZeroFrame();
|
||||
}
|
||||
|
||||
void UConvaiFaceSyncComponent::TickComponent(float DeltaTime, ELevelTick TickType,
|
||||
FActorComponentTickFunction* ThisTickFunction)
|
||||
{
|
||||
Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
|
||||
|
||||
// Interpolate blendshapes and advance animation sequence
|
||||
if (IsValidSequence(MainSequenceBuffer))
|
||||
{
|
||||
SequenceCriticalSection.Lock();
|
||||
|
||||
// Calculate time passed since ConvaiProcessLipSyncAdvanced was called
|
||||
double CurrentTime = FPlatformTime::Seconds();
|
||||
CurrentSequenceTimePassed = CurrentTime - StartTime;
|
||||
|
||||
if (CurrentSequenceTimePassed > MainSequenceBuffer.Duration)
|
||||
{
|
||||
ConvaiStopLipSync();
|
||||
SequenceCriticalSection.Unlock();
|
||||
return;
|
||||
}
|
||||
|
||||
bIsPlaying = true;
|
||||
|
||||
// Calculate frame duration and offsets
|
||||
float FrameDuration = MainSequenceBuffer.Duration / MainSequenceBuffer.AnimationFrames.Num();
|
||||
float FrameOffset = FrameDuration * 0.5f;
|
||||
int32 FrameIndex;
|
||||
int32 BufferIndex;
|
||||
|
||||
TMap<FName, float> StartFrame;
|
||||
TMap<FName, float> EndFrame;
|
||||
float Alpha;
|
||||
|
||||
// Choose the current and next BlendShapes
|
||||
if (CurrentSequenceTimePassed <= FrameOffset)
|
||||
{
|
||||
//StartFrame = ZeroBlendshapeFrame;
|
||||
StartFrame = GetCurrentFrame();
|
||||
EndFrame = MainSequenceBuffer.AnimationFrames[0].BlendShapes;
|
||||
Alpha = CurrentSequenceTimePassed / FrameOffset + 0.5;
|
||||
FrameIndex = MainSequenceBuffer.AnimationFrames[0].FrameIndex;
|
||||
BufferIndex = 0;
|
||||
}
|
||||
else if (CurrentSequenceTimePassed >= MainSequenceBuffer.Duration - FrameOffset)
|
||||
{
|
||||
int LastFrameIdx = MainSequenceBuffer.AnimationFrames.Num() - 1;
|
||||
StartFrame = MainSequenceBuffer.AnimationFrames[LastFrameIdx].BlendShapes;
|
||||
EndFrame = GenerateZeroFrame();
|
||||
Alpha = (CurrentSequenceTimePassed - (MainSequenceBuffer.Duration - FrameOffset)) / FrameOffset;
|
||||
FrameIndex = MainSequenceBuffer.AnimationFrames[LastFrameIdx].FrameIndex;
|
||||
BufferIndex = LastFrameIdx;
|
||||
}
|
||||
else
|
||||
{
|
||||
int CurrentFrameIndex = FMath::FloorToInt((CurrentSequenceTimePassed - FrameOffset) / FrameDuration);
|
||||
CurrentFrameIndex = FMath::Min(CurrentFrameIndex, MainSequenceBuffer.AnimationFrames.Num() - 1);
|
||||
int NextFrameIndex = FMath::Min(CurrentFrameIndex + 1, MainSequenceBuffer.AnimationFrames.Num() - 1);
|
||||
StartFrame = MainSequenceBuffer.AnimationFrames[CurrentFrameIndex].BlendShapes;
|
||||
EndFrame = MainSequenceBuffer.AnimationFrames[NextFrameIndex].BlendShapes;
|
||||
Alpha = (CurrentSequenceTimePassed - FrameOffset - (CurrentFrameIndex * FrameDuration)) / FrameDuration;
|
||||
|
||||
Apply_StartEndFrames_PostProcessing(CurrentFrameIndex, NextFrameIndex, Alpha, StartFrame, EndFrame);
|
||||
|
||||
FrameIndex = MainSequenceBuffer.AnimationFrames[CurrentFrameIndex].FrameIndex;
|
||||
BufferIndex = CurrentFrameIndex;
|
||||
}
|
||||
SequenceCriticalSection.Unlock();
|
||||
|
||||
// CONVAI_LOG(ConvaiFaceSyncLog, Log, TEXT("Evaluate: FrameIndex:%d Alpha: %f FramesLeft: %d"), FrameIndex, Alpha, MainSequenceBuffer.AnimationFrames.Num() - BufferIndex);
|
||||
|
||||
//CurrentBlendShapesMap = StartFrame;
|
||||
CurrentBlendShapesMap = InterpolateFrames(StartFrame, EndFrame, Alpha);
|
||||
|
||||
ApplyPostProcessing();
|
||||
|
||||
// Trigger the blueprint event
|
||||
OnVisemesDataReady.ExecuteIfBound();
|
||||
}
|
||||
}
|
||||
|
||||
void UConvaiFaceSyncComponent::ConvaiApplyPrecomputedFacialAnimation(uint8* InPCMData, uint32 InPCMDataSize, uint32 InSampleRate, uint32 InNumChannels, FAnimationSequence FaceSequence)
|
||||
{
|
||||
SequenceCriticalSection.Lock();
|
||||
MainSequenceBuffer.AnimationFrames.Append(FaceSequence.AnimationFrames);
|
||||
MainSequenceBuffer.Duration += FaceSequence.Duration;
|
||||
MainSequenceBuffer.FrameRate = FaceSequence.FrameRate;
|
||||
CalculateStartingTime();
|
||||
SequenceCriticalSection.Unlock();
|
||||
|
||||
if (IsRecordingLipSync)
|
||||
{
|
||||
FScopeLock ScopeLock(&RecordingCriticalSection);
|
||||
RecordedSequenceBuffer.AnimationFrames.Append(FaceSequence.AnimationFrames);
|
||||
RecordedSequenceBuffer.Duration += FaceSequence.Duration;
|
||||
RecordedSequenceBuffer.FrameRate = FaceSequence.FrameRate;
|
||||
}
|
||||
}
|
||||
|
||||
void UConvaiFaceSyncComponent::ConvaiApplyFacialFrame(FAnimationFrame FaceFrame, float Duration)
|
||||
{
|
||||
SequenceCriticalSection.Lock();
|
||||
if (!GeneratesVisemesAsBlendshapes())
|
||||
{
|
||||
float* sil = FaceFrame.BlendShapes.Find("sil");
|
||||
if (sil != nullptr && *sil < 0)
|
||||
{
|
||||
ClearMainSequence();
|
||||
Stopping = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
MainSequenceBuffer.AnimationFrames.Add(FaceFrame);
|
||||
MainSequenceBuffer.Duration += Duration;
|
||||
MainSequenceBuffer.FrameRate = Duration==0? -1 : 1.0/Duration;
|
||||
SequenceCriticalSection.Unlock();
|
||||
|
||||
if (IsRecordingLipSync)
|
||||
{
|
||||
FScopeLock ScopeLock(&RecordingCriticalSection);
|
||||
RecordedSequenceBuffer.AnimationFrames.Add(FaceFrame);
|
||||
RecordedSequenceBuffer.Duration += Duration;
|
||||
RecordedSequenceBuffer.FrameRate = Duration == 0 ? -1 : 1.0 / Duration;
|
||||
}
|
||||
}
|
||||
|
||||
void UConvaiFaceSyncComponent::StartRecordingLipSync()
|
||||
{
|
||||
if (IsRecordingLipSync)
|
||||
{
|
||||
CONVAI_LOG(ConvaiFaceSyncLog, Warning, TEXT("Cannot start Recording LipSync while already recording LipSync"));
|
||||
return;
|
||||
}
|
||||
|
||||
CONVAI_LOG(ConvaiFaceSyncLog, Log, TEXT("Started Recording LipSync"));
|
||||
IsRecordingLipSync = true;
|
||||
}
|
||||
|
||||
FAnimationSequenceBP UConvaiFaceSyncComponent::FinishRecordingLipSync()
|
||||
{
|
||||
CONVAI_LOG(ConvaiFaceSyncLog, Log, TEXT("Finished Recording LipSync - Total Frames: %d - Duration: %f"), RecordedSequenceBuffer.AnimationFrames.Num(), RecordedSequenceBuffer.Duration);
|
||||
|
||||
if (!IsRecordingLipSync)
|
||||
return FAnimationSequenceBP();
|
||||
|
||||
FScopeLock ScopeLock(&RecordingCriticalSection);
|
||||
IsRecordingLipSync = false;
|
||||
|
||||
FAnimationSequenceBP AnimationSequenceBP;
|
||||
AnimationSequenceBP.AnimationSequence = RecordedSequenceBuffer;
|
||||
RecordedSequenceBuffer.AnimationFrames.Empty();
|
||||
RecordedSequenceBuffer.Duration = 0;
|
||||
return AnimationSequenceBP;
|
||||
}
|
||||
|
||||
bool UConvaiFaceSyncComponent::PlayRecordedLipSync(FAnimationSequenceBP RecordedLipSync, int StartFrame, int EndFrame, float OverwriteDuration)
|
||||
{
|
||||
if (!IsValidSequence(RecordedLipSync.AnimationSequence))
|
||||
{
|
||||
CONVAI_LOG(ConvaiFaceSyncLog, Warning, TEXT("Recorded LipSync is not valid - Total Frames: %d - Duration: %f"), RecordedLipSync.AnimationSequence.AnimationFrames.Num(), RecordedLipSync.AnimationSequence.Duration);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (IsRecordingLipSync)
|
||||
{
|
||||
CONVAI_LOG(ConvaiFaceSyncLog, Warning, TEXT("Cannot Play Recorded LipSync while Recording LipSync"));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (IsValidSequence(MainSequenceBuffer))
|
||||
{
|
||||
CONVAI_LOG(ConvaiFaceSyncLog, Warning, TEXT("Playing Recorded LipSync and stopping currently playing LipSync"));
|
||||
ConvaiStopLipSync();
|
||||
}
|
||||
|
||||
if (StartFrame > 0 && StartFrame > RecordedLipSync.AnimationSequence.AnimationFrames.Num() - 1)
|
||||
{
|
||||
CONVAI_LOG(ConvaiFaceSyncLog, Warning, TEXT("StartFrame is greater than the recorded LipSync - StartFrame: %d Total Frames: %d - Duration: %f"), StartFrame, RecordedLipSync.AnimationSequence.AnimationFrames.Num(), RecordedLipSync.AnimationSequence.Duration);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
if (EndFrame > 0 && EndFrame < StartFrame)
|
||||
{
|
||||
CONVAI_LOG(ConvaiFaceSyncLog, Warning, TEXT("StartFrame cannot be greater than the EndFrame"));
|
||||
return false;
|
||||
}
|
||||
|
||||
//if (EndFrame > 0 && EndFrame > RecordedLipSync.AnimationSequence.AnimationFrames.Num() - 1)
|
||||
//{
|
||||
// CONVAI_LOG(ConvaiFaceSyncLog, Warning, TEXT("EndFrame is greater than the recorded LipSync - EndFrame: %d Total Frames: %d - Duration: %f"), EndFrame, RecordedLipSync.AnimationSequence.AnimationFrames.Num(), RecordedLipSync.AnimationSequence.Duration);
|
||||
// return false;
|
||||
//}
|
||||
|
||||
if (EndFrame > 0 && EndFrame < RecordedLipSync.AnimationSequence.AnimationFrames.Num()-1)
|
||||
{
|
||||
float FrameDuration = RecordedLipSync.AnimationSequence.Duration / RecordedLipSync.AnimationSequence.AnimationFrames.Num();
|
||||
int NumRemovedFrames = RecordedLipSync.AnimationSequence.AnimationFrames.Num() - EndFrame - 1;
|
||||
RecordedLipSync.AnimationSequence.AnimationFrames.RemoveAt(EndFrame + 1, NumRemovedFrames);
|
||||
float RemovedDuration = NumRemovedFrames * FrameDuration;
|
||||
RecordedLipSync.AnimationSequence.Duration -= RemovedDuration;
|
||||
}
|
||||
|
||||
if (StartFrame > 0)
|
||||
{
|
||||
float FrameDuration = RecordedLipSync.AnimationSequence.Duration / RecordedLipSync.AnimationSequence.AnimationFrames.Num();
|
||||
int NumRemovedFrames = StartFrame;
|
||||
RecordedLipSync.AnimationSequence.AnimationFrames.RemoveAt(0, NumRemovedFrames);
|
||||
float RemovedDuration = NumRemovedFrames * FrameDuration;
|
||||
RecordedLipSync.AnimationSequence.Duration -= RemovedDuration;
|
||||
}
|
||||
|
||||
if (OverwriteDuration > 0)
|
||||
{
|
||||
RecordedLipSync.AnimationSequence.Duration = OverwriteDuration;
|
||||
}
|
||||
|
||||
CONVAI_LOG(ConvaiFaceSyncLog, Log, TEXT("Playing Recorded LipSync - Total Frames: %d - Duration: %f"), RecordedLipSync.AnimationSequence.AnimationFrames.Num(), RecordedLipSync.AnimationSequence.Duration);
|
||||
ConvaiApplyPrecomputedFacialAnimation(nullptr, 0, 0, 0, RecordedLipSync.AnimationSequence);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UConvaiFaceSyncComponent::IsValidSequence(const FAnimationSequence& Sequence)
|
||||
{
|
||||
if (Sequence.Duration > 0 && Sequence.AnimationFrames.Num() > 0 && Sequence.AnimationFrames[0].BlendShapes.Num() > 0)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
bool UConvaiFaceSyncComponent::IsPlaying()
|
||||
{
|
||||
return bIsPlaying;
|
||||
}
|
||||
|
||||
void UConvaiFaceSyncComponent::CalculateStartingTime()
|
||||
{
|
||||
if (!bIsPlaying)
|
||||
StartTime = FPlatformTime::Seconds();
|
||||
}
|
||||
|
||||
void UConvaiFaceSyncComponent::ForceRecalculateStartTime()
|
||||
{
|
||||
StartTime = FPlatformTime::Seconds();
|
||||
}
|
||||
|
||||
void UConvaiFaceSyncComponent::ClearMainSequence()
|
||||
{
|
||||
SequenceCriticalSection.Lock();
|
||||
MainSequenceBuffer.AnimationFrames.Empty();
|
||||
MainSequenceBuffer.Duration = 0;
|
||||
SequenceCriticalSection.Unlock();
|
||||
}
|
||||
|
||||
TMap<FName, float> UConvaiFaceSyncComponent::InterpolateFrames(const TMap<FName, float>& StartFrame, const TMap<FName, float>& EndFrame, float Alpha)
|
||||
{
|
||||
const TArray<FString>& CurveNames = GeneratesVisemesAsBlendshapes() ? ConvaiConstants::BlendShapesNames : ConvaiConstants::VisemeNames;
|
||||
TMap<FName, float> Result;
|
||||
for (const auto& CurveName : CurveNames)
|
||||
{
|
||||
float StartValue = StartFrame.Contains(*CurveName) ? StartFrame[*CurveName] : 0.0;
|
||||
float EndValue = EndFrame.Contains(*CurveName) ? EndFrame[*CurveName] : 0.0;
|
||||
Result.Add(*CurveName, FMath::Lerp(StartValue, EndValue, Alpha));
|
||||
}
|
||||
return Result;
|
||||
}
|
||||
|
||||
void UConvaiFaceSyncComponent::ConvaiStopLipSync()
|
||||
{
|
||||
bIsPlaying = false;
|
||||
CurrentSequenceTimePassed = 0;
|
||||
ClearMainSequence();
|
||||
SetCurrentFrametoZero();
|
||||
OnVisemesDataReady.ExecuteIfBound();
|
||||
// CONVAI_LOG(ConvaiFaceSyncLog, Warning, TEXT("Stopping LipSync"));
|
||||
}
|
||||
1131
ConvAI/Convai/Source/Convai/Private/ConvaiGRPC.cpp
Normal file
1131
ConvAI/Convai/Source/Convai/Private/ConvaiGRPC.cpp
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,256 @@
|
||||
// Copyright 2022 Convai Inc. All Rights Reserved.
|
||||
|
||||
#include "ConvaiNarrativeDesignProxy.h"
|
||||
#include "ConvaiUtils.h"
|
||||
#include "../Convai.h"
|
||||
#include "Engine.h"
|
||||
#include "RestAPI/ConvaiURL.h"
|
||||
|
||||
DEFINE_LOG_CATEGORY(ConvaiNarrativeHTTP);
|
||||
|
||||
UFetchNarrativeSectionsProxy* UFetchNarrativeSectionsProxy::FetchNarrativeSections(UObject* WorldContextObject, FString InCharacterId)
|
||||
{
|
||||
UFetchNarrativeSectionsProxy* Proxy = NewObject<UFetchNarrativeSectionsProxy>();
|
||||
Proxy->WorldPtr = GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::LogAndReturnNull);
|
||||
Proxy->CharacterId = InCharacterId;
|
||||
return Proxy;
|
||||
}
|
||||
|
||||
void UFetchNarrativeSectionsProxy::Activate()
|
||||
{
|
||||
|
||||
UWorld* World = WorldPtr.Get();
|
||||
|
||||
if (!World)
|
||||
{
|
||||
CONVAI_LOG(ConvaiNarrativeHTTP, Warning, TEXT("Could not get a pointer to world!"));
|
||||
failed();
|
||||
return;
|
||||
}
|
||||
|
||||
FHttpModule* Http = &FHttpModule::Get();
|
||||
if (!Http)
|
||||
{
|
||||
CONVAI_LOG(ConvaiNarrativeHTTP, Warning, TEXT("Could not get a pointer to http module!"));
|
||||
failed();
|
||||
return;
|
||||
}
|
||||
|
||||
TPair<FString, FString> AuthHeaderAndKey = UConvaiUtils::GetAuthHeaderAndKey();
|
||||
FString AuthKey = AuthHeaderAndKey.Value;
|
||||
FString AuthHeader = AuthHeaderAndKey.Key;
|
||||
|
||||
// Form Validation
|
||||
if (
|
||||
!UConvaiFormValidation::ValidateAuthKey(AuthKey)
|
||||
|| !UConvaiFormValidation::ValidateCharacterID(CharacterId)
|
||||
)
|
||||
{
|
||||
CONVAI_LOG(ConvaiNarrativeHTTP, Warning, TEXT("UFetchNarrativeSectionsProxy::Activate Invalid Character or API key"));
|
||||
failed();
|
||||
return;
|
||||
}
|
||||
|
||||
FHttpModule* HttpModule = &FHttpModule::Get();
|
||||
TSharedRef<IHttpRequest, ESPMode::ThreadSafe> HttpRequest = HttpModule->CreateRequest();
|
||||
HttpRequest->OnProcessRequestComplete().BindUObject(this, &UFetchNarrativeSectionsProxy::OnHttpRequestCompleted);
|
||||
HttpRequest->SetURL(UConvaiURL::GetFullURL(TEXT("character/narrative/list-sections"), false));
|
||||
HttpRequest->SetVerb(TEXT("POST"));
|
||||
HttpRequest->SetHeader(TEXT("Content-Type"), TEXT("application/json"));
|
||||
HttpRequest->SetHeader(AuthHeader, AuthKey);
|
||||
HttpRequest->SetContentAsString(FString::Printf(TEXT("{\"character_id\": \"%s\"}"), *CharacterId));
|
||||
HttpRequest->ProcessRequest();
|
||||
}
|
||||
|
||||
void UFetchNarrativeSectionsProxy::OnHttpRequestCompleted(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful)
|
||||
{
|
||||
NarrativeSections.Empty();
|
||||
if (!Response)
|
||||
{
|
||||
if (bWasSuccessful)
|
||||
{
|
||||
CONVAI_LOG(ConvaiNarrativeHTTP, Warning, TEXT("HTTP request succeded - But response pointer is invalid"));
|
||||
}
|
||||
else
|
||||
{
|
||||
CONVAI_LOG(ConvaiNarrativeHTTP, Warning, TEXT("HTTP request failed - Response pointer is invalid"));
|
||||
}
|
||||
|
||||
failed();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!bWasSuccessful || Response->GetResponseCode() < 200 || Response->GetResponseCode() > 299)
|
||||
{
|
||||
CONVAI_LOG(ConvaiNarrativeHTTP, Warning, TEXT("HTTP request failed with code %d"), Response->GetResponseCode());
|
||||
CONVAI_LOG(ConvaiNarrativeHTTP, Warning, TEXT("Response:%s"), *Response->GetContentAsString());
|
||||
|
||||
failed();
|
||||
return;
|
||||
}
|
||||
|
||||
TArray<TSharedPtr<FJsonValue>> JsonArray;
|
||||
const TSharedRef<TJsonReader<>> Reader = TJsonReaderFactory<>::Create(Response->GetContentAsString());
|
||||
|
||||
if (FJsonSerializer::Deserialize(Reader, JsonArray)) // Deserialize into a JSON array
|
||||
{
|
||||
for (const TSharedPtr<FJsonValue>& Val : JsonArray)
|
||||
{
|
||||
FNarrativeSection Section;
|
||||
if (FJsonObjectConverter::JsonObjectToUStruct(Val->AsObject().ToSharedRef(), FNarrativeSection::StaticStruct(), &Section))
|
||||
{
|
||||
NarrativeSections.Add(Section);
|
||||
}
|
||||
}
|
||||
success();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void UFetchNarrativeSectionsProxy::failed()
|
||||
{
|
||||
//auto InvocationList = OnFailure.GetAllObjects();
|
||||
//CONVAI_LOG(ConvaiNarrativeHTTP, Log, TEXT("OnFailure delegate is bound to %d functions before invokation."), InvocationList.Num());
|
||||
|
||||
OnFailure.Broadcast(NarrativeSections);
|
||||
//CONVAI_LOG(ConvaiNarrativeHTTP, Log, TEXT("OnFailure delegate is bound to %d functions after invokation."), InvocationList.Num());
|
||||
finish();
|
||||
}
|
||||
|
||||
void UFetchNarrativeSectionsProxy::success()
|
||||
{
|
||||
//auto InvocationList = OnSuccess.GetAllObjects();
|
||||
//CONVAI_LOG(ConvaiNarrativeHTTP, Log, TEXT("OnSuccess delegate is bound to %d functions before invokation."), InvocationList.Num());
|
||||
|
||||
OnSuccess.Broadcast(NarrativeSections);
|
||||
//CONVAI_LOG(ConvaiNarrativeHTTP, Log, TEXT("OnSuccess delegate is bound to %d functions after invokation."), InvocationList.Num());
|
||||
finish();
|
||||
}
|
||||
|
||||
void UFetchNarrativeSectionsProxy::finish()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
UFetchNarrativeTriggersProxy* UFetchNarrativeTriggersProxy::FetchNarrativeTriggers(UObject* WorldContextObject, FString InCharacterId)
|
||||
{
|
||||
UFetchNarrativeTriggersProxy* Proxy = NewObject<UFetchNarrativeTriggersProxy>();
|
||||
Proxy->WorldPtr = GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::LogAndReturnNull);
|
||||
Proxy->CharacterId = InCharacterId;
|
||||
return Proxy;
|
||||
}
|
||||
|
||||
void UFetchNarrativeTriggersProxy::Activate()
|
||||
{
|
||||
|
||||
UWorld* World = WorldPtr.Get();
|
||||
|
||||
if (!World)
|
||||
{
|
||||
CONVAI_LOG(ConvaiNarrativeHTTP, Warning, TEXT("Could not get a pointer to world!"));
|
||||
failed();
|
||||
return;
|
||||
}
|
||||
|
||||
FHttpModule* Http = &FHttpModule::Get();
|
||||
if (!Http)
|
||||
{
|
||||
CONVAI_LOG(ConvaiNarrativeHTTP, Warning, TEXT("Could not get a pointer to http module!"));
|
||||
failed();
|
||||
return;
|
||||
}
|
||||
|
||||
TPair<FString, FString> AuthHeaderAndKey = UConvaiUtils::GetAuthHeaderAndKey();
|
||||
FString AuthKey = AuthHeaderAndKey.Value;
|
||||
FString AuthHeader = AuthHeaderAndKey.Key;
|
||||
|
||||
// Form Validation
|
||||
if (
|
||||
!UConvaiFormValidation::ValidateAuthKey(AuthKey)
|
||||
|| !UConvaiFormValidation::ValidateCharacterID(CharacterId)
|
||||
)
|
||||
{
|
||||
CONVAI_LOG(ConvaiNarrativeHTTP, Warning, TEXT("UFetchNarrativeTriggersProxy::Activate Invalid Character or API key"));
|
||||
failed();
|
||||
return;
|
||||
}
|
||||
FHttpModule* HttpModule = &FHttpModule::Get();
|
||||
TSharedRef<IHttpRequest, ESPMode::ThreadSafe> HttpRequest = HttpModule->CreateRequest();
|
||||
HttpRequest->OnProcessRequestComplete().BindUObject(this, &UFetchNarrativeTriggersProxy::OnHttpRequestCompleted);
|
||||
HttpRequest->SetURL(UConvaiURL::GetFullURL(TEXT("character/narrative/list-triggers"), false));
|
||||
HttpRequest->SetVerb(TEXT("POST"));
|
||||
HttpRequest->SetHeader(TEXT("Content-Type"), TEXT("application/json"));
|
||||
HttpRequest->SetHeader(AuthHeader, AuthKey);
|
||||
HttpRequest->SetContentAsString(FString::Printf(TEXT("{\"character_id\": \"%s\"}"), *CharacterId));
|
||||
HttpRequest->ProcessRequest();
|
||||
}
|
||||
|
||||
void UFetchNarrativeTriggersProxy::OnHttpRequestCompleted(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful)
|
||||
{
|
||||
NarrativeTriggers.Empty();
|
||||
if (!Response)
|
||||
{
|
||||
if (bWasSuccessful)
|
||||
{
|
||||
CONVAI_LOG(ConvaiNarrativeHTTP, Warning, TEXT("HTTP request succeded - But response pointer is invalid"));
|
||||
}
|
||||
else
|
||||
{
|
||||
CONVAI_LOG(ConvaiNarrativeHTTP, Warning, TEXT("HTTP request failed - Response pointer is invalid"));
|
||||
}
|
||||
|
||||
failed();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!bWasSuccessful || Response->GetResponseCode() < 200 || Response->GetResponseCode() > 299)
|
||||
{
|
||||
CONVAI_LOG(ConvaiNarrativeHTTP, Warning, TEXT("HTTP request failed with code %d"), Response->GetResponseCode());
|
||||
CONVAI_LOG(ConvaiNarrativeHTTP, Warning, TEXT("Response:%s"), *Response->GetContentAsString());
|
||||
|
||||
failed();
|
||||
return;
|
||||
}
|
||||
|
||||
TArray<TSharedPtr<FJsonValue>> JsonArray;
|
||||
const TSharedRef<TJsonReader<>> Reader = TJsonReaderFactory<>::Create(Response->GetContentAsString());
|
||||
|
||||
if (FJsonSerializer::Deserialize(Reader, JsonArray)) // Deserialize into a JSON array
|
||||
{
|
||||
for (const TSharedPtr<FJsonValue>& Val : JsonArray)
|
||||
{
|
||||
FNarrativeTrigger Trigger;
|
||||
if (FJsonObjectConverter::JsonObjectToUStruct(Val->AsObject().ToSharedRef(), FNarrativeTrigger::StaticStruct(), &Trigger))
|
||||
{
|
||||
NarrativeTriggers.Add(Trigger);
|
||||
}
|
||||
}
|
||||
success();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void UFetchNarrativeTriggersProxy::failed()
|
||||
{
|
||||
//auto InvocationList = OnFailure.GetAllObjects();
|
||||
//CONVAI_LOG(ConvaiNarrativeHTTP, Log, TEXT("OnFailure delegate is bound to %d functions before invokation."), InvocationList.Num());
|
||||
|
||||
OnFailure.Broadcast(NarrativeTriggers);
|
||||
//CONVAI_LOG(ConvaiNarrativeHTTP, Log, TEXT("OnFailure delegate is bound to %d functions after invokation."), InvocationList.Num());
|
||||
finish();
|
||||
}
|
||||
|
||||
void UFetchNarrativeTriggersProxy::success()
|
||||
{
|
||||
//auto InvocationList = OnSuccess.GetAllObjects();
|
||||
//CONVAI_LOG(ConvaiNarrativeHTTP, Log, TEXT("OnSuccess delegate is bound to %d functions before invokation."), InvocationList.Num());
|
||||
|
||||
OnSuccess.Broadcast(NarrativeTriggers);
|
||||
//CONVAI_LOG(ConvaiNarrativeHTTP, Log, TEXT("OnSuccess delegate is bound to %d functions after invokation."), InvocationList.Num());
|
||||
finish();
|
||||
}
|
||||
|
||||
void UFetchNarrativeTriggersProxy::finish()
|
||||
{
|
||||
}
|
||||
|
||||
984
ConvAI/Convai/Source/Convai/Private/ConvaiPlayerComponent.cpp
Normal file
984
ConvAI/Convai/Source/Convai/Private/ConvaiPlayerComponent.cpp
Normal file
@@ -0,0 +1,984 @@
|
||||
// Copyright 2022 Convai Inc. All Rights Reserved.
|
||||
|
||||
|
||||
#include "ConvaiPlayerComponent.h"
|
||||
#include "ConvaiAudioCaptureComponent.h"
|
||||
#include "ConvaiChatbotComponent.h"
|
||||
#include "ConvaiActionUtils.h"
|
||||
#include "ConvaiUtils.h"
|
||||
#include "ConvaiDefinitions.h"
|
||||
#include "Runtime/Launch/Resources/Version.h"
|
||||
#include "Net/UnrealNetwork.h"
|
||||
#include "Misc/FileHelper.h"
|
||||
#include "Http.h"
|
||||
#include "ConvaiUtils.h"
|
||||
#include "Containers/UnrealString.h"
|
||||
#include "Kismet/GameplayStatics.h"
|
||||
#include "AudioMixerBlueprintLibrary.h"
|
||||
#include "Engine/GameEngine.h"
|
||||
#include "Sound/SoundWave.h"
|
||||
#include "AudioDevice.h"
|
||||
#include "AudioMixerDevice.h"
|
||||
#include "UObject/ConstructorHelpers.h"
|
||||
#include "ConvaiSubsystem.h"
|
||||
#include "Engine/GameInstance.h"
|
||||
|
||||
DEFINE_LOG_CATEGORY(ConvaiPlayerLog);
|
||||
|
||||
static FAudioDevice* GetAudioDeviceFromWorldContext(const UObject* WorldContextObject)
|
||||
{
|
||||
UWorld* ThisWorld = GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::LogAndReturnNull);
|
||||
if (!ThisWorld || !ThisWorld->bAllowAudioPlayback || ThisWorld->GetNetMode() == NM_DedicatedServer)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return ThisWorld->GetAudioDevice().GetAudioDevice();
|
||||
}
|
||||
|
||||
static Audio::FMixerDevice* GetAudioMixerDeviceFromWorldContext(const UObject* WorldContextObject)
|
||||
{
|
||||
if (FAudioDevice* AudioDevice = GetAudioDeviceFromWorldContext(WorldContextObject))
|
||||
{
|
||||
#if ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION >= 3
|
||||
bool Found = AudioDevice != nullptr;
|
||||
#else
|
||||
bool Found = AudioDevice != nullptr && AudioDevice->IsAudioMixerEnabled();
|
||||
#endif
|
||||
|
||||
if (!Found)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
return static_cast<Audio::FMixerDevice*>(AudioDevice);
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
UConvaiPlayerComponent::UConvaiPlayerComponent()
|
||||
{
|
||||
PrimaryComponentTick.bCanEverTick = true;
|
||||
//SetIsReplicated(true);
|
||||
bAutoActivate = true;
|
||||
PlayerName = "Guest";
|
||||
|
||||
IsInit = false;
|
||||
VoiceCaptureRingBuffer.Init(ConvaiConstants::VoiceCaptureRingBufferCapacity);
|
||||
VoiceCaptureBuffer.Empty(ConvaiConstants::VoiceCaptureBufferSize);
|
||||
UsePixelStreamingMicInput = true;
|
||||
|
||||
const FString FoundSubmixPath = "/ConvAI/Submixes/AudioInput.AudioInput";
|
||||
static ConstructorHelpers::FObjectFinder<USoundSubmixBase> SoundSubmixFinder(*FoundSubmixPath);
|
||||
if (SoundSubmixFinder.Succeeded())
|
||||
{
|
||||
_FoundSubmix = SoundSubmixFinder.Object;
|
||||
}
|
||||
}
|
||||
|
||||
void UConvaiPlayerComponent::OnComponentCreated()
|
||||
{
|
||||
Super::OnComponentCreated();
|
||||
|
||||
AudioCaptureComponent = NewObject<UConvaiAudioCaptureComponent>(this, UConvaiAudioCaptureComponent::StaticClass(), TEXT("ConvaiAudioCapture"));
|
||||
if (AudioCaptureComponent)
|
||||
{
|
||||
AudioCaptureComponent->RegisterComponent();
|
||||
}
|
||||
//AudioCaptureComponent->SetupAttachment(this);
|
||||
|
||||
if (_FoundSubmix != nullptr) {
|
||||
AudioCaptureComponent->SoundSubmix = _FoundSubmix;
|
||||
CONVAI_LOG(ConvaiPlayerLog, Log, TEXT("UConvaiPlayerComponent: Found submix \"AudioInput\""));
|
||||
}
|
||||
else
|
||||
{
|
||||
CONVAI_LOG(ConvaiPlayerLog, Warning, TEXT("UConvaiPlayerComponent: Audio Submix was not found, please ensure an audio submix exists at this directory: \"/ConvAI/Submixes/AudioInput\" then restart Unreal Engine"));
|
||||
}
|
||||
}
|
||||
|
||||
void UConvaiPlayerComponent::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
|
||||
{
|
||||
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
|
||||
|
||||
DOREPLIFETIME(UConvaiPlayerComponent, PlayerName);
|
||||
}
|
||||
|
||||
bool UConvaiPlayerComponent::Init()
|
||||
{
|
||||
if (IsInit)
|
||||
{
|
||||
CONVAI_LOG(ConvaiPlayerLog, Log, TEXT("AudioCaptureComponent is already init"));
|
||||
return true;
|
||||
}
|
||||
|
||||
FString commandMicNoiseGateThreshold = "voice.MicNoiseGateThreshold 0.01";
|
||||
FString commandSilenceDetectionThreshold = "voice.SilenceDetectionThreshold 0.001";
|
||||
|
||||
APlayerController* PController = UGameplayStatics::GetPlayerController(GetWorld(), 0);
|
||||
if (PController)
|
||||
{
|
||||
PController->ConsoleCommand(*commandMicNoiseGateThreshold, true);
|
||||
PController->ConsoleCommand(*commandSilenceDetectionThreshold, true);
|
||||
}
|
||||
|
||||
AudioCaptureComponent = Cast<UConvaiAudioCaptureComponent>(GetOwner()->GetComponentByClass(UConvaiAudioCaptureComponent::StaticClass()));
|
||||
if (!IsValid(AudioCaptureComponent))
|
||||
{
|
||||
CONVAI_LOG(ConvaiPlayerLog, Warning, TEXT("Init: AudioCaptureComponent is not valid"));
|
||||
return false;
|
||||
}
|
||||
|
||||
IsInit = true;
|
||||
Token = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
void UConvaiPlayerComponent::SetPlayerName(FString NewPlayerName)
|
||||
{
|
||||
PlayerName = NewPlayerName;
|
||||
|
||||
if (GetIsReplicated())
|
||||
{
|
||||
SetPlayerNameServer(PlayerName);
|
||||
}
|
||||
}
|
||||
|
||||
void UConvaiPlayerComponent::SetPlayerNameServer_Implementation(const FString& NewPlayerName)
|
||||
{
|
||||
PlayerName = NewPlayerName;
|
||||
}
|
||||
|
||||
void UConvaiPlayerComponent::SetSpeakerID(FString NewSpeakerID)
|
||||
{
|
||||
SpeakerID = NewSpeakerID;
|
||||
|
||||
if (GetIsReplicated())
|
||||
{
|
||||
SetSpeakerIDServer(SpeakerID);
|
||||
}
|
||||
}
|
||||
|
||||
void UConvaiPlayerComponent::SetSpeakerIDServer_Implementation(const FString& NewSpeakerID)
|
||||
{
|
||||
SpeakerID = NewSpeakerID;
|
||||
}
|
||||
|
||||
bool UConvaiPlayerComponent::GetDefaultCaptureDeviceInfo(FCaptureDeviceInfoBP& OutInfo)
|
||||
{
|
||||
if (!IsValid(AudioCaptureComponent))
|
||||
{
|
||||
CONVAI_LOG(ConvaiPlayerLog, Warning, TEXT("GetDefaultCaptureDeviceInfo: AudioCaptureComponent is not valid"));
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool UConvaiPlayerComponent::GetCaptureDeviceInfo(FCaptureDeviceInfoBP& OutInfo, int DeviceIndex)
|
||||
{
|
||||
if (!IsValid(AudioCaptureComponent))
|
||||
{
|
||||
CONVAI_LOG(ConvaiPlayerLog, Warning, TEXT("GetCaptureDeviceInfo: AudioCaptureComponent is not valid"));
|
||||
return false;
|
||||
}
|
||||
Audio::FCaptureDeviceInfo OutDeviceInfo;
|
||||
if (AudioCaptureComponent->GetCaptureDeviceInfo(OutDeviceInfo, DeviceIndex))
|
||||
{
|
||||
OutInfo.bSupportsHardwareAEC = OutDeviceInfo.bSupportsHardwareAEC;
|
||||
OutInfo.LongDeviceId = OutDeviceInfo.DeviceId;
|
||||
OutInfo.DeviceName = OutDeviceInfo.DeviceName;
|
||||
OutInfo.InputChannels = OutDeviceInfo.InputChannels;
|
||||
OutInfo.PreferredSampleRate = OutDeviceInfo.PreferredSampleRate;
|
||||
OutInfo.DeviceIndex = DeviceIndex;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
TArray<FCaptureDeviceInfoBP> UConvaiPlayerComponent::GetAvailableCaptureDeviceDetails()
|
||||
{
|
||||
TArray<FCaptureDeviceInfoBP> FCaptureDevicesInfoBP;
|
||||
if (!IsValid(AudioCaptureComponent))
|
||||
{
|
||||
CONVAI_LOG(ConvaiPlayerLog, Warning, TEXT("GetAvailableCaptureDeviceDetails: AudioCaptureComponent is not valid"));
|
||||
return FCaptureDevicesInfoBP;
|
||||
}
|
||||
|
||||
int i = 0;
|
||||
for (auto DeviceInfo : AudioCaptureComponent->GetCaptureDevicesAvailable())
|
||||
{
|
||||
FCaptureDeviceInfoBP CaptureDeviceInfoBP;
|
||||
CaptureDeviceInfoBP.bSupportsHardwareAEC = DeviceInfo.bSupportsHardwareAEC;
|
||||
CaptureDeviceInfoBP.LongDeviceId = DeviceInfo.DeviceId;
|
||||
CaptureDeviceInfoBP.DeviceName = DeviceInfo.DeviceName;
|
||||
CaptureDeviceInfoBP.InputChannels = DeviceInfo.InputChannels;
|
||||
CaptureDeviceInfoBP.PreferredSampleRate = DeviceInfo.PreferredSampleRate;
|
||||
CaptureDeviceInfoBP.DeviceIndex = i++;
|
||||
FCaptureDevicesInfoBP.Add(CaptureDeviceInfoBP);
|
||||
}
|
||||
return FCaptureDevicesInfoBP;
|
||||
}
|
||||
|
||||
TArray<FString> UConvaiPlayerComponent::GetAvailableCaptureDeviceNames()
|
||||
{
|
||||
TArray<FString> AvailableDeviceNames;
|
||||
if (!IsValid(AudioCaptureComponent))
|
||||
{
|
||||
CONVAI_LOG(ConvaiPlayerLog, Warning, TEXT("GetAvailableCaptureDeviceNames: AudioCaptureComponent is not valid"));
|
||||
return AvailableDeviceNames;
|
||||
}
|
||||
|
||||
for (auto CaptureDeviceInfo : GetAvailableCaptureDeviceDetails())
|
||||
{
|
||||
AvailableDeviceNames.Add(CaptureDeviceInfo.DeviceName);
|
||||
}
|
||||
|
||||
return AvailableDeviceNames;
|
||||
}
|
||||
|
||||
void UConvaiPlayerComponent::GetActiveCaptureDevice(FCaptureDeviceInfoBP& OutInfo)
|
||||
{
|
||||
if (!IsValid(AudioCaptureComponent))
|
||||
{
|
||||
CONVAI_LOG(ConvaiPlayerLog, Warning, TEXT("GetActiveCaptureDevice: AudioCaptureComponent is not valid"));
|
||||
return;
|
||||
}
|
||||
Audio::FCaptureDeviceInfo OutDeviceInfo;
|
||||
int SelectedDeviceIndex = AudioCaptureComponent->GetActiveCaptureDevice(OutDeviceInfo);
|
||||
OutInfo.bSupportsHardwareAEC = OutDeviceInfo.bSupportsHardwareAEC;
|
||||
OutInfo.LongDeviceId = OutDeviceInfo.DeviceId;
|
||||
OutInfo.DeviceName = OutDeviceInfo.DeviceName;
|
||||
OutInfo.InputChannels = OutDeviceInfo.InputChannels;
|
||||
OutInfo.PreferredSampleRate = OutDeviceInfo.PreferredSampleRate;
|
||||
OutInfo.DeviceIndex = SelectedDeviceIndex;
|
||||
}
|
||||
|
||||
bool UConvaiPlayerComponent::SetCaptureDeviceByIndex(int DeviceIndex)
|
||||
{
|
||||
if (!IsValid(AudioCaptureComponent))
|
||||
{
|
||||
CONVAI_LOG(ConvaiPlayerLog, Warning, TEXT("SetCaptureDeviceByIndex: AudioCaptureComponent is not valid"));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (DeviceIndex >= GetAvailableCaptureDeviceDetails().Num())
|
||||
{
|
||||
CONVAI_LOG(ConvaiPlayerLog, Warning, TEXT("SetCaptureDeviceByIndex: Invalid Device Index: %d - Max possible index: %d."), DeviceIndex, GetAvailableCaptureDeviceDetails().Num() - 1);
|
||||
return false;
|
||||
}
|
||||
|
||||
return AudioCaptureComponent->SetCaptureDevice(DeviceIndex);
|
||||
}
|
||||
|
||||
bool UConvaiPlayerComponent::SetCaptureDeviceByName(FString DeviceName)
|
||||
{
|
||||
if (!IsValid(AudioCaptureComponent))
|
||||
{
|
||||
CONVAI_LOG(ConvaiPlayerLog, Warning, TEXT("SetCaptureDeviceByName: AudioCaptureComponent is not valid"));
|
||||
return false;
|
||||
}
|
||||
|
||||
bool bDeviceFound = false;
|
||||
int DeviceIndex = -1;
|
||||
for (auto CaptureDeviceInfo : GetAvailableCaptureDeviceDetails())
|
||||
{
|
||||
if (CaptureDeviceInfo.DeviceName == DeviceName)
|
||||
{
|
||||
bDeviceFound = true;
|
||||
DeviceIndex = CaptureDeviceInfo.DeviceIndex;
|
||||
}
|
||||
}
|
||||
|
||||
TArray<FString> AvailableDeviceNames;
|
||||
|
||||
if (!bDeviceFound)
|
||||
{
|
||||
AvailableDeviceNames = GetAvailableCaptureDeviceNames();
|
||||
CONVAI_LOG(ConvaiPlayerLog, Warning, TEXT("SetCaptureDeviceByName: Could not find Device name: %s - Available Device names are: [%s]."), *DeviceName, *FString::Join(AvailableDeviceNames, *FString(" - ")));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!SetCaptureDeviceByIndex(DeviceIndex))
|
||||
{
|
||||
AvailableDeviceNames = GetAvailableCaptureDeviceNames();
|
||||
CONVAI_LOG(ConvaiPlayerLog, Warning, TEXT("SetCaptureDeviceByName: SetCaptureDeviceByIndex failed for index: %d and device name: %s - Available Device names are: [%s]."), DeviceIndex, *DeviceName, *FString::Join(AvailableDeviceNames, *FString(" - ")));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void UConvaiPlayerComponent::SetMicrophoneVolumeMultiplier(float InVolumeMultiplier, bool& Success)
|
||||
{
|
||||
Success = false;
|
||||
if (IsValid(AudioCaptureComponent))
|
||||
{
|
||||
AudioCaptureComponent->SetVolumeMultiplier(InVolumeMultiplier);
|
||||
Success = true;
|
||||
}
|
||||
|
||||
if (PixelStreamingAudioComponent.IsValid())
|
||||
{
|
||||
AudioCaptureComponent->SetVolumeMultiplier(InVolumeMultiplier*2); // multiply by 2 because Pixel Streaming mic is usually very low
|
||||
Success = true;
|
||||
}
|
||||
|
||||
if (!IsValid(AudioCaptureComponent) && !PixelStreamingAudioComponent.IsValid())
|
||||
{
|
||||
CONVAI_LOG(ConvaiPlayerLog, Warning, TEXT("SetMicrophoneVolumeMultiplier: AudioCaptureComponent and PixelStreamingAudioComponent are not valid"));
|
||||
}
|
||||
}
|
||||
|
||||
void UConvaiPlayerComponent::GetMicrophoneVolumeMultiplier(float& OutVolumeMultiplier, bool& Success)
|
||||
{
|
||||
Success = false;
|
||||
if (!IsValid(AudioCaptureComponent))
|
||||
{
|
||||
CONVAI_LOG(ConvaiPlayerLog, Warning, TEXT("SetMicrophoneVolumeMultiplier: AudioCaptureComponent is not valid"));
|
||||
return;
|
||||
}
|
||||
auto InternalAudioComponent = AudioCaptureComponent->GetAudioComponent();
|
||||
if (!InternalAudioComponent)
|
||||
{
|
||||
CONVAI_LOG(ConvaiPlayerLog, Warning, TEXT("GetMicrophoneVolumeMultiplier: InternalAudioComponent is not valid"));
|
||||
}
|
||||
OutVolumeMultiplier = InternalAudioComponent->VolumeMultiplier;
|
||||
Success = true;
|
||||
}
|
||||
|
||||
// void UConvaiPlayerComponent::GetIfHardwareFeatureIsSupported(EHardwareInputFeatureBP FeatureType, bool& Success)
|
||||
// {
|
||||
// Success = false;
|
||||
// if (!IsValid(AudioCaptureComponent))
|
||||
// {
|
||||
// CONVAI_LOG(ConvaiPlayerLog, Warning, TEXT("GetIfHardwareFeatureIsSupported: AudioCaptureComponent is not valid"));
|
||||
// return;
|
||||
// }
|
||||
|
||||
// auto CaptureSynth = AudioCaptureComponent->GetCaptureSynth();
|
||||
// if (CaptureSynth == nullptr)
|
||||
// {
|
||||
// CONVAI_LOG(ConvaiPlayerLog, Warning, TEXT("GetIfHardwareFeatureIsSupported: CaptureSynth is not valid"));
|
||||
// return;
|
||||
// }
|
||||
|
||||
// auto AudioCapture = CaptureSynth->GetAudioCapture();
|
||||
// if (AudioCapture == nullptr)
|
||||
// {
|
||||
// CONVAI_LOG(ConvaiPlayerLog, Warning, TEXT("GetIfHardwareFeatureIsSupported: AudioCapture is not valid"));
|
||||
// return;
|
||||
// }
|
||||
// Success = AudioCapture->GetIfHardwareFeatureIsSupported((Audio::EHardwareInputFeature)FeatureType);
|
||||
// }
|
||||
|
||||
// void UConvaiPlayerComponent::SetHardwareFeatureEnabled(EHardwareInputFeatureBP FeatureType, bool bIsEnabled, bool& Success)
|
||||
// {
|
||||
// bool HardwareFeatureIsSupported;
|
||||
// GetIfHardwareFeatureIsSupported(FeatureType, HardwareFeatureIsSupported);
|
||||
// if (!HardwareFeatureIsSupported)
|
||||
// {
|
||||
// if (!bIsEnabled)
|
||||
// {
|
||||
// Success = true;
|
||||
// return;
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// Success = false;
|
||||
// return;
|
||||
// }
|
||||
// }
|
||||
|
||||
// auto AudioCapture = AudioCaptureComponent->GetCaptureSynth()->GetAudioCapture();
|
||||
// AudioCapture->SetHardwareFeatureEnabled((Audio::EHardwareInputFeature)FeatureType, bIsEnabled);
|
||||
// Success = true;
|
||||
// }
|
||||
|
||||
void UConvaiPlayerComponent::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
|
||||
{
|
||||
Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
|
||||
|
||||
if (!IsInit || !IsValid(AudioCaptureComponent))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
UpdateVoiceCapture(DeltaTime);
|
||||
}
|
||||
|
||||
void UConvaiPlayerComponent::EndPlay(const EEndPlayReason::Type EndPlayReason)
|
||||
{
|
||||
// Unregister from the ConvaiSubsystem
|
||||
if (UGameInstance* GameInstance = GetWorld()->GetGameInstance())
|
||||
{
|
||||
if (UConvaiSubsystem* ConvaiSubsystem = GameInstance->GetSubsystem<UConvaiSubsystem>())
|
||||
{
|
||||
if (IsValid(ConvaiSubsystem))
|
||||
{
|
||||
ConvaiSubsystem->UnregisterPlayerComponent(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (IsRecording)
|
||||
FinishRecording();
|
||||
|
||||
if (IsStreaming)
|
||||
FinishTalking();
|
||||
|
||||
Super::EndPlay(EndPlayReason);
|
||||
}
|
||||
|
||||
bool UConvaiPlayerComponent::IsPixelStreamingEnabledAndAllowed()
|
||||
{
|
||||
return UsePixelStreamingMicInput && PixelStreamingAudioComponent.IsValid();
|
||||
}
|
||||
|
||||
void UConvaiPlayerComponent::UpdateVoiceCapture(float DeltaTime)
|
||||
{
|
||||
if (IsRecording || IsStreaming) {
|
||||
RemainingTimeUntilNextUpdate -= DeltaTime;
|
||||
if (RemainingTimeUntilNextUpdate <= 0)
|
||||
{
|
||||
float ExpectedRecordingTime = DeltaTime > TIME_BETWEEN_VOICE_UPDATES_SECS ? DeltaTime : TIME_BETWEEN_VOICE_UPDATES_SECS;
|
||||
FAudioThread::RunCommandOnAudioThread([this, ExpectedRecordingTime]()
|
||||
{
|
||||
StopVoiceChunkCapture();
|
||||
StartVoiceChunkCapture(ExpectedRecordingTime);
|
||||
});
|
||||
RemainingTimeUntilNextUpdate = TIME_BETWEEN_VOICE_UPDATES_SECS;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
RemainingTimeUntilNextUpdate = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void UConvaiPlayerComponent::StartVoiceChunkCapture(float ExpectedRecordingTime)
|
||||
{
|
||||
//GEngine->AddOnScreenDebugMessage(-1, 15.0f, FColor::Yellow, FString::Printf(TEXT("StartVoiceChunkCapture() in VoiceCaptureComp.cpp")));
|
||||
//CONVAI_LOG(LogTemp, Warning, TEXT("StartVoiceChunkCapture() in VoiceCaptureComp.cpp"));
|
||||
UAudioMixerBlueprintLibrary::StartRecordingOutput(this, ExpectedRecordingTime, Cast<USoundSubmix>(AudioCaptureComponent->SoundSubmix));
|
||||
}
|
||||
|
||||
void UConvaiPlayerComponent::ReadRecordedBuffer(Audio::AlignedFloatBuffer& RecordedBuffer, float& OutNumChannels, float& OutSampleRate)
|
||||
{
|
||||
if (Audio::FMixerDevice* MixerDevice = GetAudioMixerDeviceFromWorldContext(this))
|
||||
{
|
||||
// call the thing here.
|
||||
RecordedBuffer = MixerDevice->StopRecording(Cast<USoundSubmix>(AudioCaptureComponent->SoundSubmix), OutNumChannels, OutSampleRate);
|
||||
|
||||
if (RecordedBuffer.Num() == 0)
|
||||
{
|
||||
//CONVAI_LOG(ConvaiPlayerLog, Warning, TEXT("ReadRecordedBuffer: No audio data. Did you call Start Recording Output?"));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
CONVAI_LOG(ConvaiPlayerLog, Warning, TEXT("ReadRecordedBuffer: Could not get MixerDevice"));
|
||||
}
|
||||
}
|
||||
|
||||
void UConvaiPlayerComponent::StartAudioCaptureComponent()
|
||||
{
|
||||
if (IsPixelStreamingEnabledAndAllowed())
|
||||
{
|
||||
PixelStreamingAudioComponent->Start();
|
||||
}
|
||||
else
|
||||
{
|
||||
AudioCaptureComponent->Start();
|
||||
}
|
||||
}
|
||||
|
||||
void UConvaiPlayerComponent::StopAudioCaptureComponent()
|
||||
{
|
||||
if (IsPixelStreamingEnabledAndAllowed())
|
||||
{
|
||||
PixelStreamingAudioComponent->Stop();
|
||||
}
|
||||
AudioCaptureComponent->Stop();
|
||||
}
|
||||
|
||||
void UConvaiPlayerComponent::StopVoiceChunkCapture()
|
||||
{
|
||||
//USoundWave* SoundWaveMic = UAudioMixerBlueprintLibrary::StopRecordingOutput(this, EAudioRecordingExportType::SoundWave, "Convsound", "ConvSound", Cast<USoundSubmix>(AudioCaptureComponent->SoundSubmix));
|
||||
|
||||
float NumChannels;
|
||||
float SampleRate;
|
||||
Audio::AlignedFloatBuffer RecordedBuffer = Audio::AlignedFloatBuffer();
|
||||
|
||||
ReadRecordedBuffer(RecordedBuffer, NumChannels, SampleRate);
|
||||
//NumChannels = 2;
|
||||
//SampleRate = ConvaiConstants::VoiceCaptureSampleRate;
|
||||
|
||||
if (RecordedBuffer.Num() == 0)
|
||||
return;
|
||||
|
||||
Audio::TSampleBuffer<int16> Int16Buffer = Audio::TSampleBuffer<int16>(RecordedBuffer, NumChannels, SampleRate);
|
||||
TArray<int16> OutConverted;
|
||||
|
||||
if (NumChannels > 1 || SampleRate != ConvaiConstants::VoiceCaptureSampleRate)
|
||||
{
|
||||
UConvaiUtils::ResampleAudio(SampleRate, ConvaiConstants::VoiceCaptureSampleRate, NumChannels, true, (TArray<int16>)Int16Buffer.GetArrayView(), Int16Buffer.GetNumSamples(), OutConverted);
|
||||
}
|
||||
else
|
||||
{
|
||||
OutConverted = (TArray<int16>)Int16Buffer.GetArrayView();
|
||||
}
|
||||
//OutConverted = (TArray<int16>)Int16Buffer.GetArrayView();
|
||||
//OutConverted = TArray<int16>(Int16Buffer.GetData(), Int16Buffer.GetNumSamples());
|
||||
|
||||
//CONVAI_LOG(ConvaiPlayerLog, Log, TEXT("Int16Buffer.GetNumSamples() %i, NumChannels %f, SampleRate %f"), Int16Buffer.GetNumSamples(), NumChannels, SampleRate);
|
||||
//CONVAI_LOG(ConvaiPlayerLog, Log, TEXT("OutConverted.Num() %i"), OutConverted.Num());
|
||||
|
||||
if (IsRecording)
|
||||
{
|
||||
VoiceCaptureBuffer.Append((uint8*)OutConverted.GetData(), OutConverted.Num() * sizeof(int16));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!ReplicateVoiceToNetwork)
|
||||
{
|
||||
if (IsStreaming)
|
||||
VoiceCaptureRingBuffer.Enqueue((uint8*)OutConverted.GetData(), OutConverted.Num() * sizeof(int16));
|
||||
|
||||
onDataReceived_Delegate.ExecuteIfBound();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Stream voice data
|
||||
AddPCMDataToSend(TArray<uint8>((uint8*)OutConverted.GetData(), OutConverted.Num() * sizeof(int16)), false, ConvaiConstants::VoiceCaptureSampleRate, 1);
|
||||
}
|
||||
}
|
||||
|
||||
void UConvaiPlayerComponent::StartRecording()
|
||||
{
|
||||
if (IsRecording)
|
||||
{
|
||||
CONVAI_LOG(ConvaiPlayerLog, Warning, TEXT("StartRecording: already recording!"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (IsStreaming)
|
||||
{
|
||||
CONVAI_LOG(ConvaiPlayerLog, Warning, TEXT("StartRecording: already talking!"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!IsInit)
|
||||
{
|
||||
CONVAI_LOG(ConvaiPlayerLog, Log, TEXT("StartRecording: Initializing..."));
|
||||
if (!Init())
|
||||
{
|
||||
CONVAI_LOG(ConvaiPlayerLog, Warning, TEXT("StartRecording: Could not initialize"));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
CONVAI_LOG(ConvaiPlayerLog, Log, TEXT("Started Recording "));
|
||||
StartAudioCaptureComponent(); //Start the AudioCaptureComponent
|
||||
|
||||
// reset audio buffers
|
||||
StartVoiceChunkCapture();
|
||||
StopVoiceChunkCapture();
|
||||
VoiceCaptureBuffer.Empty(ConvaiConstants::VoiceCaptureBufferSize);
|
||||
|
||||
IsRecording = true;
|
||||
}
|
||||
|
||||
USoundWave* UConvaiPlayerComponent::FinishRecording()
|
||||
{
|
||||
if (!IsRecording)
|
||||
{
|
||||
CONVAI_LOG(ConvaiPlayerLog, Warning, TEXT("FinishRecording: did not start recording"));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
CONVAI_LOG(ConvaiPlayerLog, Log, TEXT("Stopped Recording "));
|
||||
StopVoiceChunkCapture();
|
||||
|
||||
USoundWave* OutSoundWave = UConvaiUtils::PCMDataToSoundWav(VoiceCaptureBuffer, 1, ConvaiConstants::VoiceCaptureSampleRate);
|
||||
StopAudioCaptureComponent(); //stop the AudioCaptureComponent
|
||||
if (IsValid(OutSoundWave))
|
||||
CONVAI_LOG(ConvaiPlayerLog, Log, TEXT("OutSoundWave->GetDuration(): %f seconds "), OutSoundWave->GetDuration());
|
||||
IsRecording = false;
|
||||
return OutSoundWave;
|
||||
}
|
||||
|
||||
void UConvaiPlayerComponent::StartTalking(
|
||||
UConvaiChatbotComponent* ConvaiChatbotComponent,
|
||||
UConvaiEnvironment* Environment,
|
||||
bool GenerateActions,
|
||||
bool VoiceResponse,
|
||||
bool RunOnServer,
|
||||
bool StreamPlayerMic,
|
||||
bool UseServerAPI_Key)
|
||||
{
|
||||
if (IsStreaming)
|
||||
{
|
||||
CONVAI_LOG(ConvaiPlayerLog, Warning, TEXT("StartTalking: already talking!"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (IsRecording)
|
||||
{
|
||||
CONVAI_LOG(ConvaiPlayerLog, Warning, TEXT("StartTalking: already recording!"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!IsInit)
|
||||
{
|
||||
CONVAI_LOG(ConvaiPlayerLog, Log, TEXT("StartTalking Initializing..."));
|
||||
if (!Init())
|
||||
{
|
||||
CONVAI_LOG(ConvaiPlayerLog, Warning, TEXT("StartTalking Could not initialize"));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!IsValid(ConvaiChatbotComponent))
|
||||
{
|
||||
if (RunOnServer)
|
||||
{
|
||||
CONVAI_LOG(ConvaiPlayerLog, Log, TEXT("StartTalking: ConvaiChatbotComponent is not valid, will still stream voice chat to network"));
|
||||
}
|
||||
else
|
||||
{
|
||||
CONVAI_LOG(ConvaiPlayerLog, Warning, TEXT("StartTalking: ConvaiChatbotComponent is not valid"));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
CONVAI_LOG(ConvaiPlayerLog, Log, TEXT("Started Talking"));
|
||||
|
||||
StartAudioCaptureComponent(); //Start the AudioCaptureComponent
|
||||
|
||||
// reset audio buffers
|
||||
StartVoiceChunkCapture();
|
||||
StopVoiceChunkCapture();
|
||||
|
||||
IsStreaming = true;
|
||||
VoiceCaptureRingBuffer.Empty();
|
||||
|
||||
ReplicateVoiceToNetwork = RunOnServer;
|
||||
|
||||
TPair<FString, FString> AuthHeaderAndKey = UConvaiUtils::GetAuthHeaderAndKey();
|
||||
FString AuthKey = AuthHeaderAndKey.Value;
|
||||
FString AuthHeader = AuthHeaderAndKey.Key;
|
||||
|
||||
FString ClientAuthKey = UseServerAPI_Key ? FString("") : AuthKey;
|
||||
|
||||
// Ensure that the local version of the chatbot component has the ReplicateVoiceToNetwork set properly
|
||||
ConvaiChatbotComponent->ReplicateVoiceToNetwork = RunOnServer;
|
||||
|
||||
if (RunOnServer)
|
||||
{
|
||||
if (IsValid(Environment))
|
||||
StartTalkingServer(ConvaiChatbotComponent, true, Environment->Actions, Environment->Objects, Environment->Characters, Environment->MainCharacter, GenerateActions, VoiceResponse, StreamPlayerMic, UseServerAPI_Key, ClientAuthKey, AuthHeader);
|
||||
else
|
||||
StartTalkingServer(ConvaiChatbotComponent, false, TArray<FString>(), TArray<FConvaiObjectEntry>(), TArray<FConvaiObjectEntry>(), FConvaiObjectEntry(), GenerateActions, VoiceResponse, StreamPlayerMic, UseServerAPI_Key, ClientAuthKey, AuthHeader);
|
||||
}
|
||||
else
|
||||
{
|
||||
bool UseOverrideAuthKey = false;
|
||||
ConvaiChatbotComponent->StartGetResponseStream(this, FString(""), Environment, GenerateActions, VoiceResponse, false, UseOverrideAuthKey, FString(""), FString(""), Token, SpeakerID);
|
||||
CurrentConvaiChatbotComponent = ConvaiChatbotComponent;
|
||||
}
|
||||
}
|
||||
|
||||
void UConvaiPlayerComponent::FinishTalking()
|
||||
{
|
||||
if (!IsStreaming)
|
||||
{
|
||||
CONVAI_LOG(ConvaiPlayerLog, Warning, TEXT("FinishTalking did not start talking"));
|
||||
return;
|
||||
}
|
||||
|
||||
StopVoiceChunkCapture();
|
||||
StopAudioCaptureComponent(); //stop the AudioCaptureComponent
|
||||
IsStreaming = false;
|
||||
|
||||
if (ReplicateVoiceToNetwork)
|
||||
{
|
||||
FinishTalkingServer();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Invalidate the token by generating a new one
|
||||
GenerateNewToken();
|
||||
|
||||
if (IsValid(CurrentConvaiChatbotComponent))
|
||||
{
|
||||
CONVAI_LOG(ConvaiPlayerLog, Log, TEXT("FinishTalking calling FinishGetResponseStream"));
|
||||
CurrentConvaiChatbotComponent->FinishGetResponseStream(this);
|
||||
CurrentConvaiChatbotComponent = nullptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
CONVAI_LOG(ConvaiPlayerLog, Warning, TEXT("FinishTalking failed to call FinishGetResponseStream"));
|
||||
}
|
||||
}
|
||||
|
||||
CONVAI_LOG(ConvaiPlayerLog, Log, TEXT("Finished Talking"));
|
||||
}
|
||||
|
||||
void UConvaiPlayerComponent::StartTalkingServer_Implementation(
|
||||
class UConvaiChatbotComponent* ConvaiChatbotComponent,
|
||||
bool EnvironemntSent,
|
||||
const TArray<FString>& Actions,
|
||||
const TArray<FConvaiObjectEntry>& Objects,
|
||||
const TArray<FConvaiObjectEntry>& Characters,
|
||||
FConvaiObjectEntry MainCharacter,
|
||||
bool GenerateActions,
|
||||
bool VoiceResponse,
|
||||
bool StreamPlayerMic,
|
||||
bool UseServerAPI_Key,
|
||||
const FString& ClientAuthKey,
|
||||
const FString& AuthHeader)
|
||||
{
|
||||
// if "StreamPlayerMic" is true then "bShouldMuteGlobal" should be false, meaning we will play the player's audio on other clients
|
||||
bShouldMuteGlobal = !StreamPlayerMic;
|
||||
|
||||
// Make sure the ring buffer is empty
|
||||
VoiceCaptureRingBuffer.Empty();
|
||||
|
||||
// if "ConvaiChatbotComponent" is valid then run StartGetResponseStream function
|
||||
if (IsValid(ConvaiChatbotComponent))
|
||||
{
|
||||
UConvaiEnvironment* Environment = nullptr;
|
||||
if (EnvironemntSent)
|
||||
{
|
||||
if (IsValid(ConvaiChatbotComponent->Environment))
|
||||
Environment = ConvaiChatbotComponent->Environment;
|
||||
else
|
||||
{
|
||||
Environment = UConvaiEnvironment::CreateConvaiEnvironment();
|
||||
}
|
||||
|
||||
Environment->Actions = Actions;
|
||||
Environment->Characters = Characters;
|
||||
Environment->Objects = Objects;
|
||||
Environment->MainCharacter = MainCharacter;
|
||||
}
|
||||
bool UseOverrideAuthKey = !UseServerAPI_Key;
|
||||
ConvaiChatbotComponent->StartGetResponseStream(this, FString(""), Environment, GenerateActions, VoiceResponse, true, UseOverrideAuthKey, ClientAuthKey, AuthHeader, Token, SpeakerID);
|
||||
CurrentConvaiChatbotComponent = ConvaiChatbotComponent;
|
||||
}
|
||||
}
|
||||
|
||||
void UConvaiPlayerComponent::FinishTalkingServer_Implementation()
|
||||
{
|
||||
// Invalidate the token by generating a new one
|
||||
GenerateNewToken();
|
||||
|
||||
if (IsValid(CurrentConvaiChatbotComponent))
|
||||
{
|
||||
CONVAI_LOG(ConvaiPlayerLog, Log, TEXT("FinishTalking calling FinishGetResponseStream"));
|
||||
CurrentConvaiChatbotComponent->FinishGetResponseStream(this);
|
||||
CurrentConvaiChatbotComponent = nullptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
CONVAI_LOG(ConvaiPlayerLog, Warning, TEXT("FinishTalking failed to call FinishGetResponseStream"));
|
||||
}
|
||||
}
|
||||
|
||||
void UConvaiPlayerComponent::SendText(UConvaiChatbotComponent* ConvaiChatbotComponent, FString Text, UConvaiEnvironment* Environment, bool GenerateActions, bool VoiceResponse, bool RunOnServer, bool UseServerAPI_Key)
|
||||
{
|
||||
//FString Error;
|
||||
//if (GenerateActions && !UConvaiActions::ValidateEnvironment(Environment, Error))
|
||||
//{
|
||||
// CONVAI_LOG(ConvaiPlayerLog, Warning, TEXT("SendText: %s"), *Error);
|
||||
// CONVAI_LOG(ConvaiPlayerLog, Log, TEXT("SendText: Environment object seems to have issues -> setting GenerateActions to false"));
|
||||
// GenerateActions = false;
|
||||
//}
|
||||
|
||||
|
||||
if (!IsValid(ConvaiChatbotComponent))
|
||||
{
|
||||
CONVAI_LOG(ConvaiPlayerLog, Warning, TEXT("SendText: ConvaiChatbotComponent is not valid"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (Text.Len() == 0)
|
||||
{
|
||||
CONVAI_LOG(ConvaiPlayerLog, Warning, TEXT("SendText: Text is empty"));
|
||||
return;
|
||||
}
|
||||
|
||||
ReplicateVoiceToNetwork = RunOnServer;
|
||||
|
||||
TPair<FString, FString> AuthHeaderAndKey = UConvaiUtils::GetAuthHeaderAndKey();
|
||||
FString AuthKey = AuthHeaderAndKey.Value;
|
||||
FString AuthHeader = AuthHeaderAndKey.Key;
|
||||
|
||||
FString ClientAuthKey = UseServerAPI_Key ? FString("") : AuthKey;
|
||||
|
||||
// Ensure that the local version of the chatbot component has the ReplicateVoiceToNetwork set properly
|
||||
ConvaiChatbotComponent->ReplicateVoiceToNetwork = RunOnServer;
|
||||
|
||||
if (RunOnServer)
|
||||
{
|
||||
if (IsValid(Environment))
|
||||
SendTextServer(ConvaiChatbotComponent, Text, true, Environment->Actions, Environment->Objects, Environment->Characters, Environment->MainCharacter, GenerateActions, VoiceResponse, UseServerAPI_Key, ClientAuthKey, AuthHeader);
|
||||
else
|
||||
SendTextServer(ConvaiChatbotComponent, Text, false, TArray<FString>(), TArray<FConvaiObjectEntry>(), TArray<FConvaiObjectEntry>(), FConvaiObjectEntry(), GenerateActions, VoiceResponse, UseServerAPI_Key, ClientAuthKey, AuthHeader);
|
||||
}
|
||||
else
|
||||
{
|
||||
bool UseOverrideAuthKey = false;
|
||||
ConvaiChatbotComponent->StartGetResponseStream(this, Text, Environment, GenerateActions, VoiceResponse, false, UseOverrideAuthKey, FString(""), FString(""), Token, SpeakerID);
|
||||
|
||||
// Invalidate the token by generating a new one
|
||||
GenerateNewToken();
|
||||
|
||||
CurrentConvaiChatbotComponent = ConvaiChatbotComponent;
|
||||
}
|
||||
}
|
||||
|
||||
void UConvaiPlayerComponent::SendTextServer_Implementation(
|
||||
UConvaiChatbotComponent* ConvaiChatbotComponent,
|
||||
const FString& Text,
|
||||
bool EnvironemntSent,
|
||||
const TArray<FString>& Actions,
|
||||
const TArray<FConvaiObjectEntry>& Objects,
|
||||
const TArray<FConvaiObjectEntry>& Characters,
|
||||
FConvaiObjectEntry MainCharacter,
|
||||
bool GenerateActions,
|
||||
bool VoiceResponse,
|
||||
bool UseServerAPI_Key,
|
||||
const FString& ClientAuthKey,
|
||||
const FString& AuthHeader)
|
||||
{
|
||||
if (!IsValid(ConvaiChatbotComponent))
|
||||
{
|
||||
CONVAI_LOG(ConvaiPlayerLog, Warning, TEXT("SendTextServer: ConvaiChatbotComponent is not valid"));
|
||||
return;
|
||||
}
|
||||
|
||||
UConvaiEnvironment* Environment;
|
||||
if (IsValid(ConvaiChatbotComponent->Environment))
|
||||
Environment = ConvaiChatbotComponent->Environment;
|
||||
else
|
||||
{
|
||||
Environment = UConvaiEnvironment::CreateConvaiEnvironment();
|
||||
}
|
||||
|
||||
Environment->Actions = Actions;
|
||||
Environment->Characters = Characters;
|
||||
Environment->Objects = Objects;
|
||||
Environment->MainCharacter = MainCharacter;
|
||||
|
||||
bool UseOverrideAuthKey = !UseServerAPI_Key;
|
||||
ConvaiChatbotComponent->StartGetResponseStream(this, Text, Environment, GenerateActions, VoiceResponse, true, UseOverrideAuthKey, ClientAuthKey, AuthHeader, Token, SpeakerID);
|
||||
|
||||
// Invalidate the token by generating a new one
|
||||
GenerateNewToken();
|
||||
|
||||
CurrentConvaiChatbotComponent = ConvaiChatbotComponent;
|
||||
}
|
||||
|
||||
bool UConvaiPlayerComponent::ShouldMuteLocal()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UConvaiPlayerComponent::ShouldMuteGlobal()
|
||||
{
|
||||
return bShouldMuteGlobal;
|
||||
}
|
||||
|
||||
void UConvaiPlayerComponent::OnServerAudioReceived(uint8* VoiceData, uint32 VoiceDataSize, bool ContainsHeaderData, uint32 SampleRate, uint32 NumChannels)
|
||||
{
|
||||
//if (IsRecording)
|
||||
// VoiceCaptureBuffer.Append(VoiceData, VoiceDataSize);
|
||||
|
||||
//if (IsStreaming)
|
||||
// VoiceCaptureRingBuffer.Enqueue(VoiceData, VoiceDataSize);
|
||||
|
||||
//CONVAI_LOG(ConvaiPlayerLog, Log, TEXT("OnServerAudioReceived received %d bytes"), VoiceDataSize);
|
||||
//onDataReceived_Delegate.ExecuteIfBound();
|
||||
|
||||
// Simply enque the data into the ring buffer
|
||||
VoiceCaptureRingBuffer.Enqueue(VoiceData, VoiceDataSize);
|
||||
}
|
||||
|
||||
void UConvaiPlayerComponent::BeginPlay()
|
||||
{
|
||||
Super::BeginPlay();
|
||||
|
||||
if (IsValid(AudioCaptureComponent))
|
||||
{
|
||||
AudioCaptureComponent->AttachToComponent(this, FAttachmentTransformRules::KeepRelativeTransform);
|
||||
}
|
||||
else
|
||||
{
|
||||
CONVAI_LOG(ConvaiPlayerLog, Error, TEXT("Could not attach AudioCaptureComponent"));
|
||||
}
|
||||
|
||||
if (!IsInit)
|
||||
{
|
||||
if (!Init())
|
||||
{
|
||||
CONVAI_LOG(ConvaiPlayerLog, Warning, TEXT("Could not initialize Audio Decoder"));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Register with the ConvaiSubsystem
|
||||
if (UGameInstance* GameInstance = GetWorld()->GetGameInstance())
|
||||
{
|
||||
if (UConvaiSubsystem* ConvaiSubsystem = GameInstance->GetSubsystem<UConvaiSubsystem>())
|
||||
{
|
||||
if (IsValid(ConvaiSubsystem))
|
||||
{
|
||||
ConvaiSubsystem->RegisterPlayerComponent(this);
|
||||
}
|
||||
else
|
||||
{
|
||||
CONVAI_LOG(ConvaiPlayerLog, Warning, TEXT("BeginPlay: ConvaiSubsystem is not valid"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool UConvaiPlayerComponent::ConsumeStreamingBuffer(TArray<uint8>& Buffer)
|
||||
{
|
||||
int Datalength = VoiceCaptureRingBuffer.RingDataUsage();
|
||||
if (Datalength <= 0)
|
||||
return false;
|
||||
|
||||
Buffer.SetNumUninitialized(Datalength, false);
|
||||
VoiceCaptureRingBuffer.Dequeue(Buffer.GetData(), Datalength);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void UConvaiPlayerComponent::SetIsStreamingServer_Implementation(bool value)
|
||||
{
|
||||
IsStreaming = value;
|
||||
if (IsStreaming == false)
|
||||
onDataReceived_Delegate.ExecuteIfBound(); // In case a consumer was waiting on this delegate
|
||||
}
|
||||
|
||||
void UConvaiPlayerComponent::BeginDestroy()
|
||||
{
|
||||
// Fallback unregistration in case EndPlay wasn't called
|
||||
if (UGameInstance* GameInstance = GetWorld() ? GetWorld()->GetGameInstance() : nullptr)
|
||||
{
|
||||
if (UConvaiSubsystem* ConvaiSubsystem = GameInstance->GetSubsystem<UConvaiSubsystem>())
|
||||
{
|
||||
if (IsValid(ConvaiSubsystem))
|
||||
{
|
||||
ConvaiSubsystem->UnregisterPlayerComponent(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Super::BeginDestroy();
|
||||
}
|
||||
232
ConvAI/Convai/Source/Convai/Private/ConvaiSpeechToTextProxy.cpp
Normal file
232
ConvAI/Convai/Source/Convai/Private/ConvaiSpeechToTextProxy.cpp
Normal file
@@ -0,0 +1,232 @@
|
||||
// Copyright 2022 Convai Inc. All Rights Reserved.
|
||||
|
||||
|
||||
#include "ConvaiSpeechToTextProxy.h"
|
||||
#include "ConvaiUtils.h"
|
||||
#include "Sound/SoundWave.h"
|
||||
#include "Misc/FileHelper.h"
|
||||
#include "Misc/Paths.h"
|
||||
#include "AudioDecompress.h"
|
||||
#include "Engine.h"
|
||||
#include "JsonObjectConverter.h"
|
||||
#include "../Convai.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
static FString TextToSpeechURL() { return UConvaiURL::GetFullURL(TEXT("tts"), false); }
|
||||
static FString SpeechToTextURL() { return UConvaiURL::GetFullURL(TEXT("stt/"), false); }
|
||||
}
|
||||
|
||||
|
||||
DEFINE_LOG_CATEGORY(ConvaiS2THttpLog);
|
||||
|
||||
UConvaiSpeechToTextProxy* UConvaiSpeechToTextProxy::CreateSpeech2TextFromFileNameQueryProxy(UObject* WorldContextObject, FString filename)
|
||||
{
|
||||
UConvaiSpeechToTextProxy* Proxy = NewObject<UConvaiSpeechToTextProxy>();
|
||||
Proxy->WorldPtr = GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::LogAndReturnNull);
|
||||
Proxy->URL = SpeechToTextURL();
|
||||
|
||||
if (!FPaths::FileExists(filename))
|
||||
{
|
||||
// check if the file is relative to the save/BouncedWavFiles directory
|
||||
FString SaveDir = FPaths::ConvertRelativePathToFull(FPaths::ProjectSavedDir());
|
||||
filename = FPaths::Combine(SaveDir, FString("BouncedWavFiles"), filename);
|
||||
if (!FPaths::FileExists(filename))
|
||||
{
|
||||
//if (!FPaths::GameAgnosticSavedDir)
|
||||
CONVAI_LOG(ConvaiS2THttpLog, Warning, TEXT("File does not exist!, %s"), *filename);
|
||||
Proxy->failed();
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// Read the file into a byte array
|
||||
FFileHelper::LoadFileToArray(Proxy->Payload, *filename, 0);
|
||||
|
||||
Proxy->bStereo = true;
|
||||
return Proxy;
|
||||
}
|
||||
|
||||
UConvaiSpeechToTextProxy* UConvaiSpeechToTextProxy::CreateSpeech2TextFromSoundWaveQueryProxy(UObject* WorldContextObject, USoundWave* SoundWave)
|
||||
{
|
||||
UConvaiSpeechToTextProxy* Proxy = NewObject<UConvaiSpeechToTextProxy>();
|
||||
Proxy->WorldPtr = GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::LogAndReturnNull);
|
||||
Proxy->URL = SpeechToTextURL();
|
||||
|
||||
if (SoundWave == nullptr)
|
||||
{
|
||||
CONVAI_LOG(ConvaiS2THttpLog, Warning, TEXT("Sound wave is invalid!"));
|
||||
Proxy->failed();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
//Proxy->Payload.SetNum(SoundWave->TotalSamples * 2);
|
||||
uint8* PCMData = nullptr;
|
||||
TArray<uint8> RawPCMData;
|
||||
int32 OutSampleRate = -1;
|
||||
int32 outNumChannels = -1;
|
||||
//int32 numBytes = SoundWave->GeneratePCMData(PCMData, SoundWave->TotalSamples);
|
||||
|
||||
if (SoundWave->RawPCMData == nullptr || SoundWave->RawPCMDataSize <= 0) {
|
||||
CONVAI_LOG(LogTemp, Display, TEXT("SoundWave PCM Data is compressed. Starting Decompressing....."));
|
||||
RawPCMData = UConvaiUtils::ExtractPCMDataFromSoundWave(SoundWave, OutSampleRate, outNumChannels);
|
||||
|
||||
if (RawPCMData.Num() > 0) {
|
||||
CONVAI_LOG(LogTemp, Display, TEXT("SoundWave PCM Data decompression successfully done....."));
|
||||
}
|
||||
else {
|
||||
CONVAI_LOG(LogTemp,Warning,TEXT("SoundWave couldn't be decompressed successuflly !!!"));
|
||||
}
|
||||
//CONVAI_LOG(ConvaiS2THttpLog, Warning, TEXT("RawPCMData is invalid!"));
|
||||
}
|
||||
else {
|
||||
RawPCMData = TArray<uint8>(SoundWave->RawPCMData, SoundWave->RawPCMDataSize);
|
||||
}
|
||||
|
||||
//TArray<uint8> AudioBuffer(SoundWave->RawPCMData, SoundWave->RawPCMDataSize);
|
||||
|
||||
SerializeWaveFile(Proxy->Payload, RawPCMData.GetData(), RawPCMData.Num(), SoundWave->NumChannels, SoundWave->GetSampleRateForCurrentPlatform());
|
||||
|
||||
//CONVAI_LOG(ConvaiS2THttpLog, Warning, TEXT("Sound wave sample rate: %f"), SoundWave->GetSampleRateForCurrentPlatform());
|
||||
Proxy->bStereo = SoundWave->NumChannels>1? true : false;
|
||||
return Proxy;
|
||||
}
|
||||
|
||||
UConvaiSpeechToTextProxy* UConvaiSpeechToTextProxy::CreateSpeech2TextFromArrayQueryProxy(UObject* WorldContextObject, TArray<uint8> Payload)
|
||||
{
|
||||
UConvaiSpeechToTextProxy* Proxy = NewObject<UConvaiSpeechToTextProxy>();
|
||||
Proxy->WorldPtr = GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::LogAndReturnNull);
|
||||
Proxy->URL = SpeechToTextURL();
|
||||
Proxy->Payload = Payload;
|
||||
Proxy->bStereo = true;
|
||||
return Proxy;
|
||||
}
|
||||
|
||||
|
||||
void UConvaiSpeechToTextProxy::Activate()
|
||||
{
|
||||
UWorld* World = WorldPtr.Get();
|
||||
|
||||
if (!World)
|
||||
{
|
||||
CONVAI_LOG(ConvaiS2THttpLog, Warning, TEXT("Could not get a pointer to world!"));
|
||||
failed();
|
||||
return;
|
||||
}
|
||||
|
||||
FHttpModule* Http = &FHttpModule::Get();
|
||||
if (!Http)
|
||||
{
|
||||
CONVAI_LOG(ConvaiS2THttpLog, Warning, TEXT("Could not get a pointer to http module!"));
|
||||
failed();
|
||||
return;
|
||||
}
|
||||
|
||||
if (Payload.Num() <= 44)
|
||||
{
|
||||
CONVAI_LOG(ConvaiS2THttpLog, Warning, TEXT("Payload size is too small, %d bytes!"), Payload.Num());
|
||||
failed();
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
TPair<FString, FString> AuthHeaderAndKey = UConvaiUtils::GetAuthHeaderAndKey();
|
||||
FString AuthKey = AuthHeaderAndKey.Value;
|
||||
FString AuthHeader = AuthHeaderAndKey.Key;
|
||||
|
||||
// Form Validation
|
||||
if (!UConvaiFormValidation::ValidateAuthKey(AuthKey) || !UConvaiFormValidation::ValidateInputVoice(Payload))
|
||||
{
|
||||
failed();
|
||||
return;
|
||||
}
|
||||
|
||||
TArray<uint8> monoWavBytes;
|
||||
if (bStereo)
|
||||
{
|
||||
// Change the wav file from 2 channels to 1 channel
|
||||
UConvaiUtils::StereoToMono(Payload, monoWavBytes);
|
||||
}
|
||||
else
|
||||
{
|
||||
monoWavBytes = Payload;
|
||||
}
|
||||
|
||||
// Create the request
|
||||
FHttpRequestRef Request = Http->CreateRequest();
|
||||
Request->OnProcessRequestComplete().BindUObject(this, &UConvaiSpeechToTextProxy::onHttpRequestComplete);
|
||||
|
||||
// Set request fields
|
||||
Request->SetURL(URL);
|
||||
Request->SetVerb("POST");
|
||||
Request->SetHeader(TEXT("User-Agent"), TEXT("X-UnrealEngine-Agent"));
|
||||
|
||||
Request->SetHeader("Content-Type", "multipart/form-data; boundary=blahblahsomeboundary");
|
||||
Request->SetHeader(AuthHeader, AuthKey);
|
||||
|
||||
// prepare request content data
|
||||
//FString a = "\r\n--blahblahsomeboundary\r\n";
|
||||
//FString b = "Content-Disposition: form-data; name=\"Request->SetHeader(AuthHeader, AuthKey);\"\r\n\r\n";
|
||||
// Request->SetHeader(AuthHeader, AuthKey);
|
||||
FString c = "\r\n--blahblahsomeboundary\r\n";
|
||||
FString d = "Content-Disposition: form-data; name=\"file\"; filename=\"out.wav\"\r\n\r\n";
|
||||
|
||||
// UpFileRawData
|
||||
FString e = "\r\n--blahblahsomeboundary--\r\n";
|
||||
|
||||
TArray<uint8> data;
|
||||
//data.Append((uint8*)TCHAR_TO_UTF8(*a), a.Len());
|
||||
//data.Append((uint8*)TCHAR_TO_UTF8(*b), b.Len());
|
||||
//data.Append((uint8*)TCHAR_TO_UTF8(*Request->SetHeader(AuthHeader, AuthKey);), Request->SetHeader(AuthHeader, AuthKey);.Len());
|
||||
data.Append((uint8*)TCHAR_TO_UTF8(*c), c.Len());
|
||||
data.Append((uint8*)TCHAR_TO_UTF8(*d), d.Len());
|
||||
data.Append(monoWavBytes);
|
||||
data.Append((uint8*)TCHAR_TO_UTF8(*e), e.Len());
|
||||
|
||||
|
||||
Request->SetContent(data);
|
||||
|
||||
// Run the request
|
||||
if (!Request->ProcessRequest()) failed();
|
||||
}
|
||||
|
||||
|
||||
void UConvaiSpeechToTextProxy::onHttpRequestComplete(FHttpRequestPtr RequestPtr, FHttpResponsePtr ResponsePtr, bool bWasSuccessful)
|
||||
{
|
||||
if (!bWasSuccessful || ResponsePtr->GetResponseCode() < 200 || ResponsePtr->GetResponseCode() > 299)
|
||||
{
|
||||
CONVAI_LOG(ConvaiS2THttpLog, Warning, TEXT("HTTP request failed with code %d"), ResponsePtr->GetResponseCode());
|
||||
CONVAI_LOG(ConvaiS2THttpLog, Warning, TEXT("Response:%s"), *ResponsePtr->GetContentAsString());
|
||||
|
||||
this->Response = ResponsePtr->GetContentAsString();
|
||||
failed();
|
||||
return;
|
||||
}
|
||||
|
||||
this->Response = ResponsePtr->GetContentAsString();
|
||||
// Clean the string
|
||||
this->Response = this->Response.LeftChop(3);
|
||||
this->Response = this->Response.RightChop(12);
|
||||
success();
|
||||
}
|
||||
|
||||
|
||||
void UConvaiSpeechToTextProxy::failed()
|
||||
{
|
||||
OnFailure.Broadcast(Response);
|
||||
finish();
|
||||
}
|
||||
|
||||
|
||||
void UConvaiSpeechToTextProxy::success()
|
||||
{
|
||||
OnSuccess.Broadcast(Response);
|
||||
finish();
|
||||
}
|
||||
|
||||
|
||||
void UConvaiSpeechToTextProxy::finish()
|
||||
{
|
||||
}
|
||||
|
||||
481
ConvAI/Convai/Source/Convai/Private/ConvaiSubsystem.cpp
Normal file
481
ConvAI/Convai/Source/Convai/Private/ConvaiSubsystem.cpp
Normal file
@@ -0,0 +1,481 @@
|
||||
// Copyright 2022 Convai Inc. All Rights Reserved.
|
||||
|
||||
#include "ConvaiSubsystem.h"
|
||||
#include "ConvaiAndroid.h"
|
||||
#include "Engine/Engine.h"
|
||||
#include "Async/Async.h"
|
||||
#include "../Convai.h"
|
||||
#include "ConvaiUtils.h"
|
||||
#include "HAL/PlatformProcess.h"
|
||||
#include "ConvaiChatbotComponent.h"
|
||||
#include "ConvaiPlayerComponent.h"
|
||||
|
||||
THIRD_PARTY_INCLUDES_START
|
||||
// grpc includes
|
||||
#include <grpc++/grpc++.h>
|
||||
#include <chrono>
|
||||
THIRD_PARTY_INCLUDES_END
|
||||
|
||||
|
||||
using grpc::Channel;
|
||||
using grpc::ClientAsyncResponseReader;
|
||||
using grpc::ClientContext;
|
||||
using grpc::CompletionQueue;
|
||||
using grpc::Status;
|
||||
using service::ConvaiService;
|
||||
|
||||
DEFINE_LOG_CATEGORY(ConvaiSubsystemLog);
|
||||
|
||||
using grpc::SslCredentialsOptions;
|
||||
|
||||
#if PLATFORM_WINDOWS
|
||||
#include <wincrypt.h>
|
||||
namespace
|
||||
{
|
||||
|
||||
std::string utf8Encode(const std::wstring& wstr)
|
||||
{
|
||||
if (wstr.empty())
|
||||
return std::string();
|
||||
|
||||
int sizeNeeded = WideCharToMultiByte(CP_UTF8, 0, &wstr[0], (int)wstr.size(),
|
||||
NULL, 0, NULL, NULL);
|
||||
std::string strTo(sizeNeeded, 0);
|
||||
WideCharToMultiByte(CP_UTF8, 0, &wstr[0], (int)wstr.size(),
|
||||
&strTo[0], sizeNeeded, NULL, NULL);
|
||||
return strTo;
|
||||
}
|
||||
|
||||
|
||||
SslCredentialsOptions getSslOptions() {
|
||||
SslCredentialsOptions result;
|
||||
|
||||
std::string pem_root_certs = R"(-----BEGIN CERTIFICATE-----
|
||||
MIIFjDCCA3SgAwIBAgINAgCOsgIzNmWLZM3bmzANBgkqhkiG9w0BAQsFADBHMQsw
|
||||
CQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEU
|
||||
MBIGA1UEAxMLR1RTIFJvb3QgUjEwHhcNMjAwODEzMDAwMDQyWhcNMjcwOTMwMDAw
|
||||
MDQyWjBGMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZp
|
||||
Y2VzIExMQzETMBEGA1UEAxMKR1RTIENBIDFENDCCASIwDQYJKoZIhvcNAQEBBQAD
|
||||
ggEPADCCAQoCggEBAKvAqqPCE27l0w9zC8dTPIE89bA+xTmDaG7y7VfQ4c+mOWhl
|
||||
UebUQpK0yv2r678RJExK0HWDjeq+nLIHN1Em5j6rARZixmyRSjhIR0KOQPGBMUld
|
||||
saztIIJ7O0g/82qj/vGDl//3t4tTqxiRhLQnTLXJdeB+2DhkdU6IIgx6wN7E5NcU
|
||||
H3Rcsejcqj8p5Sj19vBm6i1FhqLGymhMFroWVUGO3xtIH91dsgy4eFKcfKVLWK3o
|
||||
2190Q0Lm/SiKmLbRJ5Au4y1euFJm2JM9eB84Fkqa3ivrXWUeVtye0CQdKvsY2Fka
|
||||
zvxtxvusLJzLWYHk55zcRAacDA2SeEtBbQfD1qsCAwEAAaOCAXYwggFyMA4GA1Ud
|
||||
DwEB/wQEAwIBhjAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwEgYDVR0T
|
||||
AQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQUJeIYDrJXkZQq5dRdhpCD3lOzuJIwHwYD
|
||||
VR0jBBgwFoAU5K8rJnEaK0gnhS9SZizv8IkTcT4waAYIKwYBBQUHAQEEXDBaMCYG
|
||||
CCsGAQUFBzABhhpodHRwOi8vb2NzcC5wa2kuZ29vZy9ndHNyMTAwBggrBgEFBQcw
|
||||
AoYkaHR0cDovL3BraS5nb29nL3JlcG8vY2VydHMvZ3RzcjEuZGVyMDQGA1UdHwQt
|
||||
MCswKaAnoCWGI2h0dHA6Ly9jcmwucGtpLmdvb2cvZ3RzcjEvZ3RzcjEuY3JsME0G
|
||||
A1UdIARGMEQwCAYGZ4EMAQIBMDgGCisGAQQB1nkCBQMwKjAoBggrBgEFBQcCARYc
|
||||
aHR0cHM6Ly9wa2kuZ29vZy9yZXBvc2l0b3J5LzANBgkqhkiG9w0BAQsFAAOCAgEA
|
||||
IVToy24jwXUr0rAPc924vuSVbKQuYw3nLflLfLh5AYWEeVl/Du18QAWUMdcJ6o/q
|
||||
FZbhXkBH0PNcw97thaf2BeoDYY9Ck/b+UGluhx06zd4EBf7H9P84nnrwpR+4GBDZ
|
||||
K+Xh3I0tqJy2rgOqNDflr5IMQ8ZTWA3yltakzSBKZ6XpF0PpqyCRvp/NCGv2KX2T
|
||||
uPCJvscp1/m2pVTtyBjYPRQ+QuCQGAJKjtN7R5DFrfTqMWvYgVlpCJBkwlu7+7KY
|
||||
3cTIfzE7cmALskMKNLuDz+RzCcsYTsVaU7Vp3xL60OYhqFkuAOOxDZ6pHOj9+OJm
|
||||
YgPmOT4X3+7L51fXJyRH9KfLRP6nT31D5nmsGAOgZ26/8T9hsBW1uo9ju5fZLZXV
|
||||
VS5H0HyIBMEKyGMIPhFWrlt/hFS28N1zaKI0ZBGD3gYgDLbiDT9fGXstpk+Fmc4o
|
||||
lVlWPzXe81vdoEnFbr5M272HdgJWo+WhT9BYM0Ji+wdVmnRffXgloEoluTNcWzc4
|
||||
1dFpgJu8fF3LG0gl2ibSYiCi9a6hvU0TppjJyIWXhkJTcMJlPrWx1VytEUGrX2l0
|
||||
JDwRjW/656r0KVB02xHRKvm2ZKI03TglLIpmVCK3kBKkKNpBNkFt8rhafcCKOb9J
|
||||
x/9tpNFlQTl7B39rJlJWkR17QnZqVptFePFORoZmFzM=
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIFVzCCAz+gAwIBAgINAgPlk28xsBNJiGuiFzANBgkqhkiG9w0BAQwFADBHMQsw
|
||||
CQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEU
|
||||
MBIGA1UEAxMLR1RTIFJvb3QgUjEwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAw
|
||||
MDAwWjBHMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZp
|
||||
Y2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjEwggIiMA0GCSqGSIb3DQEBAQUA
|
||||
A4ICDwAwggIKAoICAQC2EQKLHuOhd5s73L+UPreVp0A8of2C+X0yBoJx9vaMf/vo
|
||||
27xqLpeXo4xL+Sv2sfnOhB2x+cWX3u+58qPpvBKJXqeqUqv4IyfLpLGcY9vXmX7w
|
||||
Cl7raKb0xlpHDU0QM+NOsROjyBhsS+z8CZDfnWQpJSMHobTSPS5g4M/SCYe7zUjw
|
||||
TcLCeoiKu7rPWRnWr4+wB7CeMfGCwcDfLqZtbBkOtdh+JhpFAz2weaSUKK0Pfybl
|
||||
qAj+lug8aJRT7oM6iCsVlgmy4HqMLnXWnOunVmSPlk9orj2XwoSPwLxAwAtcvfaH
|
||||
szVsrBhQf4TgTM2S0yDpM7xSma8ytSmzJSq0SPly4cpk9+aCEI3oncKKiPo4Zor8
|
||||
Y/kB+Xj9e1x3+naH+uzfsQ55lVe0vSbv1gHR6xYKu44LtcXFilWr06zqkUspzBmk
|
||||
MiVOKvFlRNACzqrOSbTqn3yDsEB750Orp2yjj32JgfpMpf/VjsPOS+C12LOORc92
|
||||
wO1AK/1TD7Cn1TsNsYqiA94xrcx36m97PtbfkSIS5r762DL8EGMUUXLeXdYWk70p
|
||||
aDPvOmbsB4om3xPXV2V4J95eSRQAogB/mqghtqmxlbCluQ0WEdrHbEg8QOB+DVrN
|
||||
VjzRlwW5y0vtOUucxD/SVRNuJLDWcfr0wbrM7Rv1/oFB2ACYPTrIrnqYNxgFlQID
|
||||
AQABo0IwQDAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4E
|
||||
FgQU5K8rJnEaK0gnhS9SZizv8IkTcT4wDQYJKoZIhvcNAQEMBQADggIBAJ+qQibb
|
||||
C5u+/x6Wki4+omVKapi6Ist9wTrYggoGxval3sBOh2Z5ofmmWJyq+bXmYOfg6LEe
|
||||
QkEzCzc9zolwFcq1JKjPa7XSQCGYzyI0zzvFIoTgxQ6KfF2I5DUkzps+GlQebtuy
|
||||
h6f88/qBVRRiClmpIgUxPoLW7ttXNLwzldMXG+gnoot7TiYaelpkttGsN/H9oPM4
|
||||
7HLwEXWdyzRSjeZ2axfG34arJ45JK3VmgRAhpuo+9K4l/3wV3s6MJT/KYnAK9y8J
|
||||
ZgfIPxz88NtFMN9iiMG1D53Dn0reWVlHxYciNuaCp+0KueIHoI17eko8cdLiA6Ef
|
||||
MgfdG+RCzgwARWGAtQsgWSl4vflVy2PFPEz0tv/bal8xa5meLMFrUKTX5hgUvYU/
|
||||
Z6tGn6D/Qqc6f1zLXbBwHSs09dR2CQzreExZBfMzQsNhFRAbd03OIozUhfJFfbdT
|
||||
6u9AWpQKXCBfTkBdYiJ23//OYb2MI3jSNwLgjt7RETeJ9r/tSQdirpLsQBqvFAnZ
|
||||
0E6yove+7u7Y/9waLd64NnHi/Hm3lCXRSHNboTXns5lndcEZOitHTtNCjv0xyBZm
|
||||
2tIMPNuzjsmhDYAPexZ3FL//2wmUspO8IFgV6dtxQ/PeEMMA3KgqlbbC1j+Qa3bb
|
||||
bP6MvPJwNQzcmRk13NfIRmPVNnGuV/u3gm3c
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw
|
||||
TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh
|
||||
cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4
|
||||
WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu
|
||||
ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY
|
||||
MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc
|
||||
h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+
|
||||
0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U
|
||||
A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW
|
||||
T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH
|
||||
B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC
|
||||
B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv
|
||||
KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn
|
||||
OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn
|
||||
jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw
|
||||
qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI
|
||||
rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV
|
||||
HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq
|
||||
hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL
|
||||
ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ
|
||||
3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK
|
||||
NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5
|
||||
ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur
|
||||
TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC
|
||||
jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc
|
||||
oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq
|
||||
4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA
|
||||
mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d
|
||||
emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc=
|
||||
-----END CERTIFICATE-----
|
||||
)";
|
||||
|
||||
result.pem_root_certs = pem_root_certs;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
SslCredentialsOptions getSslOptionsystem()
|
||||
{
|
||||
// Start with hardcoded certificates
|
||||
SslCredentialsOptions result = getSslOptions();
|
||||
|
||||
// Open root certificate store.
|
||||
HANDLE hRootCertStore = CertOpenSystemStoreW(NULL, L"ROOT");
|
||||
if (!hRootCertStore)
|
||||
return result;
|
||||
|
||||
// Get all root certificates and append them.
|
||||
PCCERT_CONTEXT pCert = NULL;
|
||||
while ((pCert = CertEnumCertificatesInStore(hRootCertStore, pCert)) != NULL)
|
||||
{
|
||||
// Append this certificate in PEM formatted data.
|
||||
DWORD size = 0;
|
||||
CryptBinaryToStringW(pCert->pbCertEncoded, pCert->cbCertEncoded,
|
||||
CRYPT_STRING_BASE64HEADER, NULL, &size);
|
||||
std::vector<WCHAR> pem(size);
|
||||
CryptBinaryToStringW(pCert->pbCertEncoded, pCert->cbCertEncoded,
|
||||
CRYPT_STRING_BASE64HEADER, pem.data(), &size);
|
||||
|
||||
result.pem_root_certs += utf8Encode(pem.data());
|
||||
}
|
||||
|
||||
CertCloseStore(hRootCertStore, 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
};
|
||||
#endif
|
||||
|
||||
namespace
|
||||
{
|
||||
const char* grpc_connectivity_state_str[] =
|
||||
{
|
||||
"GRPC_CHANNEL_IDLE",
|
||||
"GRPC_CHANNEL_CONNECTING",
|
||||
"GRPC_CHANNEL_READY",
|
||||
"GRPC_CHANNEL_TRANSIENT_FAILURE",
|
||||
"GRPC_CHANNEL_SHUTDOWN"
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
uint32 FgRPCClient::Run()
|
||||
{
|
||||
void* got_tag;
|
||||
bool ok = false;
|
||||
CONVAI_LOG(ConvaiSubsystemLog, Log, TEXT("Start Run"));
|
||||
|
||||
// Block until the next result is available in the completion queue "cq
|
||||
while (bIsRunning && cq_.Next(&got_tag, &ok)) {
|
||||
if (got_tag)
|
||||
{
|
||||
FgRPC_Delegate* gRPC_Delegate = static_cast<FgRPC_Delegate*>(got_tag);
|
||||
|
||||
if (bIsRunning)
|
||||
{
|
||||
gRPC_Delegate->ExecuteIfBound(ok);
|
||||
}
|
||||
else
|
||||
{
|
||||
CONVAI_LOG(ConvaiSubsystemLog, Log, TEXT("Could not run gRPC delegate due to thread closing down"));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
CONVAI_LOG(ConvaiSubsystemLog, Log, TEXT("Bad got_tag"));
|
||||
}
|
||||
|
||||
}
|
||||
CONVAI_LOG(ConvaiSubsystemLog, Log, TEXT("End Run"));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void FgRPCClient::StartStub()
|
||||
{
|
||||
OnStateChangeDelegate = FgRPC_Delegate::CreateRaw(this, &FgRPCClient::OnStateChange);
|
||||
CreateChannel();
|
||||
|
||||
bIsRunning = true;
|
||||
Thread.Reset(FRunnableThread::Create(this, TEXT("gRPC_Stub")));
|
||||
}
|
||||
|
||||
void FgRPCClient::CreateChannel()
|
||||
{
|
||||
CONVAI_LOG(ConvaiSubsystemLog, Log, TEXT("gRPC Creating Channel..."));
|
||||
grpc::ChannelArguments args;
|
||||
args.SetMaxReceiveMessageSize(2147483647);
|
||||
|
||||
args.SetInt(GRPC_ARG_CLIENT_IDLE_TIMEOUT_MS, INT_MAX);
|
||||
|
||||
args.SetInt(GRPC_ARG_KEEPALIVE_TIME_MS, 30000); // Send keepalive ping every 30 seconds
|
||||
args.SetInt(GRPC_ARG_KEEPALIVE_TIMEOUT_MS, 60000); // Wait 5 seconds for keepalive response
|
||||
args.SetInt(GRPC_ARG_KEEPALIVE_PERMIT_WITHOUT_CALLS, 1); // Allow keepalive even when no active calls
|
||||
|
||||
Channel = grpc::CreateCustomChannel(Target, Creds, args);
|
||||
|
||||
//Channel->NotifyOnStateChange(grpc_connectivity_state::GRPC_CHANNEL_CONNECTING, std::chrono::system_clock::time_point().max(), &cq_, (void*)&OnStateChangeDelegate);
|
||||
}
|
||||
|
||||
void FgRPCClient::OnStateChange(bool ok)
|
||||
{
|
||||
grpc_connectivity_state state;
|
||||
|
||||
if (Channel)
|
||||
state = Channel->GetState(true);
|
||||
else
|
||||
state = grpc_connectivity_state::GRPC_CHANNEL_SHUTDOWN;
|
||||
|
||||
|
||||
if (state == grpc_connectivity_state::GRPC_CHANNEL_SHUTDOWN)
|
||||
{
|
||||
if (!bIsRunning)
|
||||
{
|
||||
CONVAI_LOG(ConvaiSubsystemLog, Warning, TEXT("gRPC channel state changed to %s... Attempting to reconnect"), *FString(grpc_connectivity_state_str[state]));
|
||||
CreateChannel();
|
||||
}
|
||||
else
|
||||
{
|
||||
CONVAI_LOG(ConvaiSubsystemLog, Log, TEXT("gRPC channel state changed to %s... Closing"), *FString(grpc_connectivity_state_str[state]));
|
||||
}
|
||||
//Channel->NotifyOnStateChange(state, std::chrono::system_clock::time_point().max(), &cq_, (void*)&OnStateChangeDelegate);
|
||||
}
|
||||
else
|
||||
{
|
||||
CONVAI_LOG(ConvaiSubsystemLog, Log, TEXT("gRPC channel state changed to %s"), *FString(grpc_connectivity_state_str[state]));
|
||||
Channel->NotifyOnStateChange(state, std::chrono::system_clock::time_point().max(), &cq_, (void*)&OnStateChangeDelegate);
|
||||
}
|
||||
}
|
||||
|
||||
void FgRPCClient::Exit()
|
||||
{
|
||||
if (!bIsRunning)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
bIsRunning = false;
|
||||
{
|
||||
FScopeLock Lock(&CriticalSection);
|
||||
cq_.Shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
FgRPCClient::FgRPCClient(std::string InTarget,
|
||||
const std::shared_ptr<grpc::ChannelCredentials>& InCreds)
|
||||
: bIsRunning(false),
|
||||
Creds(InCreds),
|
||||
Target(InTarget)
|
||||
{
|
||||
}
|
||||
|
||||
std::unique_ptr<ConvaiService::Stub> FgRPCClient::GetNewStub()
|
||||
{
|
||||
FScopeLock Lock(&CriticalSection);
|
||||
|
||||
// # TODO (Mohamed): Handling Mic permissions requires refactoring and a unified pipeline for all platforms
|
||||
// Delaying asking for permission for MacOS due to a crash
|
||||
#ifdef __APPLE__
|
||||
GetAppleMicPermission();
|
||||
#endif
|
||||
|
||||
grpc_connectivity_state state = Channel->GetState(false);
|
||||
|
||||
if (state != grpc_connectivity_state::GRPC_CHANNEL_READY)
|
||||
{
|
||||
CONVAI_LOG(ConvaiSubsystemLog, Warning, TEXT("gRPC channel not ready yet.. Current State: %s"), *FString(grpc_connectivity_state_str[state]));
|
||||
}
|
||||
return ConvaiService::NewStub(Channel);
|
||||
}
|
||||
|
||||
CompletionQueue* FgRPCClient::GetCompletionQueue()
|
||||
{
|
||||
return &cq_;
|
||||
}
|
||||
|
||||
UConvaiSubsystem::UConvaiSubsystem()
|
||||
: UGameInstanceSubsystem()
|
||||
{
|
||||
}
|
||||
|
||||
void UConvaiSubsystem::Initialize(FSubsystemCollectionBase& Collection)
|
||||
{
|
||||
Super::Initialize(Collection);
|
||||
|
||||
//AsyncTask(ENamedThreads::GameThread, [WeakThis = MakeWeakObjectPtr(this)]
|
||||
// {
|
||||
// Check command line for insecure connection flag first, then fall back to settings
|
||||
bool AllowInsecureConnection = false;
|
||||
FString InsecureConnectionStr = UCommandLineUtils::GetCommandLineFlagValueAsString(TEXT("ConvaiAllowInsecure"), TEXT(""));
|
||||
if (!InsecureConnectionStr.IsEmpty())
|
||||
{
|
||||
// Convert string to bool
|
||||
AllowInsecureConnection = InsecureConnectionStr.ToBool();
|
||||
CONVAI_LOG(ConvaiSubsystemLog, Log, TEXT("Using insecure connection setting from command line: %s"),
|
||||
AllowInsecureConnection ? TEXT("true") : TEXT("false"));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Use setting from ConvaiSettings
|
||||
AllowInsecureConnection = Convai::Get().GetConvaiSettings()->AllowInsecureConnection;
|
||||
}
|
||||
|
||||
std::shared_ptr<grpc::ChannelCredentials> channel_creds;
|
||||
#if PLATFORM_WINDOWS
|
||||
if (AllowInsecureConnection)
|
||||
channel_creds = grpc::InsecureChannelCredentials();
|
||||
else
|
||||
{
|
||||
// Check if we should use system certificates (can be overridden by command line)
|
||||
bool UseSystemCerts = Convai::Get().GetConvaiSettings()->UseSystemCertificates;
|
||||
FString UseSystemCertsStr = UCommandLineUtils::GetCommandLineFlagValueAsString(TEXT("ConvaiUseSystemCerts"), TEXT(""));
|
||||
if (!UseSystemCertsStr.IsEmpty())
|
||||
{
|
||||
UseSystemCerts = UseSystemCertsStr.ToBool();
|
||||
CONVAI_LOG(ConvaiSubsystemLog, Log, TEXT("Using system certificates setting from command line: %s"),
|
||||
UseSystemCerts ? TEXT("true") : TEXT("false"));
|
||||
}
|
||||
|
||||
if (UseSystemCerts)
|
||||
{
|
||||
CONVAI_LOG(ConvaiSubsystemLog, Log, TEXT("Using both hardcoded and system SSL certificates"));
|
||||
channel_creds = grpc::SslCredentials(getSslOptionsystem());
|
||||
}
|
||||
else
|
||||
{
|
||||
channel_creds = grpc::SslCredentials(getSslOptions());
|
||||
}
|
||||
}
|
||||
#else
|
||||
if (AllowInsecureConnection)
|
||||
channel_creds = grpc::InsecureChannelCredentials();
|
||||
else
|
||||
channel_creds = grpc::SslCredentials(grpc::SslCredentialsOptions());
|
||||
#endif
|
||||
|
||||
// Get URL from command line first, then settings, then default
|
||||
FString URL = Convai::Get().GetConvaiSettings()->CustomURL;
|
||||
URL.TrimEndInline();
|
||||
URL.TrimStartInline();
|
||||
|
||||
// Check for command line parameter
|
||||
FString CommandLineURL = UCommandLineUtils::GetCommandLineFlagValueAsString(TEXT("ConvaiStreamURL"), TEXT(""));
|
||||
if (!CommandLineURL.IsEmpty())
|
||||
{
|
||||
URL = CommandLineURL;
|
||||
CONVAI_LOG(ConvaiSubsystemLog, Log, TEXT("Using stream URL from command line: %s"), *URL);
|
||||
}
|
||||
// If settings URL is empty, use default
|
||||
else if (URL.IsEmpty())
|
||||
{
|
||||
URL = "stream.convai.com";
|
||||
}
|
||||
|
||||
gRPC_Runnable = MakeShareable(new FgRPCClient(TCHAR_TO_UTF8(*URL), channel_creds));
|
||||
|
||||
//gRPC_Runnable = MakeShareable(new FgRPCClient(std::string("0.tcp.us-cal-1.ngrok.io:13976"), channel_creds));
|
||||
|
||||
gRPC_Runnable->StartStub();
|
||||
CONVAI_LOG(ConvaiSubsystemLog, Log, TEXT("UConvaiSubsystem Started"));
|
||||
|
||||
#if PLATFORM_ANDROID
|
||||
GetAndroidMicPermission();
|
||||
#endif
|
||||
//});
|
||||
}
|
||||
|
||||
void UConvaiSubsystem::Deinitialize()
|
||||
{
|
||||
gRPC_Runnable->Exit();
|
||||
Super::Deinitialize();
|
||||
CONVAI_LOG(ConvaiSubsystemLog, Log, TEXT("UConvaiSubsystem Stopped"));
|
||||
}
|
||||
|
||||
void UConvaiSubsystem::GetAndroidMicPermission()
|
||||
{
|
||||
if (!UConvaiAndroid::ConvaiAndroidHasMicrophonePermission())
|
||||
UConvaiAndroid::ConvaiAndroidAskMicrophonePermission();
|
||||
}
|
||||
|
||||
void UConvaiSubsystem::RegisterChatbotComponent(UConvaiChatbotComponent* ChatbotComponent)
|
||||
{
|
||||
if (IsValid(ChatbotComponent) && !RegisteredChatbotComponents.Contains(ChatbotComponent))
|
||||
{
|
||||
RegisteredChatbotComponents.Add(ChatbotComponent);
|
||||
}
|
||||
}
|
||||
|
||||
void UConvaiSubsystem::UnregisterChatbotComponent(UConvaiChatbotComponent* ChatbotComponent)
|
||||
{
|
||||
if (RegisteredChatbotComponents.Contains(ChatbotComponent))
|
||||
{
|
||||
RegisteredChatbotComponents.Remove(ChatbotComponent);
|
||||
}
|
||||
}
|
||||
|
||||
TArray<UConvaiChatbotComponent*> UConvaiSubsystem::GetAllChatbotComponents() const
|
||||
{
|
||||
return RegisteredChatbotComponents;
|
||||
}
|
||||
|
||||
void UConvaiSubsystem::RegisterPlayerComponent(UConvaiPlayerComponent* PlayerComponent)
|
||||
{
|
||||
if (IsValid(PlayerComponent) && !RegisteredPlayerComponents.Contains(PlayerComponent))
|
||||
{
|
||||
RegisteredPlayerComponents.Add(PlayerComponent);
|
||||
CONVAI_LOG(ConvaiSubsystemLog, Verbose, TEXT("Registered player component: %s"), *PlayerComponent->GetName());
|
||||
}
|
||||
}
|
||||
|
||||
void UConvaiSubsystem::UnregisterPlayerComponent(UConvaiPlayerComponent* PlayerComponent)
|
||||
{
|
||||
if (RegisteredPlayerComponents.Contains(PlayerComponent))
|
||||
{
|
||||
RegisteredPlayerComponents.Remove(PlayerComponent);
|
||||
CONVAI_LOG(ConvaiSubsystemLog, Verbose, TEXT("Unregistered player component: %s"), *PlayerComponent->GetName());
|
||||
}
|
||||
}
|
||||
|
||||
TArray<UConvaiPlayerComponent*> UConvaiSubsystem::GetAllPlayerComponents() const
|
||||
{
|
||||
return RegisteredPlayerComponents;
|
||||
}
|
||||
155
ConvAI/Convai/Source/Convai/Private/ConvaiTextToSpeechProxy.cpp
Normal file
155
ConvAI/Convai/Source/Convai/Private/ConvaiTextToSpeechProxy.cpp
Normal file
@@ -0,0 +1,155 @@
|
||||
// Copyright 2022 Convai Inc. All Rights Reserved.
|
||||
|
||||
#include "ConvaiTextToSpeechProxy.h"
|
||||
#include "ConvaiUtils.h"
|
||||
#include "Containers/UnrealString.h"
|
||||
#include "Sound/SoundWave.h"
|
||||
#include "Engine.h"
|
||||
#include "JsonObjectConverter.h"
|
||||
#include "RestAPI/ConvaiURL.h"
|
||||
|
||||
#include "../Convai.h"
|
||||
|
||||
DEFINE_LOG_CATEGORY(ConvaiT2SHttpLog);
|
||||
|
||||
|
||||
namespace {
|
||||
const char* TTS_Voice_Type_str[] = {
|
||||
"MALE",
|
||||
"FEMALE",
|
||||
"WUKMale 1",
|
||||
"WUKFemale 1",
|
||||
"SUKMale 1",
|
||||
"WAFemale 1",
|
||||
"WAMale 1",
|
||||
"SIFemale 1",
|
||||
"SIMale 1",
|
||||
"SUFemale 1",
|
||||
"SUMale 1",
|
||||
"WUFemale 1",
|
||||
"WUMale 1",
|
||||
"Trixie",
|
||||
"Twilight Sparkle",
|
||||
"Celestia",
|
||||
"Spike",
|
||||
"Applejack"
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
UConvaiTextToSpeechProxy* UConvaiTextToSpeechProxy::CreateTextToSpeechQueryProxy(UObject* WorldContextObject, FString Transcript, FString Voice)
|
||||
{
|
||||
UConvaiTextToSpeechProxy* Proxy = NewObject<UConvaiTextToSpeechProxy>();
|
||||
Proxy->WorldPtr = GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::LogAndReturnNull);
|
||||
Proxy->URL = UConvaiURL::GetFullURL(TEXT("tts"), false);
|
||||
|
||||
Proxy->Transcript = Transcript;
|
||||
Proxy->VoiceStr = Voice;
|
||||
//Proxy->VoiceStr = FString(TTS_Voice_Type_str[uint8(Voice)]);
|
||||
|
||||
|
||||
return Proxy;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void UConvaiTextToSpeechProxy::Activate()
|
||||
{
|
||||
UWorld* World = WorldPtr.Get();
|
||||
|
||||
if (!World)
|
||||
{
|
||||
CONVAI_LOG(ConvaiT2SHttpLog, Warning, TEXT("Could not get a pointer to world!"));
|
||||
failed();
|
||||
return;
|
||||
}
|
||||
|
||||
FHttpModule* Http = &FHttpModule::Get();
|
||||
if (!Http)
|
||||
{
|
||||
CONVAI_LOG(ConvaiT2SHttpLog, Warning, TEXT("Could not get a pointer to http module!"));
|
||||
failed();
|
||||
return;
|
||||
}
|
||||
|
||||
TPair<FString, FString> AuthHeaderAndKey = UConvaiUtils::GetAuthHeaderAndKey();
|
||||
FString AuthKey = AuthHeaderAndKey.Value;
|
||||
FString AuthHeader = AuthHeaderAndKey.Key;
|
||||
|
||||
// Form Validation
|
||||
if (!UConvaiFormValidation::ValidateAuthKey(AuthKey) || !UConvaiFormValidation::ValidateInputText(Transcript) || !UConvaiFormValidation::ValidateVoiceType(VoiceStr))
|
||||
{
|
||||
failed();
|
||||
return;
|
||||
}
|
||||
|
||||
// Create the request
|
||||
FHttpRequestRef Request = Http->CreateRequest();
|
||||
Request->OnProcessRequestComplete().BindUObject(this, &UConvaiTextToSpeechProxy::onHttpRequestComplete);
|
||||
|
||||
// Set request fields
|
||||
Request->SetURL(URL);
|
||||
Request->SetVerb("POST");
|
||||
Request->SetHeader(TEXT("User-Agent"), TEXT("X-UnrealEngine-Agent"));
|
||||
Request->SetHeader(TEXT("Content-Type"), TEXT("application/json"));
|
||||
Request->SetHeader(AuthHeader, AuthKey);
|
||||
|
||||
// prepare json data
|
||||
FString JsonString;
|
||||
TSharedRef<TJsonWriter<TCHAR>> JsonWriter = TJsonWriterFactory<TCHAR>::Create(&JsonString);
|
||||
JsonWriter->WriteObjectStart();
|
||||
JsonWriter->WriteValue(AuthHeader, AuthKey);
|
||||
JsonWriter->WriteValue("transcript", Transcript);
|
||||
JsonWriter->WriteValue("voice", VoiceStr);
|
||||
JsonWriter->WriteValue("filename", FString("testAudio"));
|
||||
//JsonWriter->WriteValue("encoding", (VoiceStr.ToLower() == "female" || VoiceStr.ToLower() == "male")? FString("wav") : FString("mp3"));
|
||||
JsonWriter->WriteValue("encoding", FString("wav"));
|
||||
JsonWriter->WriteObjectEnd();
|
||||
JsonWriter->Close();
|
||||
// Insert the content into the request
|
||||
Request->SetContentAsString(JsonString);
|
||||
|
||||
// Debug
|
||||
//CONVAI_LOG(ConvaiT2SHttpLog, Warning, TEXT("%s"), *UConvaiUtils::ByteArrayToString(Request->GetContent()));
|
||||
|
||||
// Initiate the request
|
||||
if (!Request->ProcessRequest()) failed();
|
||||
}
|
||||
|
||||
void UConvaiTextToSpeechProxy::onHttpRequestComplete(FHttpRequestPtr RequestPtr, FHttpResponsePtr ResponsePtr, bool bWasSuccessful)
|
||||
{
|
||||
if (!bWasSuccessful || ResponsePtr->GetResponseCode() < 200 || ResponsePtr->GetResponseCode() > 299)
|
||||
{
|
||||
CONVAI_LOG(ConvaiT2SHttpLog, Warning, TEXT("HTTP request failed with code %d, and with response:%s"),ResponsePtr->GetResponseCode(), *ResponsePtr->GetContentAsString());
|
||||
failed();
|
||||
return;
|
||||
}
|
||||
|
||||
this->SoundWave = UConvaiUtils::WavDataToSoundWave(ResponsePtr->GetContent());
|
||||
|
||||
//this->SoundWave = UConvaiUtils::PCMDataToSoundWav(ResponsePtr->GetContent(), 1, 44100); // use this if in PCM format
|
||||
if (this->SoundWave == nullptr)
|
||||
{
|
||||
CONVAI_LOG(ConvaiT2SHttpLog, Warning, TEXT("Failed to decode response content to a sound wave"));
|
||||
failed();
|
||||
return;
|
||||
}
|
||||
success();
|
||||
}
|
||||
|
||||
void UConvaiTextToSpeechProxy::failed()
|
||||
{
|
||||
OnFailure.Broadcast(SoundWave);
|
||||
finish();
|
||||
}
|
||||
|
||||
void UConvaiTextToSpeechProxy::success()
|
||||
{
|
||||
OnSuccess.Broadcast(SoundWave);
|
||||
finish();
|
||||
}
|
||||
|
||||
void UConvaiTextToSpeechProxy::finish()
|
||||
{
|
||||
}
|
||||
|
||||
1272
ConvAI/Convai/Source/Convai/Private/ConvaiUtils.cpp
Normal file
1272
ConvAI/Convai/Source/Convai/Private/ConvaiUtils.cpp
Normal file
File diff suppressed because it is too large
Load Diff
39
ConvAI/Convai/Source/Convai/Private/Mac/ConvaiApple.mm
Normal file
39
ConvAI/Convai/Source/Convai/Private/Mac/ConvaiApple.mm
Normal file
@@ -0,0 +1,39 @@
|
||||
#ifdef __APPLE__
|
||||
#import <AVFoundation/AVFoundation.h>
|
||||
|
||||
bool GetAppleMicPermission()
|
||||
{
|
||||
__block bool permissionGranted = false;
|
||||
|
||||
switch ([AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeAudio])
|
||||
{
|
||||
case AVAuthorizationStatusAuthorized:
|
||||
// Already authorized
|
||||
permissionGranted = true;
|
||||
break;
|
||||
case AVAuthorizationStatusNotDetermined:
|
||||
{
|
||||
dispatch_semaphore_t sem = dispatch_semaphore_create(0);
|
||||
// The user has not yet been asked to grant access to the audio capture device.
|
||||
[AVCaptureDevice requestAccessForMediaType:AVMediaTypeAudio completionHandler:^(BOOL granted)
|
||||
{
|
||||
if (granted)
|
||||
{
|
||||
// Permission granted
|
||||
permissionGranted = true;
|
||||
}
|
||||
dispatch_semaphore_signal(sem);
|
||||
}];
|
||||
dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
|
||||
break;
|
||||
}
|
||||
case AVAuthorizationStatusRestricted:
|
||||
case AVAuthorizationStatusDenied:
|
||||
// The user has previously denied access or can't grant access.
|
||||
permissionGranted = false;
|
||||
break;
|
||||
}
|
||||
|
||||
return permissionGranted;
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,72 @@
|
||||
#if defined(_WIN32) || defined(__ANDROID__)
|
||||
#if defined(_MSC_VER)
|
||||
#pragma warning (disable:4018) // 'expression' : signed/unsigned mismatch
|
||||
#pragma warning (disable:4065) // switch statement contains 'default' but no 'case' labels
|
||||
#pragma warning (disable:4146) // unary minus operator applied to unsigned type, result still unsigned
|
||||
#pragma warning (disable:4244) // 'conversion' conversion from 'type1' to 'type2', possible loss of data
|
||||
#pragma warning (disable:4251) // 'identifier' : class 'type' needs to have dll-interface to be used by clients of class 'type2'
|
||||
#pragma warning (disable:4267) // 'var' : conversion from 'size_t' to 'type', possible loss of data
|
||||
#pragma warning (disable:4305) // 'identifier' : truncation from 'type1' to 'type2'
|
||||
#pragma warning (disable:4307) // 'operator' : integral constant overflow
|
||||
#pragma warning (disable:4309) // 'conversion' : truncation of constant value
|
||||
#pragma warning (disable:4334) // 'operator' : result of 32-bit shift implicitly converted to 64 bits (was 64-bit shift intended?)
|
||||
#pragma warning (disable:4355) // 'this' : used in base member initializer list
|
||||
#pragma warning (disable:4506) // no definition for inline function 'function'
|
||||
#pragma warning (disable:4996) // The compiler encountered a deprecated declaration.
|
||||
#pragma warning (disable:4125) // decimal digit terminates octal escape sequence
|
||||
#pragma warning (disable:4800) // decimal digit terminates octal escape sequence
|
||||
#endif
|
||||
|
||||
// Generated by the gRPC C++ plugin.
|
||||
// If you make any local change, they will be lost.
|
||||
// source: arkit_blend_shapes.proto
|
||||
|
||||
#include "arkit_blend_shapes.pb.h"
|
||||
#include "arkit_blend_shapes.grpc.pb.h"
|
||||
|
||||
#include <functional>
|
||||
#include <grpcpp/impl/codegen/async_stream.h>
|
||||
#include <grpcpp/impl/codegen/async_unary_call.h>
|
||||
#include <grpcpp/impl/codegen/channel_interface.h>
|
||||
#include <grpcpp/impl/codegen/client_unary_call.h>
|
||||
#include <grpcpp/impl/codegen/client_callback.h>
|
||||
#include <grpcpp/impl/codegen/message_allocator.h>
|
||||
#include <grpcpp/impl/codegen/method_handler.h>
|
||||
#include <grpcpp/impl/codegen/rpc_service_method.h>
|
||||
#include <grpcpp/impl/codegen/server_callback.h>
|
||||
#include <grpcpp/impl/codegen/server_callback_handlers.h>
|
||||
#include <grpcpp/impl/codegen/server_context.h>
|
||||
#include <grpcpp/impl/codegen/service_type.h>
|
||||
#include <grpcpp/impl/codegen/sync_stream.h>
|
||||
namespace service {
|
||||
|
||||
} // namespace service
|
||||
|
||||
#else
|
||||
// Generated by the gRPC C++ plugin.
|
||||
// If you make any local change, they will be lost.
|
||||
// source: arkit_blend_shapes.proto
|
||||
|
||||
#include "arkit_blend_shapes.pb.h"
|
||||
#include "arkit_blend_shapes.grpc.pb.h"
|
||||
|
||||
#include <functional>
|
||||
#include <grpcpp/support/async_stream.h>
|
||||
#include <grpcpp/support/async_unary_call.h>
|
||||
#include <grpcpp/impl/codegen/channel_interface.h>
|
||||
#include <grpcpp/impl/codegen/client_unary_call.h>
|
||||
#include <grpcpp/impl/codegen/client_callback.h>
|
||||
#include <grpcpp/impl/codegen/message_allocator.h>
|
||||
#include <grpcpp/impl/codegen/method_handler.h>
|
||||
#include <grpcpp/impl/codegen/rpc_service_method.h>
|
||||
#include <grpcpp/impl/codegen/server_callback.h>
|
||||
#include <grpcpp/impl/codegen/server_callback_handlers.h>
|
||||
#include <grpcpp/impl/codegen/server_context.h>
|
||||
#include <grpcpp/impl/codegen/service_type.h>
|
||||
#include <grpcpp/impl/codegen/sync_stream.h>
|
||||
namespace service {
|
||||
|
||||
} // namespace service
|
||||
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,89 @@
|
||||
#if defined(_WIN32) || defined(__ANDROID__)
|
||||
#if defined(_MSC_VER)
|
||||
#pragma warning (disable:4018) // 'expression' : signed/unsigned mismatch
|
||||
#pragma warning (disable:4065) // switch statement contains 'default' but no 'case' labels
|
||||
#pragma warning (disable:4146) // unary minus operator applied to unsigned type, result still unsigned
|
||||
#pragma warning (disable:4244) // 'conversion' conversion from 'type1' to 'type2', possible loss of data
|
||||
#pragma warning (disable:4251) // 'identifier' : class 'type' needs to have dll-interface to be used by clients of class 'type2'
|
||||
#pragma warning (disable:4267) // 'var' : conversion from 'size_t' to 'type', possible loss of data
|
||||
#pragma warning (disable:4305) // 'identifier' : truncation from 'type1' to 'type2'
|
||||
#pragma warning (disable:4307) // 'operator' : integral constant overflow
|
||||
#pragma warning (disable:4309) // 'conversion' : truncation of constant value
|
||||
#pragma warning (disable:4334) // 'operator' : result of 32-bit shift implicitly converted to 64 bits (was 64-bit shift intended?)
|
||||
#pragma warning (disable:4355) // 'this' : used in base member initializer list
|
||||
#pragma warning (disable:4506) // no definition for inline function 'function'
|
||||
#pragma warning (disable:4996) // The compiler encountered a deprecated declaration.
|
||||
#pragma warning (disable:4125) // decimal digit terminates octal escape sequence
|
||||
#pragma warning (disable:4800) // decimal digit terminates octal escape sequence
|
||||
#endif
|
||||
|
||||
// Generated by the gRPC C++ plugin.
|
||||
// If you make any local change, they will be lost.
|
||||
// source: arkit_blend_shapes.proto
|
||||
#ifndef GRPC_arkit_5fblend_5fshapes_2eproto__INCLUDED
|
||||
#define GRPC_arkit_5fblend_5fshapes_2eproto__INCLUDED
|
||||
|
||||
#include "arkit_blend_shapes.pb.h"
|
||||
|
||||
#include <functional>
|
||||
#include <grpc/impl/codegen/port_platform.h>
|
||||
#include <grpcpp/impl/codegen/async_generic_service.h>
|
||||
#include <grpcpp/impl/codegen/async_stream.h>
|
||||
#include <grpcpp/impl/codegen/async_unary_call.h>
|
||||
#include <grpcpp/impl/codegen/client_callback.h>
|
||||
#include <grpcpp/impl/codegen/client_context.h>
|
||||
#include <grpcpp/impl/codegen/completion_queue.h>
|
||||
#include <grpcpp/impl/codegen/message_allocator.h>
|
||||
#include <grpcpp/impl/codegen/method_handler.h>
|
||||
#include <grpcpp/impl/codegen/proto_utils.h>
|
||||
#include <grpcpp/impl/codegen/rpc_method.h>
|
||||
#include <grpcpp/impl/codegen/server_callback.h>
|
||||
#include <grpcpp/impl/codegen/server_callback_handlers.h>
|
||||
#include <grpcpp/impl/codegen/server_context.h>
|
||||
#include <grpcpp/impl/codegen/service_type.h>
|
||||
#include <grpcpp/impl/codegen/status.h>
|
||||
#include <grpcpp/impl/codegen/stub_options.h>
|
||||
#include <grpcpp/impl/codegen/sync_stream.h>
|
||||
|
||||
namespace service {
|
||||
|
||||
} // namespace service
|
||||
|
||||
|
||||
#endif // GRPC_arkit_5fblend_5fshapes_2eproto__INCLUDED
|
||||
#else
|
||||
// Generated by the gRPC C++ plugin.
|
||||
// If you make any local change, they will be lost.
|
||||
// source: arkit_blend_shapes.proto
|
||||
#ifndef GRPC_arkit_5fblend_5fshapes_2eproto__INCLUDED
|
||||
#define GRPC_arkit_5fblend_5fshapes_2eproto__INCLUDED
|
||||
|
||||
#include "arkit_blend_shapes.pb.h"
|
||||
|
||||
#include <functional>
|
||||
#include <grpcpp/generic/async_generic_service.h>
|
||||
#include <grpcpp/support/async_stream.h>
|
||||
#include <grpcpp/support/async_unary_call.h>
|
||||
#include <grpcpp/impl/codegen/client_callback.h>
|
||||
#include <grpcpp/impl/codegen/client_context.h>
|
||||
#include <grpcpp/impl/codegen/completion_queue.h>
|
||||
#include <grpcpp/impl/codegen/message_allocator.h>
|
||||
#include <grpcpp/impl/codegen/method_handler.h>
|
||||
#include <grpcpp/impl/codegen/proto_utils.h>
|
||||
#include <grpcpp/impl/codegen/rpc_method.h>
|
||||
#include <grpcpp/impl/codegen/server_callback.h>
|
||||
#include <grpcpp/impl/codegen/server_callback_handlers.h>
|
||||
#include <grpcpp/impl/codegen/server_context.h>
|
||||
#include <grpcpp/impl/codegen/service_type.h>
|
||||
#include <grpcpp/impl/codegen/status.h>
|
||||
#include <grpcpp/impl/codegen/stub_options.h>
|
||||
#include <grpcpp/impl/codegen/sync_stream.h>
|
||||
|
||||
namespace service {
|
||||
|
||||
} // namespace service
|
||||
|
||||
|
||||
#endif // GRPC_arkit_5fblend_5fshapes_2eproto__INCLUDED
|
||||
|
||||
#endif
|
||||
4193
ConvAI/Convai/Source/Convai/Private/Proto/arkit_blend_shapes.pb.cc
Normal file
4193
ConvAI/Convai/Source/Convai/Private/Proto/arkit_blend_shapes.pb.cc
Normal file
File diff suppressed because it is too large
Load Diff
4170
ConvAI/Convai/Source/Convai/Private/Proto/arkit_blend_shapes.pb.h
Normal file
4170
ConvAI/Convai/Source/Convai/Private/Proto/arkit_blend_shapes.pb.h
Normal file
File diff suppressed because it is too large
Load Diff
552
ConvAI/Convai/Source/Convai/Private/Proto/service.grpc.pb.cc
Normal file
552
ConvAI/Convai/Source/Convai/Private/Proto/service.grpc.pb.cc
Normal file
@@ -0,0 +1,552 @@
|
||||
#if defined(_WIN32) || defined(__ANDROID__)
|
||||
#if defined(_MSC_VER)
|
||||
#pragma warning (disable:4018) // 'expression' : signed/unsigned mismatch
|
||||
#pragma warning (disable:4065) // switch statement contains 'default' but no 'case' labels
|
||||
#pragma warning (disable:4146) // unary minus operator applied to unsigned type, result still unsigned
|
||||
#pragma warning (disable:4244) // 'conversion' conversion from 'type1' to 'type2', possible loss of data
|
||||
#pragma warning (disable:4251) // 'identifier' : class 'type' needs to have dll-interface to be used by clients of class 'type2'
|
||||
#pragma warning (disable:4267) // 'var' : conversion from 'size_t' to 'type', possible loss of data
|
||||
#pragma warning (disable:4305) // 'identifier' : truncation from 'type1' to 'type2'
|
||||
#pragma warning (disable:4307) // 'operator' : integral constant overflow
|
||||
#pragma warning (disable:4309) // 'conversion' : truncation of constant value
|
||||
#pragma warning (disable:4334) // 'operator' : result of 32-bit shift implicitly converted to 64 bits (was 64-bit shift intended?)
|
||||
#pragma warning (disable:4355) // 'this' : used in base member initializer list
|
||||
#pragma warning (disable:4506) // no definition for inline function 'function'
|
||||
#pragma warning (disable:4996) // The compiler encountered a deprecated declaration.
|
||||
#pragma warning (disable:4125) // decimal digit terminates octal escape sequence
|
||||
#pragma warning (disable:4800) // decimal digit terminates octal escape sequence
|
||||
#endif
|
||||
|
||||
// Generated by the gRPC C++ plugin.
|
||||
// If you make any local change, they will be lost.
|
||||
// source: service.proto
|
||||
|
||||
#include "service.pb.h"
|
||||
#include "service.grpc.pb.h"
|
||||
|
||||
#include <functional>
|
||||
#include <grpcpp/impl/codegen/async_stream.h>
|
||||
#include <grpcpp/impl/codegen/async_unary_call.h>
|
||||
#include <grpcpp/impl/codegen/channel_interface.h>
|
||||
#include <grpcpp/impl/codegen/client_unary_call.h>
|
||||
#include <grpcpp/impl/codegen/client_callback.h>
|
||||
#include <grpcpp/impl/codegen/message_allocator.h>
|
||||
#include <grpcpp/impl/codegen/method_handler.h>
|
||||
#include <grpcpp/impl/codegen/rpc_service_method.h>
|
||||
#include <grpcpp/impl/codegen/server_callback.h>
|
||||
#include <grpcpp/impl/codegen/server_callback_handlers.h>
|
||||
#include <grpcpp/impl/codegen/server_context.h>
|
||||
#include <grpcpp/impl/codegen/service_type.h>
|
||||
#include <grpcpp/impl/codegen/sync_stream.h>
|
||||
namespace service {
|
||||
|
||||
static const char* ConvaiService_method_names[] = {
|
||||
"/service.ConvaiService/Hello",
|
||||
"/service.ConvaiService/HelloStream",
|
||||
"/service.ConvaiService/SpeechToText",
|
||||
"/service.ConvaiService/GetResponse",
|
||||
"/service.ConvaiService/GetResponseSingle",
|
||||
"/service.ConvaiService/SubmitFeedback",
|
||||
};
|
||||
|
||||
std::unique_ptr< ConvaiService::Stub> ConvaiService::NewStub(const std::shared_ptr< ::grpc::ChannelInterface>& channel, const ::grpc::StubOptions& options) {
|
||||
(void)options;
|
||||
std::unique_ptr< ConvaiService::Stub> stub(new ConvaiService::Stub(channel));
|
||||
return stub;
|
||||
}
|
||||
|
||||
ConvaiService::Stub::Stub(const std::shared_ptr< ::grpc::ChannelInterface>& channel)
|
||||
: channel_(channel), rpcmethod_Hello_(ConvaiService_method_names[0], ::grpc::internal::RpcMethod::NORMAL_RPC, channel)
|
||||
, rpcmethod_HelloStream_(ConvaiService_method_names[1], ::grpc::internal::RpcMethod::BIDI_STREAMING, channel)
|
||||
, rpcmethod_SpeechToText_(ConvaiService_method_names[2], ::grpc::internal::RpcMethod::BIDI_STREAMING, channel)
|
||||
, rpcmethod_GetResponse_(ConvaiService_method_names[3], ::grpc::internal::RpcMethod::BIDI_STREAMING, channel)
|
||||
, rpcmethod_GetResponseSingle_(ConvaiService_method_names[4], ::grpc::internal::RpcMethod::SERVER_STREAMING, channel)
|
||||
, rpcmethod_SubmitFeedback_(ConvaiService_method_names[5], ::grpc::internal::RpcMethod::NORMAL_RPC, channel)
|
||||
{}
|
||||
|
||||
::grpc::Status ConvaiService::Stub::Hello(::grpc::ClientContext* context, const ::service::HelloRequest& request, ::service::HelloResponse* response) {
|
||||
return ::grpc::internal::BlockingUnaryCall< ::service::HelloRequest, ::service::HelloResponse, ::grpc::protobuf::MessageLite, ::grpc::protobuf::MessageLite>(channel_.get(), rpcmethod_Hello_, context, request, response);
|
||||
}
|
||||
|
||||
void ConvaiService::Stub::experimental_async::Hello(::grpc::ClientContext* context, const ::service::HelloRequest* request, ::service::HelloResponse* response, std::function<void(::grpc::Status)> f) {
|
||||
::grpc::internal::CallbackUnaryCall< ::service::HelloRequest, ::service::HelloResponse, ::grpc::protobuf::MessageLite, ::grpc::protobuf::MessageLite>(stub_->channel_.get(), stub_->rpcmethod_Hello_, context, request, response, std::move(f));
|
||||
}
|
||||
|
||||
void ConvaiService::Stub::experimental_async::Hello(::grpc::ClientContext* context, const ::service::HelloRequest* request, ::service::HelloResponse* response, ::grpc::experimental::ClientUnaryReactor* reactor) {
|
||||
::grpc::internal::ClientCallbackUnaryFactory::Create< ::grpc::protobuf::MessageLite, ::grpc::protobuf::MessageLite>(stub_->channel_.get(), stub_->rpcmethod_Hello_, context, request, response, reactor);
|
||||
}
|
||||
|
||||
::grpc::ClientAsyncResponseReader< ::service::HelloResponse>* ConvaiService::Stub::PrepareAsyncHelloRaw(::grpc::ClientContext* context, const ::service::HelloRequest& request, ::grpc::CompletionQueue* cq) {
|
||||
return ::grpc::internal::ClientAsyncResponseReaderHelper::Create< ::service::HelloResponse, ::service::HelloRequest, ::grpc::protobuf::MessageLite, ::grpc::protobuf::MessageLite>(channel_.get(), cq, rpcmethod_Hello_, context, request);
|
||||
}
|
||||
|
||||
::grpc::ClientAsyncResponseReader< ::service::HelloResponse>* ConvaiService::Stub::AsyncHelloRaw(::grpc::ClientContext* context, const ::service::HelloRequest& request, ::grpc::CompletionQueue* cq) {
|
||||
auto* result =
|
||||
this->PrepareAsyncHelloRaw(context, request, cq);
|
||||
result->StartCall();
|
||||
return result;
|
||||
}
|
||||
|
||||
::grpc::ClientReaderWriter< ::service::HelloRequest, ::service::HelloResponse>* ConvaiService::Stub::HelloStreamRaw(::grpc::ClientContext* context) {
|
||||
return ::grpc::internal::ClientReaderWriterFactory< ::service::HelloRequest, ::service::HelloResponse>::Create(channel_.get(), rpcmethod_HelloStream_, context);
|
||||
}
|
||||
|
||||
void ConvaiService::Stub::experimental_async::HelloStream(::grpc::ClientContext* context, ::grpc::experimental::ClientBidiReactor< ::service::HelloRequest,::service::HelloResponse>* reactor) {
|
||||
::grpc::internal::ClientCallbackReaderWriterFactory< ::service::HelloRequest,::service::HelloResponse>::Create(stub_->channel_.get(), stub_->rpcmethod_HelloStream_, context, reactor);
|
||||
}
|
||||
|
||||
::grpc::ClientAsyncReaderWriter< ::service::HelloRequest, ::service::HelloResponse>* ConvaiService::Stub::AsyncHelloStreamRaw(::grpc::ClientContext* context, ::grpc::CompletionQueue* cq, void* tag) {
|
||||
return ::grpc::internal::ClientAsyncReaderWriterFactory< ::service::HelloRequest, ::service::HelloResponse>::Create(channel_.get(), cq, rpcmethod_HelloStream_, context, true, tag);
|
||||
}
|
||||
|
||||
::grpc::ClientAsyncReaderWriter< ::service::HelloRequest, ::service::HelloResponse>* ConvaiService::Stub::PrepareAsyncHelloStreamRaw(::grpc::ClientContext* context, ::grpc::CompletionQueue* cq) {
|
||||
return ::grpc::internal::ClientAsyncReaderWriterFactory< ::service::HelloRequest, ::service::HelloResponse>::Create(channel_.get(), cq, rpcmethod_HelloStream_, context, false, nullptr);
|
||||
}
|
||||
|
||||
::grpc::ClientReaderWriter< ::service::STTRequest, ::service::STTResponse>* ConvaiService::Stub::SpeechToTextRaw(::grpc::ClientContext* context) {
|
||||
return ::grpc::internal::ClientReaderWriterFactory< ::service::STTRequest, ::service::STTResponse>::Create(channel_.get(), rpcmethod_SpeechToText_, context);
|
||||
}
|
||||
|
||||
void ConvaiService::Stub::experimental_async::SpeechToText(::grpc::ClientContext* context, ::grpc::experimental::ClientBidiReactor< ::service::STTRequest,::service::STTResponse>* reactor) {
|
||||
::grpc::internal::ClientCallbackReaderWriterFactory< ::service::STTRequest,::service::STTResponse>::Create(stub_->channel_.get(), stub_->rpcmethod_SpeechToText_, context, reactor);
|
||||
}
|
||||
|
||||
::grpc::ClientAsyncReaderWriter< ::service::STTRequest, ::service::STTResponse>* ConvaiService::Stub::AsyncSpeechToTextRaw(::grpc::ClientContext* context, ::grpc::CompletionQueue* cq, void* tag) {
|
||||
return ::grpc::internal::ClientAsyncReaderWriterFactory< ::service::STTRequest, ::service::STTResponse>::Create(channel_.get(), cq, rpcmethod_SpeechToText_, context, true, tag);
|
||||
}
|
||||
|
||||
::grpc::ClientAsyncReaderWriter< ::service::STTRequest, ::service::STTResponse>* ConvaiService::Stub::PrepareAsyncSpeechToTextRaw(::grpc::ClientContext* context, ::grpc::CompletionQueue* cq) {
|
||||
return ::grpc::internal::ClientAsyncReaderWriterFactory< ::service::STTRequest, ::service::STTResponse>::Create(channel_.get(), cq, rpcmethod_SpeechToText_, context, false, nullptr);
|
||||
}
|
||||
|
||||
::grpc::ClientReaderWriter< ::service::GetResponseRequest, ::service::GetResponseResponse>* ConvaiService::Stub::GetResponseRaw(::grpc::ClientContext* context) {
|
||||
return ::grpc::internal::ClientReaderWriterFactory< ::service::GetResponseRequest, ::service::GetResponseResponse>::Create(channel_.get(), rpcmethod_GetResponse_, context);
|
||||
}
|
||||
|
||||
void ConvaiService::Stub::experimental_async::GetResponse(::grpc::ClientContext* context, ::grpc::experimental::ClientBidiReactor< ::service::GetResponseRequest,::service::GetResponseResponse>* reactor) {
|
||||
::grpc::internal::ClientCallbackReaderWriterFactory< ::service::GetResponseRequest,::service::GetResponseResponse>::Create(stub_->channel_.get(), stub_->rpcmethod_GetResponse_, context, reactor);
|
||||
}
|
||||
|
||||
::grpc::ClientAsyncReaderWriter< ::service::GetResponseRequest, ::service::GetResponseResponse>* ConvaiService::Stub::AsyncGetResponseRaw(::grpc::ClientContext* context, ::grpc::CompletionQueue* cq, void* tag) {
|
||||
return ::grpc::internal::ClientAsyncReaderWriterFactory< ::service::GetResponseRequest, ::service::GetResponseResponse>::Create(channel_.get(), cq, rpcmethod_GetResponse_, context, true, tag);
|
||||
}
|
||||
|
||||
::grpc::ClientAsyncReaderWriter< ::service::GetResponseRequest, ::service::GetResponseResponse>* ConvaiService::Stub::PrepareAsyncGetResponseRaw(::grpc::ClientContext* context, ::grpc::CompletionQueue* cq) {
|
||||
return ::grpc::internal::ClientAsyncReaderWriterFactory< ::service::GetResponseRequest, ::service::GetResponseResponse>::Create(channel_.get(), cq, rpcmethod_GetResponse_, context, false, nullptr);
|
||||
}
|
||||
|
||||
::grpc::ClientReader< ::service::GetResponseResponse>* ConvaiService::Stub::GetResponseSingleRaw(::grpc::ClientContext* context, const ::service::GetResponseRequestSingle& request) {
|
||||
return ::grpc::internal::ClientReaderFactory< ::service::GetResponseResponse>::Create(channel_.get(), rpcmethod_GetResponseSingle_, context, request);
|
||||
}
|
||||
|
||||
void ConvaiService::Stub::experimental_async::GetResponseSingle(::grpc::ClientContext* context, ::service::GetResponseRequestSingle* request, ::grpc::experimental::ClientReadReactor< ::service::GetResponseResponse>* reactor) {
|
||||
::grpc::internal::ClientCallbackReaderFactory< ::service::GetResponseResponse>::Create(stub_->channel_.get(), stub_->rpcmethod_GetResponseSingle_, context, request, reactor);
|
||||
}
|
||||
|
||||
::grpc::ClientAsyncReader< ::service::GetResponseResponse>* ConvaiService::Stub::AsyncGetResponseSingleRaw(::grpc::ClientContext* context, const ::service::GetResponseRequestSingle& request, ::grpc::CompletionQueue* cq, void* tag) {
|
||||
return ::grpc::internal::ClientAsyncReaderFactory< ::service::GetResponseResponse>::Create(channel_.get(), cq, rpcmethod_GetResponseSingle_, context, request, true, tag);
|
||||
}
|
||||
|
||||
::grpc::ClientAsyncReader< ::service::GetResponseResponse>* ConvaiService::Stub::PrepareAsyncGetResponseSingleRaw(::grpc::ClientContext* context, const ::service::GetResponseRequestSingle& request, ::grpc::CompletionQueue* cq) {
|
||||
return ::grpc::internal::ClientAsyncReaderFactory< ::service::GetResponseResponse>::Create(channel_.get(), cq, rpcmethod_GetResponseSingle_, context, request, false, nullptr);
|
||||
}
|
||||
|
||||
::grpc::Status ConvaiService::Stub::SubmitFeedback(::grpc::ClientContext* context, const ::service::FeedbackRequest& request, ::service::FeedbackResponse* response) {
|
||||
return ::grpc::internal::BlockingUnaryCall< ::service::FeedbackRequest, ::service::FeedbackResponse, ::grpc::protobuf::MessageLite, ::grpc::protobuf::MessageLite>(channel_.get(), rpcmethod_SubmitFeedback_, context, request, response);
|
||||
}
|
||||
|
||||
void ConvaiService::Stub::experimental_async::SubmitFeedback(::grpc::ClientContext* context, const ::service::FeedbackRequest* request, ::service::FeedbackResponse* response, std::function<void(::grpc::Status)> f) {
|
||||
::grpc::internal::CallbackUnaryCall< ::service::FeedbackRequest, ::service::FeedbackResponse, ::grpc::protobuf::MessageLite, ::grpc::protobuf::MessageLite>(stub_->channel_.get(), stub_->rpcmethod_SubmitFeedback_, context, request, response, std::move(f));
|
||||
}
|
||||
|
||||
void ConvaiService::Stub::experimental_async::SubmitFeedback(::grpc::ClientContext* context, const ::service::FeedbackRequest* request, ::service::FeedbackResponse* response, ::grpc::experimental::ClientUnaryReactor* reactor) {
|
||||
::grpc::internal::ClientCallbackUnaryFactory::Create< ::grpc::protobuf::MessageLite, ::grpc::protobuf::MessageLite>(stub_->channel_.get(), stub_->rpcmethod_SubmitFeedback_, context, request, response, reactor);
|
||||
}
|
||||
|
||||
::grpc::ClientAsyncResponseReader< ::service::FeedbackResponse>* ConvaiService::Stub::PrepareAsyncSubmitFeedbackRaw(::grpc::ClientContext* context, const ::service::FeedbackRequest& request, ::grpc::CompletionQueue* cq) {
|
||||
return ::grpc::internal::ClientAsyncResponseReaderHelper::Create< ::service::FeedbackResponse, ::service::FeedbackRequest, ::grpc::protobuf::MessageLite, ::grpc::protobuf::MessageLite>(channel_.get(), cq, rpcmethod_SubmitFeedback_, context, request);
|
||||
}
|
||||
|
||||
::grpc::ClientAsyncResponseReader< ::service::FeedbackResponse>* ConvaiService::Stub::AsyncSubmitFeedbackRaw(::grpc::ClientContext* context, const ::service::FeedbackRequest& request, ::grpc::CompletionQueue* cq) {
|
||||
auto* result =
|
||||
this->PrepareAsyncSubmitFeedbackRaw(context, request, cq);
|
||||
result->StartCall();
|
||||
return result;
|
||||
}
|
||||
|
||||
ConvaiService::Service::Service() {
|
||||
AddMethod(new ::grpc::internal::RpcServiceMethod(
|
||||
ConvaiService_method_names[0],
|
||||
::grpc::internal::RpcMethod::NORMAL_RPC,
|
||||
new ::grpc::internal::RpcMethodHandler< ConvaiService::Service, ::service::HelloRequest, ::service::HelloResponse, ::grpc::protobuf::MessageLite, ::grpc::protobuf::MessageLite>(
|
||||
[](ConvaiService::Service* service,
|
||||
::grpc::ServerContext* ctx,
|
||||
const ::service::HelloRequest* req,
|
||||
::service::HelloResponse* resp) {
|
||||
return service->Hello(ctx, req, resp);
|
||||
}, this)));
|
||||
AddMethod(new ::grpc::internal::RpcServiceMethod(
|
||||
ConvaiService_method_names[1],
|
||||
::grpc::internal::RpcMethod::BIDI_STREAMING,
|
||||
new ::grpc::internal::BidiStreamingHandler< ConvaiService::Service, ::service::HelloRequest, ::service::HelloResponse>(
|
||||
[](ConvaiService::Service* service,
|
||||
::grpc::ServerContext* ctx,
|
||||
::grpc::ServerReaderWriter<::service::HelloResponse,
|
||||
::service::HelloRequest>* stream) {
|
||||
return service->HelloStream(ctx, stream);
|
||||
}, this)));
|
||||
AddMethod(new ::grpc::internal::RpcServiceMethod(
|
||||
ConvaiService_method_names[2],
|
||||
::grpc::internal::RpcMethod::BIDI_STREAMING,
|
||||
new ::grpc::internal::BidiStreamingHandler< ConvaiService::Service, ::service::STTRequest, ::service::STTResponse>(
|
||||
[](ConvaiService::Service* service,
|
||||
::grpc::ServerContext* ctx,
|
||||
::grpc::ServerReaderWriter<::service::STTResponse,
|
||||
::service::STTRequest>* stream) {
|
||||
return service->SpeechToText(ctx, stream);
|
||||
}, this)));
|
||||
AddMethod(new ::grpc::internal::RpcServiceMethod(
|
||||
ConvaiService_method_names[3],
|
||||
::grpc::internal::RpcMethod::BIDI_STREAMING,
|
||||
new ::grpc::internal::BidiStreamingHandler< ConvaiService::Service, ::service::GetResponseRequest, ::service::GetResponseResponse>(
|
||||
[](ConvaiService::Service* service,
|
||||
::grpc::ServerContext* ctx,
|
||||
::grpc::ServerReaderWriter<::service::GetResponseResponse,
|
||||
::service::GetResponseRequest>* stream) {
|
||||
return service->GetResponse(ctx, stream);
|
||||
}, this)));
|
||||
AddMethod(new ::grpc::internal::RpcServiceMethod(
|
||||
ConvaiService_method_names[4],
|
||||
::grpc::internal::RpcMethod::SERVER_STREAMING,
|
||||
new ::grpc::internal::ServerStreamingHandler< ConvaiService::Service, ::service::GetResponseRequestSingle, ::service::GetResponseResponse>(
|
||||
[](ConvaiService::Service* service,
|
||||
::grpc::ServerContext* ctx,
|
||||
const ::service::GetResponseRequestSingle* req,
|
||||
::grpc::ServerWriter<::service::GetResponseResponse>* writer) {
|
||||
return service->GetResponseSingle(ctx, req, writer);
|
||||
}, this)));
|
||||
AddMethod(new ::grpc::internal::RpcServiceMethod(
|
||||
ConvaiService_method_names[5],
|
||||
::grpc::internal::RpcMethod::NORMAL_RPC,
|
||||
new ::grpc::internal::RpcMethodHandler< ConvaiService::Service, ::service::FeedbackRequest, ::service::FeedbackResponse, ::grpc::protobuf::MessageLite, ::grpc::protobuf::MessageLite>(
|
||||
[](ConvaiService::Service* service,
|
||||
::grpc::ServerContext* ctx,
|
||||
const ::service::FeedbackRequest* req,
|
||||
::service::FeedbackResponse* resp) {
|
||||
return service->SubmitFeedback(ctx, req, resp);
|
||||
}, this)));
|
||||
}
|
||||
|
||||
ConvaiService::Service::~Service() {
|
||||
}
|
||||
|
||||
::grpc::Status ConvaiService::Service::Hello(::grpc::ServerContext* context, const ::service::HelloRequest* request, ::service::HelloResponse* response) {
|
||||
(void) context;
|
||||
(void) request;
|
||||
(void) response;
|
||||
return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "");
|
||||
}
|
||||
|
||||
::grpc::Status ConvaiService::Service::HelloStream(::grpc::ServerContext* context, ::grpc::ServerReaderWriter< ::service::HelloResponse, ::service::HelloRequest>* stream) {
|
||||
(void) context;
|
||||
(void) stream;
|
||||
return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "");
|
||||
}
|
||||
|
||||
::grpc::Status ConvaiService::Service::SpeechToText(::grpc::ServerContext* context, ::grpc::ServerReaderWriter< ::service::STTResponse, ::service::STTRequest>* stream) {
|
||||
(void) context;
|
||||
(void) stream;
|
||||
return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "");
|
||||
}
|
||||
|
||||
::grpc::Status ConvaiService::Service::GetResponse(::grpc::ServerContext* context, ::grpc::ServerReaderWriter< ::service::GetResponseResponse, ::service::GetResponseRequest>* stream) {
|
||||
(void) context;
|
||||
(void) stream;
|
||||
return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "");
|
||||
}
|
||||
|
||||
::grpc::Status ConvaiService::Service::GetResponseSingle(::grpc::ServerContext* context, const ::service::GetResponseRequestSingle* request, ::grpc::ServerWriter< ::service::GetResponseResponse>* writer) {
|
||||
(void) context;
|
||||
(void) request;
|
||||
(void) writer;
|
||||
return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "");
|
||||
}
|
||||
|
||||
::grpc::Status ConvaiService::Service::SubmitFeedback(::grpc::ServerContext* context, const ::service::FeedbackRequest* request, ::service::FeedbackResponse* response) {
|
||||
(void) context;
|
||||
(void) request;
|
||||
(void) response;
|
||||
return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "");
|
||||
}
|
||||
|
||||
|
||||
} // namespace service
|
||||
|
||||
#else
|
||||
// Generated by the gRPC C++ plugin.
|
||||
// If you make any local change, they will be lost.
|
||||
// source: service.proto
|
||||
|
||||
#include "service.pb.h"
|
||||
#include "service.grpc.pb.h"
|
||||
|
||||
#include <functional>
|
||||
#include <grpcpp/support/async_stream.h>
|
||||
#include <grpcpp/support/async_unary_call.h>
|
||||
#include <grpcpp/impl/codegen/channel_interface.h>
|
||||
#include <grpcpp/impl/codegen/client_unary_call.h>
|
||||
#include <grpcpp/impl/codegen/client_callback.h>
|
||||
#include <grpcpp/impl/codegen/message_allocator.h>
|
||||
#include <grpcpp/impl/codegen/method_handler.h>
|
||||
#include <grpcpp/impl/codegen/rpc_service_method.h>
|
||||
#include <grpcpp/impl/codegen/server_callback.h>
|
||||
#include <grpcpp/impl/codegen/server_callback_handlers.h>
|
||||
#include <grpcpp/impl/codegen/server_context.h>
|
||||
#include <grpcpp/impl/codegen/service_type.h>
|
||||
#include <grpcpp/impl/codegen/sync_stream.h>
|
||||
namespace service {
|
||||
|
||||
static const char* ConvaiService_method_names[] = {
|
||||
"/service.ConvaiService/Hello",
|
||||
"/service.ConvaiService/HelloStream",
|
||||
"/service.ConvaiService/SpeechToText",
|
||||
"/service.ConvaiService/GetResponse",
|
||||
"/service.ConvaiService/GetResponseSingle",
|
||||
"/service.ConvaiService/SubmitFeedback",
|
||||
};
|
||||
|
||||
std::unique_ptr< ConvaiService::Stub> ConvaiService::NewStub(const std::shared_ptr< ::grpc::ChannelInterface>& channel, const ::grpc::StubOptions& options) {
|
||||
(void)options;
|
||||
std::unique_ptr< ConvaiService::Stub> stub(new ConvaiService::Stub(channel, options));
|
||||
return stub;
|
||||
}
|
||||
|
||||
ConvaiService::Stub::Stub(const std::shared_ptr< ::grpc::ChannelInterface>& channel, const ::grpc::StubOptions& options)
|
||||
: channel_(channel), rpcmethod_Hello_(ConvaiService_method_names[0], options.suffix_for_stats(),::grpc::internal::RpcMethod::NORMAL_RPC, channel)
|
||||
, rpcmethod_HelloStream_(ConvaiService_method_names[1], options.suffix_for_stats(),::grpc::internal::RpcMethod::BIDI_STREAMING, channel)
|
||||
, rpcmethod_SpeechToText_(ConvaiService_method_names[2], options.suffix_for_stats(),::grpc::internal::RpcMethod::BIDI_STREAMING, channel)
|
||||
, rpcmethod_GetResponse_(ConvaiService_method_names[3], options.suffix_for_stats(),::grpc::internal::RpcMethod::BIDI_STREAMING, channel)
|
||||
, rpcmethod_GetResponseSingle_(ConvaiService_method_names[4], options.suffix_for_stats(),::grpc::internal::RpcMethod::SERVER_STREAMING, channel)
|
||||
, rpcmethod_SubmitFeedback_(ConvaiService_method_names[5], options.suffix_for_stats(),::grpc::internal::RpcMethod::NORMAL_RPC, channel)
|
||||
{}
|
||||
|
||||
::grpc::Status ConvaiService::Stub::Hello(::grpc::ClientContext* context, const ::service::HelloRequest& request, ::service::HelloResponse* response) {
|
||||
return ::grpc::internal::BlockingUnaryCall< ::service::HelloRequest, ::service::HelloResponse, ::grpc::protobuf::MessageLite, ::grpc::protobuf::MessageLite>(channel_.get(), rpcmethod_Hello_, context, request, response);
|
||||
}
|
||||
|
||||
void ConvaiService::Stub::async::Hello(::grpc::ClientContext* context, const ::service::HelloRequest* request, ::service::HelloResponse* response, std::function<void(::grpc::Status)> f) {
|
||||
::grpc::internal::CallbackUnaryCall< ::service::HelloRequest, ::service::HelloResponse, ::grpc::protobuf::MessageLite, ::grpc::protobuf::MessageLite>(stub_->channel_.get(), stub_->rpcmethod_Hello_, context, request, response, std::move(f));
|
||||
}
|
||||
|
||||
void ConvaiService::Stub::async::Hello(::grpc::ClientContext* context, const ::service::HelloRequest* request, ::service::HelloResponse* response, ::grpc::ClientUnaryReactor* reactor) {
|
||||
::grpc::internal::ClientCallbackUnaryFactory::Create< ::grpc::protobuf::MessageLite, ::grpc::protobuf::MessageLite>(stub_->channel_.get(), stub_->rpcmethod_Hello_, context, request, response, reactor);
|
||||
}
|
||||
|
||||
::grpc::ClientAsyncResponseReader< ::service::HelloResponse>* ConvaiService::Stub::PrepareAsyncHelloRaw(::grpc::ClientContext* context, const ::service::HelloRequest& request, ::grpc::CompletionQueue* cq) {
|
||||
return ::grpc::internal::ClientAsyncResponseReaderHelper::Create< ::service::HelloResponse, ::service::HelloRequest, ::grpc::protobuf::MessageLite, ::grpc::protobuf::MessageLite>(channel_.get(), cq, rpcmethod_Hello_, context, request);
|
||||
}
|
||||
|
||||
::grpc::ClientAsyncResponseReader< ::service::HelloResponse>* ConvaiService::Stub::AsyncHelloRaw(::grpc::ClientContext* context, const ::service::HelloRequest& request, ::grpc::CompletionQueue* cq) {
|
||||
auto* result =
|
||||
this->PrepareAsyncHelloRaw(context, request, cq);
|
||||
result->StartCall();
|
||||
return result;
|
||||
}
|
||||
|
||||
::grpc::ClientReaderWriter< ::service::HelloRequest, ::service::HelloResponse>* ConvaiService::Stub::HelloStreamRaw(::grpc::ClientContext* context) {
|
||||
return ::grpc::internal::ClientReaderWriterFactory< ::service::HelloRequest, ::service::HelloResponse>::Create(channel_.get(), rpcmethod_HelloStream_, context);
|
||||
}
|
||||
|
||||
void ConvaiService::Stub::async::HelloStream(::grpc::ClientContext* context, ::grpc::ClientBidiReactor< ::service::HelloRequest,::service::HelloResponse>* reactor) {
|
||||
::grpc::internal::ClientCallbackReaderWriterFactory< ::service::HelloRequest,::service::HelloResponse>::Create(stub_->channel_.get(), stub_->rpcmethod_HelloStream_, context, reactor);
|
||||
}
|
||||
|
||||
::grpc::ClientAsyncReaderWriter< ::service::HelloRequest, ::service::HelloResponse>* ConvaiService::Stub::AsyncHelloStreamRaw(::grpc::ClientContext* context, ::grpc::CompletionQueue* cq, void* tag) {
|
||||
return ::grpc::internal::ClientAsyncReaderWriterFactory< ::service::HelloRequest, ::service::HelloResponse>::Create(channel_.get(), cq, rpcmethod_HelloStream_, context, true, tag);
|
||||
}
|
||||
|
||||
::grpc::ClientAsyncReaderWriter< ::service::HelloRequest, ::service::HelloResponse>* ConvaiService::Stub::PrepareAsyncHelloStreamRaw(::grpc::ClientContext* context, ::grpc::CompletionQueue* cq) {
|
||||
return ::grpc::internal::ClientAsyncReaderWriterFactory< ::service::HelloRequest, ::service::HelloResponse>::Create(channel_.get(), cq, rpcmethod_HelloStream_, context, false, nullptr);
|
||||
}
|
||||
|
||||
::grpc::ClientReaderWriter< ::service::STTRequest, ::service::STTResponse>* ConvaiService::Stub::SpeechToTextRaw(::grpc::ClientContext* context) {
|
||||
return ::grpc::internal::ClientReaderWriterFactory< ::service::STTRequest, ::service::STTResponse>::Create(channel_.get(), rpcmethod_SpeechToText_, context);
|
||||
}
|
||||
|
||||
void ConvaiService::Stub::async::SpeechToText(::grpc::ClientContext* context, ::grpc::ClientBidiReactor< ::service::STTRequest,::service::STTResponse>* reactor) {
|
||||
::grpc::internal::ClientCallbackReaderWriterFactory< ::service::STTRequest,::service::STTResponse>::Create(stub_->channel_.get(), stub_->rpcmethod_SpeechToText_, context, reactor);
|
||||
}
|
||||
|
||||
::grpc::ClientAsyncReaderWriter< ::service::STTRequest, ::service::STTResponse>* ConvaiService::Stub::AsyncSpeechToTextRaw(::grpc::ClientContext* context, ::grpc::CompletionQueue* cq, void* tag) {
|
||||
return ::grpc::internal::ClientAsyncReaderWriterFactory< ::service::STTRequest, ::service::STTResponse>::Create(channel_.get(), cq, rpcmethod_SpeechToText_, context, true, tag);
|
||||
}
|
||||
|
||||
::grpc::ClientAsyncReaderWriter< ::service::STTRequest, ::service::STTResponse>* ConvaiService::Stub::PrepareAsyncSpeechToTextRaw(::grpc::ClientContext* context, ::grpc::CompletionQueue* cq) {
|
||||
return ::grpc::internal::ClientAsyncReaderWriterFactory< ::service::STTRequest, ::service::STTResponse>::Create(channel_.get(), cq, rpcmethod_SpeechToText_, context, false, nullptr);
|
||||
}
|
||||
|
||||
::grpc::ClientReaderWriter< ::service::GetResponseRequest, ::service::GetResponseResponse>* ConvaiService::Stub::GetResponseRaw(::grpc::ClientContext* context) {
|
||||
return ::grpc::internal::ClientReaderWriterFactory< ::service::GetResponseRequest, ::service::GetResponseResponse>::Create(channel_.get(), rpcmethod_GetResponse_, context);
|
||||
}
|
||||
|
||||
void ConvaiService::Stub::async::GetResponse(::grpc::ClientContext* context, ::grpc::ClientBidiReactor< ::service::GetResponseRequest,::service::GetResponseResponse>* reactor) {
|
||||
::grpc::internal::ClientCallbackReaderWriterFactory< ::service::GetResponseRequest,::service::GetResponseResponse>::Create(stub_->channel_.get(), stub_->rpcmethod_GetResponse_, context, reactor);
|
||||
}
|
||||
|
||||
::grpc::ClientAsyncReaderWriter< ::service::GetResponseRequest, ::service::GetResponseResponse>* ConvaiService::Stub::AsyncGetResponseRaw(::grpc::ClientContext* context, ::grpc::CompletionQueue* cq, void* tag) {
|
||||
return ::grpc::internal::ClientAsyncReaderWriterFactory< ::service::GetResponseRequest, ::service::GetResponseResponse>::Create(channel_.get(), cq, rpcmethod_GetResponse_, context, true, tag);
|
||||
}
|
||||
|
||||
::grpc::ClientAsyncReaderWriter< ::service::GetResponseRequest, ::service::GetResponseResponse>* ConvaiService::Stub::PrepareAsyncGetResponseRaw(::grpc::ClientContext* context, ::grpc::CompletionQueue* cq) {
|
||||
return ::grpc::internal::ClientAsyncReaderWriterFactory< ::service::GetResponseRequest, ::service::GetResponseResponse>::Create(channel_.get(), cq, rpcmethod_GetResponse_, context, false, nullptr);
|
||||
}
|
||||
|
||||
::grpc::ClientReader< ::service::GetResponseResponse>* ConvaiService::Stub::GetResponseSingleRaw(::grpc::ClientContext* context, const ::service::GetResponseRequestSingle& request) {
|
||||
return ::grpc::internal::ClientReaderFactory< ::service::GetResponseResponse>::Create(channel_.get(), rpcmethod_GetResponseSingle_, context, request);
|
||||
}
|
||||
|
||||
void ConvaiService::Stub::async::GetResponseSingle(::grpc::ClientContext* context, const ::service::GetResponseRequestSingle* request, ::grpc::ClientReadReactor< ::service::GetResponseResponse>* reactor) {
|
||||
::grpc::internal::ClientCallbackReaderFactory< ::service::GetResponseResponse>::Create(stub_->channel_.get(), stub_->rpcmethod_GetResponseSingle_, context, request, reactor);
|
||||
}
|
||||
|
||||
::grpc::ClientAsyncReader< ::service::GetResponseResponse>* ConvaiService::Stub::AsyncGetResponseSingleRaw(::grpc::ClientContext* context, const ::service::GetResponseRequestSingle& request, ::grpc::CompletionQueue* cq, void* tag) {
|
||||
return ::grpc::internal::ClientAsyncReaderFactory< ::service::GetResponseResponse>::Create(channel_.get(), cq, rpcmethod_GetResponseSingle_, context, request, true, tag);
|
||||
}
|
||||
|
||||
::grpc::ClientAsyncReader< ::service::GetResponseResponse>* ConvaiService::Stub::PrepareAsyncGetResponseSingleRaw(::grpc::ClientContext* context, const ::service::GetResponseRequestSingle& request, ::grpc::CompletionQueue* cq) {
|
||||
return ::grpc::internal::ClientAsyncReaderFactory< ::service::GetResponseResponse>::Create(channel_.get(), cq, rpcmethod_GetResponseSingle_, context, request, false, nullptr);
|
||||
}
|
||||
|
||||
::grpc::Status ConvaiService::Stub::SubmitFeedback(::grpc::ClientContext* context, const ::service::FeedbackRequest& request, ::service::FeedbackResponse* response) {
|
||||
return ::grpc::internal::BlockingUnaryCall< ::service::FeedbackRequest, ::service::FeedbackResponse, ::grpc::protobuf::MessageLite, ::grpc::protobuf::MessageLite>(channel_.get(), rpcmethod_SubmitFeedback_, context, request, response);
|
||||
}
|
||||
|
||||
void ConvaiService::Stub::async::SubmitFeedback(::grpc::ClientContext* context, const ::service::FeedbackRequest* request, ::service::FeedbackResponse* response, std::function<void(::grpc::Status)> f) {
|
||||
::grpc::internal::CallbackUnaryCall< ::service::FeedbackRequest, ::service::FeedbackResponse, ::grpc::protobuf::MessageLite, ::grpc::protobuf::MessageLite>(stub_->channel_.get(), stub_->rpcmethod_SubmitFeedback_, context, request, response, std::move(f));
|
||||
}
|
||||
|
||||
void ConvaiService::Stub::async::SubmitFeedback(::grpc::ClientContext* context, const ::service::FeedbackRequest* request, ::service::FeedbackResponse* response, ::grpc::ClientUnaryReactor* reactor) {
|
||||
::grpc::internal::ClientCallbackUnaryFactory::Create< ::grpc::protobuf::MessageLite, ::grpc::protobuf::MessageLite>(stub_->channel_.get(), stub_->rpcmethod_SubmitFeedback_, context, request, response, reactor);
|
||||
}
|
||||
|
||||
::grpc::ClientAsyncResponseReader< ::service::FeedbackResponse>* ConvaiService::Stub::PrepareAsyncSubmitFeedbackRaw(::grpc::ClientContext* context, const ::service::FeedbackRequest& request, ::grpc::CompletionQueue* cq) {
|
||||
return ::grpc::internal::ClientAsyncResponseReaderHelper::Create< ::service::FeedbackResponse, ::service::FeedbackRequest, ::grpc::protobuf::MessageLite, ::grpc::protobuf::MessageLite>(channel_.get(), cq, rpcmethod_SubmitFeedback_, context, request);
|
||||
}
|
||||
|
||||
::grpc::ClientAsyncResponseReader< ::service::FeedbackResponse>* ConvaiService::Stub::AsyncSubmitFeedbackRaw(::grpc::ClientContext* context, const ::service::FeedbackRequest& request, ::grpc::CompletionQueue* cq) {
|
||||
auto* result =
|
||||
this->PrepareAsyncSubmitFeedbackRaw(context, request, cq);
|
||||
result->StartCall();
|
||||
return result;
|
||||
}
|
||||
|
||||
ConvaiService::Service::Service() {
|
||||
AddMethod(new ::grpc::internal::RpcServiceMethod(
|
||||
ConvaiService_method_names[0],
|
||||
::grpc::internal::RpcMethod::NORMAL_RPC,
|
||||
new ::grpc::internal::RpcMethodHandler< ConvaiService::Service, ::service::HelloRequest, ::service::HelloResponse, ::grpc::protobuf::MessageLite, ::grpc::protobuf::MessageLite>(
|
||||
[](ConvaiService::Service* service,
|
||||
::grpc::ServerContext* ctx,
|
||||
const ::service::HelloRequest* req,
|
||||
::service::HelloResponse* resp) {
|
||||
return service->Hello(ctx, req, resp);
|
||||
}, this)));
|
||||
AddMethod(new ::grpc::internal::RpcServiceMethod(
|
||||
ConvaiService_method_names[1],
|
||||
::grpc::internal::RpcMethod::BIDI_STREAMING,
|
||||
new ::grpc::internal::BidiStreamingHandler< ConvaiService::Service, ::service::HelloRequest, ::service::HelloResponse>(
|
||||
[](ConvaiService::Service* service,
|
||||
::grpc::ServerContext* ctx,
|
||||
::grpc::ServerReaderWriter<::service::HelloResponse,
|
||||
::service::HelloRequest>* stream) {
|
||||
return service->HelloStream(ctx, stream);
|
||||
}, this)));
|
||||
AddMethod(new ::grpc::internal::RpcServiceMethod(
|
||||
ConvaiService_method_names[2],
|
||||
::grpc::internal::RpcMethod::BIDI_STREAMING,
|
||||
new ::grpc::internal::BidiStreamingHandler< ConvaiService::Service, ::service::STTRequest, ::service::STTResponse>(
|
||||
[](ConvaiService::Service* service,
|
||||
::grpc::ServerContext* ctx,
|
||||
::grpc::ServerReaderWriter<::service::STTResponse,
|
||||
::service::STTRequest>* stream) {
|
||||
return service->SpeechToText(ctx, stream);
|
||||
}, this)));
|
||||
AddMethod(new ::grpc::internal::RpcServiceMethod(
|
||||
ConvaiService_method_names[3],
|
||||
::grpc::internal::RpcMethod::BIDI_STREAMING,
|
||||
new ::grpc::internal::BidiStreamingHandler< ConvaiService::Service, ::service::GetResponseRequest, ::service::GetResponseResponse>(
|
||||
[](ConvaiService::Service* service,
|
||||
::grpc::ServerContext* ctx,
|
||||
::grpc::ServerReaderWriter<::service::GetResponseResponse,
|
||||
::service::GetResponseRequest>* stream) {
|
||||
return service->GetResponse(ctx, stream);
|
||||
}, this)));
|
||||
AddMethod(new ::grpc::internal::RpcServiceMethod(
|
||||
ConvaiService_method_names[4],
|
||||
::grpc::internal::RpcMethod::SERVER_STREAMING,
|
||||
new ::grpc::internal::ServerStreamingHandler< ConvaiService::Service, ::service::GetResponseRequestSingle, ::service::GetResponseResponse>(
|
||||
[](ConvaiService::Service* service,
|
||||
::grpc::ServerContext* ctx,
|
||||
const ::service::GetResponseRequestSingle* req,
|
||||
::grpc::ServerWriter<::service::GetResponseResponse>* writer) {
|
||||
return service->GetResponseSingle(ctx, req, writer);
|
||||
}, this)));
|
||||
AddMethod(new ::grpc::internal::RpcServiceMethod(
|
||||
ConvaiService_method_names[5],
|
||||
::grpc::internal::RpcMethod::NORMAL_RPC,
|
||||
new ::grpc::internal::RpcMethodHandler< ConvaiService::Service, ::service::FeedbackRequest, ::service::FeedbackResponse, ::grpc::protobuf::MessageLite, ::grpc::protobuf::MessageLite>(
|
||||
[](ConvaiService::Service* service,
|
||||
::grpc::ServerContext* ctx,
|
||||
const ::service::FeedbackRequest* req,
|
||||
::service::FeedbackResponse* resp) {
|
||||
return service->SubmitFeedback(ctx, req, resp);
|
||||
}, this)));
|
||||
}
|
||||
|
||||
ConvaiService::Service::~Service() {
|
||||
}
|
||||
|
||||
::grpc::Status ConvaiService::Service::Hello(::grpc::ServerContext* context, const ::service::HelloRequest* request, ::service::HelloResponse* response) {
|
||||
(void) context;
|
||||
(void) request;
|
||||
(void) response;
|
||||
return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "");
|
||||
}
|
||||
|
||||
::grpc::Status ConvaiService::Service::HelloStream(::grpc::ServerContext* context, ::grpc::ServerReaderWriter< ::service::HelloResponse, ::service::HelloRequest>* stream) {
|
||||
(void) context;
|
||||
(void) stream;
|
||||
return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "");
|
||||
}
|
||||
|
||||
::grpc::Status ConvaiService::Service::SpeechToText(::grpc::ServerContext* context, ::grpc::ServerReaderWriter< ::service::STTResponse, ::service::STTRequest>* stream) {
|
||||
(void) context;
|
||||
(void) stream;
|
||||
return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "");
|
||||
}
|
||||
|
||||
::grpc::Status ConvaiService::Service::GetResponse(::grpc::ServerContext* context, ::grpc::ServerReaderWriter< ::service::GetResponseResponse, ::service::GetResponseRequest>* stream) {
|
||||
(void) context;
|
||||
(void) stream;
|
||||
return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "");
|
||||
}
|
||||
|
||||
::grpc::Status ConvaiService::Service::GetResponseSingle(::grpc::ServerContext* context, const ::service::GetResponseRequestSingle* request, ::grpc::ServerWriter< ::service::GetResponseResponse>* writer) {
|
||||
(void) context;
|
||||
(void) request;
|
||||
(void) writer;
|
||||
return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "");
|
||||
}
|
||||
|
||||
::grpc::Status ConvaiService::Service::SubmitFeedback(::grpc::ServerContext* context, const ::service::FeedbackRequest* request, ::service::FeedbackResponse* response) {
|
||||
(void) context;
|
||||
(void) request;
|
||||
(void) response;
|
||||
return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "");
|
||||
}
|
||||
|
||||
|
||||
} // namespace service
|
||||
|
||||
|
||||
#endif
|
||||
2168
ConvAI/Convai/Source/Convai/Private/Proto/service.grpc.pb.h
Normal file
2168
ConvAI/Convai/Source/Convai/Private/Proto/service.grpc.pb.h
Normal file
File diff suppressed because it is too large
Load Diff
20534
ConvAI/Convai/Source/Convai/Private/Proto/service.pb.cc
Normal file
20534
ConvAI/Convai/Source/Convai/Private/Proto/service.pb.cc
Normal file
File diff suppressed because it is too large
Load Diff
23289
ConvAI/Convai/Source/Convai/Private/Proto/service.pb.h
Normal file
23289
ConvAI/Convai/Source/Convai/Private/Proto/service.pb.h
Normal file
File diff suppressed because it is too large
Load Diff
173
ConvAI/Convai/Source/Convai/Private/RestAPI/ConvaiAPIBase.cpp
Normal file
173
ConvAI/Convai/Source/Convai/Private/RestAPI/ConvaiAPIBase.cpp
Normal file
@@ -0,0 +1,173 @@
|
||||
|
||||
#include "RestAPI/ConvaiAPIBase.h"
|
||||
#include "ConvaiUtils.h"
|
||||
|
||||
DEFINE_LOG_CATEGORY(ConvaiBaseHttpLogs);
|
||||
|
||||
|
||||
void UConvaiAPIBaseProxy::Activate()
|
||||
{
|
||||
TSharedRef<CONVAI_HTTP_REQUEST_INTERFACE> Request = CONVAI_HTTP_MODULE::Get().CreateRequest();
|
||||
|
||||
if (!ConfigureRequest(Request, TEXT("")))
|
||||
{
|
||||
HandleFailure();
|
||||
return;
|
||||
}
|
||||
|
||||
Request->OnProcessRequestComplete().BindUObject(this, &ThisClass::OnHttpRequestComplete);
|
||||
|
||||
if (Request->ProcessRequest())
|
||||
{
|
||||
AddToRoot();
|
||||
}
|
||||
}
|
||||
|
||||
bool UConvaiAPIBaseProxy::ConfigureRequest(TSharedRef<CONVAI_HTTP_REQUEST_INTERFACE> Request, const TCHAR* Verb)
|
||||
{
|
||||
if (!UConvaiFormValidation::ValidateInputText(URL) || !UConvaiFormValidation::ValidateInputText(Verb))
|
||||
{
|
||||
HandleFailure();
|
||||
return false;
|
||||
}
|
||||
|
||||
TPair<FString, FString> AuthHeaderAndKey = UConvaiUtils::GetAuthHeaderAndKey();
|
||||
FString AuthKey = AuthHeaderAndKey.Value;
|
||||
FString AuthHeader = AuthHeaderAndKey.Key;
|
||||
|
||||
if (!UConvaiFormValidation::ValidateAuthKey(AuthKey))
|
||||
{
|
||||
HandleFailure();
|
||||
return false;
|
||||
}
|
||||
|
||||
Request->SetURL(URL);
|
||||
Request->SetVerb(Verb);
|
||||
Request->SetHeader(AuthHeader, AuthKey);
|
||||
|
||||
// Child classes can add filed to this object
|
||||
TSharedPtr<FJsonObject> ObjectToSend = MakeShareable(new FJsonObject);
|
||||
if (AddContentToRequestAsString(ObjectToSend))
|
||||
{
|
||||
Request->SetHeader(TEXT("Content-Type"), TEXT("application/json"));
|
||||
|
||||
FString Content;
|
||||
TSharedRef<TJsonWriter<>> Writer = TJsonWriterFactory<>::Create(&Content);
|
||||
FJsonSerializer::Serialize(ObjectToSend.ToSharedRef(), Writer);
|
||||
|
||||
Request->SetContentAsString(Content);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
CONVAI_HTTP_PAYLOAD_ARRAY_TYPE DataToSend;
|
||||
FString Boundary = TEXT("ConvaiPluginFormBoundary") + FString::FromInt(FDateTime::Now().GetTicks());
|
||||
if (AddContentToRequest(DataToSend, Boundary))
|
||||
{
|
||||
if (FString(Verb).Equals(ConvaiHttpConstants::PUT))
|
||||
{
|
||||
Request->SetHeader(TEXT("Content-Type"), TEXT("application/octet-stream"));
|
||||
Request->SetContent(DataToSend);
|
||||
return true;
|
||||
}
|
||||
|
||||
Request->SetHeader(TEXT("Content-Type"), FString::Printf(TEXT("multipart/form-data; boundary=----%s"), *Boundary));
|
||||
|
||||
// Add closing boundary
|
||||
FString ClosingBoundary = FString::Printf(TEXT("\r\n------%s--\r\n"), *Boundary);
|
||||
DataToSend.Append((uint8*)TCHAR_TO_UTF8(*ClosingBoundary), ClosingBoundary.Len());
|
||||
|
||||
// Set the request content and content length
|
||||
Request->SetHeader(TEXT("Content-Length"), FString::FromInt(DataToSend.Num()));
|
||||
Request->SetContent(DataToSend);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void UConvaiAPIBaseProxy::OnHttpRequestComplete(CONVAI_HTTP_REQUEST_PTR Request, CONVAI_HTTP_RESPONSE_PTR Response, bool bWasSuccessful)
|
||||
{
|
||||
if (!Response)
|
||||
{
|
||||
if (bWasSuccessful)
|
||||
{
|
||||
CONVAI_LOG(ConvaiBaseHttpLogs, Warning, TEXT("HTTP request succeded - But response pointer is invalid"));
|
||||
}
|
||||
else
|
||||
{
|
||||
CONVAI_LOG(ConvaiBaseHttpLogs, Warning, TEXT("HTTP request failed - Response pointer is invalid"));
|
||||
}
|
||||
|
||||
HandleFailure();
|
||||
return;
|
||||
}
|
||||
if (!bWasSuccessful || Response->GetResponseCode() < 200 || Response->GetResponseCode() > 299)
|
||||
{
|
||||
CONVAI_LOG(ConvaiBaseHttpLogs, Warning, TEXT("HTTP request failed with code %d, and with response:%s"), Response->GetResponseCode(), *Response->GetContentAsString());
|
||||
HandleFailure();
|
||||
return;
|
||||
}
|
||||
|
||||
ResponseString = Response->GetContentAsString();
|
||||
ResponseData = Response->GetContent();
|
||||
HandleSuccess();
|
||||
}
|
||||
|
||||
void UConvaiAPIBaseProxy::HandleSuccess()
|
||||
{
|
||||
RemoveFromRoot();
|
||||
}
|
||||
|
||||
void UConvaiAPIBaseProxy::HandleFailure()
|
||||
{
|
||||
RemoveFromRoot();
|
||||
}
|
||||
// END Base api proxy
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
bool UConvaiAPITokenInBodyProxy::AddContentToRequest(CONVAI_HTTP_PAYLOAD_ARRAY_TYPE& DataToSend, const FString& Boundary)
|
||||
{
|
||||
const TPair<FString, FString> AuthHeaderAndKey = UConvaiUtils::GetAuthHeaderAndKey();
|
||||
const FString AuthKey = AuthHeaderAndKey.Value;
|
||||
const FString AuthHeader = AuthHeaderAndKey.Key;
|
||||
|
||||
if (!UConvaiFormValidation::ValidateAuthKey(AuthKey))
|
||||
{
|
||||
HandleFailure();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (AuthHeader == ConvaiConstants::Auth_Token_Header)
|
||||
{
|
||||
const FString ExpIdField = FString::Printf(TEXT("\r\n------%s\r\nContent-Disposition: form-data; name=\"experience_session_id\"\r\n\r\n%s"), *Boundary, *AuthKey);
|
||||
DataToSend.Append((uint8*)TCHAR_TO_UTF8(*ExpIdField), ExpIdField.Len());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UConvaiAPITokenInBodyProxy::AddContentToRequestAsString(TSharedPtr<FJsonObject>& ObjectToSend)
|
||||
{
|
||||
const TPair<FString, FString> AuthHeaderAndKey = UConvaiUtils::GetAuthHeaderAndKey();
|
||||
const FString AuthKey = AuthHeaderAndKey.Value;
|
||||
const FString AuthHeader = AuthHeaderAndKey.Key;
|
||||
|
||||
if (!UConvaiFormValidation::ValidateAuthKey(AuthKey))
|
||||
{
|
||||
HandleFailure();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (AuthHeader == ConvaiConstants::Auth_Token_Header)
|
||||
{
|
||||
ObjectToSend->SetStringField(TEXT("experience_session_id"), AuthKey);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
328
ConvAI/Convai/Source/Convai/Private/RestAPI/ConvaiLTMProxy.cpp
Normal file
328
ConvAI/Convai/Source/Convai/Private/RestAPI/ConvaiLTMProxy.cpp
Normal file
@@ -0,0 +1,328 @@
|
||||
|
||||
#include "RestAPI/ConvaiLTMProxy.h"
|
||||
#include "RestAPI/ConvaiURL.h"
|
||||
#include "ConvaiDefinitions.h"
|
||||
#include "Utility/Log/ConvaiLogger.h"
|
||||
|
||||
DEFINE_LOG_CATEGORY(LTMHttpLogs);
|
||||
|
||||
UConvaiCreateSpeakerID* UConvaiCreateSpeakerID::ConvaiCreateSpeakerIDProxy(FString SpeakerName, FString DeviceId)
|
||||
{
|
||||
UConvaiCreateSpeakerID* Proxy = NewObject<UConvaiCreateSpeakerID>();
|
||||
Proxy->URL = UConvaiURL::GetEndpoint(EConvaiEndpoint::NewSpeaker);
|
||||
Proxy->AssociatedSpeakerName = SpeakerName;
|
||||
Proxy->AssociatedDeviceId = DeviceId;
|
||||
return Proxy;
|
||||
}
|
||||
|
||||
bool UConvaiCreateSpeakerID::ConfigureRequest(TSharedRef<CONVAI_HTTP_REQUEST_INTERFACE> Request, const TCHAR* Verb)
|
||||
{
|
||||
if (!Super::ConfigureRequest(Request, ConvaiHttpConstants::POST))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UConvaiCreateSpeakerID::AddContentToRequestAsString(TSharedPtr<FJsonObject>& ObjectToSend)
|
||||
{
|
||||
if (AssociatedSpeakerName.IsEmpty())
|
||||
{
|
||||
CONVAI_LOG(LTMHttpLogs, Error, TEXT("Speaker name is empty"));
|
||||
HandleFailure();
|
||||
return false;
|
||||
}
|
||||
|
||||
ObjectToSend->SetStringField(TEXT("name"), AssociatedSpeakerName);
|
||||
|
||||
if (!AssociatedDeviceId.IsEmpty())
|
||||
{
|
||||
ObjectToSend->SetStringField(TEXT("deviceId"), AssociatedDeviceId);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void UConvaiCreateSpeakerID::HandleSuccess()
|
||||
{
|
||||
Super::HandleSuccess();
|
||||
|
||||
TSharedPtr<FJsonObject> JsonObject;
|
||||
const TSharedRef<TJsonReader<>> Reader = TJsonReaderFactory<>::Create(ResponseString);
|
||||
|
||||
if (FJsonSerializer::Deserialize(Reader, JsonObject) && JsonObject.IsValid())
|
||||
{
|
||||
JsonObject->TryGetStringField(TEXT("speaker_id"), AssociatedSpeakerInfo.SpeakerID);
|
||||
JsonObject->TryGetStringField(TEXT("name"), AssociatedSpeakerInfo.Name);
|
||||
JsonObject->TryGetStringField(TEXT("device_id"), AssociatedSpeakerInfo.DeviceID);
|
||||
OnSuccess.Broadcast(AssociatedSpeakerInfo);
|
||||
}
|
||||
else
|
||||
{
|
||||
CONVAI_LOG(LTMHttpLogs, Error, TEXT("Parse Json failed"));
|
||||
HandleFailure();
|
||||
}
|
||||
}
|
||||
|
||||
void UConvaiCreateSpeakerID::HandleFailure()
|
||||
{
|
||||
Super::HandleFailure();
|
||||
OnFailure.Broadcast(FConvaiSpeakerInfo());
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
UConvaiListSpeakerID* UConvaiListSpeakerID::ConvaiListSpeakerIDProxy()
|
||||
{
|
||||
UConvaiListSpeakerID* Proxy = NewObject<UConvaiListSpeakerID>();
|
||||
Proxy->URL = UConvaiURL::GetEndpoint(EConvaiEndpoint::SpeakerIDList);
|
||||
return Proxy;
|
||||
}
|
||||
|
||||
bool UConvaiListSpeakerID::ConfigureRequest(TSharedRef<CONVAI_HTTP_REQUEST_INTERFACE> Request, const TCHAR* Verb)
|
||||
{
|
||||
if (!Super::ConfigureRequest(Request, ConvaiHttpConstants::POST))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void UConvaiListSpeakerID::HandleSuccess()
|
||||
{
|
||||
Super::HandleSuccess();
|
||||
|
||||
TArray<FConvaiSpeakerInfo> SpeakerInfoArray;
|
||||
if (UConvaiLTMUtils::ParseConvaiSpeakerInfoArray(ResponseString, SpeakerInfoArray))
|
||||
{
|
||||
OnSuccess.Broadcast(SpeakerInfoArray);
|
||||
}
|
||||
else
|
||||
{
|
||||
CONVAI_LOG(LTMHttpLogs, Error, TEXT("Parse speaker id failed"));
|
||||
HandleFailure();
|
||||
}
|
||||
}
|
||||
|
||||
void UConvaiListSpeakerID::HandleFailure()
|
||||
{
|
||||
Super::HandleFailure();
|
||||
OnFailure.Broadcast(TArray<FConvaiSpeakerInfo>());
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
UConvaiDeleteSpeakerID* UConvaiDeleteSpeakerID::ConvaiDeleteSpeakerIDProxy(FString SpeakerID)
|
||||
{
|
||||
UConvaiDeleteSpeakerID* Proxy = NewObject<UConvaiDeleteSpeakerID>();
|
||||
Proxy->URL = UConvaiURL::GetEndpoint(EConvaiEndpoint::DeleteSpeakerID);
|
||||
Proxy->AssociatedSpeakerID = SpeakerID;
|
||||
return Proxy;
|
||||
}
|
||||
|
||||
bool UConvaiDeleteSpeakerID::ConfigureRequest(TSharedRef<CONVAI_HTTP_REQUEST_INTERFACE> Request, const TCHAR* Verb)
|
||||
{
|
||||
if (!Super::ConfigureRequest(Request, ConvaiHttpConstants::POST))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UConvaiDeleteSpeakerID::AddContentToRequestAsString(TSharedPtr<FJsonObject>& ObjectToSend)
|
||||
{
|
||||
if (AssociatedSpeakerID.IsEmpty())
|
||||
{
|
||||
HandleFailure();
|
||||
return false;
|
||||
}
|
||||
|
||||
ObjectToSend->SetStringField(TEXT("speakerId"), AssociatedSpeakerID);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void UConvaiDeleteSpeakerID::HandleSuccess()
|
||||
{
|
||||
Super::HandleSuccess();
|
||||
OnSuccess.Broadcast(ResponseString);
|
||||
}
|
||||
|
||||
void UConvaiDeleteSpeakerID::HandleFailure()
|
||||
{
|
||||
Super::HandleFailure();
|
||||
OnFailure.Broadcast(TEXT("Http req failed"));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
UConvaiGetLTMStatus* UConvaiGetLTMStatus::ConvaiGetLTMStatusProxy(FString CharacterID)
|
||||
{
|
||||
UConvaiGetLTMStatus* Proxy = NewObject<UConvaiGetLTMStatus>();
|
||||
Proxy->URL = UConvaiURL::GetEndpoint(EConvaiEndpoint::CharacterGet);
|
||||
Proxy->AssociatedCharacterID = CharacterID;
|
||||
return Proxy;
|
||||
}
|
||||
|
||||
bool UConvaiGetLTMStatus::ConfigureRequest(TSharedRef<CONVAI_HTTP_REQUEST_INTERFACE> Request, const TCHAR* Verb)
|
||||
{
|
||||
if (!Super::ConfigureRequest(Request, ConvaiHttpConstants::POST))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UConvaiGetLTMStatus::AddContentToRequestAsString(TSharedPtr<FJsonObject>& ObjectToSend)
|
||||
{
|
||||
if (AssociatedCharacterID.IsEmpty())
|
||||
{
|
||||
HandleFailure();
|
||||
return false;
|
||||
}
|
||||
|
||||
ObjectToSend->SetStringField(TEXT("charID"), AssociatedCharacterID);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void UConvaiGetLTMStatus::HandleSuccess()
|
||||
{
|
||||
Super::HandleSuccess();
|
||||
bool bEnabled;
|
||||
if (UConvaiLTMUtils::GetLTMStatus(ResponseString, bEnabled))
|
||||
{
|
||||
OnSuccess.Broadcast(bEnabled);
|
||||
}
|
||||
else
|
||||
{
|
||||
CONVAI_LOG(LTMHttpLogs, Error, TEXT("GetLTMStatus failed"));
|
||||
HandleFailure();
|
||||
}
|
||||
}
|
||||
|
||||
void UConvaiGetLTMStatus::HandleFailure()
|
||||
{
|
||||
Super::HandleFailure();
|
||||
OnFailure.Broadcast(false);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
UConvaiSetLTMStatus* UConvaiSetLTMStatus::ConvaiSetLTMStatusProxy(FString CharacterID, bool bEnable)
|
||||
{
|
||||
|
||||
UConvaiSetLTMStatus* Proxy = NewObject<UConvaiSetLTMStatus>();
|
||||
Proxy->URL = UConvaiURL::GetEndpoint(EConvaiEndpoint::CharacterUpdate);
|
||||
Proxy->AssociatedCharacterID = CharacterID;
|
||||
Proxy->bAssociatedEnable = bEnable;
|
||||
return Proxy;
|
||||
}
|
||||
|
||||
bool UConvaiSetLTMStatus::ConfigureRequest(TSharedRef<CONVAI_HTTP_REQUEST_INTERFACE> Request, const TCHAR* Verb)
|
||||
{
|
||||
if (!Super::ConfigureRequest(Request, ConvaiHttpConstants::POST))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UConvaiSetLTMStatus::AddContentToRequestAsString(TSharedPtr<FJsonObject>& ObjectToSend)
|
||||
{
|
||||
if (AssociatedCharacterID.IsEmpty())
|
||||
{
|
||||
HandleFailure();
|
||||
return false;
|
||||
}
|
||||
|
||||
ObjectToSend->SetStringField(TEXT("charID"), AssociatedCharacterID);
|
||||
|
||||
TSharedPtr<FJsonObject> MemorySettings = MakeShared<FJsonObject>();
|
||||
MemorySettings->SetBoolField(TEXT("enabled"), bAssociatedEnable);
|
||||
|
||||
ObjectToSend->SetObjectField(TEXT("memorySettings"), MemorySettings);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void UConvaiSetLTMStatus::HandleSuccess()
|
||||
{
|
||||
Super::HandleSuccess();
|
||||
OnSuccess.Broadcast(ResponseString);
|
||||
}
|
||||
|
||||
void UConvaiSetLTMStatus::HandleFailure()
|
||||
{
|
||||
Super::HandleFailure();
|
||||
OnFailure.Broadcast(ResponseString);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
bool UConvaiLTMUtils::ParseConvaiSpeakerInfoArray(const FString& JsonString, TArray<FConvaiSpeakerInfo>& OutSpeakerInfoArray)
|
||||
{
|
||||
OutSpeakerInfoArray.Empty();
|
||||
|
||||
const TSharedRef<TJsonReader<>> Reader = TJsonReaderFactory<>::Create(JsonString);
|
||||
TArray<TSharedPtr<FJsonValue>> JsonArray;
|
||||
|
||||
if (FJsonSerializer::Deserialize(Reader, JsonArray))
|
||||
{
|
||||
for (const TSharedPtr<FJsonValue>& JsonValue : JsonArray)
|
||||
{
|
||||
TSharedPtr<FJsonObject> JsonObject = JsonValue->AsObject();
|
||||
if (JsonObject.IsValid())
|
||||
{
|
||||
FConvaiSpeakerInfo SpeakerInfo;
|
||||
JsonObject->TryGetStringField(TEXT("speaker_id"), SpeakerInfo.SpeakerID);
|
||||
JsonObject->TryGetStringField(TEXT("name"), SpeakerInfo.Name);
|
||||
JsonObject->TryGetStringField(TEXT("device_id"), SpeakerInfo.DeviceID);
|
||||
|
||||
OutSpeakerInfoArray.Add(SpeakerInfo);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool UConvaiLTMUtils::GetLTMStatus(const FString& JsonString, bool& bEnabled)
|
||||
{
|
||||
bEnabled = false;
|
||||
|
||||
TSharedRef<TJsonReader<>> Reader = TJsonReaderFactory<>::Create(JsonString);
|
||||
TSharedPtr<FJsonObject> JsonObject;
|
||||
|
||||
if (FJsonSerializer::Deserialize(Reader, JsonObject) && JsonObject.IsValid())
|
||||
{
|
||||
if (JsonObject->HasField(TEXT("memory_settings")))
|
||||
{
|
||||
TSharedPtr<FJsonObject> MemorySettingsObject = JsonObject->GetObjectField(TEXT("memory_settings"));
|
||||
if (MemorySettingsObject.IsValid() && MemorySettingsObject->HasField(TEXT("enabled")))
|
||||
{
|
||||
bEnabled = MemorySettingsObject->GetBoolField(TEXT("enabled"));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
157
ConvAI/Convai/Source/Convai/Private/RestAPI/ConvaiURL.cpp
Normal file
157
ConvAI/Convai/Source/Convai/Private/RestAPI/ConvaiURL.cpp
Normal file
@@ -0,0 +1,157 @@
|
||||
#include "RestAPI/ConvaiURL.h"
|
||||
#include "Misc/CommandLine.h"
|
||||
#include "../Convai.h"
|
||||
#include "Utility/Log/ConvaiLogger.h"
|
||||
|
||||
// Define static members
|
||||
const TCHAR UConvaiURL::BETA_SUBDOMAIN[] = TEXT("beta");
|
||||
const TCHAR UConvaiURL::PROD_SUBDOMAIN[] = TEXT("api");
|
||||
const TCHAR UConvaiURL::BASE_URL[] = TEXT("https://%s.convai.com/");
|
||||
const TCHAR UConvaiURL::BASE_URL_FORMAT[] = TEXT("https://{0}.convai.com/");
|
||||
|
||||
const TCHAR UConvaiURL::LTM_SUBDOMAIN[] = TEXT("user/speaker/");
|
||||
const TCHAR UConvaiURL::USER_SUBDOMAIN[] = TEXT("user/");
|
||||
const TCHAR UConvaiURL::CHARACTER_SUBDOMAIN[] = TEXT("character/");
|
||||
const TCHAR UConvaiURL::NARRATIVE_DESIGN_SUBDOMAIN[] = TEXT("character/narrative/");
|
||||
|
||||
TArray<EConvaiEndpoint> UConvaiURL::BetaEndpoints;
|
||||
|
||||
FString UConvaiURL::CustomBetaBaseURL = TEXT("");
|
||||
FString UConvaiURL::CustomProdBaseURL = TEXT("");
|
||||
bool UConvaiURL::bURLConfigInitialized = false;
|
||||
|
||||
void UConvaiURL::InitializeURLConfig()
|
||||
{
|
||||
if (bURLConfigInitialized)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// First check settings
|
||||
FString SettingsBetaURL = Convai::Get().GetConvaiSettings()->CustomBetaURL;
|
||||
SettingsBetaURL.TrimEndInline();
|
||||
SettingsBetaURL.TrimStartInline();
|
||||
if (!SettingsBetaURL.IsEmpty())
|
||||
{
|
||||
CustomBetaBaseURL = SettingsBetaURL;
|
||||
CONVAI_LOG(LogTemp, Log, TEXT("Using beta URL from settings: %s"), *CustomBetaBaseURL);
|
||||
}
|
||||
|
||||
FString SettingsProdURL = Convai::Get().GetConvaiSettings()->CustomProdURL;
|
||||
SettingsProdURL.TrimEndInline();
|
||||
SettingsProdURL.TrimStartInline();
|
||||
if (!SettingsProdURL.IsEmpty())
|
||||
{
|
||||
CustomProdBaseURL = SettingsProdURL;
|
||||
CONVAI_LOG(LogTemp, Log, TEXT("Using prod URL from settings: %s"), *CustomProdBaseURL);
|
||||
}
|
||||
|
||||
// Then check command line parameters (these will override settings if present)
|
||||
FString BetaURL;
|
||||
if (FParse::Value(FCommandLine::Get(), TEXT("ConvaiBetaURL="), BetaURL))
|
||||
{
|
||||
CustomBetaBaseURL = BetaURL;
|
||||
CONVAI_LOG(LogTemp, Log, TEXT("Using custom beta URL from command line: %s"), *CustomBetaBaseURL);
|
||||
}
|
||||
|
||||
FString ProdURL;
|
||||
if (FParse::Value(FCommandLine::Get(), TEXT("ConvaiProdURL="), ProdURL))
|
||||
{
|
||||
CustomProdBaseURL = ProdURL;
|
||||
CONVAI_LOG(LogTemp, Log, TEXT("Using custom prod URL from command line: %s"), *CustomProdBaseURL);
|
||||
}
|
||||
|
||||
bURLConfigInitialized = true;
|
||||
}
|
||||
|
||||
FString UConvaiURL::GetBaseURL(const bool bUseBeta)
|
||||
{
|
||||
InitializeURLConfig();
|
||||
|
||||
if (bUseBeta)
|
||||
{
|
||||
if (!CustomBetaBaseURL.IsEmpty())
|
||||
{
|
||||
return CustomBetaBaseURL;
|
||||
}
|
||||
return TEXT("https://beta.convai.com");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!CustomProdBaseURL.IsEmpty())
|
||||
{
|
||||
return CustomProdBaseURL;
|
||||
}
|
||||
return TEXT("https://api.convai.com");
|
||||
}
|
||||
}
|
||||
|
||||
FString UConvaiURL::GetFullURL(const FString& ApiPath, const bool bUseBeta)
|
||||
{
|
||||
FString BaseURL = GetBaseURL(bUseBeta);
|
||||
|
||||
// Ensure the base URL ends with a slash and the API path doesn't start with one
|
||||
if (!BaseURL.EndsWith(TEXT("/")))
|
||||
{
|
||||
BaseURL += TEXT("/");
|
||||
}
|
||||
|
||||
FString Path = ApiPath;
|
||||
if (Path.StartsWith(TEXT("/")))
|
||||
{
|
||||
Path = Path.RightChop(1);
|
||||
}
|
||||
|
||||
return BaseURL + Path;
|
||||
}
|
||||
|
||||
FString UConvaiURL::GetFormattedBaseURL(const FString& Subdomain)
|
||||
{
|
||||
return FString::Format(BASE_URL_FORMAT, { Subdomain });
|
||||
}
|
||||
|
||||
FString UConvaiURL::GetEndpoint(const EConvaiEndpoint Endpoint)
|
||||
{
|
||||
FString Api;
|
||||
switch (Endpoint)
|
||||
{
|
||||
case EConvaiEndpoint::NewSpeaker:
|
||||
Api = FString(LTM_SUBDOMAIN) + TEXT("new");
|
||||
break;
|
||||
case EConvaiEndpoint::SpeakerIDList:
|
||||
Api = FString(LTM_SUBDOMAIN) + TEXT("list");
|
||||
break;
|
||||
case EConvaiEndpoint::DeleteSpeakerID:
|
||||
Api = FString(LTM_SUBDOMAIN) + TEXT("delete");
|
||||
break;
|
||||
case EConvaiEndpoint::ReferralSourceStatus:
|
||||
Api = FString(USER_SUBDOMAIN) + TEXT("referral-source-status");
|
||||
break;
|
||||
case EConvaiEndpoint::UpdateReferralSource:
|
||||
Api = FString(USER_SUBDOMAIN) + TEXT("update-source");
|
||||
break;
|
||||
case EConvaiEndpoint::UserAPIUsage:
|
||||
Api = FString(USER_SUBDOMAIN) + TEXT("user-api-usage");
|
||||
break;
|
||||
case EConvaiEndpoint::CharacterUpdate:
|
||||
Api = FString(CHARACTER_SUBDOMAIN) + TEXT("update");
|
||||
break;
|
||||
case EConvaiEndpoint::CharacterGet:
|
||||
Api = FString(CHARACTER_SUBDOMAIN) + TEXT("get");
|
||||
break;
|
||||
case EConvaiEndpoint::ListCharacterSections:
|
||||
Api = FString(NARRATIVE_DESIGN_SUBDOMAIN) + TEXT("list-sections");
|
||||
break;
|
||||
case EConvaiEndpoint::ListCharacterTriggers:
|
||||
Api = FString(NARRATIVE_DESIGN_SUBDOMAIN) + TEXT("list-triggers");
|
||||
break;
|
||||
default:
|
||||
CONVAI_LOG(LogTemp, Warning, TEXT("Invalid endpoint!"));
|
||||
return FString();
|
||||
}
|
||||
|
||||
const bool bOnProd = !BetaEndpoints.Contains(Endpoint);
|
||||
const FString Subdomain = bOnProd ? FString(PROD_SUBDOMAIN) : FString(BETA_SUBDOMAIN);
|
||||
|
||||
return GetFormattedBaseURL(Subdomain) + Api;
|
||||
}
|
||||
196
ConvAI/Convai/Source/Convai/Private/Utility/Log/ConvaiLogger.cpp
Normal file
196
ConvAI/Convai/Source/Convai/Private/Utility/Log/ConvaiLogger.cpp
Normal file
@@ -0,0 +1,196 @@
|
||||
|
||||
#include "Utility/Log/ConvaiLogger.h"
|
||||
#include "Misc/DateTime.h"
|
||||
#include "Misc/Paths.h"
|
||||
#include "HAL/PlatformFileManager.h"
|
||||
#include "HAL/PlatformFile.h"
|
||||
#include "Misc/App.h"
|
||||
#include "HAL/PlatformProcess.h" // for FPlatformProcess
|
||||
#include "HAL/Event.h" // for FEvent methods
|
||||
|
||||
FConvaiLogger &FConvaiLogger::Get()
|
||||
{
|
||||
static FConvaiLogger Instance;
|
||||
return Instance;
|
||||
}
|
||||
|
||||
FConvaiLogger::FConvaiLogger()
|
||||
: Thread(nullptr), WakeEvent(FPlatformProcess::GetSynchEventFromPool(false)), bStopping(false)
|
||||
{
|
||||
StartThread();
|
||||
}
|
||||
|
||||
FConvaiLogger::~FConvaiLogger()
|
||||
{
|
||||
ShutdownThread();
|
||||
if (WakeEvent)
|
||||
{
|
||||
FPlatformProcess::ReturnSynchEventToPool(WakeEvent);
|
||||
}
|
||||
}
|
||||
|
||||
void FConvaiLogger::StartThread()
|
||||
{
|
||||
// Logs go in "<ProjectDir>/Saved/ConvaiLogs"
|
||||
const FString LogDir = FPaths::Combine(
|
||||
FPaths::ProjectDir(),
|
||||
TEXT("Saved"),
|
||||
TEXT("ConvaiLogs"));
|
||||
IPlatformFile &Plat = FPlatformFileManager::Get().GetPlatformFile();
|
||||
Plat.CreateDirectoryTree(*LogDir);
|
||||
|
||||
FString PixelStreamingPort=TEXT("Default");
|
||||
FParse::Value(FCommandLine::Get(), TEXT("PixelStreamingPort="), PixelStreamingPort);
|
||||
|
||||
// Build base filename: ProjectName_YYYYMMDD_HHMMSS[_Port]
|
||||
const FString Timestamp = FDateTime::Now().ToString(TEXT("%Y%m%d_%H%M%S"));
|
||||
FString BaseName = FString::Printf(TEXT("%s_%s"),
|
||||
FApp::GetProjectName(),
|
||||
*Timestamp);
|
||||
if (!PixelStreamingPort.IsEmpty())
|
||||
{
|
||||
BaseName += FString::Printf(TEXT("_%s"), *PixelStreamingPort);
|
||||
}
|
||||
|
||||
// 2) Ensure uniqueness by appending _1, _2, ... if file already exists
|
||||
FString FileName = BaseName + TEXT(".log");
|
||||
FString CandidatePath = FPaths::Combine(LogDir, FileName);
|
||||
|
||||
int32 Suffix = 1;
|
||||
while (Plat.FileExists(*CandidatePath))
|
||||
{
|
||||
FileName = FString::Printf(TEXT("%s_%d.log"), *BaseName, Suffix++);
|
||||
CandidatePath = FPaths::Combine(LogDir, FileName);
|
||||
}
|
||||
|
||||
// Finally assign the unique path
|
||||
LogFilePath = CandidatePath;
|
||||
|
||||
// Start the logger thread as before
|
||||
Thread = FRunnableThread::Create(
|
||||
this,
|
||||
TEXT("ConvaiLoggerThread"),
|
||||
0,
|
||||
TPri_BelowNormal);
|
||||
}
|
||||
|
||||
void FConvaiLogger::ShutdownThread()
|
||||
{
|
||||
bStopping = true;
|
||||
if (WakeEvent)
|
||||
WakeEvent->Trigger();
|
||||
|
||||
if (Thread)
|
||||
{
|
||||
Thread->WaitForCompletion();
|
||||
delete Thread;
|
||||
Thread = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
uint32 FConvaiLogger::Run()
|
||||
{
|
||||
while (!bStopping)
|
||||
{
|
||||
WakeEvent->Wait(500);
|
||||
|
||||
TArray<FString> Batch;
|
||||
FString Msg;
|
||||
while (MessageQueue.Dequeue(Msg))
|
||||
{
|
||||
Batch.Add(Msg);
|
||||
}
|
||||
|
||||
if (Batch.Num())
|
||||
{
|
||||
FString Combined = FString::Join(Batch, TEXT("\n")) + TEXT("\n");
|
||||
IPlatformFile &Plat = FPlatformFileManager::Get().GetPlatformFile();
|
||||
if (IFileHandle *Handle = Plat.OpenWrite(*LogFilePath, /*bAppend=*/true))
|
||||
{
|
||||
FTCHARToUTF8 Converter(*Combined);
|
||||
Handle->Write(reinterpret_cast<const uint8 *>(Converter.Get()), Converter.Length());
|
||||
Handle->Flush();
|
||||
delete Handle;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
TArray<FString> FinalBatch;
|
||||
FString Rem;
|
||||
while (MessageQueue.Dequeue(Rem))
|
||||
{
|
||||
FinalBatch.Add(Rem);
|
||||
}
|
||||
if (FinalBatch.Num())
|
||||
{
|
||||
const FString Combined = FString::Join(FinalBatch, TEXT("\n")) + TEXT("\n");
|
||||
IPlatformFile &Plat = FPlatformFileManager::Get().GetPlatformFile();
|
||||
if (IFileHandle *Handle = Plat.OpenWrite(*LogFilePath, /*bAppend=*/true))
|
||||
{
|
||||
const FTCHARToUTF8 Converter(*Combined);
|
||||
Handle->Write(reinterpret_cast<const uint8 *>(Converter.Get()), Converter.Length());
|
||||
Handle->Flush();
|
||||
delete Handle;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void FConvaiLogger::Stop()
|
||||
{
|
||||
bStopping = true;
|
||||
}
|
||||
|
||||
void FConvaiLogger::Log(const FString &Message)
|
||||
{
|
||||
const FString Formatted = FString::Printf(
|
||||
TEXT("[%s] %s"),
|
||||
*FDateTime::Now().ToString(TEXT("%H:%M:%S")),
|
||||
*Message);
|
||||
MessageQueue.Enqueue(Formatted);
|
||||
if (WakeEvent)
|
||||
WakeEvent->Trigger();
|
||||
}
|
||||
|
||||
void UConvaiBlueprintLogger::C_ConvaiLog(UObject *WorldContextObject, EC_LogLevel Verbosity, const FString &Message)
|
||||
{
|
||||
const FString ContextName = WorldContextObject
|
||||
? WorldContextObject->GetName()
|
||||
: TEXT("UnknownContext");
|
||||
|
||||
const UEnum *EnumPtr = StaticEnum<EC_LogLevel>();
|
||||
const FString VerbName = EnumPtr
|
||||
? EnumPtr->GetNameStringByValue(static_cast<int64>(Verbosity))
|
||||
: TEXT("UnknownVerbosity");
|
||||
|
||||
const FString FullMessage = FString::Printf(
|
||||
TEXT("%s : %s : %s"),
|
||||
*ContextName, *VerbName, *Message);
|
||||
|
||||
switch (Verbosity)
|
||||
{
|
||||
case EC_LogLevel::Verbose:
|
||||
UE_LOG(LogTemp, Verbose, TEXT("%s"), *FullMessage);
|
||||
break;
|
||||
case EC_LogLevel::Log:
|
||||
UE_LOG(LogTemp, Log, TEXT("%s"), *FullMessage);
|
||||
break;
|
||||
case EC_LogLevel::Warning:
|
||||
UE_LOG(LogTemp, Warning, TEXT("%s"), *FullMessage);
|
||||
break;
|
||||
case EC_LogLevel::Error:
|
||||
UE_LOG(LogTemp, Error, TEXT("%s"), *FullMessage);
|
||||
break;
|
||||
case EC_LogLevel::Fatal:
|
||||
UE_LOG(LogTemp, Fatal, TEXT("%s"), *FullMessage);
|
||||
break;
|
||||
default:
|
||||
UE_LOG(LogTemp, Log, TEXT("%s"), *FullMessage);
|
||||
break;
|
||||
}
|
||||
|
||||
FConvaiLogger::Get().Log(FullMessage);
|
||||
}
|
||||
32
ConvAI/Convai/Source/Convai/Private/getentropy_compact.c
Normal file
32
ConvAI/Convai/Source/Convai/Private/getentropy_compact.c
Normal file
@@ -0,0 +1,32 @@
|
||||
#pragma warning (disable:4668) // 'expression' : signed/unsigned mismatch
|
||||
|
||||
#ifdef PLATFORM_LINUX
|
||||
#if PLATFORM_LINUX
|
||||
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
|
||||
int getentropy(void *buffer, size_t length) {
|
||||
if (length > 256) {
|
||||
errno = EIO;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int fd = open("/dev/urandom", O_RDONLY);
|
||||
if (fd == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
ssize_t result = read(fd, buffer, length);
|
||||
int saved_errno = errno;
|
||||
close(fd);
|
||||
if (result != (ssize_t)length) {
|
||||
errno = saved_errno;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
40
ConvAI/Convai/Source/Convai/Public/ConvaiActionUtils.h
Normal file
40
ConvAI/Convai/Source/Convai/Public/ConvaiActionUtils.h
Normal file
@@ -0,0 +1,40 @@
|
||||
// Copyright 2022 Convai Inc. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Kismet/BlueprintFunctionLibrary.h"
|
||||
#include "ConvaiDefinitions.h"
|
||||
#include "ConvaiActionUtils.generated.h"
|
||||
|
||||
DECLARE_LOG_CATEGORY_EXTERN(ConvaiActionUtilsLog, Log, All);
|
||||
|
||||
class UConvaiEnvironment;
|
||||
struct FConvaiResultAction;
|
||||
struct FConvaiObjectEntry;
|
||||
|
||||
|
||||
UCLASS()
|
||||
class UConvaiActions : public UBlueprintFunctionLibrary
|
||||
{
|
||||
GENERATED_BODY()
|
||||
public:
|
||||
|
||||
// Function to split a string based on commas, but ignoring commas within quotes
|
||||
static TArray<FString> SmartSplit(const FString& SequenceString);
|
||||
|
||||
// Extract text from an action result (e.g. Says "I love AI" -> I love AI)
|
||||
static FString ExtractText(FString Action, FString ActionResult);
|
||||
|
||||
// Extract number from an action result (e.g. Waits for 5 seconds -> 5)
|
||||
static float ExtractNumber(FString ActionResult);
|
||||
|
||||
static FString FindAction(FString ActionToBeParsed, TArray<FString> Actions);
|
||||
|
||||
// Removes inner descriptions from a string e.g. (Waits for <time in seconds> becomes Waits for)
|
||||
static FString RemoveDesc(FString str);
|
||||
|
||||
static bool ParseAction(UConvaiEnvironment* Environment, FString ActionToBeParsed, FConvaiResultAction& ConvaiResultAction);
|
||||
|
||||
static bool ValidateEnvironment(UConvaiEnvironment* Environment, FString& Error);
|
||||
};
|
||||
25
ConvAI/Convai/Source/Convai/Public/ConvaiAndroid.h
Normal file
25
ConvAI/Convai/Source/Convai/Public/ConvaiAndroid.h
Normal file
@@ -0,0 +1,25 @@
|
||||
// Copyright 2022 Convai Inc. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Kismet/BlueprintFunctionLibrary.h"
|
||||
#include "ConvaiAndroid.generated.h"
|
||||
|
||||
DECLARE_LOG_CATEGORY_EXTERN(ConvaiAndroidLog, Log, All);
|
||||
|
||||
|
||||
UCLASS()
|
||||
class UConvaiAndroid : public UBlueprintFunctionLibrary
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "Convai|Android|Permissions")
|
||||
static void ConvaiAndroidAskMicrophonePermission();
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "Convai|Android|Permissions")
|
||||
static bool ConvaiAndroidHasMicrophonePermission();
|
||||
};
|
||||
147
ConvAI/Convai/Source/Convai/Public/ConvaiAudioCaptureComponent.h
Normal file
147
ConvAI/Convai/Source/Convai/Public/ConvaiAudioCaptureComponent.h
Normal file
@@ -0,0 +1,147 @@
|
||||
// Copyright 2022 Convai Inc. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "AudioCapture.h"
|
||||
#include "Components/SynthComponent.h"
|
||||
#include "AudioCaptureCore.h"
|
||||
#include "AudioCaptureDeviceInterface.h"
|
||||
#include "ConvaiAudioCaptureComponent.generated.h"
|
||||
|
||||
DECLARE_LOG_CATEGORY_EXTERN(ConvaiAudioLog, Log, All);
|
||||
|
||||
namespace Audio
|
||||
{
|
||||
/** Class which contains an FAudioCapture object and performs analysis on the audio stream, only outputing audio if it matches a detection criteria. */
|
||||
class FConvaiAudioCaptureSynth
|
||||
{
|
||||
public:
|
||||
FConvaiAudioCaptureSynth();
|
||||
virtual ~FConvaiAudioCaptureSynth();
|
||||
|
||||
TArray<FCaptureDeviceInfo> GetCaptureDevicesAvailable();
|
||||
|
||||
int32 MapInputDeviceIDToDeviceID(int32 InputDeviceID);
|
||||
|
||||
// Gets the default capture device info
|
||||
bool GetDefaultCaptureDeviceInfo(FCaptureDeviceInfo& OutInfo);
|
||||
|
||||
bool GetCaptureDeviceInfo(FCaptureDeviceInfo& OutInfo, int32 DeviceIndex);
|
||||
|
||||
// Opens up a stream to the default capture device
|
||||
bool OpenDefaultStream();
|
||||
|
||||
bool OpenStream(int32 DeviceIndex);
|
||||
|
||||
void CloseStream();
|
||||
|
||||
// Starts capturing audio
|
||||
bool StartCapturing();
|
||||
|
||||
// Stops capturing audio
|
||||
void StopCapturing();
|
||||
|
||||
// Immediately stop capturing audio
|
||||
void AbortCapturing();
|
||||
|
||||
// Returned if the capture synth is closed
|
||||
bool IsStreamOpen() const;
|
||||
|
||||
// Returns true if the capture synth is capturing audio
|
||||
bool IsCapturing() const;
|
||||
|
||||
// Retrieves audio data from the capture synth.
|
||||
// This returns audio only if there was non-zero audio since this function was last called.
|
||||
bool GetAudioData(TArray<float>& OutAudioData);
|
||||
|
||||
// Returns the number of samples enqueued in the capture synth
|
||||
int32 GetNumSamplesEnqueued();
|
||||
|
||||
FAudioCapture* GetAudioCapture();
|
||||
|
||||
private:
|
||||
|
||||
// Number of samples enqueued
|
||||
int32 NumSamplesEnqueued;
|
||||
|
||||
// Information about the default capture device we're going to use
|
||||
FCaptureDeviceInfo CaptureInfo;
|
||||
|
||||
// Audio capture object dealing with getting audio callbacks
|
||||
FAudioCapture AudioCapture;
|
||||
|
||||
// Critical section to prevent reading and writing from the captured buffer at the same time
|
||||
FCriticalSection CaptureCriticalSection;
|
||||
|
||||
// Buffer of audio capture data, yet to be copied to the output
|
||||
TArray<float> AudioCaptureData;
|
||||
|
||||
// If the object has been initialized
|
||||
bool bInitialized;
|
||||
|
||||
// If we're capturing data
|
||||
bool bIsCapturing;
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
|
||||
UCLASS(ClassGroup = Synth, meta = (BlueprintSpawnableComponent))
|
||||
class UConvaiAudioCaptureComponent : public USynthComponent
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
protected:
|
||||
|
||||
UConvaiAudioCaptureComponent(const FObjectInitializer& ObjectInitializer);
|
||||
|
||||
//~ Begin USynthComponent interface
|
||||
virtual bool Init(int32& SampleRate) override;
|
||||
virtual int32 OnGenerateAudio(float* OutAudio, int32 NumSamples) override;
|
||||
virtual void OnBeginGenerate() override;
|
||||
virtual void OnEndGenerate() override;
|
||||
//~ End USynthComponent interface
|
||||
|
||||
//~ Begin UObject interface
|
||||
virtual void BeginDestroy();
|
||||
virtual bool IsReadyForFinishDestroy() override;
|
||||
virtual void FinishDestroy() override;
|
||||
//~ End UObject interface
|
||||
|
||||
public:
|
||||
/**
|
||||
* Induced latency in audio frames to use to account for jitter between mic capture hardware and audio render hardware.
|
||||
* Increasing this number will increase latency but reduce potential for underruns.
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Latency", meta = (ClampMin = "0", ClampMax = "1024"))
|
||||
int32 JitterLatencyFrames;
|
||||
|
||||
bool GetDefaultCaptureDeviceInfo(Audio::FCaptureDeviceInfo& OutInfo);
|
||||
|
||||
bool GetCaptureDeviceInfo(Audio::FCaptureDeviceInfo& OutInfo, int32 DeviceIndex);
|
||||
|
||||
TArray<Audio::FCaptureDeviceInfo> GetCaptureDevicesAvailable();
|
||||
|
||||
int32 GetActiveCaptureDevice(Audio::FCaptureDeviceInfo& OutInfo);
|
||||
|
||||
bool SetCaptureDevice(int32 DeviceIndex);
|
||||
|
||||
Audio::FConvaiAudioCaptureSynth* GetCaptureSynth();
|
||||
|
||||
private:
|
||||
int32 SelectedDeviceIndex;
|
||||
|
||||
Audio::FConvaiAudioCaptureSynth CaptureSynth;
|
||||
TArray<float> CaptureAudioData;
|
||||
int32 CapturedAudioDataSamples;
|
||||
|
||||
bool bSuccessfullyInitialized;
|
||||
bool bIsCapturing;
|
||||
bool bIsStreamOpen;
|
||||
int32 CaptureChannels;
|
||||
int32 FramesSinceStarting;
|
||||
int32 ReadSampleIndex;
|
||||
FThreadSafeBool bIsDestroying;
|
||||
FThreadSafeBool bIsNotReadyForForFinishDestroy;
|
||||
};
|
||||
659
ConvAI/Convai/Source/Convai/Public/ConvaiAudioStreamer.h
Normal file
659
ConvAI/Convai/Source/Convai/Public/ConvaiAudioStreamer.h
Normal file
@@ -0,0 +1,659 @@
|
||||
// Copyright 2022 Convai Inc. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
//#include "CoreMinimal.h"
|
||||
// #undef UpdateResource
|
||||
#include "Components/AudioComponent.h"
|
||||
#include "RingBuffer.h"
|
||||
#include "ConvaiDefinitions.h"
|
||||
#include "Misc/ScopeLock.h"
|
||||
#include "Interfaces/VoiceCodec.h"
|
||||
|
||||
#include "CoreTypes.h"
|
||||
#include "Templates/UnrealTemplate.h"
|
||||
#include "HAL/PlatformAtomics.h"
|
||||
#include "HAL/PlatformMisc.h"
|
||||
|
||||
#include "ConvaiAudioStreamer.generated.h"
|
||||
|
||||
#define NUM_ENTROPY_VALUES 5
|
||||
|
||||
DECLARE_LOG_CATEGORY_EXTERN(ConvaiAudioStreamerLog, Log, All);
|
||||
|
||||
class USoundWaveProcedural;
|
||||
class IConvaiLipSyncInterface;
|
||||
class IConvaiLipSyncExtendedInterface;
|
||||
class IConvaiVisionInterface;
|
||||
|
||||
|
||||
/**
|
||||
* Template for queues.
|
||||
*
|
||||
* This template implements an unbounded non-intrusive queue using a lock-free linked
|
||||
* list that stores copies of the queued items. The template can operate in two modes:
|
||||
* Multiple-producers single-consumer (MPSC) and Single-producer single-consumer (SPSC).
|
||||
*
|
||||
* The queue is thread-safe in both modes. The Dequeue() method ensures thread-safety by
|
||||
* writing it in a way that does not depend on possible instruction reordering on the CPU.
|
||||
* The Enqueue() method uses an atomic compare-and-swap in multiple-producers scenarios.
|
||||
*
|
||||
* @param T The type of items stored in the queue.
|
||||
* @param Mode The queue mode (single-producer, single-consumer by default).
|
||||
* @todo gmp: Implement node pooling.
|
||||
*/
|
||||
template<typename T, EQueueMode Mode = EQueueMode::Spsc>
|
||||
class TConvaiQueue
|
||||
{
|
||||
public:
|
||||
using FElementType = T;
|
||||
|
||||
/** Default constructor. */
|
||||
TConvaiQueue()
|
||||
{
|
||||
Head = Tail = new TNode();
|
||||
}
|
||||
|
||||
/** Destructor. */
|
||||
~TConvaiQueue()
|
||||
{
|
||||
while (Tail != nullptr)
|
||||
{
|
||||
TNode* Node = Tail;
|
||||
Tail = Tail->NextNode;
|
||||
|
||||
delete Node;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes and returns the item from the tail of the queue.
|
||||
*
|
||||
* @param OutValue Will hold the returned value.
|
||||
* @return true if a value was returned, false if the queue was empty.
|
||||
* @note To be called only from consumer thread.
|
||||
* @see Empty, Enqueue, IsEmpty, Peek, Pop
|
||||
*/
|
||||
bool Dequeue(FElementType& OutItem)
|
||||
{
|
||||
TNode* Popped = Tail->NextNode;
|
||||
|
||||
if (Popped == nullptr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
TSAN_AFTER(&Tail->NextNode);
|
||||
OutItem = MoveTemp(Popped->Item);
|
||||
|
||||
TNode* OldTail = Tail;
|
||||
Tail = Popped;
|
||||
Tail->Item = FElementType();
|
||||
delete OldTail;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Empty the queue, discarding all items.
|
||||
*
|
||||
* @note To be called only from consumer thread.
|
||||
* @see Dequeue, IsEmpty, Peek, Pop
|
||||
*/
|
||||
void Empty()
|
||||
{
|
||||
while (Pop());
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an item to the head of the queue.
|
||||
*
|
||||
* @param Item The item to add.
|
||||
* @return true if the item was added, false otherwise.
|
||||
* @note To be called only from producer thread(s).
|
||||
* @see Dequeue, Pop
|
||||
*/
|
||||
bool Enqueue(const FElementType& Item)
|
||||
{
|
||||
TNode* NewNode = new TNode(Item);
|
||||
|
||||
if (NewNode == nullptr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
TNode* OldHead;
|
||||
|
||||
if (Mode == EQueueMode::Mpsc)
|
||||
{
|
||||
OldHead = (TNode*)FPlatformAtomics::InterlockedExchangePtr((void**)&Head, NewNode);
|
||||
TSAN_BEFORE(&OldHead->NextNode);
|
||||
FPlatformAtomics::InterlockedExchangePtr((void**)&OldHead->NextNode, NewNode);
|
||||
}
|
||||
else
|
||||
{
|
||||
OldHead = Head;
|
||||
Head = NewNode;
|
||||
TSAN_BEFORE(&OldHead->NextNode);
|
||||
FPlatformMisc::MemoryBarrier();
|
||||
OldHead->NextNode = NewNode;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an item to the head of the queue.
|
||||
*
|
||||
* @param Item The item to add.
|
||||
* @return true if the item was added, false otherwise.
|
||||
* @note To be called only from producer thread(s).
|
||||
* @see Dequeue, Pop
|
||||
*/
|
||||
bool Enqueue(FElementType&& Item)
|
||||
{
|
||||
TNode* NewNode = new TNode(MoveTemp(Item));
|
||||
|
||||
if (NewNode == nullptr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
TNode* OldHead;
|
||||
|
||||
if (Mode == EQueueMode::Mpsc)
|
||||
{
|
||||
OldHead = (TNode*)FPlatformAtomics::InterlockedExchangePtr((void**)&Head, NewNode);
|
||||
TSAN_BEFORE(&OldHead->NextNode);
|
||||
FPlatformAtomics::InterlockedExchangePtr((void**)&OldHead->NextNode, NewNode);
|
||||
}
|
||||
else
|
||||
{
|
||||
OldHead = Head;
|
||||
Head = NewNode;
|
||||
TSAN_BEFORE(&OldHead->NextNode);
|
||||
FPlatformMisc::MemoryBarrier();
|
||||
OldHead->NextNode = NewNode;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the queue is empty.
|
||||
*
|
||||
* @return true if the queue is empty, false otherwise.
|
||||
* @note To be called only from consumer thread.
|
||||
* @see Dequeue, Empty, Peek, Pop
|
||||
*/
|
||||
bool IsEmpty() const
|
||||
{
|
||||
return (Tail->NextNode == nullptr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Peeks at the queue's tail item without removing it.
|
||||
*
|
||||
* @param OutItem Will hold the peeked at item.
|
||||
* @return true if an item was returned, false if the queue was empty.
|
||||
* @note To be called only from consumer thread.
|
||||
* @see Dequeue, Empty, IsEmpty, Pop
|
||||
*/
|
||||
bool Peek(FElementType& OutItem) const
|
||||
{
|
||||
if (Tail->NextNode == nullptr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
OutItem = Tail->NextNode->Item;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Peek at the queue's tail item without removing it.
|
||||
*
|
||||
* This version of Peek allows peeking at a queue of items that do not allow
|
||||
* copying, such as TUniquePtr.
|
||||
*
|
||||
* @return Pointer to the item, or nullptr if queue is empty
|
||||
*/
|
||||
FElementType* Peek()
|
||||
{
|
||||
if (Tail->NextNode == nullptr)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return &Tail->NextNode->Item;
|
||||
}
|
||||
|
||||
/**
|
||||
* Peek at the queue's head item
|
||||
*
|
||||
*
|
||||
* @return Pointer to the item, or nullptr if queue is empty
|
||||
*/
|
||||
FElementType* PeekHead()
|
||||
{
|
||||
if (Tail->NextNode == nullptr)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return &Head->Item;
|
||||
}
|
||||
|
||||
|
||||
FORCEINLINE const FElementType* Peek() const
|
||||
{
|
||||
return const_cast<TConvaiQueue*>(this)->Peek();
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the item from the tail of the queue.
|
||||
*
|
||||
* @return true if a value was removed, false if the queue was empty.
|
||||
* @note To be called only from consumer thread.
|
||||
* @see Dequeue, Empty, Enqueue, IsEmpty, Peek
|
||||
*/
|
||||
bool Pop()
|
||||
{
|
||||
TNode* Popped = Tail->NextNode;
|
||||
|
||||
if (Popped == nullptr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
TSAN_AFTER(&Tail->NextNode);
|
||||
|
||||
TNode* OldTail = Tail;
|
||||
Tail = Popped;
|
||||
Tail->Item = FElementType();
|
||||
delete OldTail;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
/** Structure for the internal linked list. */
|
||||
struct TNode
|
||||
{
|
||||
/** Holds a pointer to the next node in the list. */
|
||||
TNode* volatile NextNode;
|
||||
|
||||
/** Holds the node's item. */
|
||||
FElementType Item;
|
||||
|
||||
/** Default constructor. */
|
||||
TNode()
|
||||
: NextNode(nullptr)
|
||||
{ }
|
||||
|
||||
/** Creates and initializes a new node. */
|
||||
explicit TNode(const FElementType& InItem)
|
||||
: NextNode(nullptr)
|
||||
, Item(InItem)
|
||||
{ }
|
||||
|
||||
/** Creates and initializes a new node. */
|
||||
explicit TNode(FElementType&& InItem)
|
||||
: NextNode(nullptr)
|
||||
, Item(MoveTemp(InItem))
|
||||
{ }
|
||||
};
|
||||
|
||||
/** Holds a pointer to the head of the list. */
|
||||
MS_ALIGN(16) TNode* volatile Head GCC_ALIGN(16);
|
||||
|
||||
/** Holds a pointer to the tail of the list. */
|
||||
TNode* Tail;
|
||||
|
||||
private:
|
||||
|
||||
/** Hidden copy constructor. */
|
||||
TConvaiQueue(const TConvaiQueue&) = delete;
|
||||
|
||||
/** Hidden assignment operator. */
|
||||
TConvaiQueue& operator=(const TConvaiQueue&) = delete;
|
||||
};
|
||||
|
||||
UCLASS()
|
||||
class UConvaiAudioStreamer : public UAudioComponent
|
||||
{
|
||||
GENERATED_UCLASS_BODY()
|
||||
|
||||
DECLARE_DYNAMIC_MULTICAST_SPARSE_DELEGATE(FOnStartedTalkingSignature, UConvaiAudioStreamer, OnStartedTalking);
|
||||
DECLARE_DYNAMIC_MULTICAST_SPARSE_DELEGATE(FOnFinishedTalkingSignature, UConvaiAudioStreamer, OnFinishedTalking);
|
||||
DECLARE_DYNAMIC_MULTICAST_SPARSE_DELEGATE(FOVRLipSyncVisemesDataReadySignature, UConvaiAudioStreamer, OnVisemesReady);
|
||||
|
||||
public:
|
||||
/** Send the encoded audio from the server to all clients (including the server again) */
|
||||
UFUNCTION(NetMulticast, Reliable, Category = "VoiceNetworking")
|
||||
void BroadcastVoiceDataToClients(TArray<uint8> const& EncodedVoiceData, uint32 SampleRate, uint32 NumChannels, uint32 SizeBeforeEncode);
|
||||
|
||||
/** Send the encoded audio from a client(/server) to the server, it should call at the end BroadcastVoiceDataToClients() */
|
||||
UFUNCTION(Server, Reliable, Category = "VoiceNetworking")
|
||||
virtual void ProcessEncodedVoiceData(TArray<uint8> const& EncodedVoiceData, uint32 SampleRate, uint32 NumChannels, uint32 SizeBeforeEncode);
|
||||
|
||||
/** If we should play audio on same client */
|
||||
virtual bool ShouldMuteLocal();
|
||||
|
||||
/** If we should play audio on other clients */
|
||||
virtual bool ShouldMuteGlobal();
|
||||
|
||||
virtual void OnServerAudioReceived(uint8* VoiceData, uint32 VoiceDataSize, bool ContainsHeaderData = true, uint32 SampleRate = 21000, uint32 NumChannels = 1) {};
|
||||
|
||||
void PlayVoiceSynced(uint8* VoiceData, uint32 VoiceDataSize, bool ContainsHeaderData=true, uint32 SampleRate=21000, uint32 NumChannels=1);
|
||||
|
||||
void PlayVoiceData(uint8* VoiceData, uint32 VoiceDataSize, bool ContainsHeaderData=true, uint32 SampleRate=21000, uint32 NumChannels=1);
|
||||
|
||||
void PlayVoiceData(uint8* VoiceData, uint32 VoiceDataSize, bool ContainsHeaderData, FAnimationSequence FaceSequence, uint32 SampleRate = 21000, uint32 NumChannels = 1);
|
||||
|
||||
//UFUNCTION(BlueprintCallable, Category = "Convai")
|
||||
void ForcePlayVoice(USoundWave* VoiceToPlay);
|
||||
|
||||
void StopVoice();
|
||||
|
||||
void PauseVoice();
|
||||
|
||||
void ResumeVoice();
|
||||
|
||||
void StopVoiceWithFade(float InRemainingVoiceFadeOutTime);
|
||||
|
||||
void ResetVoiceFade();
|
||||
|
||||
void UpdateVoiceFade(float DeltaTime);
|
||||
|
||||
bool IsVoiceCurrentlyFading();
|
||||
|
||||
void ClearAudioFinishedTimer();
|
||||
|
||||
bool IsLocal();
|
||||
|
||||
/** Called when starts to talk */
|
||||
UPROPERTY(BlueprintAssignable, Category = "Convai")
|
||||
FOnStartedTalkingSignature OnStartedTalking;
|
||||
|
||||
/** Called when stops to talk */
|
||||
UPROPERTY(BlueprintAssignable, Category = "Convai")
|
||||
FOnFinishedTalkingSignature OnFinishedTalking;
|
||||
|
||||
/** Called when there are LipSync visemes available */
|
||||
UPROPERTY(BlueprintAssignable, Category = "Convai|LipSync")
|
||||
FOVRLipSyncVisemesDataReadySignature OnVisemesReady;
|
||||
|
||||
IConvaiLipSyncInterface* FindFirstLipSyncComponent();
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "Convai|LipSync")
|
||||
bool SetLipSyncComponent(UActorComponent* LipSyncComponent);
|
||||
|
||||
/** Returns true, if an LipSync Component was available and attached to the character */
|
||||
UFUNCTION(BlueprintPure, BlueprintCallable, Category = "Convai|LipSync")
|
||||
bool SupportsLipSync();
|
||||
|
||||
IConvaiVisionInterface* FindFirstVisionComponent();
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "Convai|Vision")
|
||||
bool SetVisionComponent(UActorComponent* VisionComponent);
|
||||
|
||||
/** Returns true, if an Vision Component was available and attached to the character */
|
||||
UFUNCTION(BlueprintPure, BlueprintCallable, Category = "Convai|Vision")
|
||||
bool SupportsVision();
|
||||
|
||||
bool ReplicateVoiceToNetwork;
|
||||
|
||||
public:
|
||||
// UActorComponent interface
|
||||
virtual void BeginPlay() override;
|
||||
virtual void TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override;
|
||||
|
||||
// UObject Interface.
|
||||
virtual void BeginDestroy() override;
|
||||
|
||||
public:
|
||||
|
||||
FTimerHandle AudioFinishedTimerHandle;
|
||||
FTimerHandle LypSyncTimeoutTimerHandle;
|
||||
double AudioEndTime = 0.0;
|
||||
bool IsTalking = false;
|
||||
float TotalVoiceFadeOutTime;
|
||||
float RemainingVoiceFadeOutTime;
|
||||
|
||||
UPROPERTY()
|
||||
USoundWaveProcedural* SoundWaveProcedural;
|
||||
|
||||
TArray<uint8> AudioDataBuffer;
|
||||
TArray<uint8> ReceivedEncodedAudioDataBuffer;
|
||||
|
||||
IConvaiLipSyncInterface* ConvaiLipSync;
|
||||
IConvaiVisionInterface* ConvaiVision;
|
||||
|
||||
void PlayLipSyncWithPrecomputedFacialAnimationSynced(FAnimationSequence& FaceSequence);
|
||||
|
||||
void PlayLipSyncWithPrecomputedFacialAnimation(FAnimationSequence FaceSequence);
|
||||
|
||||
void PlayLipSync(uint8* InPCMData, uint32 InPCMDataSize, uint32 InSampleRate, uint32 InNumChannels);
|
||||
|
||||
void StopLipSync();
|
||||
|
||||
void PauseLipSync();
|
||||
|
||||
void ResumeLipSync();
|
||||
|
||||
virtual bool CanUseLipSync();
|
||||
|
||||
virtual void ForceRecalculateLipsyncStartTime();
|
||||
|
||||
virtual bool CanUseVision();
|
||||
|
||||
void OnVisemesReadyCallback();
|
||||
|
||||
void OnLipSyncTimeOut();
|
||||
|
||||
UFUNCTION(BlueprintPure, Category = "Convai|LipSync", Meta = (Tooltip = "Returns last predicted viseme scores"))
|
||||
const TArray<float> GetVisemes() const;
|
||||
|
||||
UFUNCTION(BlueprintPure, Category = "Convai|LipSync", Meta = (Tooltip = "Returns list of viseme names"))
|
||||
const TArray<FString> GetVisemeNames() const;
|
||||
|
||||
UFUNCTION(BlueprintPure, Category = "Convai|LipSync", Meta = (Tooltip = "Returns map of blendshapes"))
|
||||
const TMap<FName, float> ConvaiGetFaceBlendshapes() const;
|
||||
|
||||
UFUNCTION(BlueprintPure, Category = "Convai|LipSync", Meta = (Tooltip = "True if the output visemes is in Blendshape format"))
|
||||
bool GeneratesVisemesAsBlendshapes();
|
||||
|
||||
void AddFaceDataToSend(FAnimationSequence FaceSequence);
|
||||
|
||||
// Should be called in the game thread
|
||||
void AddPCMDataToSend(TArray<uint8> PCMDataToAdd, bool ContainsHeaderData = true, uint32 SampleRate = 21000, uint32 NumChannels = 1);
|
||||
|
||||
virtual void onAudioStarted();
|
||||
virtual void onAudioFinished();
|
||||
|
||||
|
||||
// Add to public section
|
||||
enum class EAudioLipSyncState : uint8
|
||||
{
|
||||
Stopped UMETA(DisplayName = "Stopped"),
|
||||
Playing UMETA(DisplayName = "Playing"),
|
||||
WaitingOnLipSync UMETA(DisplayName = "Waiting On LipSync"),
|
||||
WaitingOnAudio UMETA(DisplayName = "Waiting On Audio")
|
||||
};
|
||||
|
||||
EAudioLipSyncState CurrentState;
|
||||
|
||||
// Simplified buffer structure
|
||||
struct FAudioBuffer
|
||||
{
|
||||
TRingBuffer<uint8> Data;
|
||||
float Duration;
|
||||
uint32 SampleRate;
|
||||
uint32 NumChannels;
|
||||
|
||||
FAudioBuffer() : Duration(0.0f), SampleRate(0), NumChannels(0) {}
|
||||
|
||||
void Reset()
|
||||
{
|
||||
Data.Empty();
|
||||
Duration = 0.0f;
|
||||
SampleRate = 0;
|
||||
NumChannels = 0;
|
||||
}
|
||||
|
||||
void Init(uint32 BufferSize)
|
||||
{
|
||||
Data.Init(BufferSize);
|
||||
}
|
||||
|
||||
bool IsEmpty() const { return Data.RingDataUsage() == 0; }
|
||||
|
||||
float GetTotalDuration() const { return Duration; }
|
||||
|
||||
// Add data to the buffer
|
||||
void AppendData(const uint8* NewData, uint32 DataSize)
|
||||
{
|
||||
// Validate input parameters to prevent crashes
|
||||
if (!NewData || DataSize == 0)
|
||||
{
|
||||
return; // Ignore invalid data
|
||||
}
|
||||
Data.Enqueue(NewData, DataSize);
|
||||
}
|
||||
|
||||
// Get data from the buffer (copies to the provided buffer)
|
||||
uint32 GetData(uint8* OutBuffer, uint32 BufferSize) const
|
||||
{
|
||||
return Data.Peek(OutBuffer, BufferSize);
|
||||
}
|
||||
|
||||
// Remove data from the buffer
|
||||
void RemoveData(uint32 BytesToRemove)
|
||||
{
|
||||
// Ensure we don't try to remove more than what's available
|
||||
uint32 BytesToActuallyRemove = FMath::Min(BytesToRemove, Data.RingDataUsage());
|
||||
|
||||
if (BytesToActuallyRemove > 0)
|
||||
{
|
||||
// The TRingBuffer::Dequeue method with nullptr is designed to discard data
|
||||
// without copying it, which is exactly what we want for efficient removal
|
||||
Data.Dequeue(nullptr, BytesToActuallyRemove);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct FLipSyncBuffer
|
||||
{
|
||||
TArray<FAnimationSequence> Sequences;
|
||||
float TotalDuration;
|
||||
|
||||
FLipSyncBuffer() : TotalDuration(0.0f) {}
|
||||
|
||||
void Reset()
|
||||
{
|
||||
Sequences.Empty();
|
||||
TotalDuration = 0.0f;
|
||||
}
|
||||
|
||||
bool IsEmpty() const { return Sequences.Num() == 0; }
|
||||
|
||||
float GetTotalDuration() const { return TotalDuration; }
|
||||
|
||||
void AddSequence(const FAnimationSequence& Sequence)
|
||||
{
|
||||
Sequences.Add(Sequence);
|
||||
TotalDuration += Sequence.Duration;
|
||||
}
|
||||
};
|
||||
|
||||
FAudioBuffer AudioBuffer;
|
||||
FLipSyncBuffer LipSyncBuffer;
|
||||
|
||||
// Configuration parameters
|
||||
float MinBufferDuration;
|
||||
float AudioLipSyncRatio;
|
||||
float EnableSync;
|
||||
|
||||
// State management functions
|
||||
void TransitionToState(EAudioLipSyncState NewState);
|
||||
void HandleAudioReceived(uint8* AudioData, uint32 AudioDataSize, bool ContainsHeaderData, uint32 SampleRate, uint32 NumChannels);
|
||||
void HandleLipSyncReceived(FAnimationSequence& FaceSequence);
|
||||
bool TryPlayBufferedContent(bool force = false);
|
||||
bool HasSufficientLipSync();
|
||||
bool HasSufficientAudio() const;
|
||||
void PlayBufferedContent(float Duration);
|
||||
|
||||
/**
|
||||
* Returns the duration of content (audio and lipsync if applicable) that is
|
||||
* currently playing or buffered and ready to play.
|
||||
* @return Duration in seconds of content remaining
|
||||
*/
|
||||
UFUNCTION(BlueprintPure, Category = "Convai|Audio")
|
||||
float GetRemainingContentDuration();
|
||||
|
||||
// Add to protected section
|
||||
// Tracking variables for content
|
||||
float TotalPlayingDuration; // Total duration of content currently being played
|
||||
float TotalBufferedDuration; // Total duration of content buffered but not yet played
|
||||
bool bIsSyncingAudioAndLipSync; // Whether we're syncing audio and lipsync
|
||||
|
||||
private:
|
||||
|
||||
// Critical section for protecting SoundWaveProcedural operations
|
||||
FCriticalSection AudioConfigLock;
|
||||
FThreadSafeBool IsAudioConfiguring;
|
||||
|
||||
// Buffer for pending audio data when lock is held
|
||||
TArray<uint8> PendingAudioBuffer;
|
||||
|
||||
// Process any pending audio data
|
||||
void ProcessPendingAudio();
|
||||
|
||||
bool InitEncoder(int32 InSampleRate, int32 InNumChannels, EAudioEncodeHint EncodeHint);
|
||||
int32 Encode(const uint8* RawPCMData, uint32 RawDataSize, uint8* OutCompressedData, uint32& OutCompressedDataSize);
|
||||
void DestroyOpusEncoder();
|
||||
|
||||
bool InitDecoder(int32 InSampleRate, int32 InNumChannels);
|
||||
void Decode(const uint8* CompressedData, uint32 CompressedDataSize, uint8* OutRawPCMData, uint32& OutRawDataSize);
|
||||
void DestroyOpusDecoder();
|
||||
|
||||
void DestroyOpus();
|
||||
|
||||
/** Sample rate encoding (supports 8000, 12000, 16000, 24000, 480000) */
|
||||
int32 EncoderSampleRate;
|
||||
/** Encoded channel count (supports 1,2) */
|
||||
int32 EncoderNumChannels;
|
||||
/**
|
||||
* Number of samples encoded in a time slice "frame" (must match decoder)
|
||||
* One frame defined as (2.5, 5, 10, 20, 40 or 60 ms) of audio data
|
||||
* Voice encoding lower bound is 10ms (audio goes to 2.5ms).
|
||||
* Voice encoding upper bound is 60ms (audio goes to 20ms).
|
||||
* at 48 kHz the permitted values are 120 (2.5ms), 240 (5ms), 480 (10ms), 960 (20ms), 1920 (40ms), and 2880 (60ms)
|
||||
*/
|
||||
int32 EncoderFrameSize;
|
||||
/** Opus encoder stateful data */
|
||||
struct OpusEncoder* Encoder;
|
||||
/** Last value set in the call to Encode() */
|
||||
uint8 EncoderGeneration;
|
||||
|
||||
|
||||
/** Sample rate to decode into, regardless of encoding (supports 8000, 12000, 16000, 24000, 480000) */
|
||||
int32 DecoderSampleRate;
|
||||
/** Decoded channel count (supports 1,2) */
|
||||
int32 DecoderNumChannels;
|
||||
/**
|
||||
* Number of samples encoded in a time slice (must match encoder)
|
||||
* at 48 kHz the permitted values are 120, 240, 480, 960, 1920, and 2880
|
||||
*/
|
||||
int32 DecoderFrameSize;
|
||||
/** Opus decoder stateful data */
|
||||
struct OpusDecoder* Decoder;
|
||||
/** Generation value received from the last incoming packet */
|
||||
uint8 DecoderLastGeneration;
|
||||
|
||||
// Pre-allocated temporary buffer for audio playback
|
||||
TArray<uint8> TempAudioBuffer;
|
||||
static constexpr uint32 TempBufferSize = 1024 * 1024 * 3; // 3 MB buffer
|
||||
};
|
||||
512
ConvAI/Convai/Source/Convai/Public/ConvaiChatBotProxy.h
Normal file
512
ConvAI/Convai/Source/Convai/Public/ConvaiChatBotProxy.h
Normal file
@@ -0,0 +1,512 @@
|
||||
// Copyright 2022 Convai Inc. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Net/OnlineBlueprintCallProxyBase.h"
|
||||
#include "Http.h"
|
||||
#include "ConvaiDefinitions.h"
|
||||
#include "ConvaiChatBotProxy.generated.h"
|
||||
|
||||
|
||||
|
||||
//http log
|
||||
DECLARE_LOG_CATEGORY_EXTERN(ConvaiBotHttpLog, Log, All);
|
||||
|
||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE_FourParams(FBotQueryHttpResponseCallbackSignature, USoundWave*, AudioContent, FString, BotText, FString, NewSessionID, FString, ClassifiedAction);
|
||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE_FiveParams(FBotQueryHttpResponseWithUserTextCallbackSignature, USoundWave*, AudioContent, FString, BotText, FString, UserQuery, FString, NewSessionID, FString, ClassifiedAction);
|
||||
|
||||
class USoundWave;
|
||||
class UTexture2D;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
UCLASS()
|
||||
class UConvaiChatBotQueryProxy : public UOnlineBlueprintCallProxyBase
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
// Called when there is a successful http response
|
||||
UPROPERTY(BlueprintAssignable)
|
||||
FBotQueryHttpResponseCallbackSignature OnSuccess;
|
||||
|
||||
// Called when there is an unsuccessful http response
|
||||
UPROPERTY(BlueprintAssignable)
|
||||
FBotQueryHttpResponseCallbackSignature OnFailure;
|
||||
|
||||
|
||||
/**
|
||||
* @param UserQuery The text input to the character
|
||||
* @param VoiceResponse Whether or not we want a voice response
|
||||
* @param CharID The character ID that we want to get response from
|
||||
* @param SessionID The session ID used to track memory of a previous conversation
|
||||
* @param Classification Whether or not use classification
|
||||
* @param ClassLabels Array of classes to output a class from
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, meta = (/*DeprecatedFunction, DeprecationMessage = "Use Send Text in 'Convai Player' component for better latency",*/ BlueprintInternalUseOnly = "true", DisplayName = "DEPRECEATED Convai Chatbot Get Response", WorldContext = "WorldContextObject"), Category = "Convai|DEPRECATED")
|
||||
static UConvaiChatBotQueryProxy* CreateChatBotQueryProxy(UObject* WorldContextObject,
|
||||
FString UserQuery,
|
||||
bool VoiceResponse,
|
||||
FString CharID,
|
||||
FString SessionID,
|
||||
bool Classification,
|
||||
TArray<FString> ClassLabels);
|
||||
|
||||
|
||||
virtual void Activate() override;
|
||||
|
||||
void onHttpRequestComplete(FHttpRequestPtr RequestPtr, FHttpResponsePtr ResponsePtr, bool bWasSuccessful);
|
||||
|
||||
void failed();
|
||||
void success();
|
||||
void finish();
|
||||
|
||||
FString URL;
|
||||
FString UserQuery;
|
||||
bool VoiceResponse;
|
||||
FString CharID;
|
||||
FString SessionID;
|
||||
bool Classification;
|
||||
TArray<FString> ClassLabels;
|
||||
|
||||
// Outputs
|
||||
USoundWave* AudioContent;
|
||||
FString BotText;
|
||||
FString NewSessionID;
|
||||
FString ClassifiedAction;
|
||||
|
||||
// Pointer to the world
|
||||
TWeakObjectPtr<UWorld> WorldPtr;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
UCLASS()
|
||||
class UConvaiChatBotQueryFromAudioProxy : public UOnlineBlueprintCallProxyBase
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
// Called when there is a successful http response
|
||||
UPROPERTY(BlueprintAssignable)
|
||||
FBotQueryHttpResponseWithUserTextCallbackSignature OnSuccess;
|
||||
|
||||
// Called when there is an unsuccessful http response
|
||||
UPROPERTY(BlueprintAssignable)
|
||||
FBotQueryHttpResponseWithUserTextCallbackSignature OnFailure;
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Get a response from a character using a path to an audio file
|
||||
* @param Filename The path to the audio file, it can be relative to the game or an absolute path
|
||||
* @param VoiceResponse Whether or not we want a voice response
|
||||
* @param CharID The character ID that we want to get response from
|
||||
* @param SessionID The session ID used to track memory of a previous conversation
|
||||
* @param Classification Whether or not use classification
|
||||
* @param ClassLabels Array of classes to output a class from
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, meta = (/*DeprecatedFunction, DeprecationMessage = "Use Start Talking in 'Convai Player' component for better latency",*/ BlueprintInternalUseOnly = "true", DisplayName = "DEPRECEATED Convai Chatbot Get Response From Audio file", WorldContext = "WorldContextObject"), Category = "Convai|DEPRECATED")
|
||||
static UConvaiChatBotQueryFromAudioProxy* CreateChatBotQueryFromAudioProxy(
|
||||
UObject* WorldContextObject,
|
||||
FString Filename,
|
||||
bool VoiceResponse,
|
||||
FString CharID,
|
||||
FString SessionID,
|
||||
bool Classification,
|
||||
TArray<FString> ClassLabels);
|
||||
|
||||
/**
|
||||
* Get a response from a character using a sound wave
|
||||
* @param SoundWave The audio sound wave
|
||||
* @param VoiceResponse Whether or not we want a voice response
|
||||
* @param CharID The character ID that we want to get response from
|
||||
* @param SessionID The session ID used to track memory of a previous conversation
|
||||
* @param Classification Whether or not use classification
|
||||
* @param ClassLabels Array of classes to output a class from
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, meta = (/*DeprecatedFunction, DeprecationMessage = "Use Start Talking in 'Convai Player' component for better latency",*/ BlueprintInternalUseOnly = "true", DisplayName = "DEPRECEATED Convai Chatbot Get Response From SoundWav", WorldContext = "WorldContextObject"), Category = "Convai|DEPRECATED")
|
||||
static UConvaiChatBotQueryFromAudioProxy* CreateChatBotQueryFromSoundWavProxy(
|
||||
UObject* WorldContextObject,
|
||||
USoundWave* SoundWave,
|
||||
bool VoiceResponse,
|
||||
FString CharID,
|
||||
FString SessionID,
|
||||
bool Classification,
|
||||
TArray<FString> ClassLabels);
|
||||
|
||||
|
||||
virtual void Activate() override;
|
||||
|
||||
void onHttpRequestComplete(FHttpRequestPtr RequestPtr, FHttpResponsePtr ResponsePtr, bool bWasSuccessful);
|
||||
|
||||
void failed();
|
||||
void success();
|
||||
void finish();
|
||||
|
||||
FString URL;
|
||||
TArray<uint8> Payload;
|
||||
bool VoiceResponse;
|
||||
FString CharID;
|
||||
FString SessionID;
|
||||
bool Classification;
|
||||
TArray<FString> ClassLabels;
|
||||
|
||||
// Outputs
|
||||
USoundWave* AudioContent;
|
||||
FString BotText;
|
||||
FString UserQuery;
|
||||
FString NewSessionID;
|
||||
FString ClassifiedAction;
|
||||
|
||||
// Pointer to the world
|
||||
TWeakObjectPtr<UWorld> WorldPtr;
|
||||
};
|
||||
|
||||
|
||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FBotCreateHttpResponseCallbackSignature, FString, CharID);
|
||||
|
||||
UCLASS()
|
||||
class UConvaiChatBotCreateProxy : public UOnlineBlueprintCallProxyBase
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
// Called when there is a successful http response
|
||||
UPROPERTY(BlueprintAssignable)
|
||||
FBotCreateHttpResponseCallbackSignature OnSuccess;
|
||||
|
||||
// Called when there is an unsuccessful http response
|
||||
UPROPERTY(BlueprintAssignable)
|
||||
FBotCreateHttpResponseCallbackSignature OnFailure;
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new character and outputs its character ID
|
||||
* @param CharName The character name
|
||||
* @param Voice The voice type to be used
|
||||
* @param Backstory The backstory for the character
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, meta = (BlueprintInternalUseOnly = "true", DisplayName = "Convai Create Character", WorldContext = "WorldContextObject"), Category = "Convai|REST API")
|
||||
static UConvaiChatBotCreateProxy* CreateCharacterCreateProxy(UObject* WorldContextObject, FString CharName, FString Voice, FString Backstory);
|
||||
|
||||
UFUNCTION(BlueprintCallable, meta = (DeprecatedFunction, DeprecationMessage = "Use \"Convai Create Character\" instead", BlueprintInternalUseOnly = "true", DisplayName = "Convai Get All Chatbots IDs", WorldContext = "WorldContextObject"), Category = "Convai|REST API")
|
||||
static UConvaiChatBotCreateProxy* CreateChatBotCreateProxy(UObject* WorldContextObject, FString CharName, FString Voice, FString Backstory)
|
||||
{
|
||||
return CreateCharacterCreateProxy(WorldContextObject, CharName, Voice, Backstory);
|
||||
}
|
||||
|
||||
|
||||
|
||||
virtual void Activate() override;
|
||||
|
||||
void onHttpRequestComplete(FHttpRequestPtr RequestPtr, FHttpResponsePtr ResponsePtr, bool bWasSuccessful);
|
||||
|
||||
void failed();
|
||||
void success();
|
||||
void finish();
|
||||
|
||||
FString URL;
|
||||
FString CharName;
|
||||
FString Voice;
|
||||
FString Backstory;
|
||||
|
||||
|
||||
// Outputs
|
||||
FString CharID;
|
||||
|
||||
// Pointer to the world
|
||||
TWeakObjectPtr<UWorld> WorldPtr;
|
||||
};
|
||||
|
||||
|
||||
|
||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FBotUpdateHttpResponseCallbackSignature);
|
||||
|
||||
UCLASS()
|
||||
class UConvaiChatBotUpdateProxy : public UOnlineBlueprintCallProxyBase
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
// Called when there is a successful http response
|
||||
UPROPERTY(BlueprintAssignable)
|
||||
FBotUpdateHttpResponseCallbackSignature OnSuccess;
|
||||
|
||||
// Called when there is an unsuccessful http response
|
||||
UPROPERTY(BlueprintAssignable)
|
||||
FBotUpdateHttpResponseCallbackSignature OnFailure;
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Updates an already created character, IMPORTANT: if you do not want to update some property (e.g. Name) then leave it as empty string and it will not update
|
||||
* @param CharID The character's ID that is going to be updated
|
||||
* @param NewVoice The new voice type to be used, leave empty if you do not want to update
|
||||
* @param NewBackstory The new backstory, leave empty if you do not want to update
|
||||
* @param NewCharName The new character name, leave empty if you do not want to update
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, meta = (BlueprintInternalUseOnly = "true", DisplayName = "Convai Update Character", WorldContext = "WorldContextObject"), Category = "Convai|REST API")
|
||||
static UConvaiChatBotUpdateProxy* CreateCharacterUpdateProxy(UObject* WorldContextObject, FString CharID, FString NewVoice, FString NewBackstory, FString NewCharName, FString NewLanguage);
|
||||
|
||||
UFUNCTION(BlueprintCallable, meta = (DeprecatedFunction, DeprecationMessage = "Use \"Convai Update Character\" instead", BlueprintInternalUseOnly = "true", DisplayName = "Convai Update Chatbot", WorldContext = "WorldContextObject"), Category = "Convai|REST API")
|
||||
static UConvaiChatBotUpdateProxy* CreateChatBotUpdateProxy(UObject* WorldContextObject, FString CharID, FString NewVoice, FString NewBackstory, FString NewCharName, FString NewLanguage)
|
||||
{
|
||||
return CreateCharacterUpdateProxy(WorldContextObject, CharID, NewVoice, NewBackstory, NewCharName, NewLanguage);
|
||||
}
|
||||
|
||||
virtual void Activate() override;
|
||||
|
||||
void onHttpRequestComplete(FHttpRequestPtr RequestPtr, FHttpResponsePtr ResponsePtr, bool bWasSuccessful);
|
||||
|
||||
void failed();
|
||||
void success();
|
||||
void finish();
|
||||
|
||||
FString URL;
|
||||
FString CharID;
|
||||
FString NewCharName;
|
||||
FString NewVoice;
|
||||
FString NewBackstory;
|
||||
FString NewLanguage;
|
||||
|
||||
// Pointer to the world
|
||||
TWeakObjectPtr<UWorld> WorldPtr;
|
||||
};
|
||||
|
||||
|
||||
|
||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE_SevenParams(FBotGetDetailsHttpResponseCallbackSignature
|
||||
, FString, character_name
|
||||
, FString, voice_type
|
||||
, FString, backstory
|
||||
, FString, LanguageCode
|
||||
, bool, HasReadyPlayerMeLink
|
||||
, FString, ReadyPlayerMeLink
|
||||
, FString, AvatarImageLink);
|
||||
|
||||
UCLASS()
|
||||
class UConvaiChatBotGetDetailsProxy : public UOnlineBlueprintCallProxyBase
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
// Called when there is a successful http response
|
||||
UPROPERTY(BlueprintAssignable)
|
||||
FBotGetDetailsHttpResponseCallbackSignature OnSuccess;
|
||||
|
||||
// Called when there is an unsuccessful http response
|
||||
UPROPERTY(BlueprintAssignable)
|
||||
FBotGetDetailsHttpResponseCallbackSignature OnFailure;
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Initiates a post request to the Character API.
|
||||
* @param CharID The character's ID, you can find it from the Dashboard on the Convai.com website
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, meta = (BlueprintInternalUseOnly = "true", DisplayName = "Convai Get Character Details", WorldContext = "WorldContextObject"), Category = "Convai|REST API")
|
||||
static UConvaiChatBotGetDetailsProxy* CreateCharacterGetDetailsProxy(UObject* WorldContextObject, FString CharID);
|
||||
|
||||
UFUNCTION(BlueprintCallable, meta = (DeprecatedFunction, DeprecationMessage = "Use \"Convai Get Character Details\" instead", BlueprintInternalUseOnly = "true", DisplayName = "Convai Get All Chatbots IDs", WorldContext = "WorldContextObject"), Category = "Convai|REST API")
|
||||
static UConvaiChatBotGetDetailsProxy* CreateChatBotGetDetailsProxy(UObject* WorldContextObject, FString CharID)
|
||||
{
|
||||
return CreateCharacterGetDetailsProxy(WorldContextObject, CharID);
|
||||
}
|
||||
|
||||
|
||||
virtual void Activate() override;
|
||||
|
||||
void onHttpRequestComplete(FHttpRequestPtr RequestPtr, FHttpResponsePtr ResponsePtr, bool bWasSuccessful);
|
||||
|
||||
void failed();
|
||||
void success();
|
||||
void finish();
|
||||
|
||||
// inputs
|
||||
FString URL;
|
||||
FString CharID;
|
||||
|
||||
// outputs
|
||||
FString character_name;
|
||||
//FString user_id;
|
||||
FString voice_type;
|
||||
//FString timestamp;
|
||||
FString backstory;
|
||||
FString LanguageCode;
|
||||
bool HasReadyPlayerMeLink;
|
||||
FString ReadyPlayerMeLink;
|
||||
FString AvatarImageLink;
|
||||
|
||||
|
||||
// Pointer to the world
|
||||
TWeakObjectPtr<UWorld> WorldPtr;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FBotGetCharsHttpResponseCallbackSignature, const TArray<FString>&, CharIDs);
|
||||
|
||||
UCLASS()
|
||||
class UConvaiChatBotGetCharsProxy : public UOnlineBlueprintCallProxyBase
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
// Called when there is a successful http response
|
||||
UPROPERTY(BlueprintAssignable)
|
||||
FBotGetCharsHttpResponseCallbackSignature OnSuccess;
|
||||
|
||||
// Called when there is an unsuccessful http response
|
||||
UPROPERTY(BlueprintAssignable)
|
||||
FBotGetCharsHttpResponseCallbackSignature OnFailure;
|
||||
|
||||
/**
|
||||
* Gets all character IDs created by the user.
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, meta = (BlueprintInternalUseOnly = "true", DisplayName = "Convai Get All Characters IDs", WorldContext = "WorldContextObject"), Category = "Convai|REST API")
|
||||
static UConvaiChatBotGetCharsProxy* CreateCharacterGetCharsProxy(UObject* WorldContextObject);
|
||||
|
||||
UFUNCTION(BlueprintCallable, meta = (DeprecatedFunction, DeprecationMessage = "Use \"Convai Get All Characters IDs\" instead", BlueprintInternalUseOnly = "true", DisplayName = "Convai Get All Chatbots IDs", WorldContext = "WorldContextObject"), Category = "Convai|REST API")
|
||||
static UConvaiChatBotGetCharsProxy* CreateChatBotGetCharsProxy(UObject* WorldContextObject)
|
||||
{
|
||||
return CreateCharacterGetCharsProxy(WorldContextObject);
|
||||
}
|
||||
|
||||
virtual void Activate() override;
|
||||
|
||||
void onHttpRequestComplete(FHttpRequestPtr RequestPtr, FHttpResponsePtr ResponsePtr, bool bWasSuccessful);
|
||||
|
||||
void failed();
|
||||
void success();
|
||||
void finish();
|
||||
|
||||
FString URL;
|
||||
|
||||
// Outputs
|
||||
TArray<FString> CharIDs;
|
||||
|
||||
// Pointer to the world
|
||||
TWeakObjectPtr<UWorld> WorldPtr;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FDownloadImageHttpResponseCallbackSignature, UTexture2D*, Image);
|
||||
|
||||
UCLASS()
|
||||
class UConvaiDownloadImageProxy : public UOnlineBlueprintCallProxyBase
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
// Called when there is a successful http response
|
||||
UPROPERTY(BlueprintAssignable)
|
||||
FDownloadImageHttpResponseCallbackSignature OnSuccess;
|
||||
|
||||
// Called when there is an unsuccessful http response
|
||||
UPROPERTY(BlueprintAssignable)
|
||||
FDownloadImageHttpResponseCallbackSignature OnFailure;
|
||||
|
||||
|
||||
UFUNCTION(BlueprintCallable, meta = (BlueprintInternalUseOnly = "true", DisplayName = "Convai Download Image", WorldContext = "WorldContextObject"), Category = "Convai|REST API")
|
||||
static UConvaiDownloadImageProxy* CreateDownloadImageProxy(UObject* WorldContextObject, FString URL);
|
||||
|
||||
UFUNCTION(BlueprintCallable, meta = (BlueprintInternalUseOnly = "true", DisplayName = "Convai Download Image using RPM Link", WorldContext = "WorldContextObject"), Category = "Convai|REST API")
|
||||
static UConvaiDownloadImageProxy* CreateDownloadImageForRPMProxy(UObject* WorldContextObject, FString URL);
|
||||
|
||||
virtual void Activate() override;
|
||||
|
||||
void onHttpRequestComplete(FHttpRequestPtr RequestPtr, FHttpResponsePtr ResponsePtr, bool bWasSuccessful);
|
||||
|
||||
void failed();
|
||||
void success();
|
||||
void finish();
|
||||
|
||||
FString URL;
|
||||
|
||||
// Outputs
|
||||
UTexture2D* Image;
|
||||
|
||||
// Pointer to the world
|
||||
TWeakObjectPtr<UWorld> WorldPtr;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
USTRUCT(BlueprintType)
|
||||
struct FAvailableVoices
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
UPROPERTY(BlueprintReadOnly, category = "Convai|Language")
|
||||
TMap<FString, FVoiceLanguageStruct> AvailableVoices;
|
||||
|
||||
FAvailableVoices()
|
||||
{}
|
||||
};
|
||||
|
||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FGetAvailableVoicesCallbackSignature, const FAvailableVoices&, AvailableVoices);
|
||||
|
||||
UCLASS()
|
||||
class UConvaiGetAvailableVoicesProxy : public UOnlineBlueprintCallProxyBase
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
// Called when there is a successful http response
|
||||
UPROPERTY(BlueprintAssignable)
|
||||
FGetAvailableVoicesCallbackSignature OnSuccess;
|
||||
|
||||
// Called when there is an unsuccessful http response
|
||||
UPROPERTY(BlueprintAssignable)
|
||||
FGetAvailableVoicesCallbackSignature OnFailure;
|
||||
|
||||
/**
|
||||
* Gets all available voices.
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, meta = (BlueprintInternalUseOnly = "true", DisplayName = "Convai Get Available Voices"), Category = "Convai|REST API")
|
||||
static UConvaiGetAvailableVoicesProxy* CreateGetAvailableVoicesProxy(EVoiceType VoiceType, ELanguageType LanguageType, EGenderType Gender);
|
||||
|
||||
virtual void Activate() override;
|
||||
|
||||
void onHttpRequestComplete(FHttpRequestPtr RequestPtr, FHttpResponsePtr ResponsePtr, bool bWasSuccessful);
|
||||
|
||||
void failed();
|
||||
void success();
|
||||
|
||||
FString URL;
|
||||
EVoiceType FilterVoiceType;
|
||||
ELanguageType FilterLanguageType;
|
||||
EGenderType FilterGender;
|
||||
|
||||
// Output
|
||||
FAvailableVoices AvailableVoices;
|
||||
|
||||
private:
|
||||
// Helper functions
|
||||
bool ParseAllVoiceData(const FString& JsonString, TMap<FString, FVoiceLanguageStruct>& FilteredVoices);
|
||||
bool ParseVoiceData(TSharedPtr<FJsonObject> JsonObject, FVoiceLanguageStruct& OutVoice);
|
||||
FString GetLanguageCodeFromEnum(ELanguageType LanguageType);
|
||||
FString GetGenderFromEnum(EGenderType GenderType);
|
||||
FString GetVoiceTypeFromEnum(EVoiceType VoiceType);
|
||||
};
|
||||
466
ConvAI/Convai/Source/Convai/Public/ConvaiChatbotComponent.h
Normal file
466
ConvAI/Convai/Source/Convai/Public/ConvaiChatbotComponent.h
Normal file
@@ -0,0 +1,466 @@
|
||||
// Copyright 2022 Convai Inc. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
//#include "CoreMinimal.h"
|
||||
#include "Components/AudioComponent.h"
|
||||
#include "ConvaiAudioStreamer.h"
|
||||
#include "Containers/Map.h"
|
||||
#include "ConvaiDefinitions.h"
|
||||
|
||||
#include "ConvaiChatbotComponent.generated.h"
|
||||
|
||||
DECLARE_LOG_CATEGORY_EXTERN(ConvaiChatbotComponentLog, Log, All);
|
||||
|
||||
DECLARE_DYNAMIC_MULTICAST_SPARSE_DELEGATE_ThreeParams(FOnTranscriptionReceivedSignature_Deprecated, UConvaiChatbotComponent, OnTranscriptionReceivedEvent, FString, Transcription, bool, IsTranscriptionReady, bool, IsFinal);
|
||||
DECLARE_DYNAMIC_MULTICAST_SPARSE_DELEGATE_SixParams(FOnTranscriptionReceivedSignature_V2, UConvaiChatbotComponent, OnTranscriptionReceivedEvent_V2, UConvaiChatbotComponent*, ChatbotComponent, UConvaiPlayerComponent*, InteractingPlayerComponent, FString, PlayerName, FString, Transcription, bool, IsTranscriptionReady, bool, IsFinal);
|
||||
|
||||
DECLARE_DYNAMIC_MULTICAST_SPARSE_DELEGATE_FourParams(FOnTextReceivedSignature_Deprecated, UConvaiChatbotComponent, OnTextReceivedEvent, FString, CharacterName, FString, BotText, float, AudioDuration, bool, IsFinal);
|
||||
DECLARE_DYNAMIC_MULTICAST_SPARSE_DELEGATE_SixParams(FOnTextReceivedSignature_V2, UConvaiChatbotComponent, OnTextReceivedEvent_V2, UConvaiChatbotComponent*, ChatbotComponent, UConvaiPlayerComponent*, InteractingPlayerComponent, FString, CharacterName, FString, BotText, float, AudioDuration, bool, IsFinal);
|
||||
|
||||
DECLARE_DYNAMIC_MULTICAST_SPARSE_DELEGATE_OneParam(FOnActionReceivedSignature_Deprecated, UConvaiChatbotComponent, OnActionReceivedEvent, const TArray<FConvaiResultAction>&, SequenceOfActions);
|
||||
DECLARE_DYNAMIC_MULTICAST_SPARSE_DELEGATE_ThreeParams(FOnActionReceivedSignature_V2, UConvaiChatbotComponent, OnActionReceivedEvent_V2, UConvaiChatbotComponent*, ChatbotComponent, UConvaiPlayerComponent*, InteractingPlayerComponent, const TArray<FConvaiResultAction>&, SequenceOfActions);
|
||||
|
||||
DECLARE_DYNAMIC_MULTICAST_SPARSE_DELEGATE_TwoParams(FOnEmotionReceivedSignature, UConvaiChatbotComponent, OnEmotionStateChangedEvent, UConvaiChatbotComponent*, ChatbotComponent, UConvaiPlayerComponent*, InteractingPlayerComponent);
|
||||
|
||||
DECLARE_DYNAMIC_MULTICAST_SPARSE_DELEGATE_OneParam(FOnCharacterDataLoadSignature_Deprecated, UConvaiChatbotComponent, OnCharacterDataLoadEvent, bool, Success);
|
||||
DECLARE_DYNAMIC_MULTICAST_SPARSE_DELEGATE_TwoParams(FOnCharacterDataLoadSignature_V2, UConvaiChatbotComponent, OnCharacterDataLoadEvent_V2, UConvaiChatbotComponent*, ChatbotComponent, bool, Success);
|
||||
|
||||
DECLARE_DYNAMIC_MULTICAST_SPARSE_DELEGATE_TwoParams(FOnNarrativeSectionReceivedSignature, UConvaiChatbotComponent, OnNarrativeSectionReceivedEvent, UConvaiChatbotComponent*, ChatbotComponent, FString, NarrativeSectionID);
|
||||
|
||||
DECLARE_DYNAMIC_MULTICAST_SPARSE_DELEGATE_ThreeParams(FOnInteractionIDReceivedSignature, UConvaiChatbotComponent, OnInteractionIDReceivedEvent, UConvaiChatbotComponent*, ChatbotComponent, UConvaiPlayerComponent*, InteractingPlayerComponent, FString, InteractionID);
|
||||
|
||||
DECLARE_DYNAMIC_MULTICAST_SPARSE_DELEGATE(FOnFailureSignature, UConvaiChatbotComponent, OnFailureEvent);
|
||||
DECLARE_DYNAMIC_MULTICAST_SPARSE_DELEGATE_TwoParams(FOnInterruptedSignature, UConvaiChatbotComponent, OnInterruptedEvent, UConvaiChatbotComponent*, ChatbotComponent, UConvaiPlayerComponent*, InteractingPlayerComponent);
|
||||
|
||||
// TODO (Mohamed): Manage onDestroy/onEndPlay - should end any on-going streams
|
||||
|
||||
class UConvaiPlayerComponent;
|
||||
class USoundWaveProcedural;
|
||||
class UConvaiGRPCGetResponseProxy;
|
||||
class UConvaiChatBotGetDetailsProxy;
|
||||
|
||||
UCLASS(Blueprintable, BlueprintType, meta = (BlueprintSpawnableComponent), DisplayName = "Convai Chatbot")
|
||||
class CONVAI_API UConvaiChatbotComponent : public UConvaiAudioStreamer
|
||||
{
|
||||
GENERATED_BODY()
|
||||
public:
|
||||
|
||||
UConvaiChatbotComponent();
|
||||
|
||||
void GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const override;
|
||||
|
||||
/**
|
||||
* Returns true, if the character is being talked to, is talking, or is processing the response.
|
||||
*/
|
||||
UFUNCTION(BlueprintPure, BlueprintCallable, Category = "Convai")
|
||||
bool IsInConversation();
|
||||
|
||||
/**
|
||||
* Returns true, if the character is still processing and has not received the full response yet.
|
||||
*/
|
||||
UFUNCTION(BlueprintPure, BlueprintCallable, Category = "Convai", meta = (DisplayName = "Is Thinking"))
|
||||
bool IsProcessing();
|
||||
|
||||
/**
|
||||
* Returns true, if the character is currently listening to a player.
|
||||
*/
|
||||
UFUNCTION(BlueprintPure, BlueprintCallable, Category = "Convai")
|
||||
bool IsListening();
|
||||
|
||||
/**
|
||||
* Returns true, if the character is currently talking.
|
||||
*/
|
||||
UFUNCTION(BlueprintPure, BlueprintCallable, Category = "Convai", meta = (DisplayName = "Is Talking"))
|
||||
bool GetIsTalking();
|
||||
|
||||
/** Returns time elapsed since the character started talking */
|
||||
UFUNCTION(BlueprintPure, BlueprintCallable, Category = "Convai|Voice")
|
||||
float GetTalkingTimeElapsed();
|
||||
|
||||
/** Returns time remaining audio time for the character to speak */
|
||||
UFUNCTION(BlueprintPure, BlueprintCallable, Category = "Convai|Voice")
|
||||
float GetTalkingTimeRemaining();
|
||||
|
||||
UPROPERTY(EditAnywhere, Category = "Convai", Replicated, BlueprintSetter = LoadCharacter)
|
||||
FString CharacterID;
|
||||
|
||||
UPROPERTY(BlueprintReadOnly, Category = "Convai", Replicated)
|
||||
FString CharacterName;
|
||||
|
||||
UPROPERTY(BlueprintReadOnly, Category = "Convai", Replicated)
|
||||
FString VoiceType;
|
||||
|
||||
UPROPERTY(BlueprintReadOnly, Category = "Convai", Replicated)
|
||||
FString Backstory;
|
||||
|
||||
UPROPERTY(BlueprintReadOnly, Category = "Convai", Replicated)
|
||||
FString LanguageCode;
|
||||
|
||||
UPROPERTY(BlueprintReadOnly, Category = "Convai", Replicated)
|
||||
FString ReadyPlayerMeLink;
|
||||
|
||||
UPROPERTY(BlueprintReadOnly, Category = "Convai", Replicated)
|
||||
FString AvatarImageLink;
|
||||
|
||||
UPROPERTY(BlueprintReadOnly, Category = "Convai", Replicated, meta = (DisplayName = "Interacting Player"))
|
||||
UConvaiPlayerComponent* CurrentConvaiPlayerComponent;
|
||||
|
||||
UPROPERTY(BlueprintReadOnly, Category = "Convai|Actions", Replicated)
|
||||
TArray<FConvaiResultAction> ActionsQueue;
|
||||
|
||||
UPROPERTY(Replicated)
|
||||
FConvaiEmotionState EmotionState;
|
||||
|
||||
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Convai|Emotion", Replicated)
|
||||
bool LockEmotionState = false;
|
||||
|
||||
/**
|
||||
* Used to track memory of a previous conversation, set to -1 means no previous conversation,
|
||||
* this property will change as you talk to the character, you can save the session ID for a
|
||||
* conversation and then set it back later on to resume a conversation
|
||||
*/
|
||||
UPROPERTY(BlueprintReadWrite, Category = "Convai", Replicated)
|
||||
FString SessionID = "-1";
|
||||
|
||||
/**
|
||||
* Contains all relevant objects and characters in the scene including the (Player), and also all the actions doable by the character
|
||||
*/
|
||||
UPROPERTY(BlueprintReadWrite, Category = "Convai", BlueprintSetter = LoadEnvironment)
|
||||
UConvaiEnvironment* Environment;
|
||||
|
||||
/**
|
||||
* Time in seconds, for the character's voice audio to gradually degrade until it is completely turned off when interrupted.
|
||||
*/
|
||||
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Convai")
|
||||
float InterruptVoiceFadeOutDuration;
|
||||
|
||||
/**
|
||||
* Value between -1 and 1, a value greater than zero over extends the emotions strength and a value less than zero dimishes it.
|
||||
*/
|
||||
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Convai")
|
||||
float EmotionOffset = 0;
|
||||
|
||||
/**
|
||||
* Contains key value pairs used for Narrative Design.
|
||||
*/
|
||||
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Convai|NarrativeDesign")
|
||||
TMap<FString, FString> NarrativeTemplateKeys;
|
||||
|
||||
/**
|
||||
* Extra information that can be passed to the character, can contain any important data that the chracter needs to know about without the
|
||||
* need of player interaction or narrative triggers, e.g. Inventory items, Player health, key information to solve a puzzle, time of day, etc...
|
||||
*/
|
||||
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Convai")
|
||||
FString DynamicEnvironmentInfo;
|
||||
|
||||
/**
|
||||
* Speaker ID used for long term memory (LTM)
|
||||
*/
|
||||
UPROPERTY(BlueprintReadOnly, Category = "Convai")
|
||||
FString SpeakerID;
|
||||
|
||||
/**
|
||||
* Reset the conversation with the character and remove previous memory, this is the same as setting the session ID property to -1.
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "Convai")
|
||||
void ResetConversation();
|
||||
|
||||
/**
|
||||
* Loads a new character using its ID
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, BlueprintInternalUseOnly, Category = "Convai")
|
||||
void LoadCharacter(FString NewCharacterID);
|
||||
|
||||
UFUNCTION(BlueprintCallable, BlueprintInternalUseOnly, Category = "Convai")
|
||||
void LoadEnvironment(UConvaiEnvironment* NewConvaiEnvironment);
|
||||
|
||||
/**
|
||||
* Appends a TArray of FConvaiResultAction items to the existing ActionsQueue.
|
||||
* If ActionsQueue is not empty, it takes the first element, appends the new array to it,
|
||||
* and then reassigns it back to ActionsQueue. If ActionsQueue is empty, it simply sets ActionsQueue to the new array.
|
||||
*
|
||||
* @param NewActions Array of FConvaiResultAction items to be appended.
|
||||
*
|
||||
* @category Convai
|
||||
*/
|
||||
void AppendActionsToQueue(TArray<FConvaiResultAction> NewActions);
|
||||
|
||||
/**
|
||||
* Marks the current action as completed and handles post-execution logic.
|
||||
*
|
||||
* @param IsSuccessful A boolean flag indicating whether the executed action was successful or not. If true, the next action in the queue will be processed. If false, the current action will be retried.
|
||||
* @param Delay A float value representing the time in seconds to wait before attempting either the next action or retrying the current action, depending on the value of IsSuccessful.
|
||||
*
|
||||
* @note This function should be invoked after each action execution to manage the action queue.
|
||||
*
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "Convai|Actions")
|
||||
void HandleActionCompletion(bool IsSuccessful, float Delay);
|
||||
|
||||
/**
|
||||
* Checks if the ActionsQueue managed by the Convai chatbot component is empty.
|
||||
*
|
||||
* @return Returns true if the ActionsQueue is empty; otherwise, returns false.
|
||||
*
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Convai|Actions")
|
||||
bool IsActionsQueueEmpty();
|
||||
|
||||
/**
|
||||
* Clears the ActionsQueue managed by the Convai chatbot component.
|
||||
*
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "Convai|Actions")
|
||||
void ClearActionQueue();
|
||||
|
||||
/**
|
||||
* Fetches the first action from the ActionsQueue managed by the Convai chatbot component.
|
||||
*
|
||||
* @param ConvaiResultAction Reference to a struct that will be populated with the details of the first action in the queue.
|
||||
*
|
||||
* @return Returns true if there is at least one action in the ActionsQueue and the struct has been successfully populated; otherwise, returns false.
|
||||
*
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Convai|Actions")
|
||||
bool FetchFirstAction(FConvaiResultAction& ConvaiResultAction);
|
||||
|
||||
/**
|
||||
* Removes the first action from the ActionsQueue managed by the Convai chatbot component.
|
||||
*
|
||||
* @return Returns true if an action was successfully removed; otherwise, returns false.
|
||||
*
|
||||
*/
|
||||
bool DequeueAction();
|
||||
|
||||
/**
|
||||
* Starts executing the first action in the ActionsQueue by calling TriggerNamedBlueprintAction.
|
||||
*
|
||||
* @return Returns true if the first action was successfully started; otherwise, returns false.
|
||||
*
|
||||
*/
|
||||
UFUNCTION()
|
||||
bool StartFirstAction();
|
||||
|
||||
/**
|
||||
* Triggers a specified Blueprint event or function on the owning actor based on the given action name and parameters.
|
||||
*
|
||||
* @param ActionName The name of the Blueprint event or function to trigger. This event or function should exist in the Blueprint that owns this component.
|
||||
* @param ConvaiActionStruct A struct containing additional data or parameters to pass to the Blueprint event or function.
|
||||
*
|
||||
* @note The function attempts to dynamically find and call a Blueprint event or function in the owning actor's class. If the Blueprint event or function does not exist or if the signature doesn't match, the function will log a warning.
|
||||
*
|
||||
*/
|
||||
bool TriggerNamedBlueprintAction(const FString& ActionName, FConvaiResultAction ConvaiActionStruct);
|
||||
bool TryCallFunction(UObject* Object, const FString& FunctionName, FConvaiResultAction& ConvaiResultAction) const;
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "Convai|Emotion")
|
||||
void ForceSetEmotion(EBasicEmotions BasicEmotion, EEmotionIntensity Intensity, bool ResetOtherEmotions = false);
|
||||
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Convai|Emotion")
|
||||
float GetEmotionScore(EBasicEmotions Emotion);
|
||||
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Convai|Emotion")
|
||||
TMap<FName, float> GetEmotionBlendshapes();
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "Convai|Emotion")
|
||||
void ResetEmotionState();
|
||||
|
||||
public:
|
||||
// UFUNCTION(BlueprintCallable, Category = "Convai|Voice")
|
||||
void StartRecordingVoice();
|
||||
|
||||
// UFUNCTION(BlueprintCallable, Category = "Convai|Voice")
|
||||
USoundWave* FinishRecordingVoice();
|
||||
|
||||
// UFUNCTION(BlueprintCallable, Category = "Convai|Voice")
|
||||
bool PlayRecordedVoice(USoundWave* RecordedVoice);
|
||||
|
||||
public:
|
||||
/** Called when a new action is received from the API */
|
||||
UPROPERTY(BlueprintAssignable, Category = "Convai", meta = (DisplayName = "_DEPRECATED On Actions Received"))
|
||||
FOnActionReceivedSignature_Deprecated OnActionReceivedEvent;
|
||||
UPROPERTY(BlueprintAssignable, Category = "Convai", meta = (DisplayName = "On Actions Received"))
|
||||
FOnActionReceivedSignature_V2 OnActionReceivedEvent_V2;
|
||||
|
||||
UPROPERTY(BlueprintAssignable, Category = "Convai", meta = (DisplayName = "On Emotion State Changed"))
|
||||
FOnEmotionReceivedSignature OnEmotionStateChangedEvent;
|
||||
|
||||
/** Called when new text is received from the API, AudioDuration = 0 if no audio was received */
|
||||
UPROPERTY(BlueprintAssignable, Category = "Convai", meta = (DisplayName = "_DEPRECATED On Text Received"))
|
||||
FOnTextReceivedSignature_Deprecated OnTextReceivedEvent;
|
||||
UPROPERTY(BlueprintAssignable, Category = "Convai", meta = (DisplayName = "On Text Received"))
|
||||
FOnTextReceivedSignature_V2 OnTextReceivedEvent_V2;
|
||||
|
||||
/** Called when new transcription is available */
|
||||
UPROPERTY(BlueprintAssignable, Category = "Convai", meta = (DisplayName = "_DEPRECATED On Transcription Received"))
|
||||
FOnTranscriptionReceivedSignature_Deprecated OnTranscriptionReceivedEvent;
|
||||
UPROPERTY(BlueprintAssignable, Category = "Convai", meta = (DisplayName = "On Transcription Received"))
|
||||
FOnTranscriptionReceivedSignature_V2 OnTranscriptionReceivedEvent_V2;
|
||||
|
||||
UPROPERTY(BlueprintAssignable, Category = "Convai", meta = (DisplayName = "_DEPRECATED On Character Data Loaded"))
|
||||
FOnCharacterDataLoadSignature_Deprecated OnCharacterDataLoadEvent;
|
||||
UPROPERTY(BlueprintAssignable, Category = "Convai", meta = (DisplayName = "On Character Data Loaded"))
|
||||
FOnCharacterDataLoadSignature_V2 OnCharacterDataLoadEvent_V2;
|
||||
|
||||
UPROPERTY(BlueprintAssignable, Category = "Convai", meta = (DisplayName = "On Narrative Section Received"))
|
||||
FOnNarrativeSectionReceivedSignature OnNarrativeSectionReceivedEvent;
|
||||
|
||||
UPROPERTY(BlueprintAssignable, Category = "Convai", meta = (DisplayName = "On Interaction ID Received"))
|
||||
FOnInteractionIDReceivedSignature OnInteractionIDReceivedEvent;
|
||||
|
||||
/** Called when the character is interrupted */
|
||||
UPROPERTY(BlueprintAssignable, Category = "Convai", meta = (DisplayName = "On Interrupted"))
|
||||
FOnInterruptedSignature OnInterruptedEvent;
|
||||
|
||||
/** Called when there is an error */
|
||||
UPROPERTY(BlueprintAssignable, Category = "Convai", meta = (DisplayName = "On Failure"))
|
||||
FOnFailureSignature OnFailureEvent;
|
||||
|
||||
public:
|
||||
//UFUNCTION(BlueprintCallable, DisplayName = "Begin Transmission")
|
||||
void StartGetResponseStream(UConvaiPlayerComponent* InConvaiPlayerComponent, FString InputText, UConvaiEnvironment* InEnvironment, bool InGenerateActions, bool InVoiceResponse, bool ReplicateVoiceToNetwork, bool UseOverrideAuthKey, FString OverrideAuthKey, FString OverrideAuthHeader, uint32 InToken, FString InSpeakerID);
|
||||
|
||||
void FinishGetResponseStream(UConvaiPlayerComponent* InConvaiPlayerComponent);
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "Convai", meta = (DisplayName = "Invoke Speech"))
|
||||
void ExecuteNarrativeTrigger(FString TriggerMessage, UConvaiEnvironment* InEnvironment, bool InGenerateActions, bool InVoiceResponse, bool InReplicateOnNetwork);
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "Convai", meta = (DisplayName = "Invoke Narrative Design Trigger"))
|
||||
void InvokeNarrativeDesignTrigger(FString TriggerName, UConvaiEnvironment* InEnvironment, bool InGenerateActions, bool InVoiceResponse, bool InReplicateOnNetwork);
|
||||
|
||||
void InvokeTrigger_Internal(FString TriggerName, FString TriggerMessage, UConvaiEnvironment* InEnvironment, bool InGenerateActions, bool InVoiceResponse, bool InReplicateOnNetwork);
|
||||
|
||||
// Interrupts the current speech with a provided fade-out duration.
|
||||
// The fade-out duration is controlled by the parameter 'InVoiceFadeOutDuration'.
|
||||
UFUNCTION(BlueprintCallable, Category = "Convai")
|
||||
void InterruptSpeech(float InVoiceFadeOutDuration);
|
||||
|
||||
// Trys to clear the Interacting player variable if there were no conversation going
|
||||
// If Force clear was true, it will interrupt the conversation and clear the interacting player
|
||||
UFUNCTION(BlueprintCallable, Category = "Convai")
|
||||
void TryClearInteractingPlayer(bool& Success, bool Interrupt);
|
||||
|
||||
// Broadcasts an interruption of the current speech across a network, with a provided fade-out duration.
|
||||
// This function ensures that the interruption is communicated reliably to all connected clients.
|
||||
// The fade-out duration is controlled by the parameter 'InVoiceFadeOutDuration'.
|
||||
UFUNCTION(NetMulticast, Reliable, Category = "VoiceNetworking")
|
||||
void Broadcast_InterruptSpeech(float InVoiceFadeOutDuration);
|
||||
|
||||
public:
|
||||
// AActorComponent interface
|
||||
virtual void BeginPlay() override;
|
||||
//virtual void OnRegister() override;
|
||||
//virtual void OnUnregister() override;
|
||||
virtual void TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override;
|
||||
// End AActorComponent interface
|
||||
|
||||
//~ Begin UObject Interface.
|
||||
virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;
|
||||
virtual void BeginDestroy() override;
|
||||
//~ End UObject Interface.
|
||||
|
||||
//~ Begin UConvaiAudioStreamer Interface.
|
||||
virtual bool CanUseLipSync() override;
|
||||
virtual bool CanUseVision() override;
|
||||
//~ End UConvaiAudioStreamer Interface.
|
||||
|
||||
private:
|
||||
UConvaiChatBotGetDetailsProxy* ConvaiGetDetails();
|
||||
|
||||
TScriptDelegate<FWeakObjectPtr> ConvaiChatBotGetDetailsDelegate;
|
||||
|
||||
UFUNCTION()
|
||||
void OnConvaiGetDetailsCompleted(FString ReceivedCharacterName, FString ReceivedVoiceType, FString ReceivedBackstory, FString ReceivedLanguageCode, bool HasReadyPlayerMeLink, FString ReceivedReadyPlayerMeLink, FString ReceivedAvatarImageLink);
|
||||
|
||||
private:
|
||||
// Used when both the voice component ring buffer is empty and we have already sent all the current audio data over the stream
|
||||
void onMicrophoneDataReceived();
|
||||
|
||||
// Returns false if the player has decided to stop sending audio
|
||||
bool CheckTokenValidity();
|
||||
|
||||
// Called when a player time outs while sending audio data (for example 2 secs have passed without us getting any audio from him)
|
||||
void OnPlayerTimeOut();
|
||||
|
||||
void ClearTimeOutTimer();
|
||||
|
||||
bool HasOnGoingGetResponseStream();
|
||||
|
||||
bool CanWriteToGetResponseStream();
|
||||
|
||||
bool ConsumeMicStreamIntoBuffer();
|
||||
|
||||
private:
|
||||
void Start_GRPC_Request(bool UseOverrideAuthKey, FString OverrideAuthKey, FString OverrideAuthHeader, FString TriggerName = "", FString TriggerMessage = "");
|
||||
|
||||
void Bind_GRPC_Request_Delegates();
|
||||
|
||||
void Unbind_GRPC_Request_Delegates();
|
||||
|
||||
void Cleanup(bool StreamConnectionFinished = false);
|
||||
|
||||
private:
|
||||
UFUNCTION(NetMulticast, Reliable, Category = "Convai")
|
||||
void Broadcast_OnTranscriptionReceived(const FString& Transcription, bool IsTranscriptionReady, bool IsFinal);
|
||||
UFUNCTION(NetMulticast, Reliable, Category = "Convai")
|
||||
void Broadcast_onResponseDataReceived(const FString& ReceivedText, bool IsFinal);
|
||||
UFUNCTION(NetMulticast, Reliable, Category = "Convai")
|
||||
void Broadcast_onSessionIDReceived(const FString& ReceivedSessionID);
|
||||
UFUNCTION(NetMulticast, Reliable, Category = "Convai")
|
||||
void Broadcast_onActionSequenceReceived(const TArray<FConvaiResultAction>& ReceivedSequenceOfActions);
|
||||
UFUNCTION(NetMulticast, Reliable, Category = "Convai")
|
||||
void Broadcast_OnNarrativeSectionReceived(const FString& BT_Code, const FString& BT_Constants, const FString& ReceivedNarrativeSectionID);
|
||||
UFUNCTION(NetMulticast, Reliable, Category = "Convai")
|
||||
void Broadcast_onInteractionIDReceived(const FString& ReceivedInteractionID);
|
||||
UFUNCTION(NetMulticast, Reliable, Category = "Convai")
|
||||
void Broadcast_onEmotionReceived(const FString& ReceivedEmotionResponse, bool MultipleEmotions);
|
||||
|
||||
void OnTranscriptionReceived(FString Transcription, bool IsTranscriptionReady, bool IsFinal);
|
||||
void onResponseDataReceived(const FString ReceivedText, const TArray<uint8>& ReceivedAudio, uint32 SampleRate, bool IsFinal);
|
||||
void OnFaceDataReceived(FAnimationSequence FaceDataAnimation);
|
||||
void onSessionIDReceived(FString ReceivedSessionID);
|
||||
void onInteractionIDReceived(FString ReceivedInteractionID);
|
||||
void onActionSequenceReceived(const TArray<FConvaiResultAction>& ReceivedSequenceOfActions);
|
||||
void onEmotionReceived(FString ReceivedEmotionResponse, FAnimationFrame EmotionBlendshapesFrame, bool MultipleEmotions);
|
||||
void onFinishedReceivingData();
|
||||
void OnNarrativeSectionReceived(FString BT_Code, FString BT_Constants, FString ReceivedNarrativeSectionID);
|
||||
void onFailure();
|
||||
|
||||
private:
|
||||
UPROPERTY(Replicated, ReplicatedUsing = OnRep_EnvironmentData)
|
||||
FConvaiEnvironmentDetails ConvaiEnvironmentDetails;
|
||||
|
||||
UFUNCTION()
|
||||
void OnRep_EnvironmentData();
|
||||
|
||||
void UpdateEnvironmentData();
|
||||
|
||||
private:
|
||||
UPROPERTY()
|
||||
UConvaiChatBotGetDetailsProxy* ConvaiChatBotGetDetailsProxy;
|
||||
|
||||
UPROPERTY()
|
||||
UConvaiGRPCGetResponseProxy* ConvaiGRPCGetResponseProxy;
|
||||
|
||||
bool GenerateActions; // Should we generate actions
|
||||
bool TextInput; // Whether to use text or audio as input to the API
|
||||
bool VoiceResponse; // Require audio response from the API
|
||||
FString UserText; // Input text to send to the API in case of TextInput is set to true
|
||||
uint32 Token; // Used to check if the Convai player component is still streaming to us
|
||||
|
||||
bool StreamInProgress = false; // Are we receiving mic audio from player?
|
||||
FTimerHandle TimeOutTimerHandle; // Timeout handler for player not sending audio data through mic
|
||||
|
||||
FString LastTranscription;
|
||||
bool ReceivedFinalTranscription;
|
||||
bool ReceivedFinalData; // Did the character end his response
|
||||
FString LastPlayerName;
|
||||
TArray<uint8> PlayerInpuAudioBuffer;
|
||||
|
||||
TMap<FName, float> EmotionBlendshapes;
|
||||
float TotalReceivedAudioDuration = 0;
|
||||
|
||||
TArray<uint8> RecordedAudio;
|
||||
uint32 RecordedAudioSampleRate;
|
||||
bool IsRecordingAudio;
|
||||
};
|
||||
1094
ConvAI/Convai/Source/Convai/Public/ConvaiDefinitions.h
Normal file
1094
ConvAI/Convai/Source/Convai/Public/ConvaiDefinitions.h
Normal file
File diff suppressed because it is too large
Load Diff
108
ConvAI/Convai/Source/Convai/Public/ConvaiFaceSync.h
Normal file
108
ConvAI/Convai/Source/Convai/Public/ConvaiFaceSync.h
Normal file
@@ -0,0 +1,108 @@
|
||||
// Copyright 2022 Convai Inc. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "LipSyncInterface.h"
|
||||
|
||||
#include "Components/SceneComponent.h"
|
||||
#include "Containers/Map.h"
|
||||
#include "ConvaiDefinitions.h"
|
||||
#include "ConvaiFaceSync.generated.h"
|
||||
|
||||
DECLARE_LOG_CATEGORY_EXTERN(ConvaiFaceSyncLog, Log, All);
|
||||
|
||||
UCLASS(meta = (BlueprintSpawnableComponent), DisplayName = "Convai Face Sync")
|
||||
class CONVAI_API UConvaiFaceSyncComponent : public USceneComponent, public IConvaiLipSyncInterface
|
||||
{
|
||||
GENERATED_BODY()
|
||||
public:
|
||||
UConvaiFaceSyncComponent();
|
||||
|
||||
virtual ~UConvaiFaceSyncComponent();
|
||||
|
||||
// UActorComponent interface
|
||||
virtual void BeginPlay() override;
|
||||
// virtual void OnRegister() override;
|
||||
// virtual void OnUnregister() override;
|
||||
virtual void TickComponent(float DeltaTime, enum ELevelTick TickType,
|
||||
FActorComponentTickFunction* ThisTickFunction) override;
|
||||
// End UActorComponent interface
|
||||
|
||||
// IConvaiLipSyncInterface
|
||||
virtual void ConvaiInferFacialDataFromAudio(uint8* InPCMData, uint32 InPCMDataSize, uint32 InSampleRate, uint32 InNumChannels) override { return; }
|
||||
virtual void ConvaiStopLipSync() override;
|
||||
virtual TArray<float> ConvaiGetVisemes() override
|
||||
{
|
||||
TArray<float> VisemeValues;
|
||||
CurrentBlendShapesMap.GenerateValueArray(VisemeValues);
|
||||
return VisemeValues;
|
||||
}
|
||||
virtual TArray<FString> ConvaiGetVisemeNames() override { return ConvaiConstants::VisemeNames; }
|
||||
// End IConvaiLipSyncInterface interface
|
||||
|
||||
// IConvaiLipSyncExtendedInterface
|
||||
virtual void ConvaiApplyPrecomputedFacialAnimation(uint8* InPCMData, uint32 InPCMDataSize, uint32 InSampleRate, uint32 InNumChannels, FAnimationSequence FaceSequence) override;
|
||||
virtual void ConvaiApplyFacialFrame(FAnimationFrame FaceFrame, float Duration) override;
|
||||
virtual bool RequiresPrecomputedFaceData() override { return true; }
|
||||
virtual bool GeneratesVisemesAsBlendshapes() override { return ToggleBlendshapeOrViseme; }
|
||||
virtual TMap<FName, float> ConvaiGetFaceBlendshapes() override { return CurrentBlendShapesMap; }
|
||||
// End IConvaiLipSyncExtendedInterface interface
|
||||
|
||||
virtual void Apply_StartEndFrames_PostProcessing(const int& CurrentFrameIndex, const int& NextFrameIndex, float& Alpha, TMap<FName, float>& StartFrame, TMap<FName, float>& EndFrame){}
|
||||
virtual void ApplyPostProcessing() {}
|
||||
|
||||
// UFUNCTION(BlueprintCallable, Category = "Convai|LipSync")
|
||||
void StartRecordingLipSync();
|
||||
|
||||
// UFUNCTION(BlueprintCallable, Category = "Convai|LipSync")
|
||||
FAnimationSequenceBP FinishRecordingLipSync();
|
||||
|
||||
// UFUNCTION(BlueprintCallable, Category = "Convai|LipSync")
|
||||
bool PlayRecordedLipSync(FAnimationSequenceBP RecordedLipSync, int StartFrame, int EndFrame, float OverwriteDuration);
|
||||
|
||||
bool IsValidSequence(const FAnimationSequence &Sequence);
|
||||
|
||||
bool IsPlaying();
|
||||
|
||||
// Record the current time if this is the first LipSync sequence to be received after silence
|
||||
virtual void CalculateStartingTime();
|
||||
|
||||
virtual void ForceRecalculateStartTime() override;
|
||||
|
||||
void ClearMainSequence();
|
||||
|
||||
TMap<FName, float> InterpolateFrames(const TMap<FName, float>& StartFrame, const TMap<FName, float>& EndFrame, float Alpha);
|
||||
|
||||
TMap<FName, float> GenerateZeroFrame() { return GeneratesVisemesAsBlendshapes() ? ZeroBlendshapeFrame : ZeroVisemeFrame; }
|
||||
|
||||
void SetCurrentFrametoZero()
|
||||
{
|
||||
if (GeneratesVisemesAsBlendshapes())
|
||||
CurrentBlendShapesMap = ZeroBlendshapeFrame;
|
||||
else
|
||||
CurrentBlendShapesMap = ZeroVisemeFrame;
|
||||
}
|
||||
|
||||
TMap<FName, float> GetCurrentFrame() { return CurrentBlendShapesMap; }
|
||||
|
||||
const static TMap<FName, float> ZeroBlendshapeFrame;
|
||||
const static TMap<FName, float> ZeroVisemeFrame;
|
||||
|
||||
//UPROPERTY(EditAnywhere, Category = "Convai|LipSync")
|
||||
float AnchorValue = 0.5;
|
||||
|
||||
UPROPERTY(EditAnywhere, Category = "Convai|LipSync")
|
||||
bool ToggleBlendshapeOrViseme = false;
|
||||
|
||||
protected:
|
||||
float CurrentSequenceTimePassed;
|
||||
TMap<FName, float> CurrentBlendShapesMap;
|
||||
FAnimationSequence MainSequenceBuffer;
|
||||
FAnimationSequence RecordedSequenceBuffer;
|
||||
FCriticalSection SequenceCriticalSection;
|
||||
FCriticalSection RecordingCriticalSection;
|
||||
bool Stopping;
|
||||
bool IsRecordingLipSync;
|
||||
double StartTime;
|
||||
bool bIsPlaying;
|
||||
};
|
||||
332
ConvAI/Convai/Source/Convai/Public/ConvaiGRPC.h
Normal file
332
ConvAI/Convai/Source/Convai/Public/ConvaiGRPC.h
Normal file
@@ -0,0 +1,332 @@
|
||||
// Copyright 2022 Convai Inc. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ConvaiSubsystem.h"
|
||||
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Sound/SoundWave.h"
|
||||
#include "Net/OnlineBlueprintCallProxyBase.h"
|
||||
#include "HAL/ThreadSafeBool.h"
|
||||
#include "ConvaiDefinitions.h"
|
||||
#include "Containers/Map.h"
|
||||
#include "ConvaiGRPC.generated.h"
|
||||
|
||||
|
||||
DECLARE_LOG_CATEGORY_EXTERN(ConvaiGRPCLog, Log, All);
|
||||
DECLARE_LOG_CATEGORY_EXTERN(ConvaiGRPCFeedBackLog, Log, All);
|
||||
|
||||
class USoundWave;
|
||||
class UConvaiEnvironment;
|
||||
struct FAnimationFrame;
|
||||
struct FAnimationSequence;
|
||||
struct FConvaiResultAction;
|
||||
|
||||
DECLARE_DELEGATE_ThreeParams(FConvaiGRPCOnNarrativeDataSignature, const FString /*BT_Code*/, const FString /*BT_Constants*/, const FString /*NarrativeSectionID*/);
|
||||
DECLARE_DELEGATE_ThreeParams(FConvaiGRPCOnTranscriptionSignature, const FString /*Transcription*/, bool /*IsTranscriptionReady*/, bool /*IsFinal*/);
|
||||
DECLARE_DELEGATE_FourParams(FConvaiGRPCOnDataSignature, const FString /*ReceivedText*/, const TArray<uint8>& /*ReceivedAudio*/, uint32 /*SampleRate*/, bool /*IsFinal*/);
|
||||
DECLARE_DELEGATE_OneParam(FConvaiGRPCOnFaceDataSignature, FAnimationSequence /*FaceData*/);
|
||||
DECLARE_DELEGATE_OneParam(FConvaiGRPCOnActionsSignature, const TArray<FConvaiResultAction>& /*ActionSequence*/);
|
||||
DECLARE_DELEGATE_ThreeParams(FConvaiGRPCOnEmotionSignature, FString /*EmotionResponse*/, FAnimationFrame /*EmotionBlendshapes*/, bool /*MultipleEmotions*/);
|
||||
DECLARE_DELEGATE_OneParam(FConvaiGRPCOnSessiondIDSignature, FString /*SessionID*/);
|
||||
DECLARE_DELEGATE_OneParam(FConvaiGRPCOnInteractionIDSignature, FString /*InteractionID*/);
|
||||
DECLARE_DELEGATE(FConvaiGRPCOnEventSignature);
|
||||
|
||||
USTRUCT()
|
||||
struct FConvaiGRPCVisionParams
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
int width;
|
||||
int height;
|
||||
TArray<uint8> data;
|
||||
|
||||
FConvaiGRPCVisionParams()
|
||||
: width(0)
|
||||
, height(0)
|
||||
, data(TArray<uint8>())
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
USTRUCT()
|
||||
struct FConvaiGRPCGetResponseParams
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
FString UserQuery;
|
||||
|
||||
FString TriggerName;
|
||||
|
||||
FString TriggerMessage;
|
||||
|
||||
FString CharID;
|
||||
|
||||
bool VoiceResponse;
|
||||
|
||||
bool RequireFaceData;
|
||||
|
||||
bool GeneratesVisemesAsBlendshapes;
|
||||
|
||||
FString SessionID;
|
||||
|
||||
UConvaiEnvironment* Environment;
|
||||
|
||||
bool GenerateActions;
|
||||
|
||||
FConvaiGRPCVisionParams ConvaiGRPCVisionParams;
|
||||
|
||||
FString AuthKey;
|
||||
|
||||
FString AuthHeader;
|
||||
|
||||
TMap<FString, FString> Narrative_Template_Keys;
|
||||
|
||||
FString DynamicEnvironmentInfo;
|
||||
|
||||
FString SpeakerID;
|
||||
|
||||
FConvaiGRPCGetResponseParams()
|
||||
: UserQuery(TEXT(""))
|
||||
, TriggerName(TEXT(""))
|
||||
, TriggerMessage(TEXT(""))
|
||||
, CharID(TEXT(""))
|
||||
, VoiceResponse(false)
|
||||
, RequireFaceData(false)
|
||||
, GeneratesVisemesAsBlendshapes(false)
|
||||
, SessionID(TEXT(""))
|
||||
, Environment(nullptr)
|
||||
, GenerateActions(false)
|
||||
, AuthKey(TEXT(""))
|
||||
, AuthHeader(TEXT(""))
|
||||
, DynamicEnvironmentInfo(TEXT(""))
|
||||
, SpeakerID(TEXT(""))
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
UCLASS()
|
||||
class UConvaiGRPCGetResponseProxy : public UObject
|
||||
{
|
||||
GENERATED_BODY()
|
||||
public:
|
||||
// Called when user transcription is received
|
||||
FThreadSafeDelegateWrapper<FConvaiGRPCOnTranscriptionSignature> OnTranscriptionReceived;
|
||||
|
||||
// Called when new text and/or Audio data is received
|
||||
FThreadSafeDelegateWrapper<FConvaiGRPCOnDataSignature> OnDataReceived;
|
||||
|
||||
// Called when new face data is received
|
||||
FThreadSafeDelegateWrapper<FConvaiGRPCOnFaceDataSignature> OnFaceDataReceived;
|
||||
|
||||
// Called when actions are received
|
||||
FThreadSafeDelegateWrapper<FConvaiGRPCOnActionsSignature> OnActionsReceived;
|
||||
|
||||
// Called when new SessionID is received
|
||||
FThreadSafeDelegateWrapper<FConvaiGRPCOnSessiondIDSignature> OnSessionIDReceived;
|
||||
|
||||
// Called when the InteractionID is received
|
||||
FThreadSafeDelegateWrapper<FConvaiGRPCOnInteractionIDSignature> OnInteractionIDReceived;
|
||||
|
||||
FThreadSafeDelegateWrapper<FConvaiGRPCOnNarrativeDataSignature> OnNarrativeDataReceived;
|
||||
|
||||
// Called when new Emotion is received
|
||||
FThreadSafeDelegateWrapper<FConvaiGRPCOnEmotionSignature> OnEmotionReceived;
|
||||
|
||||
// Called when the response stream is done
|
||||
FThreadSafeDelegateWrapper<FConvaiGRPCOnEventSignature> OnFinish;
|
||||
|
||||
// Called when there is an unsuccessful response
|
||||
FThreadSafeDelegateWrapper<FConvaiGRPCOnEventSignature> OnFailure;
|
||||
|
||||
//UFUNCTION(BlueprintCallable, meta = (BlueprintInternalUseOnly = "true", DisplayName = "Convai GRPC Test", WorldContext = "WorldContextObject"), Category = "Convai|gRPC")
|
||||
static UConvaiGRPCGetResponseProxy* CreateConvaiGRPCGetResponseProxy(UObject* WorldContextObject, FConvaiGRPCGetResponseParams ConvaiGRPCGetResponseParams);
|
||||
|
||||
void Activate();
|
||||
|
||||
void WriteAudioDataToSend(uint8* Buffer, uint32 Length);
|
||||
|
||||
void FinishWriting();
|
||||
|
||||
//~ Begin UObject Interface.
|
||||
virtual void BeginDestroy() override;
|
||||
//~ End UObject Interface.
|
||||
|
||||
bool IsStreamFinished()
|
||||
{
|
||||
return ReceivedFinish;
|
||||
}
|
||||
|
||||
bool CanWriteToStream()
|
||||
{
|
||||
return !IsStreamFinished() && !LastWriteReceived && !FinishedWritingToStream;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
void CallFinish();
|
||||
|
||||
TArray<uint8> ConsumeFromAudioBuffer(bool& IsThisTheFinalWrite);
|
||||
|
||||
void LogAndEcecuteFailure(FString FuncName);
|
||||
|
||||
void ExtendDeadline();
|
||||
|
||||
void OnStreamInit(bool ok);
|
||||
void OnStreamRead(bool ok);
|
||||
void OnStreamWrite(bool ok);
|
||||
void OnStreamWriteDone(bool ok);
|
||||
void OnStreamFinish(bool ok);
|
||||
|
||||
FgRPC_Delegate OnInitStreamDelegate;
|
||||
FgRPC_Delegate OnStreamReadDelegate;
|
||||
FgRPC_Delegate OnStreamWriteDelegate;
|
||||
FgRPC_Delegate OnStreamWriteDoneDelegate; // called when a stream->writesdone is successful
|
||||
|
||||
// called when a stream->finish is successful it means that the server decided
|
||||
// to end the stream, we should check the status variable in that call
|
||||
// https://groups.google.com/g/grpc-io/c/R0NTqKaHLdE
|
||||
FgRPC_Delegate OnStreamFinishDelegate;
|
||||
|
||||
service::GetResponseRequest request;
|
||||
std::unique_ptr<service::GetResponseResponse> reply;
|
||||
|
||||
// Context for the client. It could be used to convey extra information to
|
||||
// the server and/or tweak certain RPC behaviors.
|
||||
//std::unique_ptr<ClientContext> context;
|
||||
|
||||
// Storage for the status of the RPC upon completion.
|
||||
grpc::Status status;
|
||||
|
||||
std::unique_ptr<::grpc::ClientAsyncReaderWriter< service::GetResponseRequest, service::GetResponseResponse>> stream_handler;
|
||||
|
||||
grpc::ClientContext client_context;
|
||||
|
||||
// True if we are writing audio to the server, false if we are in the receiving stage
|
||||
bool StreamInProgress = false;
|
||||
|
||||
bool FailAlreadyExecuted = false;
|
||||
|
||||
bool FinishedWritingToStream = false;
|
||||
|
||||
std::unique_ptr<service::ConvaiService::Stub> stub_;
|
||||
|
||||
grpc::CompletionQueue* cq_;
|
||||
|
||||
uint32 NumberOfAudioBytesSent = 0;
|
||||
|
||||
private:
|
||||
// Inputs
|
||||
FConvaiGRPCGetResponseParams ConvaiGRPCGetResponseParams;
|
||||
|
||||
|
||||
private:
|
||||
// becomes true if we receive a non-ok header
|
||||
FThreadSafeBool InformOnDataReceived;
|
||||
|
||||
// Stores the audio data to be streamed to the API
|
||||
TArray<uint8> AudioBuffer;
|
||||
|
||||
// True when we are informed that the "AudioBuffer" is complete and no more audio will be received
|
||||
FThreadSafeBool LastWriteReceived;
|
||||
|
||||
// Used to regulate access to the AudioBuffer to prevent racing condition
|
||||
FCriticalSection m_mutex;
|
||||
|
||||
// Used to prevent early execution of the Init delegate
|
||||
FCriticalSection GPRCInitSection;
|
||||
|
||||
// Pointer to the world
|
||||
TWeakObjectPtr<UWorld> WorldPtr;
|
||||
|
||||
FThreadSafeBool ReceivedFinish;
|
||||
FThreadSafeBool CalledFinish;
|
||||
FThreadSafeBool ReceivedFinalResponse;
|
||||
|
||||
// Retry reading counters
|
||||
int RetryCount = 0;
|
||||
int MaxRetries = 3;
|
||||
|
||||
int TotalLipSyncResponsesReceived = 0;
|
||||
};
|
||||
|
||||
|
||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FSubmitFeedbackCallbackSignature, FString, FeedbackResponse);
|
||||
|
||||
UCLASS()
|
||||
class UConvaiGRPCSubmitFeedbackProxy : public UOnlineBlueprintCallProxyBase
|
||||
{
|
||||
GENERATED_BODY()
|
||||
public:
|
||||
|
||||
// Called when there is a successful response
|
||||
UPROPERTY(BlueprintAssignable)
|
||||
FSubmitFeedbackCallbackSignature OnSuccess;
|
||||
|
||||
// Called when there is an unsuccessful response
|
||||
UPROPERTY(BlueprintAssignable)
|
||||
FSubmitFeedbackCallbackSignature OnFailure;
|
||||
|
||||
/**
|
||||
* Initiates a feedback request to the gRPC API.
|
||||
* @param InteractionID Identifies the message the feedback is being submitted for - Check "On Interaction ID Received" on the Convai Chatbot Component
|
||||
* @param Thumbs Up False if the message is incorrect or inaccurate, True otherwise.
|
||||
* @param FeedbackText Additionaly feedback that can further help the model learn why the user have given a thumbs up or thumbs down.
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, meta = (BlueprintInternalUseOnly = "true", DisplayName = "Convai Submit Feedback", WorldContext = "WorldContextObject"), Category = "Convai")
|
||||
static UConvaiGRPCSubmitFeedbackProxy* CreateConvaiGRPCSubmitFeedbackProxy(UObject* WorldContextObject, FString InteractionID, bool ThumbsUp, FString FeedbackText);
|
||||
|
||||
|
||||
virtual void Activate() override;
|
||||
|
||||
void failed();
|
||||
void success();
|
||||
void finish();
|
||||
|
||||
private:
|
||||
|
||||
FString InteractionID;
|
||||
bool ThumbsUp;
|
||||
FString FeedbackText;
|
||||
|
||||
FString Response;
|
||||
|
||||
// Pointer to the world
|
||||
TWeakObjectPtr<UWorld> WorldPtr;
|
||||
|
||||
//~ Begin UObject Interface.
|
||||
virtual void BeginDestroy() override;
|
||||
//~ End UObject Interface.
|
||||
|
||||
private:
|
||||
|
||||
void LogAndEcecuteFailure(FString FuncName);
|
||||
|
||||
void OnStreamFinish(bool ok);
|
||||
|
||||
// called when a stream->finish is successful it means that the server decided
|
||||
// to end the stream, we should check the status variable in that call
|
||||
// https://groups.google.com/g/grpc-io/c/R0NTqKaHLdE
|
||||
FgRPC_Delegate OnStreamFinishDelegate;
|
||||
|
||||
service::FeedbackRequest request;
|
||||
std::unique_ptr<service::FeedbackResponse> reply;
|
||||
|
||||
// Storage for the status of the RPC upon completion.
|
||||
grpc::Status status;
|
||||
|
||||
std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::service::FeedbackResponse>> stream_handler;
|
||||
|
||||
// Context for the client. It could be used to convey extra information to
|
||||
// the server and/or tweak certain RPC behaviors.
|
||||
grpc::ClientContext client_context;
|
||||
|
||||
std::unique_ptr<service::ConvaiService::Stub> stub_;
|
||||
|
||||
grpc::CompletionQueue* cq_;
|
||||
};
|
||||
@@ -0,0 +1,77 @@
|
||||
// Copyright 2022 Convai Inc. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Net/OnlineBlueprintCallProxyBase.h"
|
||||
#include "ConvaiDefinitions.h"
|
||||
#include "HttpModule.h"
|
||||
#include "Interfaces/IHttpRequest.h"
|
||||
#include "Interfaces/IHttpResponse.h"
|
||||
#include "JsonUtilities.h"
|
||||
#include "ConvaiNarrativeDesignProxy.generated.h"
|
||||
|
||||
DECLARE_LOG_CATEGORY_EXTERN(ConvaiNarrativeHTTP, Log, All);
|
||||
|
||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FFetchNarrativeSectionsResponseMulticastCallbackSignature, const TArray<FNarrativeSection>&, NarrativeSections);
|
||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FFetchNarrativeTriggersResponseMulticastCallbackSignature, const TArray<FNarrativeTrigger>&, NarrativeTriggers);
|
||||
|
||||
UCLASS()
|
||||
class CONVAI_API UFetchNarrativeSectionsProxy : public UOnlineBlueprintCallProxyBase
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UPROPERTY(BlueprintAssignable)
|
||||
FFetchNarrativeSectionsResponseMulticastCallbackSignature OnSuccess;
|
||||
UPROPERTY(BlueprintAssignable)
|
||||
FFetchNarrativeSectionsResponseMulticastCallbackSignature OnFailure;
|
||||
|
||||
UFUNCTION(BlueprintCallable, meta = (BlueprintInternalUseOnly = "true", DisplayName = "Convai Fetch Narrative Sections", WorldContext = "WorldContextObject"), Category = "Convai|REST API")
|
||||
static UFetchNarrativeSectionsProxy* FetchNarrativeSections(UObject* WorldContextObject, FString CharacterId);
|
||||
|
||||
virtual void Activate() override;
|
||||
|
||||
TArray<FNarrativeSection> NarrativeSections;
|
||||
|
||||
private:
|
||||
void OnHttpRequestCompleted(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful);
|
||||
|
||||
FString CharacterId;
|
||||
TWeakObjectPtr<UWorld> WorldPtr;
|
||||
|
||||
|
||||
void failed();
|
||||
void success();
|
||||
void finish();
|
||||
};
|
||||
|
||||
UCLASS()
|
||||
class CONVAI_API UFetchNarrativeTriggersProxy : public UOnlineBlueprintCallProxyBase
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UPROPERTY(BlueprintAssignable)
|
||||
FFetchNarrativeTriggersResponseMulticastCallbackSignature OnSuccess;
|
||||
UPROPERTY(BlueprintAssignable)
|
||||
FFetchNarrativeTriggersResponseMulticastCallbackSignature OnFailure;
|
||||
|
||||
UFUNCTION(BlueprintCallable, meta = (BlueprintInternalUseOnly = "true", DisplayName = "Convai Fetch Narrative Triggers", WorldContext = "WorldContextObject"), Category = "Convai|REST API")
|
||||
static UFetchNarrativeTriggersProxy* FetchNarrativeTriggers(UObject* WorldContextObject, FString CharacterId);
|
||||
|
||||
virtual void Activate() override;
|
||||
|
||||
TArray<FNarrativeTrigger> NarrativeTriggers;
|
||||
|
||||
private:
|
||||
void OnHttpRequestCompleted(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful);
|
||||
|
||||
FString CharacterId;
|
||||
TWeakObjectPtr<UWorld> WorldPtr;
|
||||
|
||||
|
||||
void failed();
|
||||
void success();
|
||||
void finish();
|
||||
};
|
||||
323
ConvAI/Convai/Source/Convai/Public/ConvaiPlayerComponent.h
Normal file
323
ConvAI/Convai/Source/Convai/Public/ConvaiPlayerComponent.h
Normal file
@@ -0,0 +1,323 @@
|
||||
// Copyright 2022 Convai Inc. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Components/AudioComponent.h"
|
||||
#include "RingBuffer.h"
|
||||
#include "ConvaiAudioStreamer.h"
|
||||
#include "Net/OnlineBlueprintCallProxyBase.h"
|
||||
#include "DSP/BufferVectorOperations.h"
|
||||
#include "ConvaiPlayerComponent.generated.h"
|
||||
|
||||
#define TIME_BETWEEN_VOICE_UPDATES_SECS 0.01
|
||||
|
||||
DECLARE_LOG_CATEGORY_EXTERN(ConvaiPlayerLog, Log, All);
|
||||
|
||||
DECLARE_DELEGATE(FonDataReceived_Delegate);
|
||||
|
||||
// class IVoiceCapture;
|
||||
class UConvaiAudioCaptureComponent;
|
||||
|
||||
// UENUM(BlueprintType)
|
||||
// enum class EHardwareInputFeatureBP : uint8
|
||||
// {
|
||||
// EchoCancellation,
|
||||
// NoiseSuppression,
|
||||
// AutomaticGainControl
|
||||
// };
|
||||
|
||||
// TODO (Mohamed): Ensure both Chatbot and Player components have the same ReplicateVoiceToNetwork value
|
||||
// TODO (Mohamed): Send Text should also be handled in this class (UConvaiPlayerComponent) like we did with voice
|
||||
|
||||
USTRUCT(BlueprintType)
|
||||
struct FCaptureDeviceInfoBP
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Convai|Microphone")
|
||||
FString DeviceName = "";
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Convai|Microphone")
|
||||
int DeviceIndex = 0;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Convai|Microphone")
|
||||
FString LongDeviceId = "";
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Convai|Microphone")
|
||||
int InputChannels = 0;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Convai|Microphone")
|
||||
int PreferredSampleRate = 0;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Convai|Microphone")
|
||||
bool bSupportsHardwareAEC = 0;
|
||||
};
|
||||
|
||||
|
||||
UCLASS(Blueprintable, BlueprintType, meta = (BlueprintSpawnableComponent), DisplayName = "Convai Player")
|
||||
class CONVAI_API UConvaiPlayerComponent : public UConvaiAudioStreamer
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
UConvaiPlayerComponent();
|
||||
|
||||
virtual void OnComponentCreated() override;
|
||||
|
||||
void GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const override;
|
||||
|
||||
bool Init();
|
||||
|
||||
//~ Begin ActorComponent Interface.
|
||||
virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;
|
||||
virtual void BeginDestroy() override;
|
||||
//~ End ActorComponent Interface.
|
||||
|
||||
public:
|
||||
|
||||
UPROPERTY(EditAnywhere, Category = "Convai", Replicated, BlueprintSetter = SetPlayerName)
|
||||
FString PlayerName;
|
||||
|
||||
/**
|
||||
* Sets a new player name
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, BlueprintInternalUseOnly, Category = "Convai")
|
||||
void SetPlayerName(FString NewPlayerName);
|
||||
|
||||
UFUNCTION(Server, Reliable, Category = "Convai|Network")
|
||||
void SetPlayerNameServer(const FString& NewPlayerName);
|
||||
|
||||
/**
|
||||
* Speaker ID used for long term memory (LTM)
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, Category = "Convai", Replicated, BlueprintSetter = SetSpeakerID)
|
||||
FString SpeakerID;
|
||||
|
||||
/**
|
||||
* Sets a new Speaker ID
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, BlueprintInternalUseOnly, Category = "Convai")
|
||||
void SetSpeakerID(FString NewSpeakerID);
|
||||
|
||||
UFUNCTION(Server, Reliable, Category = "Convai|Network")
|
||||
void SetSpeakerIDServer(const FString& NewSpeakerID);
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "Convai|Microphone")
|
||||
bool GetDefaultCaptureDeviceInfo(FCaptureDeviceInfoBP& OutInfo);
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "Convai|Microphone")
|
||||
bool GetCaptureDeviceInfo(FCaptureDeviceInfoBP& OutInfo, int DeviceIndex);
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "Convai|Microphone")
|
||||
TArray<FCaptureDeviceInfoBP> GetAvailableCaptureDeviceDetails();
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "Convai|Microphone")
|
||||
TArray<FString> GetAvailableCaptureDeviceNames();
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "Convai|Microphone")
|
||||
void GetActiveCaptureDevice(FCaptureDeviceInfoBP& OutInfo);
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "Convai|Microphone")
|
||||
bool SetCaptureDeviceByIndex(int DeviceIndex);
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "Convai|Microphone")
|
||||
bool SetCaptureDeviceByName(FString DeviceName);
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "Convai|Microphone")
|
||||
void SetMicrophoneVolumeMultiplier(float InVolumeMultiplier, bool& Success);
|
||||
|
||||
UFUNCTION(BlueprintPure, BlueprintCallable, Category = "Convai|Microphone")
|
||||
void GetMicrophoneVolumeMultiplier(float& OutVolumeMultiplier, bool& Success);
|
||||
|
||||
// UFUNCTION(BlueprintCallable, Category = "Convai|Microphone")
|
||||
// void GetIfHardwareFeatureIsSupported(EHardwareInputFeatureBP FeatureType, bool& Success);
|
||||
|
||||
// UFUNCTION(BlueprintCallable, Category = "Convai|Microphone")
|
||||
// void SetHardwareFeatureEnabled(EHardwareInputFeatureBP FeatureType, bool bIsEnabled, bool& Success);
|
||||
|
||||
/**
|
||||
* Start recording audio from the microphone, use "Finish Recording" function afterwards
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "Convai|Microphone")
|
||||
void StartRecording();
|
||||
|
||||
/**
|
||||
* Stops recording from the microphone and outputs the recorded audio as a Sound Wave
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "Convai|Microphone")
|
||||
USoundWave* FinishRecording();
|
||||
|
||||
/**
|
||||
* Starts streaming microphone audio to the character. Use "Finish Talking" afterward to let the character know that you are done talking
|
||||
* @param ConvaiChatbotComponent The character to talk to
|
||||
* @param Environment Holds all relevant objects and characters in the scene including the (Player), and also all the actions doable by the character. Use "CreateConvaiEnvironment" function to create and then use functions like (AddAction, AddCharacter, AddMainCharacter) to fill it up
|
||||
* @param GenerateActions Whether or not to generate actions (Environment has to be given and valid)
|
||||
* @param VoiceResponse If true it will generate a voice response, otherwise, it will only generate a text response.
|
||||
* @param RunOnServer If true it will run this function on the server, this can be used in multiplayer sessions to allow other players to hear the character's voice response.
|
||||
* @param StreamPlayerMic If true it will stream the player's voice to other players in the multiplayer session, this is the same effect as voice chat.
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "Convai|Microphone")
|
||||
void StartTalking(
|
||||
UConvaiChatbotComponent* ConvaiChatbotComponent,
|
||||
UConvaiEnvironment* Environment,
|
||||
bool GenerateActions,
|
||||
bool VoiceResponse,
|
||||
bool RunOnServer,
|
||||
bool StreamPlayerMic,
|
||||
bool UseServerAPI_Key);
|
||||
|
||||
/**
|
||||
* Stops streaming microphone audio to the character.
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "Convai|Microphone")
|
||||
void FinishTalking();
|
||||
|
||||
UFUNCTION(Server, Reliable, Category = "Convai|Network")
|
||||
void StartTalkingServer(
|
||||
class UConvaiChatbotComponent* ConvaiChatbotComponent,
|
||||
bool EnvironemntSent,
|
||||
const TArray<FString>& Actions,
|
||||
const TArray<FConvaiObjectEntry>& Objects,
|
||||
const TArray<FConvaiObjectEntry>& Characters,
|
||||
FConvaiObjectEntry MainCharacter,
|
||||
bool GenerateActions,
|
||||
bool VoiceResponse,
|
||||
bool StreamPlayerMic,
|
||||
bool UseServerAPI_Key,
|
||||
const FString& ClientAuthKey,
|
||||
const FString& AuthHeader);
|
||||
|
||||
UFUNCTION(Server, Reliable, Category = "Convai|Network")
|
||||
void FinishTalkingServer();
|
||||
|
||||
/**
|
||||
* Sends text to the character.
|
||||
* @param ConvaiChatbotComponent The character to talk to
|
||||
* @param Text Text to be sent to the character.
|
||||
* @param Environment Holds all relevant objects and characters in the scene including the (Player), and also all the actions doable by the character. Use "CreateConvaiEnvironment" function to create and then use functions like (AddAction, AddCharacter, AddMainCharacter) to fill it up
|
||||
* @param GenerateActions Whether or not to generate actions (Environment has to be given and valid)
|
||||
* @param VoiceResponse If true it will generate a voice response, otherwise, it will only generate a text response.
|
||||
* @param RunOnServer If true it will run this function on the server, this can be used in multiplayer sessions to allow other players to hear the character's voice response.
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "Convai|Microphone")
|
||||
void SendText(
|
||||
UConvaiChatbotComponent* ConvaiChatbotComponent,
|
||||
FString Text,
|
||||
UConvaiEnvironment* Environment,
|
||||
bool GenerateActions,
|
||||
bool VoiceResponse,
|
||||
bool RunOnServer,
|
||||
bool UseServerAPI_Key);
|
||||
|
||||
UFUNCTION(Server, Reliable, Category = "Convai|Network")
|
||||
void SendTextServer(
|
||||
UConvaiChatbotComponent* ConvaiChatbotComponent,
|
||||
const FString& Text,
|
||||
bool EnvironemntSent,
|
||||
const TArray<FString>& Actions,
|
||||
const TArray<FConvaiObjectEntry>& Objects,
|
||||
const TArray<FConvaiObjectEntry>& Characters,
|
||||
FConvaiObjectEntry MainCharacter,
|
||||
bool GenerateActions,
|
||||
bool VoiceResponse,
|
||||
bool UseServerAPI_Key,
|
||||
const FString& ClientAuthKey,
|
||||
const FString& AuthHeader);
|
||||
|
||||
virtual bool ShouldMuteLocal() override;
|
||||
|
||||
virtual bool ShouldMuteGlobal() override;
|
||||
|
||||
virtual void OnServerAudioReceived(uint8* VoiceData, uint32 VoiceDataSize, bool ContainsHeaderData = true, uint32 SampleRate = 21000, uint32 NumChannels = 1) override;
|
||||
|
||||
// UActorComponent interface
|
||||
virtual void BeginPlay() override;
|
||||
virtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override;
|
||||
|
||||
bool ConsumeStreamingBuffer(TArray<uint8>& Buffer);
|
||||
|
||||
UFUNCTION(Server, Reliable, Category = "Convai|Network")
|
||||
void SetIsStreamingServer(bool value);
|
||||
|
||||
// Returns true if microphone audio is being streamed, false otherwise.
|
||||
UFUNCTION(BlueprintPure, BlueprintCallable, Category = "Convai|Microphone", meta = (DisplayName = "Is Talking"))
|
||||
bool GetIsStreaming()
|
||||
{
|
||||
return(IsStreaming);
|
||||
}
|
||||
|
||||
// Returns true if microphone audio is being recorded, false otherwise.
|
||||
UFUNCTION(BlueprintPure, BlueprintCallable, Category = "Convai|Microphone", meta = (DisplayName = "Is Recording"))
|
||||
bool GetIsRecording()
|
||||
{
|
||||
return(IsRecording);
|
||||
}
|
||||
|
||||
FonDataReceived_Delegate& getOnDataReceivedDelegate()
|
||||
{
|
||||
return onDataReceived_Delegate;
|
||||
}
|
||||
|
||||
bool CheckTokenValidty(uint32 TokenToCheck)
|
||||
{
|
||||
return Token == TokenToCheck;
|
||||
}
|
||||
|
||||
public:
|
||||
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Convai|PixelStreaming")
|
||||
TWeakObjectPtr<USynthComponent> PixelStreamingAudioComponent;
|
||||
|
||||
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Convai|PixelStreaming")
|
||||
bool UsePixelStreamingMicInput;
|
||||
|
||||
bool IsPixelStreamingEnabledAndAllowed();
|
||||
|
||||
private:
|
||||
|
||||
uint32 GenerateNewToken()
|
||||
{
|
||||
Token += 1;
|
||||
return Token;
|
||||
}
|
||||
|
||||
UPROPERTY()
|
||||
USoundWaveProcedural* VoiceCaptureSoundWaveProcedural;
|
||||
|
||||
//TSharedPtr<IVoiceCapture> VoiceCapture;
|
||||
|
||||
// Buffer used with recording
|
||||
TArray<uint8> VoiceCaptureBuffer;
|
||||
|
||||
// Buffer used with streaming
|
||||
// TODO (Mohamed): use TCircularAudioBuffer instead
|
||||
TRingBuffer<uint8> VoiceCaptureRingBuffer;
|
||||
|
||||
UPROPERTY()
|
||||
UConvaiAudioCaptureComponent* AudioCaptureComponent = nullptr;
|
||||
|
||||
UPROPERTY()
|
||||
UConvaiChatbotComponent* CurrentConvaiChatbotComponent = nullptr;
|
||||
|
||||
void UpdateVoiceCapture(float DeltaTime);
|
||||
void StartVoiceChunkCapture(float ExpectedRecordingTime = 0.01);
|
||||
void StopVoiceChunkCapture();
|
||||
void ReadRecordedBuffer(Audio::AlignedFloatBuffer& RecordedBuffer, float& OutNumChannels, float& OutSampleRate);
|
||||
|
||||
void StartAudioCaptureComponent();
|
||||
void StopAudioCaptureComponent();
|
||||
|
||||
|
||||
FonDataReceived_Delegate onDataReceived_Delegate;
|
||||
|
||||
bool IsRecording = false;
|
||||
bool IsStreaming = false;
|
||||
bool IsInit = false;
|
||||
bool bShouldMuteGlobal;
|
||||
|
||||
float RemainingTimeUntilNextUpdate = 0;
|
||||
|
||||
// Used by a consumer (e.g. chatbot component) to validate if this player component is still streaming audio data to it
|
||||
uint32 Token;
|
||||
|
||||
USoundSubmixBase* _FoundSubmix;
|
||||
};
|
||||
88
ConvAI/Convai/Source/Convai/Public/ConvaiSpeechToTextProxy.h
Normal file
88
ConvAI/Convai/Source/Convai/Public/ConvaiSpeechToTextProxy.h
Normal file
@@ -0,0 +1,88 @@
|
||||
// Copyright 2022 Convai Inc. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Net/OnlineBlueprintCallProxyBase.h"
|
||||
#include "Http.h"
|
||||
#include "ConvaiSpeechToTextProxy.generated.h"
|
||||
|
||||
//http log
|
||||
DECLARE_LOG_CATEGORY_EXTERN(ConvaiS2THttpLog, Log, All);
|
||||
|
||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FS2THttpResponseCallbackSignature, FString, Response);
|
||||
|
||||
|
||||
class USoundWave;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
UCLASS()
|
||||
class UConvaiSpeechToTextProxy : public UOnlineBlueprintCallProxyBase
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
|
||||
|
||||
// Called when there is a successful http response
|
||||
UPROPERTY(BlueprintAssignable)
|
||||
FS2THttpResponseCallbackSignature OnSuccess;
|
||||
|
||||
// Called when there is an unsuccessful http response
|
||||
UPROPERTY(BlueprintAssignable)
|
||||
FS2THttpResponseCallbackSignature OnFailure;
|
||||
|
||||
|
||||
/**
|
||||
* Initiates a post request to the Speech To Text API.
|
||||
* @param URL The url of the API endpoint.
|
||||
* @param API_key The API key issued from the website
|
||||
* @param SoundWave SoundWave that is to be transfered to text
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, meta = (BlueprintInternalUseOnly = "true", DisplayName = "Convai Speech To Text From SoundWave", WorldContext = "WorldContextObject"), Category = "Convai|Http")
|
||||
static UConvaiSpeechToTextProxy* CreateSpeech2TextFromSoundWaveQueryProxy(UObject* WorldContextObject, USoundWave* SoundWave);
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Initiates a post request to the Speech To Text API.
|
||||
* @param URL The url of the API endpoint.
|
||||
* @param API_key The API key issued from the website
|
||||
* @param Payload Byte array containing the wav file
|
||||
* @param bStereo Set to true if the wav file has 2 channels
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, meta = (BlueprintInternalUseOnly = "true", DisplayName = "Convai Speech To Text From byte array", WorldContext = "WorldContextObject"), Category = "Convai|Http")
|
||||
static UConvaiSpeechToTextProxy* CreateSpeech2TextFromArrayQueryProxy(UObject* WorldContextObject, TArray<uint8> Payload);
|
||||
|
||||
/**
|
||||
* Initiates a post request to the Speech To Text API.
|
||||
* @param URL The url of the API endpoint.
|
||||
* @param API_key The API key issued from the website
|
||||
* @param filename The name of the wav file to be sent
|
||||
* @param bStereo Set to true if the wav file has 2 channels
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, meta = (BlueprintInternalUseOnly = "true", DisplayName = "Convai Speech To Text From file", WorldContext = "WorldContextObject"), Category = "Convai|Http")
|
||||
static UConvaiSpeechToTextProxy* CreateSpeech2TextFromFileNameQueryProxy(UObject* WorldContextObject, FString filename);
|
||||
|
||||
|
||||
|
||||
virtual void Activate() override;
|
||||
|
||||
void onHttpRequestComplete(FHttpRequestPtr RequestPtr, FHttpResponsePtr ResponsePtr, bool bWasSuccessful);
|
||||
|
||||
void failed();
|
||||
void success();
|
||||
void finish();
|
||||
|
||||
|
||||
FString URL;
|
||||
FString filename;
|
||||
TArray<uint8> Payload;
|
||||
bool bStereo;
|
||||
FString Response;
|
||||
|
||||
// Pointer to the world
|
||||
TWeakObjectPtr<UWorld> WorldPtr;
|
||||
};
|
||||
163
ConvAI/Convai/Source/Convai/Public/ConvaiSubsystem.h
Normal file
163
ConvAI/Convai/Source/Convai/Public/ConvaiSubsystem.h
Normal file
@@ -0,0 +1,163 @@
|
||||
// Copyright 2022 Convai Inc. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Subsystems/SubsystemCollection.h"
|
||||
#include "Net/OnlineBlueprintCallProxyBase.h"
|
||||
#include "Subsystems/GameInstanceSubsystem.h"
|
||||
#include "HAL/Runnable.h"
|
||||
#include "HAL/ThreadSafeBool.h"
|
||||
|
||||
THIRD_PARTY_INCLUDES_START
|
||||
#include "Proto/service.grpc.pb.h"
|
||||
#undef min
|
||||
#undef max
|
||||
#undef UpdateResource
|
||||
#undef GetObject
|
||||
#undef GetUObject
|
||||
#undef GetClassName
|
||||
#undef InterlockedExchange
|
||||
#undef InterlockedAnd
|
||||
#undef InterlockedIncrement
|
||||
#undef InterlockedDecrement
|
||||
#undef InterlockedCompareExchange
|
||||
#undef FastFence
|
||||
#undef MemoryBarrier
|
||||
#undef Yield
|
||||
#undef CaptureStackBackTrace
|
||||
#undef GetCurrentTime
|
||||
THIRD_PARTY_INCLUDES_END
|
||||
|
||||
#include "ConvaiSubsystem.generated.h"
|
||||
|
||||
#ifdef __APPLE__
|
||||
extern bool GetAppleMicPermission();
|
||||
#endif
|
||||
|
||||
namespace grpc
|
||||
{
|
||||
class Channel;
|
||||
|
||||
template <class R>
|
||||
class ClientAsyncResponseReader;
|
||||
|
||||
class ClientContext;
|
||||
class CompletionQueue;
|
||||
class Status;
|
||||
|
||||
template <class W, class R>
|
||||
class ClientAsyncReaderWriter;
|
||||
class ChannelCredentials;
|
||||
}
|
||||
|
||||
|
||||
//namespace service
|
||||
//{
|
||||
// //class ConvaiService;
|
||||
//
|
||||
// class ConvaiService::Stub;
|
||||
// class GetResponseResponse;
|
||||
// class GetResponseRequest;
|
||||
//};
|
||||
|
||||
DECLARE_LOG_CATEGORY_EXTERN(ConvaiSubsystemLog, Log, All);
|
||||
|
||||
DECLARE_DELEGATE_OneParam(FgRPC_Delegate, bool);
|
||||
|
||||
class FgRPCClient : public FRunnable {
|
||||
public:
|
||||
FgRPCClient(std::string target, const std::shared_ptr<grpc::ChannelCredentials>& creds);
|
||||
|
||||
std::unique_ptr<service::ConvaiService::Stub> GetNewStub();
|
||||
|
||||
grpc::CompletionQueue* GetCompletionQueue();
|
||||
|
||||
public:
|
||||
/**
|
||||
* FRunnable Interface
|
||||
* Loop while listening for completed responses.
|
||||
*/
|
||||
virtual uint32 Run() override;
|
||||
|
||||
void StartStub();
|
||||
|
||||
void CreateChannel();
|
||||
|
||||
void OnStateChange(bool ok);
|
||||
|
||||
virtual void Exit() override;
|
||||
|
||||
private:
|
||||
mutable FCriticalSection CriticalSection;
|
||||
FThreadSafeBool bIsRunning;
|
||||
|
||||
TUniquePtr<FRunnableThread> Thread;
|
||||
|
||||
|
||||
private:
|
||||
|
||||
// Out of the passed in Channel comes the stub, stored here, our view of the
|
||||
// server's exposed services.
|
||||
std::unique_ptr<service::ConvaiService::Stub> stub_;
|
||||
|
||||
std::shared_ptr<grpc::Channel> Channel;
|
||||
std::shared_ptr<grpc::ChannelCredentials> Creds;
|
||||
std::string Target;
|
||||
|
||||
FgRPC_Delegate OnStateChangeDelegate;
|
||||
|
||||
// The producer-consumer queue we use to communicate asynchronously with the
|
||||
// gRPC runtime.
|
||||
grpc::CompletionQueue cq_;
|
||||
};
|
||||
|
||||
|
||||
UCLASS(meta = (DisplayName = "Convai Subsystem"))
|
||||
class UConvaiSubsystem : public UGameInstanceSubsystem
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
|
||||
public:
|
||||
UConvaiSubsystem();
|
||||
|
||||
// Begin USubsystem
|
||||
virtual void Initialize(FSubsystemCollectionBase& Collection) override;
|
||||
virtual void Deinitialize() override;
|
||||
// End USubsystem
|
||||
|
||||
void GetAndroidMicPermission();
|
||||
|
||||
public:
|
||||
TSharedPtr<FgRPCClient> gRPC_Runnable;
|
||||
|
||||
public:
|
||||
// Register a chatbot component with the subsystem
|
||||
void RegisterChatbotComponent(class UConvaiChatbotComponent* ChatbotComponent);
|
||||
|
||||
// Unregister a chatbot component from the subsystem
|
||||
void UnregisterChatbotComponent(class UConvaiChatbotComponent* ChatbotComponent);
|
||||
|
||||
// Get all registered chatbot components
|
||||
TArray<class UConvaiChatbotComponent*> GetAllChatbotComponents() const;
|
||||
|
||||
public:
|
||||
// Register a player component with the subsystem
|
||||
void RegisterPlayerComponent(class UConvaiPlayerComponent* PlayerComponent);
|
||||
|
||||
// Unregister a player component from the subsystem
|
||||
void UnregisterPlayerComponent(class UConvaiPlayerComponent* PlayerComponent);
|
||||
|
||||
// Get all registered player components
|
||||
TArray<class UConvaiPlayerComponent*> GetAllPlayerComponents() const;
|
||||
|
||||
private:
|
||||
// Array to store all active chatbot components
|
||||
UPROPERTY()
|
||||
TArray<class UConvaiChatbotComponent*> RegisteredChatbotComponents;
|
||||
|
||||
private:
|
||||
// Array to store all active player components
|
||||
UPROPERTY()
|
||||
TArray<class UConvaiPlayerComponent*> RegisteredPlayerComponents;
|
||||
};
|
||||
83
ConvAI/Convai/Source/Convai/Public/ConvaiTextToSpeechProxy.h
Normal file
83
ConvAI/Convai/Source/Convai/Public/ConvaiTextToSpeechProxy.h
Normal file
@@ -0,0 +1,83 @@
|
||||
// Copyright 2022 Convai Inc. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Net/OnlineBlueprintCallProxyBase.h"
|
||||
#include "Http.h"
|
||||
#include "ConvaiDefinitions.h"
|
||||
#include "ConvaiTextToSpeechProxy.generated.h"
|
||||
|
||||
class USoundWave;
|
||||
|
||||
|
||||
|
||||
//http log
|
||||
DECLARE_LOG_CATEGORY_EXTERN(ConvaiT2SHttpLog, Log, All);
|
||||
|
||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FT2SHttpResponseCallbackSignature, USoundWave*, Wave);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
UCLASS()
|
||||
class UConvaiTextToSpeechProxy : public UOnlineBlueprintCallProxyBase
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
|
||||
|
||||
// Called when there is a successful http response
|
||||
UPROPERTY(BlueprintAssignable)
|
||||
FT2SHttpResponseCallbackSignature OnSuccess;
|
||||
|
||||
// Called when there is an unsuccessful http response
|
||||
UPROPERTY(BlueprintAssignable)
|
||||
FT2SHttpResponseCallbackSignature OnFailure;
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Initiates a post request to the Speech To Text API.
|
||||
* @param API_key The API key issued from the website
|
||||
* @param Transcript The text to be transformed to voice
|
||||
* @param Voice The voice type to be used
|
||||
*/
|
||||
//UFUNCTION(BlueprintCallable, meta = (BlueprintInternalUseOnly = "true", DisplayName = "Convai Text To Speech", WorldContext = "WorldContextObject"), Category = "Convai|Http")
|
||||
//static UConvaiTextToSpeechProxy* CreateTextToSpeechQueryProxy(UObject* WorldContextObject, FString Transcript, ETTS_Voice_Type Voice);
|
||||
|
||||
|
||||
/**
|
||||
* Initiates a post request to the Speech To Text API.
|
||||
* @param API_key The API key issued from the website
|
||||
* @param Transcript The text to be transformed to voice
|
||||
* @param Voice The voice type to be used
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, meta = (BlueprintInternalUseOnly = "true", DisplayName = "Convai Text To Speech", WorldContext = "WorldContextObject"), Category = "Convai|Http")
|
||||
static UConvaiTextToSpeechProxy* CreateTextToSpeechQueryProxy(UObject* WorldContextObject, FString Transcript, FString Voice);
|
||||
|
||||
|
||||
virtual void Activate() override;
|
||||
|
||||
void onHttpRequestComplete(FHttpRequestPtr RequestPtr, FHttpResponsePtr ResponsePtr, bool bWasSuccessful);
|
||||
|
||||
void failed();
|
||||
void success();
|
||||
void finish();
|
||||
|
||||
|
||||
FString URL;
|
||||
FString Transcript;
|
||||
ETTS_Voice_Type Voice;
|
||||
FString VoiceStr;
|
||||
|
||||
USoundWave* SoundWave;
|
||||
|
||||
// Pointer to the world
|
||||
TWeakObjectPtr<UWorld> WorldPtr;
|
||||
};
|
||||
274
ConvAI/Convai/Source/Convai/Public/ConvaiUtils.h
Normal file
274
ConvAI/Convai/Source/Convai/Public/ConvaiUtils.h
Normal file
@@ -0,0 +1,274 @@
|
||||
// Copyright 2022 Convai Inc. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Kismet/BlueprintFunctionLibrary.h"
|
||||
#include "ConvaiDefinitions.h"
|
||||
#include "Utility/Log/ConvaiLogger.h"
|
||||
#include "ConvaiUtils.generated.h"
|
||||
|
||||
DECLARE_LOG_CATEGORY_EXTERN(ConvaiUtilsLog, Log, All);
|
||||
DECLARE_LOG_CATEGORY_EXTERN(ConvaiFormValidationLog, Log, All);
|
||||
|
||||
class USoundWave;
|
||||
class APlayerController;
|
||||
class UObject;
|
||||
class UConvaiSubsystem;
|
||||
struct FAnimationFrame;
|
||||
|
||||
UCLASS()
|
||||
class CONVAI_API UConvaiUtils : public UBlueprintFunctionLibrary
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
|
||||
static UConvaiSubsystem* GetConvaiSubsystem(const UObject* WorldContextObject);
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "Convai|Utilities")
|
||||
static void StereoToMono(TArray<uint8> stereoWavBytes, TArray<uint8>& monoWavBytes);
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "Convai|Utilities")
|
||||
static bool ReadFileAsByteArray(const FString FilePath, TArray<uint8>& Bytes);
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "Convai|Utilities")
|
||||
static bool SaveByteArrayAsFile(FString FilePath, TArray<uint8> Bytes);
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "Convai|Utilities")
|
||||
static FString ByteArrayToString(TArray<uint8> Bytes);
|
||||
|
||||
// Writes a string to a file
|
||||
UFUNCTION(BlueprintCallable, Category = "Convai|Utilities")
|
||||
static bool WriteStringToFile(const FString& StringToWrite, const FString& FilePath);
|
||||
|
||||
// Reads a string from a file
|
||||
UFUNCTION(BlueprintCallable, Category = "Convai|Utilities")
|
||||
static bool ReadStringFromFile(FString& OutString, const FString& FilePath);
|
||||
|
||||
static float CalculateAudioDuration(uint32 AudioSize, uint8 Channels, uint32 SampleRate, uint8 SampleSize = 2);
|
||||
|
||||
UFUNCTION(BlueprintPure, BlueprintCallable, Category = "Convai|Utilities", meta = (WorldContext = "WorldContextObject", AutoCreateRefTerm = "IncludedCharacters, ExcludedCharacters"))
|
||||
static void ConvaiGetLookedAtCharacter(UObject* WorldContextObject, APlayerController* PlayerController, float Radius, bool PlaneView, TArray<UObject*> IncludedCharacters, TArray<UObject*> ExcludedCharacters, UConvaiChatbotComponent*& ConvaiCharacter, bool& Found);
|
||||
|
||||
UFUNCTION(BlueprintPure, BlueprintCallable, Category = "Convai|Utilities", meta = (WorldContext = "WorldContextObject"))
|
||||
static void ConvaiGetLookedAtObjectOrCharacter(UObject* WorldContextObject, APlayerController* PlayerController, float Radius, bool PlaneView, TArray<FConvaiObjectEntry> ListToSearchIn, FConvaiObjectEntry& FoundObjectOrCharacter, bool& Found);
|
||||
|
||||
UFUNCTION(BlueprintPure, BlueprintCallable, Category = "Convai|Utilities", meta = (WorldContext = "WorldContextObject"))
|
||||
static void ConvaiGetAllPlayerComponents(UObject* WorldContextObject, TArray<class UConvaiPlayerComponent*>& ConvaiPlayerComponents);
|
||||
|
||||
UFUNCTION(BlueprintPure, BlueprintCallable, Category = "Convai|Utilities", meta = (WorldContext = "WorldContextObject"))
|
||||
static void ConvaiGetAllChatbotComponents(UObject* WorldContextObject, TArray<class UConvaiChatbotComponent*>& ConvaiChatbotComponents);
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "Convai|Settings")
|
||||
static void SetAPI_Key(FString API_Key);
|
||||
|
||||
UFUNCTION(BlueprintPure, Category = "Convai|Settings")
|
||||
static FString GetAPI_Key();
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "Convai|Settings")
|
||||
static void SetAuthToken(FString AuthToken);
|
||||
|
||||
UFUNCTION(BlueprintPure, Category = "Convai|Settings")
|
||||
static FString GetAuthToken();
|
||||
|
||||
static TPair<FString, FString> GetAuthHeaderAndKey();
|
||||
|
||||
UFUNCTION(BlueprintPure, Category = "Convai|Settings")
|
||||
static FString GetTestCharacterID();
|
||||
|
||||
UFUNCTION(BlueprintPure, Category = "Convai|Settings")
|
||||
static bool IsNewActionSystemEnabled();
|
||||
|
||||
UFUNCTION(BlueprintPure, Category = "Convai|Utilities")
|
||||
static void GetPluginInfo(FString PluginName, bool& Found, FString& VersionName, FString& EngineVersion, FString& FriendlyName);
|
||||
|
||||
UFUNCTION(BlueprintPure, Category = "Convai|Utilities")
|
||||
static void GetPlatformInfo(FString& EngineVersion, FString& PlatformName);
|
||||
|
||||
UFUNCTION(BlueprintPure, Category = "Convai|Utilities")
|
||||
static TMap<FName, float> MapBlendshapes(const TMap<FName,float>& InputBlendshapes, const TMap<FName, FConvaiBlendshapeParameters>& BlendshapeMap, float GlobalMultiplier, float GlobalOffset);
|
||||
|
||||
static TArray<uint8> ExtractPCMDataFromSoundWave(USoundWave* SoundWave, int32& OutSampleRate, int32& OutNumChannels);
|
||||
|
||||
static void PCMDataToWav(TArray<uint8> InPCMBytes, TArray<uint8>& OutWaveFileData, int NumChannels, int SampleRate);
|
||||
|
||||
static USoundWave* PCMDataToSoundWav(TArray<uint8> InPCMBytes, int NumChannels, int SampleRate);
|
||||
|
||||
static USoundWave* WavDataToSoundWave(TArray<uint8> InWavData);
|
||||
|
||||
// Writes a USoundWave to a .wav file on disk
|
||||
UFUNCTION(BlueprintCallable, Category = "Convai|Utilities")
|
||||
static bool WriteSoundWaveToWavFile(USoundWave* SoundWave, const FString& FilePath);
|
||||
|
||||
// Reads a .wav file from disk and creates a USoundWave
|
||||
UFUNCTION(BlueprintPure, Category = "Convai|Utilities")
|
||||
static USoundWave* ReadWavFileAsSoundWave(const FString& FilePath);
|
||||
|
||||
static void ResampleAudio(float currentSampleRate, float targetSampleRate, int numChannels, bool reduceToMono, int16* currentPcmData, int numSamplesToConvert, TArray<int16>& outResampledPcmData);
|
||||
|
||||
static void ResampleAudio(float currentSampleRate, float targetSampleRate, int numChannels, bool reduceToMono, const TArray<int16>& currentPcmData, int numSamplesToConvert, TArray<int16>& outResampledPcmData);
|
||||
|
||||
static FString FUTF8ToFString(const char* StringToConvert);
|
||||
|
||||
static int LevenshteinDistance(const FString& s, const FString& t);
|
||||
|
||||
static TArray<FAnimationFrame> ParseJsonToBlendShapeData(const FString& JsonString);
|
||||
|
||||
static bool ParseVisemeValuesToAnimationFrame(const FString& VisemeValuesString, FAnimationFrame& AnimationFrame);
|
||||
|
||||
// UFUNCTION(BlueprintCallable, Category = "ActorFuncions", meta = (WorldContext = WorldContextObject))
|
||||
static AActor* ConvaiCloneActor(AActor* InputActor);
|
||||
|
||||
// UFUNCTION(BlueprintPure, Category = "Convai|Utilities|AnimationSequence")
|
||||
static FString ConvaiAnimationSequenceToJson(const FAnimationSequenceBP& AnimationSequenceBP);
|
||||
|
||||
// UFUNCTION(BlueprintPure, Category = "Convai|Utilities|AnimationSequence")
|
||||
static void ConvaiAnimationSequenceFromJson(const FString& JsonString, FAnimationSequenceBP& AnimationSequenceBP);
|
||||
};
|
||||
|
||||
UCLASS()
|
||||
class CONVAI_API UConvaiSettingsUtils : public UBlueprintFunctionLibrary
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
static bool GetParamValueAsString(const FString& paramName, FString& outValue);
|
||||
|
||||
static bool GetParamValueAsFloat(const FString& paramName, float& outValue);
|
||||
|
||||
static bool GetParamValueAsInt(const FString & paramName, int32 & outValue);
|
||||
};
|
||||
|
||||
UCLASS()
|
||||
class CONVAI_API UConvaiFormValidation : public UBlueprintFunctionLibrary
|
||||
{
|
||||
GENERATED_BODY()
|
||||
public:
|
||||
static bool ValidateAuthKey(FString API_Key)
|
||||
{
|
||||
if ((API_Key.Len()))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
CONVAI_LOG(LogTemp, Warning, TEXT("Empty API Key, please add it in Edit->Project Settings->Convai"));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static bool ValidateSessionID(FString SessionID)
|
||||
{
|
||||
if ((SessionID.Len()))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
CONVAI_LOG(ConvaiFormValidationLog, Warning, TEXT("Empty Session ID"));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static bool ValidateCharacterID(FString CharacterID)
|
||||
{
|
||||
if ((CharacterID.Len()))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
CONVAI_LOG(ConvaiFormValidationLog, Warning, TEXT("Empty Character ID"));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static bool ValidateInputText(FString InputText)
|
||||
{
|
||||
if ((InputText.Len()))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
CONVAI_LOG(LogTemp, Warning, TEXT("Empty Input Text"));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static bool ValidateVoiceType(FString VoiceType)
|
||||
{
|
||||
if ((VoiceType.Len()))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
CONVAI_LOG(ConvaiFormValidationLog, Warning, TEXT("Invalid Voice Type"));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static bool ValidateBackstory(FString Backstory)
|
||||
{
|
||||
if ((Backstory.Len()))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
CONVAI_LOG(ConvaiFormValidationLog, Warning, TEXT("Empty Backstory"));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static bool ValidateCharacterName(FString CharacterName)
|
||||
{
|
||||
if ((CharacterName.Len()))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
CONVAI_LOG(ConvaiFormValidationLog, Warning, TEXT("Empty Character Name"));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static bool ValidateInputVoice(TArray<uint8> InputVoiceData)
|
||||
{
|
||||
if ((InputVoiceData.Num() > 44))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
CONVAI_LOG(ConvaiFormValidationLog, Warning, TEXT("Input Voice is too short (less than 44 bytes)"));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
UCLASS()
|
||||
class UCommandLineUtils : public UBlueprintFunctionLibrary
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
|
||||
// Function to check if a flag is present in the command line - Has issue it always returns false
|
||||
//UFUNCTION(BlueprintCallable, Category = "CommandLine")
|
||||
static bool IsCommandLineFlagPresent(const FString& Flag);
|
||||
|
||||
// Function to get the value of a command line flag as an integer
|
||||
UFUNCTION(BlueprintCallable, Category = "CommandLine")
|
||||
static int32 GetCommandLineFlagValueAsInt(const FString& Flag, int32 DefaultValue = 0);
|
||||
|
||||
// Function to get the value of a command line flag as a string
|
||||
UFUNCTION(BlueprintCallable, Category = "CommandLine")
|
||||
static FString GetCommandLineFlagValueAsString(const FString& Flag, const FString& DefaultValue = TEXT(""));
|
||||
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "CommandLine")
|
||||
static FString GetCommandLineFlagValueAsStringNoDefault(const FString& Flag);
|
||||
};
|
||||
35
ConvAI/Convai/Source/Convai/Public/LipSyncInterface.h
Normal file
35
ConvAI/Convai/Source/Convai/Public/LipSyncInterface.h
Normal file
@@ -0,0 +1,35 @@
|
||||
// Copyright 2022 Convai Inc. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "GameFramework/Actor.h"
|
||||
#include "ConvaiDefinitions.h"
|
||||
#include "LipSyncInterface.generated.h"
|
||||
|
||||
DECLARE_DELEGATE(FOnVisemesDataReadySignature);
|
||||
|
||||
UINTERFACE()
|
||||
class CONVAI_API UConvaiLipSyncInterface : public UInterface
|
||||
{
|
||||
GENERATED_BODY()
|
||||
};
|
||||
|
||||
class IConvaiLipSyncInterface
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
FOnVisemesDataReadySignature OnVisemesDataReady;
|
||||
|
||||
virtual void ConvaiInferFacialDataFromAudio(uint8* InPCMData, uint32 InPCMDataSize, uint32 InSampleRate, uint32 InNumChannels) = 0;
|
||||
virtual void ConvaiStopLipSync() = 0;
|
||||
virtual TArray<float> ConvaiGetVisemes() = 0;
|
||||
virtual TArray<FString> ConvaiGetVisemeNames() = 0;
|
||||
virtual void ConvaiApplyPrecomputedFacialAnimation(uint8* InPCMData, uint32 InPCMDataSize, uint32 InSampleRate, uint32 InNumChannels, FAnimationSequence FaceSequence) = 0;
|
||||
virtual void ConvaiApplyFacialFrame(FAnimationFrame FaceFrame, float Duration) = 0;
|
||||
virtual bool RequiresPrecomputedFaceData() = 0;
|
||||
virtual bool GeneratesVisemesAsBlendshapes() = 0;
|
||||
virtual TMap<FName, float> ConvaiGetFaceBlendshapes() = 0;
|
||||
virtual void ForceRecalculateStartTime() = 0;
|
||||
};
|
||||
100
ConvAI/Convai/Source/Convai/Public/RestAPI/ConvaiAPIBase.h
Normal file
100
ConvAI/Convai/Source/Convai/Public/RestAPI/ConvaiAPIBase.h
Normal file
@@ -0,0 +1,100 @@
|
||||
// Copyright 2022 Convai Inc. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
#include "CoreMinimal.h"
|
||||
#include "Net/OnlineBlueprintCallProxyBase.h"
|
||||
#include "Dom/JsonObject.h"
|
||||
#include "Runtime/Launch/Resources/Version.h"
|
||||
|
||||
#ifdef USE_CONVAI_HTTP
|
||||
#else
|
||||
#define USE_CONVAI_HTTP 0
|
||||
#endif
|
||||
|
||||
#if USE_CONVAI_HTTP
|
||||
// Use ConvaiHttp module
|
||||
#include "ConvaihttpModule.h"
|
||||
#include "Interfaces/IConvaihttpRequest.h"
|
||||
#include "Interfaces/IConvaihttpResponse.h"
|
||||
#define CONVAI_HTTP_MODULE FConvaihttpModule
|
||||
#define CONVAI_HTTP_REQUEST_INTERFACE IConvaihttpRequest
|
||||
#define CONVAI_HTTP_RESPONSE_INTERFACE IConvaihttpResponse
|
||||
#define CONVAI_HTTP_REQUEST_PTR FConvaihttpRequestPtr
|
||||
#define CONVAI_HTTP_RESPONSE_PTR FConvaihttpResponsePtr
|
||||
#define CONVAI_HTTP_PAYLOAD_ARRAY_TYPE TArray64<uint8>
|
||||
#define CONVAI_HTTP_DOWN_PROGRESS_TYPE uint64
|
||||
#else
|
||||
// Use HTTP module
|
||||
#include "HttpModule.h"
|
||||
#include "Interfaces/IHttpRequest.h"
|
||||
#include "Interfaces/IHttpResponse.h"
|
||||
#define CONVAI_HTTP_MODULE FHttpModule
|
||||
#define CONVAI_HTTP_REQUEST_INTERFACE IHttpRequest
|
||||
#define CONVAI_HTTP_RESPONSE_INTERFACE IHttpResponse
|
||||
#define CONVAI_HTTP_REQUEST_PTR FHttpRequestPtr
|
||||
#define CONVAI_HTTP_RESPONSE_PTR FHttpResponsePtr
|
||||
#define CONVAI_HTTP_PAYLOAD_ARRAY_TYPE TArray<uint8>
|
||||
#if ENGINE_MAJOR_VERSION > 5 || (ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION >= 4)
|
||||
#define CONVAI_HTTP_DOWN_PROGRESS_TYPE uint64
|
||||
#else
|
||||
#define CONVAI_HTTP_DOWN_PROGRESS_TYPE int32
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include "ConvaiAPIBase.generated.h"
|
||||
|
||||
|
||||
DECLARE_LOG_CATEGORY_EXTERN(ConvaiBaseHttpLogs, Log, All);
|
||||
|
||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FStringHttpResponseCallbackSignature, FString, ResponseString);
|
||||
|
||||
namespace ConvaiHttpConstants
|
||||
{
|
||||
static const TCHAR* GET = TEXT("GET");
|
||||
static const TCHAR* POST = TEXT("POST");
|
||||
static const TCHAR* PUT = TEXT("PUT");
|
||||
}
|
||||
|
||||
//-------------------------------------------Base API class-----------------------------------------
|
||||
|
||||
/** Base class for all the Convai API calls */
|
||||
UCLASS()
|
||||
class CONVAI_API UConvaiAPIBaseProxy : public UOnlineBlueprintCallProxyBase
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
virtual void Activate() override;
|
||||
|
||||
protected:
|
||||
|
||||
/*IHttp interface*/
|
||||
virtual void OnHttpRequestComplete(CONVAI_HTTP_REQUEST_PTR Request, CONVAI_HTTP_RESPONSE_PTR Response, bool bWasSuccessful);
|
||||
/*END IHttp interface*/
|
||||
|
||||
virtual bool ConfigureRequest(TSharedRef<CONVAI_HTTP_REQUEST_INTERFACE> Request, const TCHAR* Verb);
|
||||
virtual bool AddContentToRequest(CONVAI_HTTP_PAYLOAD_ARRAY_TYPE& DataToSend, const FString& Boundary) { return false; }
|
||||
virtual bool AddContentToRequestAsString(TSharedPtr<FJsonObject>& ObjectToSend) { return false; }
|
||||
virtual void HandleSuccess();
|
||||
virtual void HandleFailure();
|
||||
|
||||
public:
|
||||
FString URL;
|
||||
FString ResponseString;
|
||||
CONVAI_HTTP_PAYLOAD_ARRAY_TYPE ResponseData;
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
/** Base class for all the Convai Asset manager API calls */
|
||||
UCLASS()
|
||||
class CONVAI_API UConvaiAPITokenInBodyProxy : public UConvaiAPIBaseProxy
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
protected:
|
||||
virtual bool AddContentToRequest(CONVAI_HTTP_PAYLOAD_ARRAY_TYPE& DataToSend, const FString& Boundary) override;
|
||||
virtual bool AddContentToRequestAsString(TSharedPtr<FJsonObject>& ObjectToSend) override;
|
||||
};
|
||||
|
||||
188
ConvAI/Convai/Source/Convai/Public/RestAPI/ConvaiLTMProxy.h
Normal file
188
ConvAI/Convai/Source/Convai/Public/RestAPI/ConvaiLTMProxy.h
Normal file
@@ -0,0 +1,188 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "RestAPI/ConvaiAPIBase.h"
|
||||
#include "ConvaiDefinitions.h"
|
||||
#include "Kismet/BlueprintFunctionLibrary.h"
|
||||
#include "ConvaiLTMProxy.generated.h"
|
||||
|
||||
struct FConvaiSpeakerInfo;
|
||||
|
||||
DECLARE_LOG_CATEGORY_EXTERN(LTMHttpLogs, Log, All);
|
||||
|
||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FSpeakerIDListHttpResponseCallbackSignature, const TArray<FConvaiSpeakerInfo>&, SpeakerIDs);
|
||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FSpeakerIDHttpResponseCallbackSignature, const FConvaiSpeakerInfo&, SpeakerID);
|
||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FLTMStatusHttpResponseCallbackSignature, bool, Status);
|
||||
|
||||
|
||||
|
||||
// Create speaker id
|
||||
UCLASS()
|
||||
class CONVAI_API UConvaiCreateSpeakerID : public UConvaiAPIBaseProxy
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UPROPERTY(BlueprintAssignable)
|
||||
FSpeakerIDHttpResponseCallbackSignature OnSuccess;
|
||||
|
||||
UPROPERTY(BlueprintAssignable)
|
||||
FSpeakerIDHttpResponseCallbackSignature OnFailure;
|
||||
|
||||
UFUNCTION(BlueprintCallable, meta = (BlueprintInternalUseOnly = "true", DisplayName = "Convai Create Speaker ID"), Category = "Convai|LTM")
|
||||
static UConvaiCreateSpeakerID* ConvaiCreateSpeakerIDProxy(FString SpeakerName, FString DeviceId);
|
||||
|
||||
protected:
|
||||
virtual bool ConfigureRequest(TSharedRef<CONVAI_HTTP_REQUEST_INTERFACE> Request, const TCHAR* Verb) override;
|
||||
virtual bool AddContentToRequest(CONVAI_HTTP_PAYLOAD_ARRAY_TYPE& DataToSend, const FString& Boundary) override { return false; }
|
||||
virtual bool AddContentToRequestAsString(TSharedPtr<FJsonObject>& ObjectToSend) override;
|
||||
virtual void HandleSuccess() override;
|
||||
virtual void HandleFailure() override;
|
||||
|
||||
FString AssociatedSpeakerName;
|
||||
FString AssociatedDeviceId;
|
||||
FConvaiSpeakerInfo AssociatedSpeakerInfo;
|
||||
};
|
||||
// END create speaker id
|
||||
|
||||
|
||||
|
||||
|
||||
// List speaker ids
|
||||
UCLASS()
|
||||
class CONVAI_API UConvaiListSpeakerID : public UConvaiAPIBaseProxy
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UPROPERTY(BlueprintAssignable)
|
||||
FSpeakerIDListHttpResponseCallbackSignature OnSuccess;
|
||||
|
||||
UPROPERTY(BlueprintAssignable)
|
||||
FSpeakerIDListHttpResponseCallbackSignature OnFailure;
|
||||
|
||||
UFUNCTION(BlueprintCallable, meta = (BlueprintInternalUseOnly = "true", DisplayName = "Convai List Speaker IDs"), Category = "Convai|LTM")
|
||||
static UConvaiListSpeakerID* ConvaiListSpeakerIDProxy();
|
||||
|
||||
protected:
|
||||
virtual bool ConfigureRequest(TSharedRef<CONVAI_HTTP_REQUEST_INTERFACE> Request, const TCHAR* Verb) override;
|
||||
virtual bool AddContentToRequest(CONVAI_HTTP_PAYLOAD_ARRAY_TYPE& DataToSend, const FString& Boundary) override { return false; }
|
||||
virtual bool AddContentToRequestAsString(TSharedPtr<FJsonObject>& ObjectToSend) override { return false; }
|
||||
virtual void HandleSuccess() override;
|
||||
virtual void HandleFailure() override;
|
||||
};
|
||||
// END list speaker ids
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// Delete speaker id
|
||||
UCLASS()
|
||||
class CONVAI_API UConvaiDeleteSpeakerID : public UConvaiAPIBaseProxy
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UPROPERTY(BlueprintAssignable)
|
||||
FStringHttpResponseCallbackSignature OnSuccess;
|
||||
|
||||
UPROPERTY(BlueprintAssignable)
|
||||
FStringHttpResponseCallbackSignature OnFailure;
|
||||
|
||||
UFUNCTION(BlueprintCallable, meta = (BlueprintInternalUseOnly = "true", DisplayName = "Convai Delete Speaker ID"), Category = "Convai|LTM")
|
||||
static UConvaiDeleteSpeakerID* ConvaiDeleteSpeakerIDProxy(FString SpeakerID);
|
||||
|
||||
protected:
|
||||
virtual bool ConfigureRequest(TSharedRef<CONVAI_HTTP_REQUEST_INTERFACE> Request, const TCHAR* Verb) override;
|
||||
virtual bool AddContentToRequest(CONVAI_HTTP_PAYLOAD_ARRAY_TYPE& DataToSend, const FString& Boundary) override { return false; }
|
||||
virtual bool AddContentToRequestAsString(TSharedPtr<FJsonObject>& ObjectToSend) override;
|
||||
virtual void HandleSuccess() override;
|
||||
virtual void HandleFailure() override;
|
||||
|
||||
FString AssociatedSpeakerID;
|
||||
};
|
||||
// END Delete speaker id
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// Get LTM status
|
||||
UCLASS()
|
||||
class CONVAI_API UConvaiGetLTMStatus : public UConvaiAPIBaseProxy
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UPROPERTY(BlueprintAssignable)
|
||||
FLTMStatusHttpResponseCallbackSignature OnSuccess;
|
||||
|
||||
UPROPERTY(BlueprintAssignable)
|
||||
FLTMStatusHttpResponseCallbackSignature OnFailure;
|
||||
|
||||
UFUNCTION(BlueprintCallable, meta = (BlueprintInternalUseOnly = "true", DisplayName = "Convai Get LTM Status"), Category = "Convai|LTM")
|
||||
static UConvaiGetLTMStatus* ConvaiGetLTMStatusProxy(FString CharacterID);
|
||||
|
||||
protected:
|
||||
virtual bool ConfigureRequest(TSharedRef<CONVAI_HTTP_REQUEST_INTERFACE> Request, const TCHAR* Verb) override;
|
||||
virtual bool AddContentToRequest(CONVAI_HTTP_PAYLOAD_ARRAY_TYPE& DataToSend, const FString& Boundary) override { return false; }
|
||||
virtual bool AddContentToRequestAsString(TSharedPtr<FJsonObject>& ObjectToSend) override;
|
||||
virtual void HandleSuccess() override;
|
||||
virtual void HandleFailure() override;
|
||||
|
||||
FString AssociatedCharacterID;
|
||||
};
|
||||
// END Get LTM status
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// Set LTM status
|
||||
UCLASS()
|
||||
class CONVAI_API UConvaiSetLTMStatus : public UConvaiAPIBaseProxy
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UPROPERTY(BlueprintAssignable)
|
||||
FStringHttpResponseCallbackSignature OnSuccess;
|
||||
|
||||
UPROPERTY(BlueprintAssignable)
|
||||
FStringHttpResponseCallbackSignature OnFailure;
|
||||
|
||||
UFUNCTION(BlueprintCallable, meta = (BlueprintInternalUseOnly = "true", DisplayName = "Convai Set LTM Status"), Category = "Convai|LTM")
|
||||
static UConvaiSetLTMStatus* ConvaiSetLTMStatusProxy(FString CharacterID, bool bEnable);
|
||||
|
||||
protected:
|
||||
virtual bool ConfigureRequest(TSharedRef<CONVAI_HTTP_REQUEST_INTERFACE> Request, const TCHAR* Verb) override;
|
||||
virtual bool AddContentToRequest(CONVAI_HTTP_PAYLOAD_ARRAY_TYPE& DataToSend, const FString& Boundary) override { return false; }
|
||||
virtual bool AddContentToRequestAsString(TSharedPtr<FJsonObject>& ObjectToSend) override;
|
||||
virtual void HandleSuccess() override;
|
||||
virtual void HandleFailure() override;
|
||||
|
||||
FString AssociatedCharacterID;
|
||||
bool bAssociatedEnable;
|
||||
};
|
||||
// END Set LTM status
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
UCLASS()
|
||||
class CONVAI_API UConvaiLTMUtils : public UBlueprintFunctionLibrary
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
static bool ParseConvaiSpeakerInfoArray(const FString& JsonString, TArray<FConvaiSpeakerInfo>& OutSpeakerInfoArray);
|
||||
static bool GetLTMStatus(const FString& JsonString, bool& bEnabled);
|
||||
};
|
||||
56
ConvAI/Convai/Source/Convai/Public/RestAPI/ConvaiURL.h
Normal file
56
ConvAI/Convai/Source/Convai/Public/RestAPI/ConvaiURL.h
Normal file
@@ -0,0 +1,56 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "ConvaiURL.generated.h"
|
||||
|
||||
UENUM(BlueprintType)
|
||||
enum class EConvaiEndpoint : uint8
|
||||
{
|
||||
NewSpeaker UMETA(DisplayName = "New Speaker"),
|
||||
SpeakerIDList UMETA(DisplayName = "Speaker ID List"),
|
||||
DeleteSpeakerID UMETA(DisplayName = "Delete Speaker ID"),
|
||||
ReferralSourceStatus UMETA(DisplayName = "Referral Source Status"),
|
||||
UpdateReferralSource UMETA(DisplayName = "Update Referral Source"),
|
||||
UserAPIUsage UMETA(DisplayName = "User API Usage"),
|
||||
CharacterUpdate UMETA(DisplayName = "Character Update"),
|
||||
CharacterGet UMETA(DisplayName = "Character Get"),
|
||||
ListCharacterSections UMETA(DisplayName = "List Character Sections"),
|
||||
ListCharacterTriggers UMETA(DisplayName = "List Character Triggers")
|
||||
};
|
||||
|
||||
UCLASS()
|
||||
class CONVAI_API UConvaiURL : public UObject
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
static FString GetEndpoint(EConvaiEndpoint Endpoint);
|
||||
|
||||
/** Get the base URL for a specific environment (beta or prod) */
|
||||
static FString GetBaseURL(bool bUseBeta = false);
|
||||
|
||||
/** Get the full URL for a specific API endpoint */
|
||||
static FString GetFullURL(const FString& ApiPath, bool bUseBeta = false);
|
||||
|
||||
static FString GetFormattedBaseURL(const FString& Subdomain);
|
||||
|
||||
/** Initialize URL configuration from command line or settings */
|
||||
static void InitializeURLConfig();
|
||||
|
||||
private:
|
||||
static const TCHAR BETA_SUBDOMAIN[];
|
||||
static const TCHAR PROD_SUBDOMAIN[];
|
||||
static const TCHAR BASE_URL[];
|
||||
static const TCHAR BASE_URL_FORMAT[];
|
||||
|
||||
static const TCHAR LTM_SUBDOMAIN[];
|
||||
static const TCHAR USER_SUBDOMAIN[];
|
||||
static const TCHAR CHARACTER_SUBDOMAIN[];
|
||||
static const TCHAR NARRATIVE_DESIGN_SUBDOMAIN[];
|
||||
|
||||
static TArray<EConvaiEndpoint> BetaEndpoints;
|
||||
|
||||
static FString CustomBetaBaseURL;
|
||||
static FString CustomProdBaseURL;
|
||||
static bool bURLConfigInitialized;
|
||||
};
|
||||
505
ConvAI/Convai/Source/Convai/Public/RingBuffer.h
Normal file
505
ConvAI/Convai/Source/Convai/Public/RingBuffer.h
Normal file
@@ -0,0 +1,505 @@
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Misc/SecureHash.h"
|
||||
|
||||
template< typename DataType >
|
||||
class TRingBuffer
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
TRingBuffer(uint32 BufferDataSize);
|
||||
|
||||
TRingBuffer();
|
||||
|
||||
/**
|
||||
* Default Destructor
|
||||
*/
|
||||
~TRingBuffer();
|
||||
|
||||
void Init(uint32 InBufferDataSize);
|
||||
|
||||
/**
|
||||
* Clears memory and indexes
|
||||
*/
|
||||
void Empty();
|
||||
|
||||
/**
|
||||
* Pushes a data word to the end of the FIFO. WILL OVERWRITE OLDEST if full.
|
||||
* @param Val The data word to push
|
||||
*/
|
||||
void Enqueue(const DataType& Val);
|
||||
|
||||
/**
|
||||
* Pushes a buffer of data words to the end of the FIFO. WILL OVERWRITE OLDEST if full.
|
||||
* @param ValBuf The buffer pointer
|
||||
* @param BufLen The length to copy
|
||||
*/
|
||||
void Enqueue(const DataType* ValBuf, const uint32& BufLen);
|
||||
|
||||
/**
|
||||
* Take the next data word from the FIFO buffer.
|
||||
* @param OUT ValOut The variable to receive the data word
|
||||
* @return true if the buffer was not empty, false otherwise.
|
||||
*/
|
||||
bool Dequeue(DataType& ValOut);
|
||||
|
||||
/**
|
||||
* Take the next set of data words from the FIFO buffer.
|
||||
* @param ValBuf The buffer to receive the data
|
||||
* @param BufLen The number of words requested
|
||||
* @return the number of words actually copied
|
||||
*/
|
||||
uint32 Dequeue(DataType* ValBuf, const uint32& BufLen);
|
||||
|
||||
/**
|
||||
* Get the next data word from the FIFO buffer without removing it.
|
||||
* @param OUT ValOut The variable to receive the data word
|
||||
* @return true if the buffer was not empty, false otherwise.
|
||||
*/
|
||||
bool Peek(DataType& ValOut) const;
|
||||
|
||||
/**
|
||||
* Get the next set of data words from the FIFO buffer without removing them.
|
||||
* @param ValBuf The buffer to receive the data
|
||||
* @param BufLen The number of words requested
|
||||
* @return the number of words actually copied
|
||||
*/
|
||||
uint32 Peek(DataType* ValBuf, const uint32& BufLen) const;
|
||||
|
||||
/**
|
||||
* Compare the memory in the FIFO to the memory in the given buffer
|
||||
* @param SerialBuffer The buffer containing data to compare
|
||||
* @param CompareLen The number of words to compare
|
||||
* @return < 0 if data < SerialBuffer.. 0 if data == SerialBuffer... > 0 if data > SerialBuffer
|
||||
*/
|
||||
int32 SerialCompare(const DataType* SerialBuffer, uint32 CompareLen) const;
|
||||
|
||||
/**
|
||||
* Get the SHA1 hash for the data currently in the FIFO
|
||||
* @param OutHash Receives the SHA hash
|
||||
*/
|
||||
void GetShaHash(FSHAHash& OutHash) const;
|
||||
|
||||
/**
|
||||
* Serializes the internal buffer into the given buffer
|
||||
* @param SerialBuffer A preallocated buffer to copy data into
|
||||
*/
|
||||
void Serialize(DataType* SerialBuffer) const;
|
||||
|
||||
/**
|
||||
* Square bracket operators for accessing data in the buffer by FIFO index. [0] returns the next entry in the FIFO that would get provided by Dequeue or Peek.
|
||||
* @param Index The index into the FIFO buffer
|
||||
* @return The data word
|
||||
*/
|
||||
FORCEINLINE const DataType& operator[](const int32& Index) const;
|
||||
FORCEINLINE DataType& operator[](const int32& Index);
|
||||
|
||||
/**
|
||||
* Gets the last data word in the FIFO (i.e. most recently pushed).
|
||||
* @return The last data word
|
||||
*/
|
||||
FORCEINLINE const DataType& Top() const;
|
||||
FORCEINLINE DataType& Top();
|
||||
|
||||
/**
|
||||
* Gets the first data word in the FIFO (i.e. oldest).
|
||||
* @return The first data word
|
||||
*/
|
||||
FORCEINLINE const DataType& Bottom() const;
|
||||
FORCEINLINE DataType& Bottom();
|
||||
|
||||
/**
|
||||
* Gets the buffer index that the last data word is stored in.
|
||||
* @return The index of Top()
|
||||
*/
|
||||
FORCEINLINE uint32 TopIndex() const;
|
||||
|
||||
/**
|
||||
* Gets the buffer index that the first data word is stored in.
|
||||
* @return The index of Bottom()
|
||||
*/
|
||||
FORCEINLINE uint32 BottomIndex() const;
|
||||
|
||||
/**
|
||||
* Gets the buffer index that the next enqueued word will get stored in.
|
||||
* @return The index that the next enqueued word will have
|
||||
*/
|
||||
FORCEINLINE uint32 NextIndex() const;
|
||||
|
||||
/**
|
||||
* Gets the size of the data buffer.
|
||||
* @return Template arg BufferDataSize
|
||||
*/
|
||||
FORCEINLINE uint32 RingDataSize() const;
|
||||
|
||||
/**
|
||||
* Gets the number of words currently in the FIFO.
|
||||
* @return data size
|
||||
*/
|
||||
FORCEINLINE uint32 RingDataUsage() const;
|
||||
|
||||
/**
|
||||
* Gets the total number of words that have been pushed through this buffer since clearing
|
||||
* @return total length of data passed through
|
||||
*/
|
||||
FORCEINLINE uint64 TotalDataPushed() const;
|
||||
|
||||
/**
|
||||
* Maximum size the buffer can grow to (50 MB by default)
|
||||
*/
|
||||
static constexpr uint32 MaxBufferSize = 1024 * 1024 * 50;
|
||||
|
||||
/**
|
||||
* Resizes the buffer to a new size, preserving existing data
|
||||
* @param NewBufferSize The new size of the buffer
|
||||
* @return true if resize was successful, false otherwise
|
||||
*/
|
||||
bool Resize(uint32 NewBufferSize);
|
||||
|
||||
private:
|
||||
//TRingBuffer();
|
||||
|
||||
// Buffer size
|
||||
uint32 BufferDataSize;
|
||||
|
||||
// The data memory
|
||||
DataType* Data;
|
||||
|
||||
// Value keeping track of free space
|
||||
uint32 NumDataAvailable;
|
||||
|
||||
// Value keeping track of the next data index
|
||||
uint32 DataIndex;
|
||||
|
||||
// Value to keep track of total amount of data Enqueued
|
||||
uint64 TotalNumDataPushed;
|
||||
};
|
||||
|
||||
/* TRingBuffer implementation
|
||||
*****************************************************************************/
|
||||
template< typename DataType >
|
||||
TRingBuffer< DataType >::TRingBuffer(uint32 InBufferDataSize)
|
||||
: BufferDataSize(InBufferDataSize)
|
||||
{
|
||||
Data = new DataType[BufferDataSize];
|
||||
Empty();
|
||||
}
|
||||
|
||||
|
||||
template< typename DataType >
|
||||
void TRingBuffer< DataType >::Init(uint32 InBufferDataSize)
|
||||
{
|
||||
BufferDataSize = InBufferDataSize;
|
||||
Data = new DataType[BufferDataSize];
|
||||
Empty();
|
||||
}
|
||||
|
||||
template< typename DataType >
|
||||
TRingBuffer< DataType >::TRingBuffer()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
template< typename DataType >
|
||||
TRingBuffer< DataType >::~TRingBuffer()
|
||||
{
|
||||
delete[] Data;
|
||||
}
|
||||
|
||||
template< typename DataType >
|
||||
void TRingBuffer< DataType >::Empty()
|
||||
{
|
||||
FMemory::Memzero(Data, sizeof(DataType)* BufferDataSize);
|
||||
NumDataAvailable = 0;
|
||||
DataIndex = 0;
|
||||
TotalNumDataPushed = 0;
|
||||
}
|
||||
|
||||
template< typename DataType >
|
||||
void TRingBuffer< DataType >::Enqueue(const DataType& Val)
|
||||
{
|
||||
Data[DataIndex++] = Val;
|
||||
DataIndex %= BufferDataSize;
|
||||
++TotalNumDataPushed;
|
||||
NumDataAvailable = FMath::Clamp< uint32 >(NumDataAvailable + 1, 0, BufferDataSize);
|
||||
}
|
||||
|
||||
template< typename DataType >
|
||||
void TRingBuffer< DataType >::Enqueue(const DataType* ValBuf, const uint32& BufLen)
|
||||
{
|
||||
// Check if we need to resize
|
||||
if (BufLen > BufferDataSize || NumDataAvailable + BufLen > BufferDataSize)
|
||||
{
|
||||
// Calculate new size (double the current size or enough to fit the new data, whichever is larger)
|
||||
uint32 NewSize = FMath::Max(BufferDataSize * 2, NumDataAvailable + BufLen);
|
||||
|
||||
// Cap the maximum size
|
||||
NewSize = FMath::Min(NewSize, MaxBufferSize);
|
||||
|
||||
// Try to resize
|
||||
bool ResizeSuccess = Resize(NewSize);
|
||||
|
||||
// If resize failed and the buffer is too small for the new data
|
||||
if (!ResizeSuccess && BufLen > BufferDataSize)
|
||||
{
|
||||
// We can't fit the entire new buffer, so we'll copy as much as we can
|
||||
uint32 AmountToCopy = BufferDataSize;
|
||||
|
||||
// Copy from the end of the input buffer to get the most recent data
|
||||
const DataType* SourcePtr = ValBuf + (BufLen - AmountToCopy);
|
||||
|
||||
// Clear the buffer and copy what we can
|
||||
Empty();
|
||||
FMemory::Memcpy(Data, SourcePtr, sizeof(DataType) * AmountToCopy);
|
||||
NumDataAvailable = AmountToCopy;
|
||||
DataIndex = 0;
|
||||
TotalNumDataPushed += AmountToCopy;
|
||||
|
||||
return;
|
||||
}
|
||||
// If resize failed but we can fit some data by overwriting old data
|
||||
else if (!ResizeSuccess)
|
||||
{
|
||||
// Calculate how much old data we need to discard
|
||||
uint32 SpaceNeeded = NumDataAvailable + BufLen - BufferDataSize;
|
||||
|
||||
// Discard oldest data
|
||||
NumDataAvailable -= SpaceNeeded;
|
||||
|
||||
// Continue with normal enqueue below
|
||||
}
|
||||
}
|
||||
|
||||
// Original enqueue logic
|
||||
check(BufLen <= BufferDataSize);
|
||||
const uint32 FirstPartLen = BufferDataSize - DataIndex;
|
||||
FMemory::Memcpy(Data + DataIndex, ValBuf, sizeof(DataType) * FMath::Min(FirstPartLen, BufLen));
|
||||
if (FirstPartLen < BufLen)
|
||||
{
|
||||
FMemory::Memcpy(Data, ValBuf + FirstPartLen, sizeof(DataType) * (BufLen - FirstPartLen));
|
||||
}
|
||||
DataIndex += BufLen;
|
||||
DataIndex %= BufferDataSize;
|
||||
TotalNumDataPushed += BufLen;
|
||||
NumDataAvailable = FMath::Clamp<uint32>(NumDataAvailable + BufLen, 0, BufferDataSize);
|
||||
}
|
||||
|
||||
template< typename DataType >
|
||||
bool TRingBuffer< DataType >::Dequeue(DataType& ValOut)
|
||||
{
|
||||
if (Peek(ValOut))
|
||||
{
|
||||
NumDataAvailable = FMath::Clamp< uint32 >(NumDataAvailable - 1, 0, BufferDataSize);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
template< typename DataType >
|
||||
uint32 TRingBuffer< DataType >::Dequeue(DataType* ValBuf, const uint32& BufLen)
|
||||
{
|
||||
uint32 DataProvided = 0;
|
||||
|
||||
if (ValBuf != nullptr)
|
||||
{
|
||||
// Normal case: copy data to the provided buffer
|
||||
DataProvided = Peek(ValBuf, BufLen);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Special case: just discard data without copying
|
||||
DataProvided = FMath::Min(BufLen, NumDataAvailable);
|
||||
}
|
||||
|
||||
// Update the available data count
|
||||
NumDataAvailable -= DataProvided;
|
||||
|
||||
return DataProvided;
|
||||
}
|
||||
|
||||
template< typename DataType >
|
||||
bool TRingBuffer< DataType >::Peek(DataType& ValOut) const
|
||||
{
|
||||
if (NumDataAvailable > 0)
|
||||
{
|
||||
ValOut = Bottom();
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
template< typename DataType >
|
||||
uint32 TRingBuffer< DataType >::Peek(DataType* ValBuf, const uint32& BufLen) const
|
||||
{
|
||||
const uint32 DataProvided = FMath::Min(BufLen, NumDataAvailable);
|
||||
const uint32 BottomIdx = BottomIndex();
|
||||
const uint32 BottomPartLen = BufferDataSize - BottomIdx;
|
||||
FMemory::Memcpy(ValBuf, Data + BottomIdx, sizeof(DataType)* FMath::Min(BottomPartLen, DataProvided));
|
||||
if (BottomPartLen < DataProvided)
|
||||
{
|
||||
FMemory::Memcpy(ValBuf + BottomPartLen, Data, sizeof(DataType)* (DataProvided - BottomPartLen));
|
||||
}
|
||||
return DataProvided;
|
||||
}
|
||||
|
||||
template< typename DataType >
|
||||
int32 TRingBuffer< DataType >::SerialCompare(const DataType* SerialBuffer, uint32 CompareLen) const
|
||||
{
|
||||
check(CompareLen <= BufferDataSize);
|
||||
const uint32 FirstPartLen = BufferDataSize - DataIndex;
|
||||
int32 FirstCmp = FMemory::Memcmp(Data + BottomIndex(), SerialBuffer, sizeof(DataType)* FMath::Min(FirstPartLen, CompareLen));
|
||||
if (FirstCmp != 0 || FirstPartLen >= CompareLen)
|
||||
{
|
||||
return FirstCmp;
|
||||
}
|
||||
return FMemory::Memcmp(Data, &SerialBuffer[FirstPartLen], sizeof(DataType)* (CompareLen - FirstPartLen));
|
||||
}
|
||||
|
||||
template< typename DataType >
|
||||
void TRingBuffer< DataType >::GetShaHash(FSHAHash& OutHash) const
|
||||
{
|
||||
const uint32 FirstPartLen = FMath::Min(BufferDataSize - DataIndex, NumDataAvailable);
|
||||
FSHA1 Sha;
|
||||
Sha.Update(Data + BottomIndex(), sizeof(DataType) * FirstPartLen);
|
||||
if (FirstPartLen < NumDataAvailable)
|
||||
{
|
||||
Sha.Update(Data, sizeof(DataType) * (NumDataAvailable - FirstPartLen));
|
||||
}
|
||||
Sha.Final();
|
||||
Sha.GetHash(OutHash.Hash);
|
||||
}
|
||||
|
||||
template< typename DataType >
|
||||
void TRingBuffer< DataType >::Serialize(DataType* SerialBuffer) const
|
||||
{
|
||||
const uint32 BottomIdx = BottomIndex();
|
||||
const uint32 BottomPartLen = BufferDataSize - BottomIdx;
|
||||
FMemory::Memcpy(SerialBuffer, Data + BottomIdx, sizeof(DataType)* BottomPartLen);
|
||||
FMemory::Memcpy(SerialBuffer + BottomPartLen, Data, sizeof(DataType)* (BufferDataSize - BottomPartLen));
|
||||
}
|
||||
|
||||
template< typename DataType >
|
||||
FORCEINLINE const DataType& TRingBuffer< DataType >::operator[](const int32& Index) const
|
||||
{
|
||||
return Data[(BottomIndex() + Index) % BufferDataSize];
|
||||
}
|
||||
|
||||
template< typename DataType >
|
||||
FORCEINLINE DataType& TRingBuffer< DataType >::operator[](const int32& Index)
|
||||
{
|
||||
return Data[(BottomIndex() + Index) % BufferDataSize];
|
||||
}
|
||||
|
||||
template< typename DataType >
|
||||
FORCEINLINE const DataType& TRingBuffer< DataType >::Top() const
|
||||
{
|
||||
return Data[TopIndex()];
|
||||
}
|
||||
|
||||
template< typename DataType >
|
||||
FORCEINLINE DataType& TRingBuffer< DataType >::Top()
|
||||
{
|
||||
return Data[TopIndex()];
|
||||
}
|
||||
|
||||
template< typename DataType >
|
||||
FORCEINLINE const DataType& TRingBuffer< DataType >::Bottom() const
|
||||
{
|
||||
return Data[BottomIndex()];
|
||||
}
|
||||
|
||||
template< typename DataType >
|
||||
FORCEINLINE DataType& TRingBuffer< DataType >::Bottom()
|
||||
{
|
||||
return Data[BottomIndex()];
|
||||
}
|
||||
|
||||
template< typename DataType >
|
||||
FORCEINLINE uint32 TRingBuffer< DataType >::TopIndex() const
|
||||
{
|
||||
return (DataIndex + BufferDataSize - 1) % BufferDataSize;
|
||||
}
|
||||
|
||||
template< typename DataType >
|
||||
FORCEINLINE uint32 TRingBuffer< DataType >::BottomIndex() const
|
||||
{
|
||||
return (DataIndex + BufferDataSize - NumDataAvailable) % BufferDataSize;
|
||||
}
|
||||
|
||||
template< typename DataType >
|
||||
FORCEINLINE uint32 TRingBuffer< DataType >::NextIndex() const
|
||||
{
|
||||
return DataIndex;
|
||||
}
|
||||
|
||||
template< typename DataType >
|
||||
FORCEINLINE uint32 TRingBuffer< DataType >::RingDataSize() const
|
||||
{
|
||||
return BufferDataSize;
|
||||
}
|
||||
|
||||
template< typename DataType >
|
||||
FORCEINLINE uint32 TRingBuffer< DataType >::RingDataUsage() const
|
||||
{
|
||||
return NumDataAvailable;
|
||||
}
|
||||
|
||||
template< typename DataType >
|
||||
FORCEINLINE uint64 TRingBuffer< DataType >::TotalDataPushed() const
|
||||
{
|
||||
return TotalNumDataPushed;
|
||||
}
|
||||
|
||||
template< typename DataType >
|
||||
bool TRingBuffer< DataType >::Resize(uint32 NewBufferSize)
|
||||
{
|
||||
// Don't resize if new size is smaller than current usage
|
||||
if (NewBufferSize < NumDataAvailable)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Create a new buffer
|
||||
DataType* NewData = new DataType[NewBufferSize];
|
||||
if (!NewData)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Copy existing data to the new buffer
|
||||
if (NumDataAvailable > 0)
|
||||
{
|
||||
// Get the current data in sequential order
|
||||
const uint32 BottomIdx = BottomIndex();
|
||||
const uint32 BottomPartLen = FMath::Min(BufferDataSize - BottomIdx, NumDataAvailable);
|
||||
|
||||
// Copy first part (from bottom index to end of buffer)
|
||||
FMemory::Memcpy(NewData, Data + BottomIdx, sizeof(DataType) * BottomPartLen);
|
||||
|
||||
// Copy second part (from start of buffer if wrapped)
|
||||
if (BottomPartLen < NumDataAvailable)
|
||||
{
|
||||
FMemory::Memcpy(NewData + BottomPartLen, Data, sizeof(DataType) * (NumDataAvailable - BottomPartLen));
|
||||
}
|
||||
}
|
||||
|
||||
// Clean up old buffer and update state
|
||||
delete[] Data;
|
||||
Data = NewData;
|
||||
BufferDataSize = NewBufferSize;
|
||||
|
||||
// Reset the data index to start at 0
|
||||
DataIndex = NumDataAvailable % BufferDataSize;
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
// ConvaiLogger.h
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "HAL/Runnable.h"
|
||||
#include "HAL/RunnableThread.h"
|
||||
#include "Containers/Queue.h"
|
||||
#include "Misc/DateTime.h"
|
||||
#include "Misc/Paths.h"
|
||||
#include "HAL/PlatformFileManager.h"
|
||||
#include "HAL/PlatformFile.h"
|
||||
#include "HAL/ThreadSafeBool.h"
|
||||
#include "HAL/Event.h"
|
||||
#include "Kismet/BlueprintFunctionLibrary.h"
|
||||
#include "ConvaiLogger.generated.h"
|
||||
|
||||
// In editor: log to both UE4’s log window AND your file logger
|
||||
#define CONVAI_LOG(Category, Verbosity, Format, ...) \
|
||||
UE_LOG(Category, Verbosity, Format, ##__VA_ARGS__); \
|
||||
FConvaiLogger::Get().Log( \
|
||||
FString::Printf( \
|
||||
TEXT(#Category) TEXT(" : ") TEXT(#Verbosity) TEXT(" : ") Format, \
|
||||
##__VA_ARGS__ \
|
||||
) \
|
||||
);
|
||||
|
||||
/**
|
||||
* Asynchronous, file-based logger singleton using IFileHandle in append mode.
|
||||
*/
|
||||
class CONVAI_API FConvaiLogger final : public FRunnable
|
||||
{
|
||||
public:
|
||||
static FConvaiLogger& Get();
|
||||
void Log(const FString& Message);
|
||||
|
||||
// FRunnable interface
|
||||
virtual uint32 Run() override;
|
||||
virtual void Stop() override;
|
||||
|
||||
private:
|
||||
FConvaiLogger();
|
||||
virtual ~FConvaiLogger() override;
|
||||
|
||||
// start/stop thread
|
||||
void StartThread();
|
||||
void ShutdownThread();
|
||||
|
||||
FRunnableThread* Thread;
|
||||
TQueue<FString, EQueueMode::Mpsc> MessageQueue;
|
||||
FEvent* WakeEvent;
|
||||
FString LogFilePath;
|
||||
FThreadSafeBool bStopping;
|
||||
};
|
||||
|
||||
|
||||
// BP function lib for logging
|
||||
|
||||
UENUM(BlueprintType)
|
||||
enum class EC_LogLevel : uint8
|
||||
{
|
||||
Verbose UMETA(DisplayName="Verbose"),
|
||||
Log UMETA(DisplayName="Log"),
|
||||
Warning UMETA(DisplayName="Warning"),
|
||||
Error UMETA(DisplayName="Error"),
|
||||
Fatal UMETA(DisplayName="Fatal")
|
||||
};
|
||||
|
||||
UCLASS()
|
||||
class CONVAI_API UConvaiBlueprintLogger : public UBlueprintFunctionLibrary
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
/**
|
||||
* Logs a message with context of which Blueprint object called this.
|
||||
* @param WorldContextObject – automatically filled by Blueprint
|
||||
* @param Verbosity – choose your verbosity level
|
||||
* @param Message – the text you want to log
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, meta=(WorldContext="WorldContextObject", DisplayName="ConvaiLog"), Category="Convai|Logging")
|
||||
static void C_ConvaiLog(UObject* WorldContextObject, EC_LogLevel Verbosity, const FString& Message);
|
||||
};
|
||||
140
ConvAI/Convai/Source/Convai/Public/VisionInterface.h
Normal file
140
ConvAI/Convai/Source/Convai/Public/VisionInterface.h
Normal file
@@ -0,0 +1,140 @@
|
||||
// Copyright 2022 Convai Inc. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "GameFramework/Actor.h"
|
||||
#include "ConvaiDefinitions.h"
|
||||
#include "Engine/Texture.h"
|
||||
#include "VisionInterface.generated.h"
|
||||
|
||||
|
||||
UENUM(BlueprintType)
|
||||
enum class ETextureSourceType : uint8
|
||||
{
|
||||
Texture2D UMETA(DisplayName = "Texture2D"),
|
||||
RenderTarget2D UMETA(DisplayName = "RenderTarget2D")
|
||||
};
|
||||
|
||||
UENUM(BlueprintType)
|
||||
enum class EVisionState : uint8
|
||||
{
|
||||
Stopped UMETA(DisplayName = "Stopped"), // Vision system is inactive and stopped
|
||||
Starting UMETA(DisplayName = "Starting"), // Vision system is initializing
|
||||
Capturing UMETA(DisplayName = "Capturing"), // Vision system is capturing data
|
||||
Stopping UMETA(DisplayName = "Stopping"), // Vision system is in the process of stopping
|
||||
Paused UMETA(DisplayName = "Paused"), // Vision system is paused and not capturing
|
||||
};
|
||||
|
||||
// Delegate to handle state change notifications
|
||||
DECLARE_DELEGATE_OneParam(FOnVisionStateChanged, EVisionState);
|
||||
|
||||
// Delegate to handle when the first frame has been captured
|
||||
DECLARE_DELEGATE(FOnFirstFrameCaptured);
|
||||
|
||||
// Delegate to handle when frames have stopped capturing
|
||||
DECLARE_DELEGATE(FOnFramesStopped);
|
||||
|
||||
UINTERFACE()
|
||||
class CONVAI_API UConvaiVisionInterface : public UInterface
|
||||
{
|
||||
GENERATED_BODY()
|
||||
};
|
||||
|
||||
class IConvaiVisionInterface
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
// Delegate called when the vision state changes
|
||||
FOnVisionStateChanged OnVisionStateChanged;
|
||||
|
||||
// Delegate called when the first frame is captured
|
||||
FOnFirstFrameCaptured OnFirstFrameCaptured;
|
||||
|
||||
// Delegate called when frame capturing stops
|
||||
FOnFramesStopped OnFramesStopped;
|
||||
|
||||
/**
|
||||
* Starts the vision component.
|
||||
* This will transition the state from Stopped or Paused to Capturing.
|
||||
*/
|
||||
virtual void Start() = 0;
|
||||
|
||||
/**
|
||||
* Stops the vision component.
|
||||
* This will transition the state from Capturing or Paused to Stopped.
|
||||
*/
|
||||
virtual void Stop() = 0;
|
||||
|
||||
/**
|
||||
* Retrieves the current vision state.
|
||||
* @return The current EVisionState of the component.
|
||||
*/
|
||||
virtual EVisionState GetState() const = 0;
|
||||
|
||||
/**
|
||||
* Sets the maximum frames per second (FPS) that the vision component will capture.
|
||||
* @param MaxFPS The maximum FPS value to set.
|
||||
*/
|
||||
virtual void SetMaxFPS(int MaxFPS) = 0;
|
||||
|
||||
/**
|
||||
* Gets the current maximum FPS setting.
|
||||
* @return The maximum FPS setting.
|
||||
*/
|
||||
virtual int GetMaxFPS() const = 0;
|
||||
|
||||
/**
|
||||
* Checks if compressed image data is already available (e.g., received from an external source).
|
||||
* @return True if compressed data is available, false otherwise.
|
||||
*/
|
||||
virtual bool IsCompressedDataAvailable() const = 0;
|
||||
|
||||
/**
|
||||
* Retrieves the pre-existing compressed image data (if available).
|
||||
* @param width The width of the compressed image.
|
||||
* @param height The height of the compressed image.
|
||||
* @param data The array to store the compressed image data.
|
||||
* @return True if compressed data was successfully retrieved, false otherwise.
|
||||
*/
|
||||
virtual bool GetCompressedData(int& width, int& height, TArray<uint8>& data) = 0;
|
||||
|
||||
/**
|
||||
* Captures the current frame in a compressed format with a specific compression ratio.
|
||||
* @param width The width of the captured image, populated by the function.
|
||||
* @param height The height of the captured image, populated by the function.
|
||||
* @param data An array to store the compressed image data, populated by the function.
|
||||
* @param ForceCompressionRatio A specific compression ratio to apply during capture.
|
||||
* @return True if capture succeeded, false otherwise.
|
||||
*/
|
||||
virtual bool CaptureCompressed(int& width, int& height, TArray<uint8>& data, float ForceCompressionRatio) = 0;
|
||||
|
||||
/**
|
||||
* Captures the current frame in raw format (uncompressed).
|
||||
* @param width The width of the captured image, populated by the function.
|
||||
* @param height The height of the captured image, populated by the function.
|
||||
* @param data An array to store the raw image data, populated by the function.
|
||||
* @return True if capture succeeded, false otherwise.
|
||||
*/
|
||||
virtual bool CaptureRaw(int& width, int& height, TArray<uint8>& data) = 0;
|
||||
|
||||
/**
|
||||
* Retrieves the texture of the captured image based on the source type.
|
||||
* @param TextureSourceType The type of texture source (Texture2D or RenderTarget2D).
|
||||
* @return A pointer to the UTexture containing the captured image.
|
||||
*/
|
||||
virtual UTexture* GetImageTexture(ETextureSourceType& TextureSourceType) = 0;
|
||||
|
||||
/**
|
||||
* Retrieves the last error message encountered by the vision component.
|
||||
* @return The last error message as a string.
|
||||
*/
|
||||
virtual FString GetLastErrorMessage() const = 0;
|
||||
|
||||
/**
|
||||
* Retrieves the last error code encountered by the vision component.
|
||||
* @return The last error code.
|
||||
*/
|
||||
virtual int GetLastErrorCode() const = 0;
|
||||
};
|
||||
Reference in New Issue
Block a user