Add Games File
This commit is contained in:
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"FileVersion": 3,
|
||||
"Version": 1,
|
||||
"VersionName": "1.0.0",
|
||||
"FriendlyName": "Android Speech To Text",
|
||||
"Description": "Blueprint Android speech recognition using the native SpeechRecognizer API.",
|
||||
"Category": "Input",
|
||||
"CreatedBy": "FunnyGirl",
|
||||
"CanContainContent": false,
|
||||
"IsBetaVersion": true,
|
||||
"EnabledByDefault": true,
|
||||
"Modules": [
|
||||
{
|
||||
"Name": "AndroidSpeechToText",
|
||||
"Type": "Runtime",
|
||||
"LoadingPhase": "Default"
|
||||
}
|
||||
]
|
||||
}
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<init>
|
||||
<log text="AndroidSpeechToText init"/>
|
||||
</init>
|
||||
|
||||
<androidManifestUpdates>
|
||||
<addPermission android:name="android.permission.RECORD_AUDIO"/>
|
||||
</androidManifestUpdates>
|
||||
|
||||
<prebuildCopies>
|
||||
<log text="Copying AndroidSpeechToText Java source"/>
|
||||
<copyFile src="$S(PluginDir)/Java/AndroidSpeechToText.java" dst="$S(BuildDir)/src/com/epicgames/unreal/AndroidSpeechToText.java"/>
|
||||
</prebuildCopies>
|
||||
|
||||
<proguardAdditions>
|
||||
<insert>
|
||||
-keep class com.epicgames.unreal.AndroidSpeechToText {
|
||||
public *;
|
||||
}
|
||||
</insert>
|
||||
</proguardAdditions>
|
||||
|
||||
<gameActivityOnDestroyAdditions>
|
||||
<insert>
|
||||
AndroidSpeechToText.destroyRecognizer();
|
||||
</insert>
|
||||
</gameActivityOnDestroyAdditions>
|
||||
|
||||
<gameActivityOnRequestPermissionsResultAdditions>
|
||||
<insert>
|
||||
AndroidSpeechToText.handlePermissionResult(this, requestCode, permissions, grantResults);
|
||||
</insert>
|
||||
</gameActivityOnRequestPermissionsResultAdditions>
|
||||
</root>
|
||||
+389
@@ -0,0 +1,389 @@
|
||||
package com.epicgames.unreal;
|
||||
|
||||
import android.Manifest;
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.speech.RecognitionListener;
|
||||
import android.speech.RecognizerIntent;
|
||||
import android.speech.SpeechRecognizer;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Locale;
|
||||
|
||||
public class AndroidSpeechToText {
|
||||
private static final int REQUEST_RECORD_AUDIO = 52031;
|
||||
private static final long RESTART_DELAY_MS = 250;
|
||||
private static SpeechRecognizer recognizer = null;
|
||||
private static boolean isListening = false;
|
||||
private static boolean enablePartialResults = true;
|
||||
private static boolean keepListening = false;
|
||||
private static boolean recognizerActive = false;
|
||||
private static boolean hasPendingPermissionStart = false;
|
||||
private static String pendingLanguageTag = "";
|
||||
private static boolean pendingPreferOffline = false;
|
||||
private static boolean pendingPartialResults = true;
|
||||
private static boolean pendingKeepListening = true;
|
||||
private static String activeLanguageTag = "";
|
||||
private static boolean activePreferOffline = false;
|
||||
private static boolean activePartialResults = true;
|
||||
private static WeakReference<Activity> activeActivity = new WeakReference<Activity>(null);
|
||||
private static String lastFinalText = "";
|
||||
private static String lastPartialText = "";
|
||||
private static String lastErrorMessage = "";
|
||||
private static final Handler mainHandler = new Handler(Looper.getMainLooper());
|
||||
|
||||
public static native void nativeOnReady();
|
||||
public static native void nativeOnBeginningOfSpeech();
|
||||
public static native void nativeOnEndOfSpeech();
|
||||
public static native void nativeOnPartialText(String text);
|
||||
public static native void nativeOnFinalText(String text);
|
||||
public static native void nativeOnError(int errorCode, String message);
|
||||
|
||||
public static boolean startListening(final Activity activity, final String languageTag, final boolean preferOffline, final boolean partialResults, final boolean shouldKeepListening) {
|
||||
if (activity == null) {
|
||||
keepListening = false;
|
||||
setError(SpeechRecognizer.ERROR_CLIENT, "Android activity is not available.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!SpeechRecognizer.isRecognitionAvailable(activity)) {
|
||||
keepListening = false;
|
||||
setError(1000, "Speech recognition is not available on this device.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!hasRecordAudioPermission(activity)) {
|
||||
hasPendingPermissionStart = true;
|
||||
pendingLanguageTag = languageTag != null ? languageTag : "";
|
||||
pendingPreferOffline = preferOffline;
|
||||
pendingPartialResults = partialResults;
|
||||
pendingKeepListening = shouldKeepListening;
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
activity.requestPermissions(new String[] { Manifest.permission.RECORD_AUDIO }, REQUEST_RECORD_AUDIO);
|
||||
}
|
||||
setError(SpeechRecognizer.ERROR_INSUFFICIENT_PERMISSIONS, "Microphone permission is required.");
|
||||
return false;
|
||||
}
|
||||
|
||||
enablePartialResults = partialResults;
|
||||
keepListening = shouldKeepListening;
|
||||
activeLanguageTag = languageTag != null ? languageTag : "";
|
||||
activePreferOffline = preferOffline;
|
||||
activePartialResults = partialResults;
|
||||
activeActivity = new WeakReference<Activity>(activity);
|
||||
|
||||
activity.runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
ensureRecognizer(activity);
|
||||
lastFinalText = "";
|
||||
lastPartialText = "";
|
||||
lastErrorMessage = "";
|
||||
|
||||
Intent intent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
|
||||
intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, RecognizerIntent.LANGUAGE_MODEL_FREE_FORM);
|
||||
intent.putExtra(RecognizerIntent.EXTRA_PARTIAL_RESULTS, partialResults);
|
||||
intent.putExtra(RecognizerIntent.EXTRA_CALLING_PACKAGE, activity.getPackageName());
|
||||
intent.putExtra(RecognizerIntent.EXTRA_PREFER_OFFLINE, preferOffline);
|
||||
|
||||
if (languageTag != null && languageTag.length() > 0) {
|
||||
intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE, languageTag);
|
||||
} else {
|
||||
intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE, Locale.getDefault().toLanguageTag());
|
||||
}
|
||||
|
||||
isListening = true;
|
||||
recognizerActive = true;
|
||||
recognizer.startListening(intent);
|
||||
} catch (Exception exception) {
|
||||
isListening = false;
|
||||
recognizerActive = false;
|
||||
setError(SpeechRecognizer.ERROR_CLIENT, exception.getMessage());
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static void stopListening() {
|
||||
keepListening = false;
|
||||
if (recognizer == null) {
|
||||
isListening = false;
|
||||
recognizerActive = false;
|
||||
return;
|
||||
}
|
||||
|
||||
runRecognizerCommand(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
recognizer.stopListening();
|
||||
} catch (Exception exception) {
|
||||
isListening = false;
|
||||
recognizerActive = false;
|
||||
setError(SpeechRecognizer.ERROR_CLIENT, exception.getMessage());
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static void cancelListening() {
|
||||
keepListening = false;
|
||||
if (recognizer == null) {
|
||||
isListening = false;
|
||||
recognizerActive = false;
|
||||
return;
|
||||
}
|
||||
|
||||
runRecognizerCommand(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
recognizer.cancel();
|
||||
} catch (Exception exception) {
|
||||
setError(SpeechRecognizer.ERROR_CLIENT, exception.getMessage());
|
||||
}
|
||||
isListening = false;
|
||||
recognizerActive = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static void destroyRecognizer() {
|
||||
keepListening = false;
|
||||
if (recognizer == null) {
|
||||
isListening = false;
|
||||
recognizerActive = false;
|
||||
return;
|
||||
}
|
||||
|
||||
runRecognizerCommand(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
recognizer.destroy();
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
recognizer = null;
|
||||
isListening = false;
|
||||
recognizerActive = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static boolean isListening() {
|
||||
return isListening || keepListening;
|
||||
}
|
||||
|
||||
public static boolean isSpeechRecognizerAvailable(Activity activity) {
|
||||
return activity != null && SpeechRecognizer.isRecognitionAvailable(activity);
|
||||
}
|
||||
|
||||
public static void handlePermissionResult(Activity activity, int requestCode, String[] permissions, int[] grantResults) {
|
||||
if (requestCode != REQUEST_RECORD_AUDIO || !hasPendingPermissionStart) {
|
||||
return;
|
||||
}
|
||||
|
||||
boolean granted = grantResults != null
|
||||
&& grantResults.length > 0
|
||||
&& grantResults[0] == PackageManager.PERMISSION_GRANTED;
|
||||
|
||||
if (!granted) {
|
||||
hasPendingPermissionStart = false;
|
||||
setError(SpeechRecognizer.ERROR_INSUFFICIENT_PERMISSIONS, "Microphone permission was denied.");
|
||||
return;
|
||||
}
|
||||
|
||||
String languageTag = pendingLanguageTag;
|
||||
boolean preferOffline = pendingPreferOffline;
|
||||
boolean partialResults = pendingPartialResults;
|
||||
boolean shouldKeepListening = pendingKeepListening;
|
||||
hasPendingPermissionStart = false;
|
||||
startListening(activity, languageTag, preferOffline, partialResults, shouldKeepListening);
|
||||
}
|
||||
|
||||
public static String getLastFinalText() {
|
||||
return lastFinalText;
|
||||
}
|
||||
|
||||
public static String getLastPartialText() {
|
||||
return lastPartialText;
|
||||
}
|
||||
|
||||
public static String getLastErrorMessage() {
|
||||
return lastErrorMessage;
|
||||
}
|
||||
|
||||
private static void ensureRecognizer(Activity activity) {
|
||||
if (recognizer != null) {
|
||||
recognizer.cancel();
|
||||
recognizer.destroy();
|
||||
}
|
||||
|
||||
recognizer = SpeechRecognizer.createSpeechRecognizer(activity.getApplicationContext());
|
||||
recognizer.setRecognitionListener(new RecognitionListener() {
|
||||
@Override
|
||||
public void onReadyForSpeech(Bundle params) {
|
||||
nativeOnReady();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBeginningOfSpeech() {
|
||||
nativeOnBeginningOfSpeech();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRmsChanged(float rmsdB) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBufferReceived(byte[] buffer) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEndOfSpeech() {
|
||||
recognizerActive = false;
|
||||
isListening = keepListening;
|
||||
nativeOnEndOfSpeech();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(int error) {
|
||||
recognizerActive = false;
|
||||
isListening = keepListening;
|
||||
setError(error, errorToMessage(error));
|
||||
if (shouldRestartAfterError(error)) {
|
||||
scheduleRestart();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResults(Bundle results) {
|
||||
recognizerActive = false;
|
||||
isListening = keepListening;
|
||||
String text = firstRecognitionResult(results);
|
||||
lastFinalText = text;
|
||||
nativeOnFinalText(text);
|
||||
scheduleRestart();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPartialResults(Bundle partialResults) {
|
||||
if (!enablePartialResults) {
|
||||
return;
|
||||
}
|
||||
|
||||
String text = firstRecognitionResult(partialResults);
|
||||
lastPartialText = text;
|
||||
nativeOnPartialText(text);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEvent(int eventType, Bundle params) {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static String firstRecognitionResult(Bundle results) {
|
||||
if (results == null) {
|
||||
return "";
|
||||
}
|
||||
|
||||
ArrayList<String> matches = results.getStringArrayList(SpeechRecognizer.RESULTS_RECOGNITION);
|
||||
if (matches == null || matches.isEmpty()) {
|
||||
return "";
|
||||
}
|
||||
|
||||
String first = matches.get(0);
|
||||
return first != null ? first : "";
|
||||
}
|
||||
|
||||
private static boolean hasRecordAudioPermission(Activity activity) {
|
||||
return Build.VERSION.SDK_INT < Build.VERSION_CODES.M
|
||||
|| activity.checkSelfPermission(Manifest.permission.RECORD_AUDIO) == PackageManager.PERMISSION_GRANTED;
|
||||
}
|
||||
|
||||
private static void runRecognizerCommand(Runnable command) {
|
||||
mainHandler.post(command);
|
||||
}
|
||||
|
||||
private static void scheduleRestart() {
|
||||
if (!keepListening) {
|
||||
return;
|
||||
}
|
||||
|
||||
mainHandler.postDelayed(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (!keepListening || recognizerActive) {
|
||||
return;
|
||||
}
|
||||
|
||||
Activity activity = activeActivity.get();
|
||||
if (activity == null) {
|
||||
keepListening = false;
|
||||
isListening = false;
|
||||
setError(SpeechRecognizer.ERROR_CLIENT, "Android activity is not available.");
|
||||
return;
|
||||
}
|
||||
|
||||
startListening(activity, activeLanguageTag, activePreferOffline, activePartialResults, true);
|
||||
}
|
||||
}, RESTART_DELAY_MS);
|
||||
}
|
||||
|
||||
private static boolean shouldRestartAfterError(int error) {
|
||||
return keepListening
|
||||
&& error != SpeechRecognizer.ERROR_INSUFFICIENT_PERMISSIONS
|
||||
&& error != SpeechRecognizer.ERROR_CLIENT
|
||||
&& error != 1000;
|
||||
}
|
||||
|
||||
private static void setError(int errorCode, String message) {
|
||||
lastErrorMessage = message != null ? message : errorToMessage(errorCode);
|
||||
nativeOnError(errorCode, lastErrorMessage);
|
||||
}
|
||||
|
||||
private static String errorToMessage(int error) {
|
||||
switch (error) {
|
||||
case SpeechRecognizer.ERROR_NETWORK_TIMEOUT:
|
||||
return "Network timeout.";
|
||||
case SpeechRecognizer.ERROR_NETWORK:
|
||||
return "Network error.";
|
||||
case SpeechRecognizer.ERROR_AUDIO:
|
||||
return "Audio recording error.";
|
||||
case SpeechRecognizer.ERROR_SERVER:
|
||||
return "Recognition server error.";
|
||||
case SpeechRecognizer.ERROR_CLIENT:
|
||||
return "Recognition client error.";
|
||||
case SpeechRecognizer.ERROR_SPEECH_TIMEOUT:
|
||||
return "No speech input.";
|
||||
case SpeechRecognizer.ERROR_NO_MATCH:
|
||||
return "Speech was not recognized.";
|
||||
case SpeechRecognizer.ERROR_RECOGNIZER_BUSY:
|
||||
return "Speech recognizer is busy.";
|
||||
case SpeechRecognizer.ERROR_INSUFFICIENT_PERMISSIONS:
|
||||
return "Insufficient microphone permissions.";
|
||||
case 10:
|
||||
return "Too many recognition requests.";
|
||||
case 11:
|
||||
return "Recognition server disconnected.";
|
||||
case 12:
|
||||
return "Language is not supported.";
|
||||
case 13:
|
||||
return "Language is unavailable.";
|
||||
case 1000:
|
||||
return "Speech recognition is not available on this device.";
|
||||
default:
|
||||
return "Unknown speech recognition error: " + error;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
using System.IO;
|
||||
using UnrealBuildTool;
|
||||
|
||||
public class AndroidSpeechToText : ModuleRules
|
||||
{
|
||||
public AndroidSpeechToText(ReadOnlyTargetRules Target) : base(Target)
|
||||
{
|
||||
PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;
|
||||
|
||||
PublicDependencyModuleNames.AddRange(new string[]
|
||||
{
|
||||
"Core",
|
||||
"CoreUObject",
|
||||
"Engine"
|
||||
});
|
||||
|
||||
PrivateDependencyModuleNames.AddRange(new string[]
|
||||
{
|
||||
"Projects"
|
||||
});
|
||||
|
||||
if (Target.Platform == UnrealTargetPlatform.Android)
|
||||
{
|
||||
PrivateDependencyModuleNames.AddRange(new string[]
|
||||
{
|
||||
"ApplicationCore",
|
||||
"Launch"
|
||||
});
|
||||
|
||||
string PluginPath = Utils.MakePathRelativeTo(ModuleDirectory, Target.RelativeEnginePath);
|
||||
AdditionalPropertiesForReceipt.Add("AndroidPlugin", Path.Combine(PluginPath, "Android", "AndroidSpeechToText_UPL.xml"));
|
||||
}
|
||||
}
|
||||
}
|
||||
+140
@@ -0,0 +1,140 @@
|
||||
#include "AndroidSpeechToTextComponent.h"
|
||||
|
||||
#include "AndroidSpeechToTextEvents.h"
|
||||
#include "AndroidSpeechToTextJavaBridge.h"
|
||||
|
||||
UAndroidSpeechToTextComponent::UAndroidSpeechToTextComponent()
|
||||
{
|
||||
PrimaryComponentTick.bCanEverTick = false;
|
||||
}
|
||||
|
||||
void UAndroidSpeechToTextComponent::BeginPlay()
|
||||
{
|
||||
Super::BeginPlay();
|
||||
|
||||
AndroidSpeechToTextEvents::OnReady.AddUObject(this, &UAndroidSpeechToTextComponent::HandleSpeechReady);
|
||||
AndroidSpeechToTextEvents::OnBeginning.AddUObject(this, &UAndroidSpeechToTextComponent::HandleSpeechStarted);
|
||||
AndroidSpeechToTextEvents::OnEnd.AddUObject(this, &UAndroidSpeechToTextComponent::HandleSpeechEnded);
|
||||
AndroidSpeechToTextEvents::OnPartialText.AddUObject(this, &UAndroidSpeechToTextComponent::HandlePartialText);
|
||||
AndroidSpeechToTextEvents::OnFinalText.AddUObject(this, &UAndroidSpeechToTextComponent::HandleFinalText);
|
||||
AndroidSpeechToTextEvents::OnError.AddUObject(this, &UAndroidSpeechToTextComponent::HandleSpeechError);
|
||||
}
|
||||
|
||||
void UAndroidSpeechToTextComponent::EndPlay(const EEndPlayReason::Type EndPlayReason)
|
||||
{
|
||||
AndroidSpeechToTextEvents::OnReady.RemoveAll(this);
|
||||
AndroidSpeechToTextEvents::OnBeginning.RemoveAll(this);
|
||||
AndroidSpeechToTextEvents::OnEnd.RemoveAll(this);
|
||||
AndroidSpeechToTextEvents::OnPartialText.RemoveAll(this);
|
||||
AndroidSpeechToTextEvents::OnFinalText.RemoveAll(this);
|
||||
AndroidSpeechToTextEvents::OnError.RemoveAll(this);
|
||||
|
||||
Super::EndPlay(EndPlayReason);
|
||||
}
|
||||
|
||||
bool UAndroidSpeechToTextComponent::StartListening(const FString& LanguageTag, bool bPreferOffline, bool bEnablePartialResults, bool bKeepListening)
|
||||
{
|
||||
return AndroidSpeechToTextJavaBridge::StartListening(LanguageTag, bPreferOffline, bEnablePartialResults, bKeepListening);
|
||||
}
|
||||
|
||||
void UAndroidSpeechToTextComponent::StopListening()
|
||||
{
|
||||
AndroidSpeechToTextJavaBridge::StopListening();
|
||||
}
|
||||
|
||||
void UAndroidSpeechToTextComponent::CancelListening()
|
||||
{
|
||||
AndroidSpeechToTextJavaBridge::CancelListening();
|
||||
}
|
||||
|
||||
bool UAndroidSpeechToTextComponent::IsListening() const
|
||||
{
|
||||
return AndroidSpeechToTextJavaBridge::IsListening();
|
||||
}
|
||||
|
||||
FString UAndroidSpeechToTextComponent::GetLastFinalText() const
|
||||
{
|
||||
return AndroidSpeechToTextJavaBridge::GetLastFinalText();
|
||||
}
|
||||
|
||||
FString UAndroidSpeechToTextComponent::GetLastPartialText() const
|
||||
{
|
||||
return AndroidSpeechToTextJavaBridge::GetLastPartialText();
|
||||
}
|
||||
|
||||
FString UAndroidSpeechToTextComponent::GetLastErrorMessage() const
|
||||
{
|
||||
return AndroidSpeechToTextJavaBridge::GetLastErrorMessage();
|
||||
}
|
||||
|
||||
bool UAndroidSpeechToTextComponent::IsAndroidSpeechToTextSupported()
|
||||
{
|
||||
return AndroidSpeechToTextJavaBridge::IsSupported();
|
||||
}
|
||||
|
||||
void UAndroidSpeechToTextComponent::HandleSpeechReady()
|
||||
{
|
||||
OnSpeechReady.Broadcast();
|
||||
}
|
||||
|
||||
void UAndroidSpeechToTextComponent::HandleSpeechStarted()
|
||||
{
|
||||
OnSpeechStarted.Broadcast();
|
||||
}
|
||||
|
||||
void UAndroidSpeechToTextComponent::HandleSpeechEnded()
|
||||
{
|
||||
OnSpeechEnded.Broadcast();
|
||||
}
|
||||
|
||||
void UAndroidSpeechToTextComponent::HandlePartialText(const FString& Text)
|
||||
{
|
||||
OnPartialText.Broadcast(Text);
|
||||
}
|
||||
|
||||
void UAndroidSpeechToTextComponent::HandleFinalText(const FString& Text)
|
||||
{
|
||||
OnFinalText.Broadcast(Text);
|
||||
}
|
||||
|
||||
void UAndroidSpeechToTextComponent::HandleSpeechError(int32 ErrorCode, const FString& Message)
|
||||
{
|
||||
OnSpeechError.Broadcast(ConvertErrorCode(ErrorCode), Message);
|
||||
}
|
||||
|
||||
EAndroidSpeechToTextError UAndroidSpeechToTextComponent::ConvertErrorCode(int32 ErrorCode)
|
||||
{
|
||||
switch (ErrorCode)
|
||||
{
|
||||
case 1:
|
||||
return EAndroidSpeechToTextError::NetworkTimeout;
|
||||
case 2:
|
||||
return EAndroidSpeechToTextError::Network;
|
||||
case 3:
|
||||
return EAndroidSpeechToTextError::Audio;
|
||||
case 4:
|
||||
return EAndroidSpeechToTextError::Server;
|
||||
case 5:
|
||||
return EAndroidSpeechToTextError::Client;
|
||||
case 6:
|
||||
return EAndroidSpeechToTextError::SpeechTimeout;
|
||||
case 7:
|
||||
return EAndroidSpeechToTextError::NoMatch;
|
||||
case 8:
|
||||
return EAndroidSpeechToTextError::RecognizerBusy;
|
||||
case 9:
|
||||
return EAndroidSpeechToTextError::InsufficientPermissions;
|
||||
case 10:
|
||||
return EAndroidSpeechToTextError::TooManyRequests;
|
||||
case 11:
|
||||
return EAndroidSpeechToTextError::ServerDisconnected;
|
||||
case 12:
|
||||
return EAndroidSpeechToTextError::LanguageNotSupported;
|
||||
case 13:
|
||||
return EAndroidSpeechToTextError::LanguageUnavailable;
|
||||
case 1000:
|
||||
return EAndroidSpeechToTextError::Unsupported;
|
||||
default:
|
||||
return EAndroidSpeechToTextError::Unknown;
|
||||
}
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
|
||||
DECLARE_MULTICAST_DELEGATE(FAndroidSpeechToTextNativeSimpleDelegate);
|
||||
DECLARE_MULTICAST_DELEGATE_OneParam(FAndroidSpeechToTextNativeTextDelegate, const FString&);
|
||||
DECLARE_MULTICAST_DELEGATE_TwoParams(FAndroidSpeechToTextNativeErrorDelegate, int32, const FString&);
|
||||
|
||||
namespace AndroidSpeechToTextEvents
|
||||
{
|
||||
extern FAndroidSpeechToTextNativeSimpleDelegate OnReady;
|
||||
extern FAndroidSpeechToTextNativeSimpleDelegate OnBeginning;
|
||||
extern FAndroidSpeechToTextNativeSimpleDelegate OnEnd;
|
||||
extern FAndroidSpeechToTextNativeTextDelegate OnPartialText;
|
||||
extern FAndroidSpeechToTextNativeTextDelegate OnFinalText;
|
||||
extern FAndroidSpeechToTextNativeErrorDelegate OnError;
|
||||
}
|
||||
+239
@@ -0,0 +1,239 @@
|
||||
#include "AndroidSpeechToTextJavaBridge.h"
|
||||
|
||||
#include "AndroidSpeechToTextEvents.h"
|
||||
#include "Async/Async.h"
|
||||
|
||||
#if PLATFORM_ANDROID
|
||||
#include "Android/AndroidApplication.h"
|
||||
#include "Android/AndroidJavaEnv.h"
|
||||
#include "Android/AndroidJNI.h"
|
||||
#endif
|
||||
|
||||
namespace AndroidSpeechToTextEvents
|
||||
{
|
||||
FAndroidSpeechToTextNativeSimpleDelegate OnReady;
|
||||
FAndroidSpeechToTextNativeSimpleDelegate OnBeginning;
|
||||
FAndroidSpeechToTextNativeSimpleDelegate OnEnd;
|
||||
FAndroidSpeechToTextNativeTextDelegate OnPartialText;
|
||||
FAndroidSpeechToTextNativeTextDelegate OnFinalText;
|
||||
FAndroidSpeechToTextNativeErrorDelegate OnError;
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
#if PLATFORM_ANDROID
|
||||
jclass GetJavaClass(JNIEnv* Env)
|
||||
{
|
||||
static jclass SpeechClass = FAndroidApplication::FindJavaClassGlobalRef("com/epicgames/unreal/AndroidSpeechToText");
|
||||
if (SpeechClass == nullptr)
|
||||
{
|
||||
FAndroidApplication::CheckJavaException();
|
||||
UE_LOG(LogTemp, Error, TEXT("AndroidSpeechToText Java class was not found."));
|
||||
}
|
||||
return SpeechClass;
|
||||
}
|
||||
|
||||
jmethodID GetStaticMethod(JNIEnv* Env, const ANSICHAR* MethodName, const ANSICHAR* Signature)
|
||||
{
|
||||
jclass SpeechClass = GetJavaClass(Env);
|
||||
if (SpeechClass == nullptr)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
jmethodID Method = FJavaWrapper::FindStaticMethod(Env, SpeechClass, MethodName, Signature, false);
|
||||
if (Method == nullptr)
|
||||
{
|
||||
FAndroidApplication::CheckJavaException();
|
||||
UE_LOG(LogTemp, Error, TEXT("AndroidSpeechToText Java method was not found: %s"), ANSI_TO_TCHAR(MethodName));
|
||||
}
|
||||
return Method;
|
||||
}
|
||||
|
||||
FString JavaStringResult(const ANSICHAR* MethodName)
|
||||
{
|
||||
JNIEnv* Env = FAndroidApplication::GetJavaEnv();
|
||||
jclass SpeechClass = Env ? GetJavaClass(Env) : nullptr;
|
||||
jmethodID Method = Env ? GetStaticMethod(Env, MethodName, "()Ljava/lang/String;") : nullptr;
|
||||
if (SpeechClass != nullptr && Method != nullptr)
|
||||
{
|
||||
jstring Result = static_cast<jstring>(Env->CallStaticObjectMethod(SpeechClass, Method));
|
||||
FAndroidApplication::CheckJavaException();
|
||||
return FJavaHelper::FStringFromLocalRef(Env, Result);
|
||||
}
|
||||
return FString();
|
||||
}
|
||||
#endif
|
||||
|
||||
void BroadcastSimpleOnGameThread(FAndroidSpeechToTextNativeSimpleDelegate& Delegate)
|
||||
{
|
||||
AsyncTask(ENamedThreads::GameThread, [&Delegate]()
|
||||
{
|
||||
Delegate.Broadcast();
|
||||
});
|
||||
}
|
||||
|
||||
void BroadcastTextOnGameThread(FAndroidSpeechToTextNativeTextDelegate& Delegate, FString Text)
|
||||
{
|
||||
AsyncTask(ENamedThreads::GameThread, [&Delegate, Text]()
|
||||
{
|
||||
Delegate.Broadcast(Text);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
namespace AndroidSpeechToTextJavaBridge
|
||||
{
|
||||
bool StartListening(const FString& LanguageTag, bool bPreferOffline, bool bEnablePartialResults, bool bKeepListening)
|
||||
{
|
||||
#if PLATFORM_ANDROID
|
||||
JNIEnv* Env = FAndroidApplication::GetJavaEnv();
|
||||
jclass SpeechClass = Env ? GetJavaClass(Env) : nullptr;
|
||||
jmethodID Method = Env ? GetStaticMethod(Env, "startListening", "(Landroid/app/Activity;Ljava/lang/String;ZZZ)Z") : nullptr;
|
||||
if (SpeechClass == nullptr || Method == nullptr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
auto JavaLanguageTag = FJavaHelper::ToJavaString(Env, LanguageTag);
|
||||
const jboolean Result = Env->CallStaticBooleanMethod(
|
||||
SpeechClass,
|
||||
Method,
|
||||
FJavaWrapper::GameActivityThis,
|
||||
*JavaLanguageTag,
|
||||
bPreferOffline ? JNI_TRUE : JNI_FALSE,
|
||||
bEnablePartialResults ? JNI_TRUE : JNI_FALSE,
|
||||
bKeepListening ? JNI_TRUE : JNI_FALSE);
|
||||
FAndroidApplication::CheckJavaException();
|
||||
return Result == JNI_TRUE;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
void StopListening()
|
||||
{
|
||||
#if PLATFORM_ANDROID
|
||||
JNIEnv* Env = FAndroidApplication::GetJavaEnv();
|
||||
jclass SpeechClass = Env ? GetJavaClass(Env) : nullptr;
|
||||
jmethodID Method = Env ? GetStaticMethod(Env, "stopListening", "()V") : nullptr;
|
||||
if (SpeechClass != nullptr && Method != nullptr)
|
||||
{
|
||||
Env->CallStaticVoidMethod(SpeechClass, Method);
|
||||
FAndroidApplication::CheckJavaException();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void CancelListening()
|
||||
{
|
||||
#if PLATFORM_ANDROID
|
||||
JNIEnv* Env = FAndroidApplication::GetJavaEnv();
|
||||
jclass SpeechClass = Env ? GetJavaClass(Env) : nullptr;
|
||||
jmethodID Method = Env ? GetStaticMethod(Env, "cancelListening", "()V") : nullptr;
|
||||
if (SpeechClass != nullptr && Method != nullptr)
|
||||
{
|
||||
Env->CallStaticVoidMethod(SpeechClass, Method);
|
||||
FAndroidApplication::CheckJavaException();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
bool IsListening()
|
||||
{
|
||||
#if PLATFORM_ANDROID
|
||||
JNIEnv* Env = FAndroidApplication::GetJavaEnv();
|
||||
jclass SpeechClass = Env ? GetJavaClass(Env) : nullptr;
|
||||
jmethodID Method = Env ? GetStaticMethod(Env, "isListening", "()Z") : nullptr;
|
||||
if (SpeechClass != nullptr && Method != nullptr)
|
||||
{
|
||||
const jboolean Result = Env->CallStaticBooleanMethod(SpeechClass, Method);
|
||||
FAndroidApplication::CheckJavaException();
|
||||
return Result == JNI_TRUE;
|
||||
}
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
FString GetLastFinalText()
|
||||
{
|
||||
#if PLATFORM_ANDROID
|
||||
return JavaStringResult("getLastFinalText");
|
||||
#else
|
||||
return FString();
|
||||
#endif
|
||||
}
|
||||
|
||||
FString GetLastPartialText()
|
||||
{
|
||||
#if PLATFORM_ANDROID
|
||||
return JavaStringResult("getLastPartialText");
|
||||
#else
|
||||
return FString();
|
||||
#endif
|
||||
}
|
||||
|
||||
FString GetLastErrorMessage()
|
||||
{
|
||||
#if PLATFORM_ANDROID
|
||||
return JavaStringResult("getLastErrorMessage");
|
||||
#else
|
||||
return FString();
|
||||
#endif
|
||||
}
|
||||
|
||||
bool IsSupported()
|
||||
{
|
||||
#if PLATFORM_ANDROID
|
||||
JNIEnv* Env = FAndroidApplication::GetJavaEnv();
|
||||
jclass SpeechClass = Env ? GetJavaClass(Env) : nullptr;
|
||||
jmethodID Method = Env ? GetStaticMethod(Env, "isSpeechRecognizerAvailable", "(Landroid/app/Activity;)Z") : nullptr;
|
||||
if (SpeechClass != nullptr && Method != nullptr)
|
||||
{
|
||||
const jboolean Result = Env->CallStaticBooleanMethod(SpeechClass, Method, FJavaWrapper::GameActivityThis);
|
||||
FAndroidApplication::CheckJavaException();
|
||||
return Result == JNI_TRUE;
|
||||
}
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
#if PLATFORM_ANDROID
|
||||
extern "C"
|
||||
{
|
||||
JNIEXPORT void JNICALL Java_com_epicgames_unreal_AndroidSpeechToText_nativeOnReady(JNIEnv* Env, jclass Class)
|
||||
{
|
||||
BroadcastSimpleOnGameThread(AndroidSpeechToTextEvents::OnReady);
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL Java_com_epicgames_unreal_AndroidSpeechToText_nativeOnBeginningOfSpeech(JNIEnv* Env, jclass Class)
|
||||
{
|
||||
BroadcastSimpleOnGameThread(AndroidSpeechToTextEvents::OnBeginning);
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL Java_com_epicgames_unreal_AndroidSpeechToText_nativeOnEndOfSpeech(JNIEnv* Env, jclass Class)
|
||||
{
|
||||
BroadcastSimpleOnGameThread(AndroidSpeechToTextEvents::OnEnd);
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL Java_com_epicgames_unreal_AndroidSpeechToText_nativeOnPartialText(JNIEnv* Env, jclass Class, jstring Text)
|
||||
{
|
||||
BroadcastTextOnGameThread(AndroidSpeechToTextEvents::OnPartialText, FJavaHelper::FStringFromParam(Env, Text));
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL Java_com_epicgames_unreal_AndroidSpeechToText_nativeOnFinalText(JNIEnv* Env, jclass Class, jstring Text)
|
||||
{
|
||||
BroadcastTextOnGameThread(AndroidSpeechToTextEvents::OnFinalText, FJavaHelper::FStringFromParam(Env, Text));
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL Java_com_epicgames_unreal_AndroidSpeechToText_nativeOnError(JNIEnv* Env, jclass Class, jint ErrorCode, jstring Message)
|
||||
{
|
||||
const FString ErrorMessage = FJavaHelper::FStringFromParam(Env, Message);
|
||||
AsyncTask(ENamedThreads::GameThread, [ErrorCode = static_cast<int32>(ErrorCode), ErrorMessage]()
|
||||
{
|
||||
AndroidSpeechToTextEvents::OnError.Broadcast(ErrorCode, ErrorMessage);
|
||||
});
|
||||
}
|
||||
}
|
||||
#endif
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
|
||||
namespace AndroidSpeechToTextJavaBridge
|
||||
{
|
||||
bool StartListening(const FString& LanguageTag, bool bPreferOffline, bool bEnablePartialResults, bool bKeepListening);
|
||||
void StopListening();
|
||||
void CancelListening();
|
||||
bool IsListening();
|
||||
FString GetLastFinalText();
|
||||
FString GetLastPartialText();
|
||||
FString GetLastErrorMessage();
|
||||
bool IsSupported();
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
#include "Modules/ModuleManager.h"
|
||||
|
||||
class FAndroidSpeechToTextModule : public IModuleInterface
|
||||
{
|
||||
};
|
||||
|
||||
IMPLEMENT_MODULE(FAndroidSpeechToTextModule, AndroidSpeechToText)
|
||||
+94
@@ -0,0 +1,94 @@
|
||||
#pragma once
|
||||
|
||||
#include "Components/ActorComponent.h"
|
||||
#include "AndroidSpeechToTextComponent.generated.h"
|
||||
|
||||
UENUM(BlueprintType)
|
||||
enum class EAndroidSpeechToTextError : uint8
|
||||
{
|
||||
None UMETA(DisplayName = "None"),
|
||||
NetworkTimeout UMETA(DisplayName = "Network Timeout"),
|
||||
Network UMETA(DisplayName = "Network"),
|
||||
Audio UMETA(DisplayName = "Audio"),
|
||||
Server UMETA(DisplayName = "Server"),
|
||||
Client UMETA(DisplayName = "Client"),
|
||||
SpeechTimeout UMETA(DisplayName = "Speech Timeout"),
|
||||
NoMatch UMETA(DisplayName = "No Match"),
|
||||
RecognizerBusy UMETA(DisplayName = "Recognizer Busy"),
|
||||
InsufficientPermissions UMETA(DisplayName = "Insufficient Permissions"),
|
||||
TooManyRequests UMETA(DisplayName = "Too Many Requests"),
|
||||
ServerDisconnected UMETA(DisplayName = "Server Disconnected"),
|
||||
LanguageNotSupported UMETA(DisplayName = "Language Not Supported"),
|
||||
LanguageUnavailable UMETA(DisplayName = "Language Unavailable"),
|
||||
Unsupported UMETA(DisplayName = "Unsupported"),
|
||||
Unknown UMETA(DisplayName = "Unknown")
|
||||
};
|
||||
|
||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FAndroidSpeechToTextSimpleDelegate);
|
||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FAndroidSpeechToTextTextDelegate, const FString&, Text);
|
||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FAndroidSpeechToTextErrorDelegate, EAndroidSpeechToTextError, Error, const FString&, Message);
|
||||
|
||||
UCLASS(ClassGroup = (Android), meta = (BlueprintSpawnableComponent))
|
||||
class ANDROIDSPEECHTOTEXT_API UAndroidSpeechToTextComponent : public UActorComponent
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UAndroidSpeechToTextComponent();
|
||||
|
||||
UPROPERTY(BlueprintAssignable, Category = "Android Speech To Text")
|
||||
FAndroidSpeechToTextSimpleDelegate OnSpeechReady;
|
||||
|
||||
UPROPERTY(BlueprintAssignable, Category = "Android Speech To Text")
|
||||
FAndroidSpeechToTextSimpleDelegate OnSpeechStarted;
|
||||
|
||||
UPROPERTY(BlueprintAssignable, Category = "Android Speech To Text")
|
||||
FAndroidSpeechToTextSimpleDelegate OnSpeechEnded;
|
||||
|
||||
UPROPERTY(BlueprintAssignable, Category = "Android Speech To Text")
|
||||
FAndroidSpeechToTextTextDelegate OnPartialText;
|
||||
|
||||
UPROPERTY(BlueprintAssignable, Category = "Android Speech To Text")
|
||||
FAndroidSpeechToTextTextDelegate OnFinalText;
|
||||
|
||||
UPROPERTY(BlueprintAssignable, Category = "Android Speech To Text")
|
||||
FAndroidSpeechToTextErrorDelegate OnSpeechError;
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "Android Speech To Text")
|
||||
bool StartListening(const FString& LanguageTag = TEXT(""), bool bPreferOffline = false, bool bEnablePartialResults = true, bool bKeepListening = true);
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "Android Speech To Text")
|
||||
void StopListening();
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "Android Speech To Text")
|
||||
void CancelListening();
|
||||
|
||||
UFUNCTION(BlueprintPure, Category = "Android Speech To Text")
|
||||
bool IsListening() const;
|
||||
|
||||
UFUNCTION(BlueprintPure, Category = "Android Speech To Text")
|
||||
FString GetLastFinalText() const;
|
||||
|
||||
UFUNCTION(BlueprintPure, Category = "Android Speech To Text")
|
||||
FString GetLastPartialText() const;
|
||||
|
||||
UFUNCTION(BlueprintPure, Category = "Android Speech To Text")
|
||||
FString GetLastErrorMessage() const;
|
||||
|
||||
UFUNCTION(BlueprintPure, Category = "Android Speech To Text")
|
||||
static bool IsAndroidSpeechToTextSupported();
|
||||
|
||||
protected:
|
||||
virtual void BeginPlay() override;
|
||||
virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;
|
||||
|
||||
private:
|
||||
void HandleSpeechReady();
|
||||
void HandleSpeechStarted();
|
||||
void HandleSpeechEnded();
|
||||
void HandlePartialText(const FString& Text);
|
||||
void HandleFinalText(const FString& Text);
|
||||
void HandleSpeechError(int32 ErrorCode, const FString& Message);
|
||||
|
||||
static EAndroidSpeechToTextError ConvertErrorCode(int32 ErrorCode);
|
||||
};
|
||||
Reference in New Issue
Block a user