clean project

This commit is contained in:
Helar Jaadla
2022-03-07 17:52:41 +02:00
parent a174b45bd2
commit cbeb10ec35
5100 changed files with 837159 additions and 0 deletions

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: d054f5543f909634a88982d2b2dc8e55
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,32 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the license found in the
* LICENSE file in the root directory of this source tree.
*/
using Facebook.WitAi.Lib;
using UnityEngine;
using UnityEngine.Events;
namespace Facebook.WitAi.CallbackHandlers
{
public class SimpleIntentHandler : WitResponseHandler
{
[SerializeField] public string intent;
[Range(0, 1f)]
[SerializeField] public float confidence = .9f;
[SerializeField] private UnityEvent onIntentTriggered = new UnityEvent();
public UnityEvent OnIntentTriggered => onIntentTriggered;
protected override void OnHandleResponse(WitResponseNode response)
{
var intentNode = WitResultUtilities.GetFirstIntent(response);
if (intent == intentNode["name"].Value && intentNode["confidence"].AsFloat > confidence)
{
onIntentTriggered.Invoke();
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 8d2d3ff93ff48bd40ab5bca3cf4e6d2c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,48 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the license found in the
* LICENSE file in the root directory of this source tree.
*/
using System;
using Facebook.WitAi.Lib;
using UnityEngine;
using UnityEngine.Events;
namespace Facebook.WitAi.CallbackHandlers
{
public class SimpleStringEntityHandler : WitResponseHandler
{
[SerializeField] public string intent;
[SerializeField] public string entity;
[Range(0, 1f)] [SerializeField] public float confidence = .9f;
[SerializeField] public string format;
[SerializeField] private StringEntityMatchEvent onIntentEntityTriggered
= new StringEntityMatchEvent();
public StringEntityMatchEvent OnIntentEntityTriggered => onIntentEntityTriggered;
protected override void OnHandleResponse(WitResponseNode response)
{
var intentNode = WitResultUtilities.GetFirstIntent(response);
if (intent == intentNode["name"].Value && intentNode["confidence"].AsFloat > confidence)
{
var entityValue = WitResultUtilities.GetFirstEntityValue(response, entity);
if (!string.IsNullOrEmpty(format))
{
onIntentEntityTriggered.Invoke(format.Replace("{value}", entityValue));
}
else
{
onIntentEntityTriggered.Invoke(entityValue);
}
}
}
}
[Serializable]
public class StringEntityMatchEvent : UnityEvent<string> {}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 77dceeee73817ec4c844564714e05c77
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,44 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the license found in the
* LICENSE file in the root directory of this source tree.
*/
using Facebook.WitAi.Lib;
using UnityEngine;
namespace Facebook.WitAi.CallbackHandlers
{
public abstract class WitResponseHandler : MonoBehaviour
{
[SerializeField] public VoiceService wit;
private void OnValidate()
{
if (!wit) wit = FindObjectOfType<VoiceService>();
}
private void OnEnable()
{
if (!wit) wit = FindObjectOfType<VoiceService>();
if (!wit)
{
Debug.LogError("Wit not found in scene. Disabling " + GetType().Name + " on " +
name);
enabled = false;
}
else
{
wit.events.OnResponse.AddListener(OnHandleResponse);
}
}
private void OnDisable()
{
wit.events.OnResponse.RemoveListener(OnHandleResponse);
}
protected abstract void OnHandleResponse(WitResponseNode response);
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 097381abbd7a3364f80ac4460551a2cb
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,304 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the license found in the
* LICENSE file in the root directory of this source tree.
*/
using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using Facebook.WitAi.Data;
using Facebook.WitAi.Lib;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.Serialization;
namespace Facebook.WitAi.CallbackHandlers
{
public class WitResponseMatcher : WitResponseHandler
{
[Header("Intent")]
[SerializeField] public string intent;
[FormerlySerializedAs("confidence")]
[Range(0, 1f), SerializeField] public float confidenceThreshold = .6f;
[FormerlySerializedAs("valuePaths")]
[Header("Value Matching")]
[SerializeField] public ValuePathMatcher[] valueMatchers;
[Header("Output")]
[SerializeField] private FormattedValueEvents[] formattedValueEvents;
[SerializeField] private MultiValueEvent onMultiValueEvent = new MultiValueEvent();
private static Regex valueRegex = new Regex(Regex.Escape("{value}"), RegexOptions.Compiled);
protected override void OnHandleResponse(WitResponseNode response)
{
if (IntentMatches(response))
{
if (ValueMatches(response))
{
for (int j = 0; j < formattedValueEvents.Length; j++)
{
var formatEvent = formattedValueEvents[j];
var result = formatEvent.format;
for (int i = 0; i < valueMatchers.Length; i++)
{
var reference = valueMatchers[i].Reference;
var value = reference.GetStringValue(response);
if (!string.IsNullOrEmpty(formatEvent.format))
{
if (!string.IsNullOrEmpty(value))
{
result = valueRegex.Replace(result, value, 1);
result = result.Replace("{" + i + "}", value);
}
else if (result.Contains("{" + i + "}"))
{
result = "";
break;
}
}
}
if (!string.IsNullOrEmpty(result))
{
formatEvent.onFormattedValueEvent?.Invoke(result);
}
}
}
List<string> values = new List<string>();
for (int i = 0; i < valueMatchers.Length; i++)
{
var value = valueMatchers[i].Reference.GetStringValue(response);
values.Add(value);
}
onMultiValueEvent.Invoke(values.ToArray());
}
}
private bool ValueMatches(WitResponseNode response)
{
bool matches = true;
for (int i = 0; i < valueMatchers.Length && matches; i++)
{
var matcher = valueMatchers[i];
var value = matcher.Reference.GetStringValue(response);
matches &= !matcher.contentRequired || !string.IsNullOrEmpty(value);
switch (matcher.matchMethod)
{
case MatchMethod.RegularExpression:
matches &= Regex.Match(value, matcher.matchValue).Success;
break;
case MatchMethod.Text:
matches &= value == matcher.matchValue;
break;
case MatchMethod.IntegerComparison:
matches &= CompareInt(value, matcher);
break;
case MatchMethod.FloatComparison:
matches &= CompareFloat(value, matcher);
break;
case MatchMethod.DoubleComparison:
matches &= CompareDouble(value, matcher);
break;
}
}
return matches;
}
private bool CompareDouble(string value, ValuePathMatcher matcher)
{
double dValue;
// This one is freeform based on the input so we will retrun false if it is not parsable
if (!double.TryParse(value, out dValue)) return false;
// We will throw an exception if match value is not a numeric value. This is a developer
// error.
double dMatchValue = double.Parse(matcher.matchValue);
switch (matcher.comparisonMethod)
{
case ComparisonMethod.Equals:
return Math.Abs(dValue - dMatchValue) < matcher.floatingPointComparisonTolerance;
case ComparisonMethod.NotEquals:
return Math.Abs(dValue - dMatchValue) > matcher.floatingPointComparisonTolerance;
case ComparisonMethod.Greater:
return dValue > dMatchValue;
case ComparisonMethod.Less:
return dValue < dMatchValue;
case ComparisonMethod.GreaterThanOrEqualTo:
return dValue >= dMatchValue;
case ComparisonMethod.LessThanOrEqualTo:
return dValue <= dMatchValue;
}
return false;
}
private bool CompareFloat(string value, ValuePathMatcher matcher)
{
float dValue;
// This one is freeform based on the input so we will retrun false if it is not parsable
if (!float.TryParse(value, out dValue)) return false;
// We will throw an exception if match value is not a numeric value. This is a developer
// error.
float dMatchValue = float.Parse(matcher.matchValue);
switch (matcher.comparisonMethod)
{
case ComparisonMethod.Equals:
return Math.Abs(dValue - dMatchValue) <
matcher.floatingPointComparisonTolerance;
case ComparisonMethod.NotEquals:
return Math.Abs(dValue - dMatchValue) >
matcher.floatingPointComparisonTolerance;
case ComparisonMethod.Greater:
return dValue > dMatchValue;
case ComparisonMethod.Less:
return dValue < dMatchValue;
case ComparisonMethod.GreaterThanOrEqualTo:
return dValue >= dMatchValue;
case ComparisonMethod.LessThanOrEqualTo:
return dValue <= dMatchValue;
}
return false;
}
private bool CompareInt(string value, ValuePathMatcher matcher)
{
int dValue;
// This one is freeform based on the input so we will retrun false if it is not parsable
if (!int.TryParse(value, out dValue)) return false;
// We will throw an exception if match value is not a numeric value. This is a developer
// error.
int dMatchValue = int.Parse(matcher.matchValue);
switch (matcher.comparisonMethod)
{
case ComparisonMethod.Equals:
return dValue == dMatchValue;
case ComparisonMethod.NotEquals:
return dValue != dMatchValue;
case ComparisonMethod.Greater:
return dValue > dMatchValue;
case ComparisonMethod.Less:
return dValue < dMatchValue;
case ComparisonMethod.GreaterThanOrEqualTo:
return dValue >= dMatchValue;
case ComparisonMethod.LessThanOrEqualTo:
return dValue <= dMatchValue;
}
return false;
}
private bool IntentMatches(WitResponseNode response)
{
var intentNode = response.GetFirstIntent();
if (string.IsNullOrEmpty(intent))
{
return true;
}
if (intent == intentNode["name"].Value)
{
var actualConfidence = intentNode["confidence"].AsFloat;
if (actualConfidence >= confidenceThreshold)
{
return true;
}
Debug.Log($"{intent} matched, but confidence ({actualConfidence.ToString("F")}) was below threshold ({confidenceThreshold.ToString("F")})");
}
return false;
}
}
[Serializable]
public class MultiValueEvent : UnityEvent<string[]>
{
}
[Serializable]
public class ValueEvent : UnityEvent<string>
{ }
[Serializable]
public class FormattedValueEvents
{
[Tooltip("Modify the string output, values can be inserted with {value} or {0}, {1}, {2}")]
public string format;
public ValueEvent onFormattedValueEvent = new ValueEvent();
}
[Serializable]
public class ValuePathMatcher
{
[Tooltip("The path to a value within a WitResponseNode")]
public string path;
[Tooltip("A reference to a wit value object")]
public WitValue witValueReference;
[Tooltip("Does this path need to have text in the value to be considered a match")]
public bool contentRequired = true;
[Tooltip("If set the match value will be treated as a regular expression.")]
public MatchMethod matchMethod;
[Tooltip("The operator used to compare the value with the match value. Ex: response.value > matchValue")]
public ComparisonMethod comparisonMethod;
[Tooltip("Value used to compare with the result when Match Required is set")]
public string matchValue;
[Tooltip("The variance allowed when comparing two floating point values for equality")]
public double floatingPointComparisonTolerance = .0001f;
private WitResponseReference pathReference;
public WitResponseReference Reference
{
get
{
if (witValueReference) return witValueReference.Reference;
if (null == pathReference || pathReference.path != path)
{
pathReference = WitResultUtilities.GetWitResponseReference(path);
}
return pathReference;
}
}
}
public enum ComparisonMethod
{
Equals,
NotEquals,
Greater,
GreaterThanOrEqualTo,
Less,
LessThanOrEqualTo
}
public enum MatchMethod
{
None,
Text,
RegularExpression,
IntegerComparison,
FloatComparison,
DoubleComparison
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 591c3d6f017c11b4faa41506d75635b9
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,59 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the license found in the
* LICENSE file in the root directory of this source tree.
*/
using System.Text.RegularExpressions;
using Facebook.WitAi.Lib;
using Facebook.WitAi.Utilities;
using UnityEngine;
namespace Facebook.WitAi.CallbackHandlers
{
public class WitUtteranceMatcher : WitResponseHandler
{
[SerializeField] private string searchText;
[SerializeField] private bool exactMatch = true;
[SerializeField] private bool useRegex;
[SerializeField] private StringEvent onUtteranceMatched = new StringEvent();
private Regex regex;
protected override void OnHandleResponse(WitResponseNode response)
{
var text = response["text"].Value;
if (useRegex)
{
if (null == regex)
{
regex = new Regex(searchText, RegexOptions.Compiled | RegexOptions.IgnoreCase);
}
var match = regex.Match(text);
if (match.Success)
{
if (exactMatch && match.Value == text)
{
onUtteranceMatched?.Invoke(text);
}
else
{
onUtteranceMatched?.Invoke(text);
}
}
}
else if (exactMatch && text.ToLower() == searchText.ToLower())
{
onUtteranceMatched?.Invoke(text);
}
else if (text.ToLower().Contains(searchText.ToLower()))
{
onUtteranceMatched?.Invoke(text);
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d0a93535f06eabe47bf93b4b504873c8
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 5c9bc9136c8441e48996d814209d4c2e
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,47 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the license found in the
* LICENSE file in the root directory of this source tree.
*/
using System;
namespace Facebook.WitAi.Data
{
[Serializable]
public class AudioEncoding
{
public enum Endian
{
Big,
Little
}
/// <summary>
/// The expected encoding of the mic pcm data
/// </summary>
public string encoding = "signed-integer";
/// <summary>
/// The number of bits per sample
/// </summary>
public int bits = 16;
/// <summary>
/// The sample rate used to capture audio
/// </summary>
public int samplerate = 16000;
/// <summary>
/// The endianess of the data
/// </summary>
public Endian endian = Endian.Little;
public override string ToString()
{
return $"audio/raw;bits={bits};rate={samplerate / 1000}k;encoding={encoding};endian={endian.ToString().ToLower()}";
;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 991b11599dd41ac448cf8d6114e29d36
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: d99f42b1387bffe4b845b1bf9f9cf5cb
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,48 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the license found in the
* LICENSE file in the root directory of this source tree.
*/
using System;
using Facebook.WitAi.Configuration;
using Facebook.WitAi.Lib;
using UnityEngine;
namespace Facebook.WitAi.Data.Configuration
{
[Serializable]
public class WitApplication : WitConfigurationData
{
[SerializeField] public string name;
[SerializeField] public string id;
[SerializeField] public string lang;
[SerializeField] public bool isPrivate;
[SerializeField] public string createdAt;
#if UNITY_EDITOR
protected override WitRequest OnCreateRequest()
{
return witConfiguration.GetAppRequest(id);
}
public override void UpdateData(WitResponseNode appWitResponse)
{
id = appWitResponse["id"].Value;
name = appWitResponse["name"].Value;
lang = appWitResponse["lang"].Value;
isPrivate = appWitResponse["private"].AsBool;
createdAt = appWitResponse["created_at"].Value;
}
public static WitApplication FromJson(WitResponseNode appWitResponse)
{
var app = new WitApplication();
app.UpdateData(appWitResponse);
return app;
}
#endif
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 7ca958861dc343789f31f1d597fd24cf
timeCreated: 1621351239

View File

@@ -0,0 +1,59 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the license found in the
* LICENSE file in the root directory of this source tree.
*/
using System;
using Facebook.WitAi.Configuration;
using Facebook.WitAi.Data.Entities;
using Facebook.WitAi.Data.Intents;
using Facebook.WitAi.Data.Traits;
using UnityEngine;
#if UNITY_EDITOR
using UnityEditor;
#endif
namespace Facebook.WitAi.Data.Configuration
{
public class WitConfiguration : ScriptableObject
{
[HideInInspector]
[SerializeField] public WitApplication application;
[HideInInspector] [SerializeField] public string configId;
/// <summary>
/// Access token used in builds to make requests for data from Wit.ai
/// </summary>
[Tooltip("Access token used in builds to make requests for data from Wit.ai")]
[SerializeField] public string clientAccessToken;
[Tooltip("The number of milliseconds to wait before requests to Wit.ai will timeout")]
[SerializeField] public int timeoutMS = 10000;
/// <summary>
/// Configuration parameters to set up a custom endpoint for testing purposes and request forwarding. The default values here will work for most.
/// </summary>
[Tooltip("Configuration parameters to set up a custom endpoint for testing purposes and request forwarding. The default values here will work for most.")]
[SerializeField] public WitEndpointConfig endpointConfiguration = new WitEndpointConfig();
[SerializeField] public WitEntity[] entities;
[SerializeField] public WitIntent[] intents;
[SerializeField] public WitTrait[] traits;
public WitApplication Application => application;
private void OnEnable()
{
#if UNITY_EDITOR
if (string.IsNullOrEmpty(configId))
{
configId = GUID.Generate().ToString();
EditorUtility.SetDirty(this);
}
#endif
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ae5a46bda3295124c99b0e6537ac7252
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,53 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the license found in the
* LICENSE file in the root directory of this source tree.
*/
using System;
using Facebook.WitAi.Data.Configuration;
using Facebook.WitAi.Lib;
using UnityEngine;
namespace Facebook.WitAi.Configuration
{
[Serializable]
public abstract class WitConfigurationData
{
[SerializeField] public WitConfiguration witConfiguration;
#if UNITY_EDITOR
public void UpdateData(Action onUpdateComplete = null)
{
if (!witConfiguration)
{
onUpdateComplete?.Invoke();
return;
}
var request = OnCreateRequest();
request.onResponse = (r) => OnUpdateData(r, onUpdateComplete);
request.Request();
}
protected abstract WitRequest OnCreateRequest();
private void OnUpdateData(WitRequest request, Action onUpdateComplete)
{
if (request.StatusCode == 200)
{
UpdateData(request.ResponseData);
}
else
{
Debug.LogError(request.StatusDescription);
}
onUpdateComplete?.Invoke();
}
public abstract void UpdateData(WitResponseNode data);
#endif
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 002ec29e2fc64951b9d57ab4cdbf659e
timeCreated: 1621354945

View File

@@ -0,0 +1,48 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the license found in the
* LICENSE file in the root directory of this source tree.
*/
using System;
using Facebook.WitAi.Data.Configuration;
using UnityEngine;
namespace Facebook.WitAi.Configuration
{
[Serializable]
public class WitEndpointConfig
{
private static WitEndpointConfig defaultEndpointConfig = new WitEndpointConfig();
public string uriScheme;
public string authority;
public int port;
public string witApiVersion;
public string speech;
public string message;
public string UriScheme => string.IsNullOrEmpty(uriScheme) ? WitRequest.URI_SCHEME : uriScheme;
public string Authority =>
string.IsNullOrEmpty(authority) ? WitRequest.URI_AUTHORITY : authority;
public string WitApiVersion => string.IsNullOrEmpty(witApiVersion)
? WitRequest.WIT_API_VERSION
: witApiVersion;
public string Speech =>
string.IsNullOrEmpty(speech) ? WitRequest.WIT_ENDPOINT_SPEECH : speech;
public string Message =>
string.IsNullOrEmpty(message) ? WitRequest.WIT_ENDPOINT_MESSAGE : message;
public static WitEndpointConfig GetEndpointConfig(WitConfiguration witConfig)
{
return witConfig && null != witConfig.endpointConfiguration
? witConfig.endpointConfiguration
: defaultEndpointConfig;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 61a0415bfdfa4d64e89801fbc0b30ec7
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,24 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the license found in the
* LICENSE file in the root directory of this source tree.
*/
using Facebook.WitAi.Interfaces;
namespace Facebook.WitAi.Configuration
{
public class WitRequestOptions
{
/// <summary>
/// An interface that provides a list of entities that should be used for nlu resolution.
/// </summary>
public IDynamicEntitiesProvider dynamicEntities;
/// <summary>
/// The maximum number of intent matches to return
/// </summary>
public int nBestIntents = -1;
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a6834fd3c55fb41a1885302e6043f5d6
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,64 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the license found in the
* LICENSE file in the root directory of this source tree.
*/
using System;
using Facebook.WitAi.Data.Configuration;
using Facebook.WitAi.Interfaces;
using UnityEngine;
using UnityEngine.Serialization;
namespace Facebook.WitAi.Configuration
{
[Serializable]
public class WitRuntimeConfiguration
{
[Tooltip("Configuration for the application used in this instance of Wit.ai services")]
[SerializeField]
public WitConfiguration witConfiguration;
[Header("Keepalive")]
[Tooltip("The minimum volume from the mic needed to keep the activation alive")]
[SerializeField]
public float minKeepAliveVolume = .0005f;
[FormerlySerializedAs("minKeepAliveTime")]
[Tooltip(
"The amount of time in seconds an activation will be kept open after volume is under the keep alive threshold")]
[SerializeField]
public float minKeepAliveTimeInSeconds = 2f;
[FormerlySerializedAs("minTranscriptionKeepAliveTime")]
[Tooltip(
"The amount of time in seconds an activation will be kept open after words have been detected in the live transcription")]
[SerializeField]
public float minTranscriptionKeepAliveTimeInSeconds = 1f;
[Tooltip("The maximum amount of time in seconds the mic will stay active")]
[Range(0, 20f)]
[SerializeField]
public float maxRecordingTime = 20;
[Header("Sound Activation")]
[Tooltip("The minimum volume level needed to be heard to start collecting data from the audio source.")]
[SerializeField] public float soundWakeThreshold = .0005f;
[Tooltip("The length of the individual samples read from the audio source")]
[Range(10, 500)] [SerializeField] public int sampleLengthInMs = 10;
[Tooltip("The total audio data that should be buffered for lookback purposes on sound based activations.")]
[SerializeField] public float micBufferLengthInSeconds = 1;
[Header("Custom Transcription")]
[Tooltip(
"If true, the audio recorded in the activation will be sent to Wit.ai for processing. If a custom transcription provider is set and this is false, only the transcription will be sent to Wit.ai for processing")]
[SerializeField]
public bool sendAudioToWit = true;
[Tooltip("A custom provider that returns text to be used for nlu processing on activation instead of sending audio.")]
[SerializeField] public CustomTranscriptionProvider customTranscriptionProvider;
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: bd8d40c9f75b44b5940fcc21769d6af0
timeCreated: 1629397017

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: d92a289db3a8408dba9dd973090b1ade
timeCreated: 1632287743

View File

@@ -0,0 +1,40 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the license found in the
* LICENSE file in the root directory of this source tree.
*/
using System.Collections.Generic;
using Facebook.WitAi.Interfaces;
using Facebook.WitAi.Lib;
namespace Facebook.WitAi.Data.Entities
{
public class WitDynamicEntities : IDynamicEntitiesProvider
{
public WitResponseClass entities;
public WitDynamicEntities()
{
entities = new WitResponseClass();
}
public void Add(WitSimpleDynamicEntity entity)
{
KeyValuePair<string, WitResponseArray> pair = entity.GetEntityPair();
entities.Add(pair.Key, pair.Value);
}
public void Add(WitDynamicEntity entity)
{
KeyValuePair<string, WitResponseArray> pair = entity.GetEntityPair();
entities.Add(pair.Key, pair.Value);
}
public string ToJSON()
{
return entities.ToString();
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a296282c67dc244dd974baca4699c554
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,52 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the license found in the
* LICENSE file in the root directory of this source tree.
*/
using System.Collections.Generic;
using Facebook.WitAi.Interfaces;
using Facebook.WitAi.Lib;
namespace Facebook.WitAi.Data.Entities
{
public class WitDynamicEntity : IDynamicEntitiesProvider
{
public string entity;
public Dictionary<string, List<string>> keywordsToSynonyms;
public WitDynamicEntity(string entity, Dictionary<string, List<string>> keywordsToSynonyms)
{
this.entity = entity;
this.keywordsToSynonyms = keywordsToSynonyms;
}
public KeyValuePair<string, WitResponseArray> GetEntityPair() {
var keywordEntries = new WitResponseArray();
foreach (var keywordToSynonyms in keywordsToSynonyms)
{
var synonyms = new WitResponseArray();
foreach (string synonym in keywordToSynonyms.Value)
{
synonyms.Add(new WitResponseData(synonym));
}
var keywordEntry = new WitResponseClass();
keywordEntry.Add("keyword", new WitResponseData(keywordToSynonyms.Key));
keywordEntry.Add("synonyms", synonyms);
keywordEntries.Add(keywordEntry);
}
return new KeyValuePair<string, WitResponseArray>(entity, keywordEntries);
}
public string ToJSON()
{
KeyValuePair<string, WitResponseArray> pair = this.GetEntityPair();
var root = new WitResponseClass();
root.Add(pair.Key, pair.Value);
return root.ToString();
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: efe24d6c4ed424d25a19396c7a2eec38
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,59 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the license found in the
* LICENSE file in the root directory of this source tree.
*/
using System;
using Facebook.WitAi.Configuration;
using Facebook.WitAi.Data.Keywords;
using Facebook.WitAi.Lib;
using UnityEngine;
namespace Facebook.WitAi.Data.Entities
{
[Serializable]
public class WitEntity : WitConfigurationData
{
[SerializeField] public string id;
[SerializeField] public string name;
[SerializeField] public string[] lookups;
[SerializeField] public WitEntityRole[] roles;
[SerializeField] public WitKeyword[] keywords;
#if UNITY_EDITOR
protected override WitRequest OnCreateRequest()
{
return witConfiguration.GetEntityRequest(name);
}
public override void UpdateData(WitResponseNode entityWitResponse)
{
id = entityWitResponse["id"].Value;
name = entityWitResponse["name"].Value;
lookups = entityWitResponse["lookups"].AsStringArray;
var roleArray = entityWitResponse["roles"].AsArray;
roles = new WitEntityRole[roleArray.Count];
for (int i = 0; i < roleArray.Count; i++)
{
roles[i] = WitEntityRole.FromJson(roleArray[i]);
}
var keywordArray = entityWitResponse["keywords"].AsArray;
keywords = new WitKeyword[keywordArray.Count];
for (int i = 0; i < keywordArray.Count; i++)
{
keywords[i] = WitKeyword.FromJson(keywordArray[i]);
}
}
public static WitEntity FromJson(WitResponseNode entityWitResponse)
{
var entity = new WitEntity();
entity.UpdateData(entityWitResponse);
return entity;
}
#endif
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 8b8d1931ec6f428ba4f36da3ddcb15f3
timeCreated: 1621351104

View File

@@ -0,0 +1,31 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the license found in the
* LICENSE file in the root directory of this source tree.
*/
using System;
using Facebook.WitAi.Lib;
using UnityEngine;
namespace Facebook.WitAi.Data.Keywords
{
[Serializable]
public class WitEntityRole
{
[SerializeField] public string id;
[SerializeField] public string name;
#if UNITY_EDITOR
public static WitEntityRole FromJson(WitResponseNode roleNode)
{
return new WitEntityRole()
{
id = roleNode["id"],
name = roleNode["name"]
};
}
#endif
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 5f1cc15e5a59a854e837f452c8f4f4d6
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,49 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the license found in the
* LICENSE file in the root directory of this source tree.
*/
using System.Collections.Generic;
using Facebook.WitAi.Interfaces;
using Facebook.WitAi.Lib;
namespace Facebook.WitAi.Data.Entities
{
public class WitSimpleDynamicEntity : IDynamicEntitiesProvider
{
public List<string> keywords;
public string entity;
public WitSimpleDynamicEntity(string entityIdentifier, List<string> words)
{
entity = entityIdentifier;
keywords = words;
}
public KeyValuePair<string, WitResponseArray> GetEntityPair() {
var keywordEntries = new WitResponseArray();
foreach (string keyword in keywords)
{
var synonyms = new WitResponseArray();
synonyms.Add(new WitResponseData(keyword));
var keywordEntry = new WitResponseClass();
keywordEntry.Add("keyword", new WitResponseData(keyword));
keywordEntry.Add("synonyms", synonyms);
keywordEntries.Add(keywordEntry);
}
return new KeyValuePair<string, WitResponseArray>(entity, keywordEntries);
}
public string ToJSON()
{
KeyValuePair<string, WitResponseArray> pair = this.GetEntityPair();
var root = new WitResponseClass();
root.Add(pair.Key, pair.Value);
return root.ToString();
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e15f31f7f3b6c49fb96d0553d125fe22
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 7de07ce5d8884e15924606bffef7ed67
timeCreated: 1632287863

View File

@@ -0,0 +1,52 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the license found in the
* LICENSE file in the root directory of this source tree.
*/
using System;
using Facebook.WitAi.Configuration;
using Facebook.WitAi.Data.Entities;
using Facebook.WitAi.Lib;
using UnityEngine;
namespace Facebook.WitAi.Data.Intents
{
[Serializable]
public class WitIntent : WitConfigurationData
{
[SerializeField] public string id;
[SerializeField] public string name;
[SerializeField] public WitEntity[] entities;
#if UNITY_EDITOR
protected override WitRequest OnCreateRequest()
{
return witConfiguration.GetIntentRequest(name);
}
public override void UpdateData(WitResponseNode intentWitResponse)
{
id = intentWitResponse["id"].Value;
name = intentWitResponse["name"].Value;
var entityArray = intentWitResponse["entities"].AsArray;
var n = entityArray.Count;
entities = new WitEntity[n];
for (int i = 0; i < n; i++)
{
entities[i] = WitEntity.FromJson(entityArray[i]);
entities[i].witConfiguration = witConfiguration;
}
}
public static WitIntent FromJson(WitResponseNode intentWitResponse)
{
var intent = new WitIntent();
intent.UpdateData(intentWitResponse);
return intent;
}
#endif
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 5ce915454f6441089c0cc0ff88d57e00
timeCreated: 1621351293

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 90f74c8768a041f1803383e7c62e89e0
timeCreated: 1632287849

View File

@@ -0,0 +1,31 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the license found in the
* LICENSE file in the root directory of this source tree.
*/
using System;
using Facebook.WitAi.Lib;
using UnityEngine;
namespace Facebook.WitAi.Data.Keywords
{
[Serializable]
public class WitKeyword
{
[SerializeField] public string keyword;
[SerializeField] public string[] synonyms;
#if UNITY_EDITOR
public static WitKeyword FromJson(WitResponseNode keywordNode)
{
return new WitKeyword()
{
keyword = keywordNode["keyword"],
synonyms = keywordNode["synonyms"].AsStringArray
};
}
#endif
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: fe1fb6d27cfb4950b5a20bf9277959c2
timeCreated: 1621351323

View File

@@ -0,0 +1,182 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the license found in the
* LICENSE file in the root directory of this source tree.
*/
using System;
using UnityEngine;
namespace Facebook.WitAi.Data
{
public class RingBuffer<T>
{
public delegate void OnDataAdded(T[] data, int offset, int length);
public OnDataAdded OnDataAddedEvent;
private T[] buffer;
private int bufferIndex;
private long bufferDataLength;
public int Capacity => buffer.Length;
public void Clear(bool eraseData = false)
{
bufferIndex = 0;
bufferDataLength = 0;
if (eraseData)
{
for (int i = 0; i < buffer.Length; i++)
{
buffer[i] = default(T);
}
}
}
public class Marker
{
public long bufferDataIndex;
public int index;
public RingBuffer<T> ringBuffer;
public bool IsValid => ringBuffer.bufferDataLength - bufferDataIndex <= ringBuffer.Capacity;
public int Read(T[] buffer, int offset, int length, bool skipToNextValid = false)
{
int read = -1;
if (!IsValid && skipToNextValid && ringBuffer.bufferDataLength > ringBuffer.Capacity)
{
bufferDataIndex = ringBuffer.bufferDataLength - ringBuffer.Capacity;
}
if (IsValid)
{
read = this.ringBuffer.Read(buffer, offset, length, bufferDataIndex);
bufferDataIndex += read;
index += read;
if (index > buffer.Length) index -= buffer.Length;
}
return read;
}
}
public RingBuffer(int capacity)
{
buffer = new T[capacity];
}
private int CopyToBuffer(T[] data, int offset, int length, int bufferIndex)
{
if (length > buffer.Length)
throw new ArgumentException(
"Push data exceeds buffer size.");
if (bufferIndex + length < buffer.Length)
{
Array.Copy(data, offset, buffer, bufferIndex, length);
return bufferIndex + length;
}
else
{
int len = Mathf.Min(length, buffer.Length);
int endChunkLength = buffer.Length - bufferIndex;
int wrappedChunkLength = len - endChunkLength;
try
{
Array.Copy(data, offset, buffer, bufferIndex, endChunkLength);
Array.Copy(data, offset + endChunkLength, buffer, 0, wrappedChunkLength);
return wrappedChunkLength;
}
catch (ArgumentException e)
{
throw e;
}
}
}
private int CopyFromBuffer(T[] data, int offset, int length, int bufferIndex)
{
if (length > buffer.Length)
throw new ArgumentException(
$"Push data exceeds buffer size {length} < {buffer.Length}" );
if (bufferIndex + length < buffer.Length)
{
Array.Copy(buffer, bufferIndex, data, offset, length);
return bufferIndex + length;
}
else
{
var l = Mathf.Min(buffer.Length, length);
int endChunkLength = buffer.Length - bufferIndex;
int wrappedChunkLength = l - endChunkLength;
Array.Copy(buffer, bufferIndex, data, offset, endChunkLength);
Array.Copy(buffer, 0, data, offset + endChunkLength, wrappedChunkLength);
return wrappedChunkLength;
}
}
public void Push(T[] data, int offset, int length)
{
lock (buffer)
{
bufferIndex = CopyToBuffer(data, offset, length, bufferIndex);
bufferDataLength += length;
OnDataAddedEvent?.Invoke(data, offset, length);
}
}
public int Read(T[] data, int offset, int length, long bufferDataIndex)
{
lock (buffer)
{
int read = (int) (Math.Min(bufferDataIndex + length, bufferDataLength) -
bufferDataIndex);
int bufferIndex = this.bufferIndex - (int) (bufferDataLength - bufferDataIndex);
if (bufferIndex < 0)
{
bufferIndex = buffer.Length + bufferIndex;
}
CopyFromBuffer(data, offset, length, bufferIndex);
return read;
}
}
public Marker CreateMarker(int offset = 0)
{
var markerPosition = bufferDataLength + offset;
if (markerPosition < 0)
{
markerPosition = 0;
}
int bufIndex = bufferIndex + offset;
if (bufIndex < 0)
{
bufIndex = buffer.Length + bufIndex;
}
if (bufIndex > buffer.Length)
{
bufIndex = bufIndex - buffer.Length;
}
var marker = new Marker()
{
ringBuffer = this,
bufferDataIndex = markerPosition,
index = bufIndex
};
return marker;
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: e01ee0aa0d794f7abb30675495fd7ca9
timeCreated: 1626392023

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 982d60138e5147b88a5117adc9ded942
timeCreated: 1632287691

View File

@@ -0,0 +1,48 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the license found in the
* LICENSE file in the root directory of this source tree.
*/
using System;
using Facebook.WitAi.Configuration;
using Facebook.WitAi.Lib;
using UnityEngine;
namespace Facebook.WitAi.Data.Traits
{
[Serializable]
public class WitTrait : WitConfigurationData
{
[SerializeField] public string id;
[SerializeField] public string name;
[SerializeField] public WitTraitValue[] values;
#if UNITY_EDITOR
protected override WitRequest OnCreateRequest()
{
return witConfiguration.GetTraitRequest(name);
}
public override void UpdateData(WitResponseNode traitWitResponse)
{
id = traitWitResponse["id"].Value;
name = traitWitResponse["name"].Value;
var valueArray = traitWitResponse["values"].AsArray;
var n = valueArray.Count;
values = new WitTraitValue[n];
for (int i = 0; i < n; i++) {
values[i] = WitTraitValue.FromJson(valueArray[i]);
}
}
public static WitTrait FromJson(WitResponseNode traitWitResponse)
{
var trait = new WitTrait();
trait.UpdateData(traitWitResponse);
return trait;
}
#endif
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 79360a5e236054d4b85a1370ec7d7a96
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,31 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the license found in the
* LICENSE file in the root directory of this source tree.
*/
using System;
using Facebook.WitAi.Lib;
using UnityEngine;
namespace Facebook.WitAi.Data.Traits
{
[Serializable]
public class WitTraitValue
{
[SerializeField] public string id;
[SerializeField] public string value;
#if UNITY_EDITOR
public static WitTraitValue FromJson(WitResponseNode traitValueNode)
{
return new WitTraitValue()
{
id = traitValueNode["id"],
value = traitValueNode["value"]
};
}
#endif
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 3dfd1d331e52e46608615baf91fad0b3
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,43 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the license found in the
* LICENSE file in the root directory of this source tree.
*/
using System;
using Facebook.WitAi.Lib;
using UnityEngine;
namespace Facebook.WitAi.Data
{
public class WitFloatValue : WitValue
{
[SerializeField] public float equalityTolerance = .0001f;
public override object GetValue(WitResponseNode response)
{
return GetFloatValue(response);
}
public override bool Equals(WitResponseNode response, object value)
{
float fValue = 0;
if (value is float f)
{
fValue = f;
}
else if(null != value && !float.TryParse("" + value, out fValue))
{
return false;
}
return Math.Abs(GetFloatValue(response) - fValue) < equalityTolerance;
}
public float GetFloatValue(WitResponseNode response)
{
return Reference.GetFloatValue(response);
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: ab8864132457433b8281c87f23398770
timeCreated: 1624318241

View File

@@ -0,0 +1,39 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the license found in the
* LICENSE file in the root directory of this source tree.
*/
using Facebook.WitAi.Lib;
namespace Facebook.WitAi.Data
{
public class WitIntValue : WitValue
{
public override object GetValue(WitResponseNode response)
{
return GetIntValue(response);
}
public override bool Equals(WitResponseNode response, object value)
{
int iValue = 0;
if (value is int i)
{
iValue = i;
}
else if (null != value && !int.TryParse("" + value, out iValue))
{
return false;
}
return GetIntValue(response) == iValue;
}
public int GetIntValue(WitResponseNode response)
{
return Reference.GetIntValue(response);
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 02641aecd6e142a1b375ed2fb84b8303
timeCreated: 1624318209

View File

@@ -0,0 +1,34 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the license found in the
* LICENSE file in the root directory of this source tree.
*/
using Facebook.WitAi.Lib;
namespace Facebook.WitAi.Data
{
public class WitStringValue : WitValue
{
public override object GetValue(WitResponseNode response)
{
return GetStringValue(response);
}
public override bool Equals(WitResponseNode response, object value)
{
if (value is string sValue)
{
return GetStringValue(response) == sValue;
}
return "" + value == GetStringValue(response);
}
public string GetStringValue(WitResponseNode response)
{
return Reference.GetStringValue(response);
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 872feba58ea14c069669a1d25959b22c
timeCreated: 1624318051

View File

@@ -0,0 +1,40 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the license found in the
* LICENSE file in the root directory of this source tree.
*/
using Facebook.WitAi.Lib;
using UnityEngine;
namespace Facebook.WitAi.Data
{
public abstract class WitValue : ScriptableObject
{
[SerializeField] public string path;
private WitResponseReference reference;
public WitResponseReference Reference
{
get
{
if (null == reference)
{
reference = WitResultUtilities.GetWitResponseReference(path);
}
return reference;
}
}
public abstract object GetValue(WitResponseNode response);
public abstract bool Equals(WitResponseNode response, object value);
public string ToString(WitResponseNode response)
{
return Reference.GetStringValue(response);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d14d7c8a51621ce419693992b114e2ce
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 401db7761c526c3448f1af1d549ec9f9
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,77 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the license found in the
* LICENSE file in the root directory of this source tree.
*/
using System;
using UnityEngine;
using UnityEngine.Events;
namespace Facebook.WitAi.Events
{
[Serializable]
public class VoiceEvents
{
[Header("Activation Result Events")]
[Tooltip("Called when a response from Wit.ai has been received")]
public WitResponseEvent OnResponse = new WitResponseEvent();
[Tooltip(
"Called when there was an error with a WitRequest or the RuntimeConfiguration is not properly configured.")]
public WitErrorEvent OnError = new WitErrorEvent();
[Tooltip(
"Called when the activation stopped because the network request was aborted. This can be via a timeout or call to AbortActivation.")]
public UnityEvent OnAborted = new UnityEvent();
[Tooltip(
"Called when a a request has completed and all response and error callbacks have fired.")]
public UnityEvent OnRequestCompleted = new UnityEvent();
[Header("Mic Events")]
[Tooltip("Called when the volume level of the mic input has changed")]
public WitMicLevelChangedEvent OnMicLevelChanged = new WitMicLevelChangedEvent();
/// <summary>
/// Called when a request is created. This happens at the beginning of
/// an activation before the microphone is activated (if in use).
/// </summary>
[Header("Activation/Deactivation Events")]
[Tooltip(
"Called when a request is created. This happens at the beginning of an activation before the microphone is activated (if in use)")]
public WitRequestCreatedEvent OnRequestCreated = new WitRequestCreatedEvent();
[Tooltip("Called when the microphone has started collecting data collecting data to be sent to Wit.ai. There may be some buffering before data transmission starts.")]
public UnityEvent OnStartListening = new UnityEvent();
[Tooltip(
"Called when the voice service is no longer collecting data from the microphone")]
public UnityEvent OnStoppedListening = new UnityEvent();
[Tooltip(
"Called when the microphone input volume has been below the volume threshold for the specified duration and microphone data is no longer being collected")]
public UnityEvent OnStoppedListeningDueToInactivity = new UnityEvent();
[Tooltip(
"The microphone has stopped recording because maximum recording time has been hit for this activation")]
public UnityEvent OnStoppedListeningDueToTimeout = new UnityEvent();
[Tooltip("The Deactivate() method has been called ending the current activation.")]
public UnityEvent OnStoppedListeningDueToDeactivation = new UnityEvent();
[Tooltip("Fired when recording stops, the minimum volume threshold was hit, and data is being sent to the server.")]
public UnityEvent OnMicDataSent = new UnityEvent();
[Tooltip("Fired when the minimum wake threshold is hit after an activation")]
public UnityEvent OnMinimumWakeThresholdHit = new UnityEvent();
[Header("Transcription Events")]
[Tooltip("Message fired when a partial transcription has been received.")]
public WitTranscriptionEvent OnPartialTranscription = new WitTranscriptionEvent();
[Tooltip("Message received when a complete transcription is received.")]
public WitTranscriptionEvent OnFullTranscription = new WitTranscriptionEvent();
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 371044f8a8a85b34aa78547a31c99aa0
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,17 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the license found in the
* LICENSE file in the root directory of this source tree.
*/
using System;
using UnityEngine.Events;
namespace Facebook.WitAi.Events
{
[Serializable]
public class WitErrorEvent : UnityEvent<string, string>
{
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 8a3a49a4b636e7e4d937f923f434789a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,17 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the license found in the
* LICENSE file in the root directory of this source tree.
*/
using System;
using UnityEngine.Events;
namespace Facebook.WitAi.Events
{
[Serializable]
public class WitMicLevelChangedEvent : UnityEvent<float>
{
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 533049bdcc7a20a41b6fc9f90b951fd0
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,18 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the license found in the
* LICENSE file in the root directory of this source tree.
*/
using System;
using UnityEngine.Events;
namespace Facebook.WitAi.Events
{
[Serializable]
public class WitRequestCreatedEvent : UnityEvent<WitRequest>
{
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 2134bc577e5e4ae2a9a3a1e37f1ac0dc
timeCreated: 1623191846

View File

@@ -0,0 +1,18 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the license found in the
* LICENSE file in the root directory of this source tree.
*/
using System;
using Facebook.WitAi.Lib;
using UnityEngine.Events;
namespace Facebook.WitAi.Events
{
[Serializable]
public class WitResponseEvent : UnityEvent<WitResponseNode>
{
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 7ef88cd3e80185c4a90060968bd7c213
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,15 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the license found in the
* LICENSE file in the root directory of this source tree.
*/
using System;
using UnityEngine.Events;
namespace Facebook.WitAi.Events
{
[Serializable]
public class WitTranscriptionEvent : UnityEvent<string> { }
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 984bfa2088e54a9ca440be6b82c17409
timeCreated: 1627418910

View File

@@ -0,0 +1,13 @@
{
"name": "Facebook.WitAI",
"references": [],
"includePlatforms": [],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": true,
"defineConstraints": [],
"versionDefines": [],
"noEngineReferences": false
}

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 4504b1a6e0fdcc3498c30b266e4a63bf
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: a21d7613f5cf4e8e84108c72a77f7982
timeCreated: 1627419158

View File

@@ -0,0 +1,35 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the license found in the
* LICENSE file in the root directory of this source tree.
*/
using Facebook.WitAi.Events;
using UnityEngine;
using UnityEngine.Events;
namespace Facebook.WitAi.Interfaces
{
public abstract class CustomTranscriptionProvider : MonoBehaviour, ITranscriptionProvider
{
[SerializeField] private bool overrideMicLevel = false;
private WitTranscriptionEvent onPartialTranscription = new WitTranscriptionEvent();
private WitTranscriptionEvent onFullTranscription = new WitTranscriptionEvent();
private UnityEvent onStoppedListening = new UnityEvent();
private UnityEvent onStartListening = new UnityEvent();
private WitMicLevelChangedEvent onMicLevelChanged = new WitMicLevelChangedEvent();
public string LastTranscription { get; }
public WitTranscriptionEvent OnPartialTranscription => onPartialTranscription;
public WitTranscriptionEvent OnFullTranscription => onFullTranscription;
public UnityEvent OnStoppedListening => onStoppedListening;
public UnityEvent OnStartListening => onStartListening;
public WitMicLevelChangedEvent OnMicLevelChanged => onMicLevelChanged;
public bool OverrideMicLevel => overrideMicLevel;
public abstract void Activate();
public abstract void Deactivate();
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 50fac6ce6a884eaa84d6cfb63f450670
timeCreated: 1627419888

View File

@@ -0,0 +1,53 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the license found in the
* LICENSE file in the root directory of this source tree.
*/
using System;
using Facebook.WitAi.Data;
namespace Facebook.WitAi.Interfaces
{
public interface IAudioInputSource
{
/// <summary>
/// Invoked when the instance starts Recording.
/// </summary>
event Action OnStartRecording;
/// <summary>
/// Invoked when an AudioClip couldn't be created to start recording.
/// </summary>
event Action OnStartRecordingFailed;
/// <summary>
/// Invoked everytime an audio frame is collected. Includes the frame.
/// </summary>
event Action<int, float[], float> OnSampleReady;
/// <summary>
/// Invoked when the instance stop Recording.
/// </summary>
event Action OnStopRecording;
void StartRecording(int sampleLen);
void StopRecording();
bool IsRecording { get; }
/// <summary>
/// Settings determining how audio is encoded by the source.
///
/// NOTE: Default values for AudioEncoding are server optimized to reduce latency.
/// </summary>
AudioEncoding AudioEncoding { get; }
/// <summary>
/// Return true if input is available.
/// </summary>
bool IsInputAvailable { get; }
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 81c62281766f8244abee4cbc076cc41c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,17 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the license found in the
* LICENSE file in the root directory of this source tree.
*/
namespace Facebook.WitAi.Interfaces
{
public interface IDynamicEntitiesProvider
{
/// <summary>
/// Used to get dynamic entities
/// </summary>
string ToJSON();
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a8d2a4f3de747488da5e7ae246145740
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,60 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the license found in the
* LICENSE file in the root directory of this source tree.
*/
using Facebook.WitAi.Events;
using UnityEngine.Events;
namespace Facebook.WitAi.Interfaces
{
public interface ITranscriptionProvider
{
/// <summary>
/// Provides the last transcription value (could be a partial transcription)
/// </summary>
string LastTranscription { get; }
/// <summary>
/// Callback used to notify Wit subscribers of a partial transcription.
/// </summary>
WitTranscriptionEvent OnPartialTranscription { get; }
/// <summary>
/// Callback used to notify Wit subscribers of a full transcription
/// </summary>
WitTranscriptionEvent OnFullTranscription { get; }
/// <summary>
/// Callback used to notify Wit subscribers when the mic is active and transcription has begun
/// </summary>
UnityEvent OnStoppedListening { get; }
/// <summary>
/// Callback used to notify Wit subscribers when the mic is inactive and transcription has stopped
/// </summary>
UnityEvent OnStartListening { get; }
/// <summary>
/// Callback used to notify Wit subscribers on mic volume level changes
/// </summary>
WitMicLevelChangedEvent OnMicLevelChanged { get; }
/// <summary>
/// Tells Wit if the mic input levels from the transcription service should be used directly
/// </summary>
bool OverrideMicLevel { get; }
/// <summary>
/// Called when wit is activated
/// </summary>
void Activate();
/// <summary>
/// Called when
/// </summary>
void Deactivate();
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: bec6f48a64514913a8463a5c3b28657f
timeCreated: 1627419181

View File

@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 4bed4430ab45442989d8b3e16635c337
folderAsset: yes
timeCreated: 1457198032
licenseType: Store
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,324 @@
// This code is licensed under the MIT License.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files(the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions :
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
// Source: https://github.com/adrenak/unimic/blob/master/Assets/UniMic/Runtime/Mic.cs
using System;
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using Facebook.WitAi.Data;
using Facebook.WitAi.Interfaces;
namespace Facebook.WitAi.Lib
{
[RequireComponent(typeof(AudioSource))]
public class Mic : MonoBehaviour, IAudioInputSource
{
// ================================================
#region MEMBERS
// ================================================
/// <summary>
/// Whether the microphone is running
/// </summary>
public bool IsRecording { get; private set; }
/// <summary>
/// Settings used to encode audio. Defaults to optimal server settings
/// </summary>
public AudioEncoding AudioEncoding { get; } = new AudioEncoding();
/// <summary>
/// Last populated audio sample
/// </summary>
public float[] Sample { get; private set; }
/// <summary>
/// Sample duration/length in milliseconds
/// </summary>
public int SampleDurationMS { get; private set; }
public bool IsInputAvailable => AudioClip;
/// <summary>
/// The length of the sample float array
/// </summary>
public int SampleLength
{
get { return AudioEncoding.samplerate * SampleDurationMS / 1000; }
}
/// <summary>
/// The AudioClip currently being streamed in the Mic
/// </summary>
public AudioClip AudioClip { get; private set; }
private List<string> devices;
/// <summary>
/// List of all the available Mic devices
/// </summary>
public List<string> Devices
{
get
{
if (null == devices)
{
devices = new List<string>();
foreach (var device in Microphone.devices)
{
devices.Add(device);
}
}
return devices;
}
}
/// <summary>
/// Index of the current Mic device in m_Devices
/// </summary>
public int CurrentDeviceIndex { get; private set; } = -1;
/// <summary>
/// Gets the name of the Mic device currently in use
/// </summary>
public string CurrentDeviceName
{
get
{
if (CurrentDeviceIndex < 0 || CurrentDeviceIndex >= Devices.Count)
return string.Empty;
return Devices[CurrentDeviceIndex];
}
}
int m_SampleCount = 0;
#endregion
// ================================================
#region EVENTS
// ================================================
/// <summary>
/// Invoked when the instance starts Recording.
/// </summary>
public event Action OnStartRecording;
/// <summary>
/// Invoked when an AudioClip couldn't be created to start recording.
/// </summary>
public event Action OnStartRecordingFailed;
/// <summary>
/// Invoked everytime an audio frame is collected. Includes the frame.
/// </summary>
public event Action<int, float[], float> OnSampleReady;
/// <summary>
/// Invoked when the instance stop Recording.
/// </summary>
public event Action OnStopRecording;
#endregion
// ================================================
#region METHODS
// ================================================
static Mic m_Instance;
public static Mic Instance
{
get
{
if (m_Instance == null)
m_Instance = GameObject.FindObjectOfType<Mic>();
if (m_Instance == null)
{
m_Instance = new GameObject("UniMic.Mic").AddComponent<Mic>();
DontDestroyOnLoad(m_Instance.gameObject);
}
return m_Instance;
}
}
public static Mic Instantiate()
{
return Instance;
}
void Awake()
{
CurrentDeviceIndex = 0;
}
private void OnEnable()
{
StartMicrophone();
}
private void OnDisable()
{
StopMicrophone();
}
/// <summary>
/// Changes to a Mic device for Recording
/// </summary>
/// <param name="index">The index of the Mic device. Refer to <see cref="Devices"/></param>
public void ChangeDevice(int index)
{
StopMicrophone();
CurrentDeviceIndex = index;
StartMicrophone();
}
private void StartMicrophone()
{
Debug.Log("[Mic] Reserved mic " + CurrentDeviceName);
AudioClip = Microphone.Start(CurrentDeviceName, true, 1, AudioEncoding.samplerate);
}
private void StopMicrophone()
{
Debug.Log("[Mic] Released mic " + CurrentDeviceName);
Microphone.End(CurrentDeviceName);
Destroy(AudioClip);
AudioClip = null;
}
/// <summary>
/// Starts to stream the input of the current Mic device
/// </summary>
public void StartRecording(int sampleLen = 10)
{
if (!IsInputAvailable)
{
Debug.LogWarning("Tried to start recording when no input is available.");
return;
}
StopRecording();
if (!Microphone.IsRecording(CurrentDeviceName))
{
Debug.Log("[Mic] " + CurrentDeviceName + " was not started when starting recording, restarting mic.");
StartMicrophone();
}
IsRecording = true;
SampleDurationMS = sampleLen;
Sample = new float[AudioEncoding.samplerate / 1000 * SampleDurationMS * AudioClip.channels];
if (AudioClip)
{
StartCoroutine(ReadRawAudio());
// Make sure we seek before we start reading data
Microphone.GetPosition(CurrentDeviceName);
Debug.Log("[Mic] Started recording with " + CurrentDeviceName);
if (OnStartRecording != null)
OnStartRecording.Invoke();
}
else
{
OnStartRecordingFailed.Invoke();
}
}
/// <summary>
/// Ends the Mic stream.
/// </summary>
public void StopRecording()
{
if (!IsRecording) return;
IsRecording = false;
StopCoroutine(ReadRawAudio());
Debug.Log("[Mic] Stopped recording with " + CurrentDeviceName);
if (OnStopRecording != null)
OnStopRecording.Invoke();
}
IEnumerator ReadRawAudio()
{
int loops = 0;
int readAbsPos = Microphone.GetPosition(CurrentDeviceName);
int prevPos = readAbsPos;
float[] temp = new float[Sample.Length];
while (AudioClip != null && Microphone.IsRecording(CurrentDeviceName) && IsRecording)
{
bool isNewDataAvailable = true;
while (isNewDataAvailable && AudioClip)
{
int currPos = Microphone.GetPosition(CurrentDeviceName);
if (currPos < prevPos)
loops++;
prevPos = currPos;
var currAbsPos = loops * AudioClip.samples + currPos;
var nextReadAbsPos = readAbsPos + temp.Length;
float levelMax = 0;
if (nextReadAbsPos < currAbsPos)
{
AudioClip.GetData(temp, readAbsPos % AudioClip.samples);
for (int i = 0; i < temp.Length; i++)
{
float wavePeak = temp[i] * temp[i];
if (levelMax < wavePeak)
{
levelMax = wavePeak;
}
}
Sample = temp;
m_SampleCount++;
OnSampleReady?.Invoke(m_SampleCount, Sample, levelMax);
readAbsPos = nextReadAbsPos;
isNewDataAvailable = true;
}
else
isNewDataAvailable = false;
}
yield return null;
}
}
#endregion
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 8114fd1d3bfe3ce48bffcfaec698d3f0
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 5ef328494fca85148bb746014c16c292
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: eaaa645786994d1eacdac103429cb049
timeCreated: 1626731133

View File

@@ -0,0 +1,144 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the license found in the
* LICENSE file in the root directory of this source tree.
*/
using System;
using UnityEngine;
using UnityEngine.Events;
using System.Collections;
using System.Collections.Generic;
namespace Facebook.WitAi.Utilities
{
public static class CoroutineUtility
{
// Start coroutine
public static CoroutinePerformer StartCoroutine(IEnumerator asyncMethod)
{
CoroutinePerformer performer = GetPerformer();
performer.CoroutineBegin(asyncMethod);
return performer;
}
// Get performer
private static CoroutinePerformer GetPerformer()
{
CoroutinePerformer performer = new GameObject("Coroutine").AddComponent<CoroutinePerformer>();
//performer.gameObject.hideFlags = HideFlags.DontSave;
performer.gameObject.hideFlags = HideFlags.HideAndDontSave;
return performer;
}
// Coroutine performer
public class CoroutinePerformer : MonoBehaviour
{
// Coroutine
public bool isRunning { get; private set; }
private Coroutine _runtimeCoroutine;
// Dont destroy
private void Awake()
{
DontDestroyOnLoad(gameObject);
}
// Perform coroutine
public void CoroutineBegin(IEnumerator asyncMethod)
{
// Cannot call twice
if (isRunning)
{
return;
}
// Begin running
isRunning = true;
#if UNITY_EDITOR
// Editor mode
if (!Application.isPlaying)
{
_editorMethod = asyncMethod;
UnityEditor.EditorApplication.update += EditorCoroutineIterate;
return;
}
#endif
// Begin coroutine
_runtimeCoroutine = StartCoroutine(RuntimeCoroutineIterate(asyncMethod));
}
#if UNITY_EDITOR
// Editor iterate
private IEnumerator _editorMethod;
private void EditorCoroutineIterate()
{
if (_editorMethod != null)
{
if (!_editorMethod.MoveNext())
{
CoroutineComplete();
}
}
}
#endif
// Runtime iterate
private IEnumerator RuntimeCoroutineIterate(IEnumerator asyncMethod)
{
// Wait for completion
yield return StartCoroutine(asyncMethod);
// Complete
CoroutineComplete();
}
// Cancel on destroy
private void OnDestroy()
{
if (isRunning)
{
CoroutineCancel();
}
}
// Cancel coroutine
public void CoroutineCancel()
{
if (isRunning)
{
CoroutineComplete();
}
}
// Completed
private void CoroutineComplete()
{
// Ignore unless running
if (!isRunning)
{
return;
}
// Done
isRunning = false;
#if UNITY_EDITOR
// Complete
if (_editorMethod != null)
{
UnityEditor.EditorApplication.update -= EditorCoroutineIterate;
_editorMethod = null;
}
#endif
// Stop coroutine
if (_runtimeCoroutine != null)
{
StopCoroutine(_runtimeCoroutine);
_runtimeCoroutine = null;
}
// Destroy
DestroyImmediate(gameObject);
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 035128205ec689d418add9b65e3e21a7
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,34 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the license found in the
* LICENSE file in the root directory of this source tree.
*/
using System;
using UnityEngine;
using UnityEngine.Events;
namespace Facebook.WitAi.Utilities
{
public class FloatToStringEvent : MonoBehaviour
{
[SerializeField] private string format;
[SerializeField] private StringEvent onFloatToString = new StringEvent();
public void ConvertFloatToString(float value)
{
if (string.IsNullOrEmpty(format))
{
onFloatToString?.Invoke(value.ToString());
}
else
{
onFloatToString?.Invoke(value.ToString(format));
}
}
}
[Serializable]
public class StringEvent : UnityEvent<string> {}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: c63293fa01c74b159135d721528a5c47
timeCreated: 1626731148

View File

@@ -0,0 +1,150 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the license found in the
* LICENSE file in the root directory of this source tree.
*/
using Facebook.WitAi.Configuration;
using Facebook.WitAi.Events;
using Facebook.WitAi.Interfaces;
using UnityEngine;
namespace Facebook.WitAi
{
public abstract class VoiceService : MonoBehaviour, IVoiceService
{
[Tooltip("Events that will fire before, during and after an activation")] [SerializeField]
public VoiceEvents events = new VoiceEvents();
/// <summary>
/// Returns true if this voice service is currently active and listening with the mic
/// </summary>
public abstract bool Active { get; }
/// <summary>
/// Returns true if the service is actively communicating with Wit.ai during an Activation. The mic may or may not still be active while this is true.
/// </summary>
public abstract bool IsRequestActive { get; }
/// <summary>
/// Gets/Sets a custom transcription provider. This can be used to replace any built in asr
/// with an on device model or other provided source
/// </summary>
public abstract ITranscriptionProvider TranscriptionProvider { get; set; }
/// <summary>
/// Returns true if this voice service is currently reading data from the microphone
/// </summary>
public abstract bool MicActive { get; }
public VoiceEvents VoiceEvents
{
get => events;
set => events = value;
}
/// <summary>
/// Returns true if the audio input should be read in an activation
/// </summary>
protected abstract bool ShouldSendMicData { get; }
/// <summary>
/// Start listening for sound or speech from the user and start sending data to Wit.ai once sound or speech has been detected.
/// </summary>
public abstract void Activate();
/// <summary>
/// Activate the microphone and send data for NLU processing. Includes optional additional request parameters like dynamic entities and maximum results.
/// </summary>
/// <param name="requestOptions"></param>
public abstract void Activate(WitRequestOptions requestOptions);
/// <summary>
/// Activate the microphone and send data for NLU processing immediately without waiting for sound/speech from the user to begin.
/// </summary>
public abstract void ActivateImmediately();
/// <summary>
/// Activate the microphone and send data for NLU processing immediately without waiting for sound/speech from the user to begin. Includes optional additional request parameters like dynamic entities and maximum results.
/// </summary>
public abstract void ActivateImmediately(WitRequestOptions requestOptions);
/// <summary>
/// Stop listening and submit any remaining buffered microphone data for processing.
/// </summary>
public abstract void Deactivate();
/// <summary>
/// Stop listening and abort any requests that may be active without waiting for a response.
/// </summary>
public abstract void DeactivateAndAbortRequest();
/// <summary>
/// Send text data for NLU processing. Results will return the same way a voice based activation would.
/// </summary>
/// <param name="text"></param>
public abstract void Activate(string text);
/// <summary>
/// Send text data for NLU processing with custom request options.
/// </summary>
/// <param name="text"></param>
/// <param name="requestOptions"></param>
public abstract void Activate(string text, WitRequestOptions requestOptions);
}
public interface IVoiceService
{
/// <summary>
/// Returns true if this voice service is currently active and listening with the mic
/// </summary>
bool Active { get; }
bool IsRequestActive { get; }
bool MicActive { get; }
VoiceEvents VoiceEvents { get; set; }
ITranscriptionProvider TranscriptionProvider { get; set; }
/// <summary>
/// Activate the microphone and send data for NLU processing.
/// </summary>
void Activate();
/// <summary>
/// Activate the microphone and send data for NLU processing with custom request options.
/// </summary>
/// <param name="requestOptions"></param>
void Activate(WitRequestOptions requestOptions);
void ActivateImmediately();
void ActivateImmediately(WitRequestOptions requestOptions);
/// <summary>
/// Stop listening and submit the collected microphone data for processing.
/// </summary>
void Deactivate();
/// <summary>
/// Stop listening and abort any requests that may be active without waiting for a response.
/// </summary>
void DeactivateAndAbortRequest();
/// <summary>
/// Send text data for NLU processing
/// </summary>
/// <param name="text"></param>
void Activate(string transcription);
/// <summary>
/// Send text data for NLU processing with custom request options.
/// </summary>
/// <param name="text"></param>
/// <param name="requestOptions"></param>
void Activate(string text, WitRequestOptions requestOptions);
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 1f35aa1bc031e0946bf0aae2b8137ea1
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,543 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the license found in the
* LICENSE file in the root directory of this source tree.
*/
using UnityEngine;
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Net;
using Facebook.WitAi.Configuration;
using Facebook.WitAi.Data;
using Facebook.WitAi.Interfaces;
using Facebook.WitAi.Lib;
using UnityEngine.Serialization;
namespace Facebook.WitAi
{
public class Wit : VoiceService, IWitRuntimeConfigProvider
{
[Header("Wit Configuration")]
[FormerlySerializedAs("configuration")]
[Tooltip("The configuration that will be used when activating wit. This includes api key.")]
[SerializeField]
private WitRuntimeConfiguration runtimeConfiguration = new WitRuntimeConfiguration();
private float activationTime;
private IAudioInputSource micInput;
private WitRequestOptions currentRequestOptions;
private float lastMinVolumeLevelTime;
private WitRequest activeRequest;
private bool isSoundWakeActive;
private RingBuffer<byte> micDataBuffer;
private RingBuffer<byte>.Marker lastSampleMarker;
private byte[] writeBuffer;
private bool minKeepAliveWasHit;
private bool isActive;
private byte[] byteDataBuffer;
private ITranscriptionProvider activeTranscriptionProvider;
private Coroutine timeLimitCoroutine;
// Transcription based endpointing
private bool receivedTranscription;
private float lastWordTime;
#if DEBUG_SAMPLE
private FileStream sampleFile;
#endif
/// <summary>
/// Returns true if wit is currently active and listening with the mic
/// </summary>
public override bool Active => isActive || IsRequestActive;
public override bool IsRequestActive => null != activeRequest && activeRequest.IsActive;
public WitRuntimeConfiguration RuntimeConfiguration
{
get => runtimeConfiguration;
set => runtimeConfiguration = value;
}
/// <summary>
/// Gets/Sets a custom transcription provider. This can be used to replace any built in asr
/// with an on device model or other provided source
/// </summary>
public override ITranscriptionProvider TranscriptionProvider
{
get => activeTranscriptionProvider;
set
{
if (null != activeTranscriptionProvider)
{
activeTranscriptionProvider.OnFullTranscription.RemoveListener(
OnFullTranscription);
activeTranscriptionProvider.OnPartialTranscription.RemoveListener(
OnPartialTranscription);
activeTranscriptionProvider.OnMicLevelChanged.RemoveListener(
OnTranscriptionMicLevelChanged);
activeTranscriptionProvider.OnStartListening.RemoveListener(
OnStartListening);
activeTranscriptionProvider.OnStoppedListening.RemoveListener(
OnStoppedListening);
}
activeTranscriptionProvider = value;
if (null != activeTranscriptionProvider)
{
activeTranscriptionProvider.OnFullTranscription.AddListener(
OnFullTranscription);
activeTranscriptionProvider.OnPartialTranscription.AddListener(
OnPartialTranscription);
activeTranscriptionProvider.OnMicLevelChanged.AddListener(
OnTranscriptionMicLevelChanged);
activeTranscriptionProvider.OnStartListening.AddListener(
OnStartListening);
activeTranscriptionProvider.OnStoppedListening.AddListener(
OnStoppedListening);
}
}
}
public override bool MicActive => null != micInput && micInput.IsRecording;
protected override bool ShouldSendMicData => runtimeConfiguration.sendAudioToWit ||
null == activeTranscriptionProvider;
private void Awake()
{
if (null == activeTranscriptionProvider &&
runtimeConfiguration.customTranscriptionProvider)
{
TranscriptionProvider = runtimeConfiguration.customTranscriptionProvider;
}
micInput = GetComponent<IAudioInputSource>();
if (micInput == null)
{
micInput = gameObject.AddComponent<Mic>();
}
}
private void OnEnable()
{
#if UNITY_EDITOR
// Make sure we have a mic input after a script recompile
if (null == micInput)
{
micInput = GetComponent<IAudioInputSource>();
}
#endif
micInput.OnSampleReady += OnSampleReady;
micInput.OnStartRecording += OnStartListening;
micInput.OnStopRecording += OnStoppedListening;
}
private void OnDisable()
{
micInput.OnSampleReady -= OnSampleReady;
micInput.OnStartRecording -= OnStartListening;
micInput.OnStopRecording -= OnStoppedListening;
}
private void OnSampleReady(int sampleCount, float[] sample, float levelMax)
{
if (null == TranscriptionProvider || !TranscriptionProvider.OverrideMicLevel)
{
OnMicLevelChanged(levelMax);
}
if (null != micDataBuffer)
{
if (isSoundWakeActive && levelMax > runtimeConfiguration.soundWakeThreshold)
{
lastSampleMarker = micDataBuffer.CreateMarker(
(int) (-runtimeConfiguration.micBufferLengthInSeconds * 1000 *
runtimeConfiguration.sampleLengthInMs));
}
byte[] data = Convert(sample);
micDataBuffer.Push(data, 0, data.Length);
#if DEBUG_SAMPLE
sampleFile.Write(data, 0, data.Length);
#endif
}
if (IsRequestActive && activeRequest.IsRequestStreamActive)
{
if (null != micDataBuffer && micDataBuffer.Capacity > 0)
{
if (null == writeBuffer)
{
writeBuffer = new byte[sample.Length * 2];
}
// Flush the marker buffer to catch up
int read;
while ((read = lastSampleMarker.Read(writeBuffer, 0, writeBuffer.Length, true)) > 0)
{
activeRequest.Write(writeBuffer, 0, read);
}
}
else
{
byte[] sampleBytes = Convert(sample);
activeRequest.Write(sampleBytes, 0, sampleBytes.Length);
}
if (receivedTranscription)
{
if (Time.time - lastWordTime >
runtimeConfiguration.minTranscriptionKeepAliveTimeInSeconds)
{
Debug.Log("Deactivated due to inactivity. No new words detected.");
DeactivateRequest();
events.OnStoppedListeningDueToInactivity?.Invoke();
}
}
else if (Time.time - lastMinVolumeLevelTime >
runtimeConfiguration.minKeepAliveTimeInSeconds)
{
Debug.Log("Deactivated input due to inactivity.");
DeactivateRequest();
events.OnStoppedListeningDueToInactivity?.Invoke();
}
}
else if (isSoundWakeActive && levelMax > runtimeConfiguration.soundWakeThreshold)
{
events.OnMinimumWakeThresholdHit?.Invoke();
isSoundWakeActive = false;
ActivateImmediately(currentRequestOptions);
}
}
private void OnFullTranscription(string transcription)
{
DeactivateRequest();
events.OnFullTranscription?.Invoke(transcription);
if (runtimeConfiguration.customTranscriptionProvider)
{
SendTranscription(transcription, new WitRequestOptions());
}
}
private void OnPartialTranscription(string transcription)
{
receivedTranscription = true;
lastWordTime = Time.time;
events.OnPartialTranscription.Invoke(transcription);
}
private void OnTranscriptionMicLevelChanged(float level)
{
if (null != TranscriptionProvider && TranscriptionProvider.OverrideMicLevel)
{
OnMicLevelChanged(level);
}
}
private void OnMicLevelChanged(float level)
{
if (level > runtimeConfiguration.minKeepAliveVolume)
{
lastMinVolumeLevelTime = Time.time;
minKeepAliveWasHit = true;
}
events.OnMicLevelChanged?.Invoke(level);
}
private void OnStoppedListening()
{
events?.OnStoppedListening?.Invoke();
}
private void OnStartListening()
{
events?.OnStartListening?.Invoke();
}
private IEnumerator DeactivateDueToTimeLimit()
{
yield return new WaitForSeconds(runtimeConfiguration.maxRecordingTime);
Debug.Log("Deactivated due to time limit.");
DeactivateRequest();
events.OnStoppedListeningDueToTimeout?.Invoke();
timeLimitCoroutine = null;
}
/// <summary>
/// Activate the microphone and send data to Wit for NLU processing.
/// </summary>
public override void Activate()
{
Activate(new WitRequestOptions());
}
/// <summary>
/// Activate the microphone and send data to Wit for NLU processing.
/// </summary>
public override void Activate(WitRequestOptions requestOptions)
{
if (isActive) return;
if (micInput.IsRecording) micInput.StopRecording();
if (!micInput.IsRecording && ShouldSendMicData)
{
if (null == micDataBuffer && runtimeConfiguration.micBufferLengthInSeconds > 0)
{
micDataBuffer = new RingBuffer<byte>((int) Mathf.Ceil(2 *
runtimeConfiguration.micBufferLengthInSeconds * 1000 *
runtimeConfiguration.sampleLengthInMs));
}
minKeepAliveWasHit = false;
isSoundWakeActive = true;
#if DEBUG_SAMPLE
var file = Application.dataPath + "/test.pcm";
sampleFile = File.Open(file, FileMode.Create);
Debug.Log("Writing recording to file: " + file);
#endif
if (micInput.IsInputAvailable)
{
micInput.StartRecording(sampleLen: runtimeConfiguration.sampleLengthInMs);
}
else
{
events.OnError.Invoke("Input Error", "No input source was available. Cannot activate for voice input.");
}
}
if (!isActive)
{
activeTranscriptionProvider?.Activate();
isActive = true;
lastMinVolumeLevelTime = float.PositiveInfinity;
currentRequestOptions = requestOptions;
}
}
public override void ActivateImmediately()
{
ActivateImmediately(new WitRequestOptions());
}
public override void ActivateImmediately(WitRequestOptions requestOptions)
{
// Make sure we aren't checking activation time until
// the mic starts recording. If we're already recording for a live
// recording, we just triggered an activation so we will reset the
// last minvolumetime to ensure a minimum time from activation time
activationTime = float.PositiveInfinity;
lastMinVolumeLevelTime = float.PositiveInfinity;
lastWordTime = float.PositiveInfinity;
receivedTranscription = false;
if (ShouldSendMicData)
{
activeRequest = RuntimeConfiguration.witConfiguration.SpeechRequest(requestOptions);
activeRequest.audioEncoding = micInput.AudioEncoding;
activeRequest.onPartialTranscription = OnPartialTranscription;
activeRequest.onFullTranscription = OnFullTranscription;
activeRequest.onInputStreamReady = r => OnWitReadyForData();
activeRequest.onResponse = HandleResult;
events.OnRequestCreated?.Invoke(activeRequest);
activeRequest.Request();
timeLimitCoroutine = StartCoroutine(DeactivateDueToTimeLimit());
}
if (!isActive)
{
activeTranscriptionProvider?.Activate();
isActive = true;
}
}
private void OnWitReadyForData()
{
activationTime = Time.time;
lastMinVolumeLevelTime = Time.time;
if (!micInput.IsRecording && micInput.IsInputAvailable)
{
micInput.StartRecording(runtimeConfiguration.sampleLengthInMs);
}
}
/// <summary>
/// Stop listening and submit the collected microphone data to wit for processing.
/// </summary>
public override void Deactivate()
{
var recording = micInput.IsRecording;
DeactivateRequest();
if (recording)
{
events.OnStoppedListeningDueToDeactivation?.Invoke();
}
}
/// <summary>
/// Stop listening and abort any requests that may be active without waiting for a response.
/// </summary>
public override void DeactivateAndAbortRequest()
{
var recording = micInput.IsRecording;
DeactivateRequest(true);
if (recording)
{
events.OnStoppedListeningDueToDeactivation?.Invoke();
}
}
private void DeactivateRequest(bool abort = false)
{
if (null != timeLimitCoroutine)
{
StopCoroutine(timeLimitCoroutine);
timeLimitCoroutine = null;
}
if (micInput.IsRecording)
{
micInput.StopRecording();
#if DEBUG_SAMPLE
sampleFile.Close();
#endif
}
micDataBuffer?.Clear();
writeBuffer = null;
lastSampleMarker = null;
minKeepAliveWasHit = false;
activeTranscriptionProvider?.Deactivate();
if (IsRequestActive)
{
if (abort)
{
activeRequest.AbortRequest();
}
else
{
activeRequest.CloseRequestStream();
}
if (minKeepAliveWasHit)
{
events.OnMicDataSent?.Invoke();
}
}
isActive = false;
}
private byte[] Convert(float[] samples)
{
var sampleCount = samples.Length;
if (null == byteDataBuffer || byteDataBuffer.Length != sampleCount)
{
byteDataBuffer = new byte[sampleCount * 2];
}
int rescaleFactor = 32767; //to convert float to Int16
for (int i = 0; i < sampleCount; i++)
{
short data = (short) (samples[i] * rescaleFactor);
byteDataBuffer[i * 2] = (byte) data;
byteDataBuffer[i * 2 + 1] = (byte) (data >> 8);
}
return byteDataBuffer;
}
/// <summary>
/// Send text data to Wit.ai for NLU processing
/// </summary>
/// <param name="text"></param>
/// <param name="requestOptions"></param>
public override void Activate(string text, WitRequestOptions requestOptions)
{
if (Active) return;
SendTranscription(text, requestOptions);
}
/// <summary>
/// Send text data to Wit.ai for NLU processing
/// </summary>
/// <param name="text"></param>
public override void Activate(string text)
{
Activate(text, new WitRequestOptions());
}
private void SendTranscription(string transcription, WitRequestOptions requestOptions)
{
isActive = true;
activeRequest =
RuntimeConfiguration.witConfiguration.MessageRequest(transcription, requestOptions);
activeRequest.onResponse = HandleResult;
events.OnRequestCreated?.Invoke(activeRequest);
activeRequest.Request();
}
/// <summary>
/// Main thread call to handle result callbacks
/// </summary>
/// <param name="request"></param>
private void HandleResult(WitRequest request)
{
isActive = false;
if (request.StatusCode == (int) HttpStatusCode.OK)
{
if (null != request.ResponseData)
{
events?.OnResponse?.Invoke(request.ResponseData);
}
else
{
events?.OnError?.Invoke("No Data", "No data was returned from the server.");
}
}
else
{
DeactivateRequest();
if (request.StatusCode != WitRequest.ERROR_CODE_ABORTED)
{
events?.OnError?.Invoke("HTTP Error " + request.StatusCode,
request.StatusDescription);
}
else
{
events?.OnAborted?.Invoke();
}
}
events?.OnRequestCompleted?.Invoke();
activeRequest = null;
}
}
public interface IWitRuntimeConfigProvider
{
WitRuntimeConfiguration RuntimeConfiguration { get; }
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 89cc923dc7c7b4f0b91a7df096c80b00
timeCreated: 1459040294
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,131 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the license found in the
* LICENSE file in the root directory of this source tree.
*/
using System;
using Facebook.WitAi.Data.Configuration;
using UnityEngine;
#if UNITY_EDITOR
using UnityEditor;
#endif
namespace Facebook.WitAi
{
public class WitAuthUtility
{
public static ITokenValidationProvider tokenValidator = new DefaultTokenValidatorProvider();
public static bool IsServerTokenValid()
{
return tokenValidator.IsServerTokenValid(ServerToken);
}
public static bool IsServerTokenValid(string token)
{
return tokenValidator.IsServerTokenValid(token);
}
private static string serverToken;
public static string GetAppServerToken(WitConfiguration configuration,
string defaultValue = "")
{
return GetAppServerToken(configuration?.application?.id, defaultValue);
}
public static string GetAppServerToken(string appId, string defaultValue = "")
{
#if UNITY_EDITOR
return EditorPrefs.GetString("Wit::AppIdToToken::" + appId, defaultValue);
#else
return "";
#endif
}
public static string GetAppId(string serverToken, string defaultValue = "")
{
#if UNITY_EDITOR
return EditorPrefs.GetString("Wit::TokenToAppId::" + serverToken, defaultValue);
#else
return "";
#endif
}
public static void SetAppServerToken(string appId, string token)
{
#if UNITY_EDITOR
EditorPrefs.SetString("Wit::AppIdToToken::" + appId, token);
EditorPrefs.SetString("Wit::TokenToAppId::" + token, appId);
#endif
}
private static void SavePrefs()
{
}
public static string ServerToken
{
#if UNITY_EDITOR
get
{
if (null == serverToken)
{
try
{
serverToken = EditorPrefs.GetString("Wit::ServerToken", "");
}
catch (Exception e)
{
// This will happen if we don't prime the server token on the main thread and
// we access the server token editorpref value in a request.
Debug.LogError(e.Message);
}
}
return serverToken;
}
set
{
serverToken = value;
EditorPrefs.SetString("Wit::ServerToken", serverToken);
}
#else
get => "";
#endif
}
public class DefaultTokenValidatorProvider : ITokenValidationProvider
{
public bool IsTokenValid(string appId, string token)
{
return IsServerTokenValid(token);
}
public bool IsServerTokenValid(string serverToken)
{
return null != serverToken && serverToken.Length == 32;
}
}
public interface ITokenValidationProvider
{
bool IsTokenValid(string appId, string token);
bool IsServerTokenValid(string serverToken);
}
#if UNITY_EDITOR
public static void InitEditorTokens()
{
if (null == serverToken)
{
serverToken = EditorPrefs.GetString("Wit::ServerToken", "");
}
}
#endif
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 429feb10fcc04f544b5d400a5c0cabd3
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,764 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the license found in the
* LICENSE file in the root directory of this source tree.
*/
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading;
using Facebook.WitAi.Configuration;
using Facebook.WitAi.Data;
using Facebook.WitAi.Data.Configuration;
using Facebook.WitAi.Lib;
using UnityEngine;
using SystemInfo = UnityEngine.SystemInfo;
using Facebook.WitAi.Utilities;
#if UNITY_EDITOR
using UnityEditor;
#endif
namespace Facebook.WitAi
{
/// <summary>
/// Manages a single request lifecycle when sending/receiving data from Wit.ai.
///
/// Note: This is not intended to be instantiated directly. Requests should be created with the
/// WitRequestFactory
/// </summary>
public class WitRequest
{
/// <summary>
/// Error code thrown when an exception is caught during processing or
/// some other general error happens that is not an error from the server
/// </summary>
public const int ERROR_CODE_GENERAL = -1;
/// <summary>
/// Error code returned when no configuration is defined
/// </summary>
public const int ERROR_CODE_NO_CONFIGURATION = -2;
/// <summary>
/// Error code returned when the client token has not been set in the
/// Wit configuration.
/// </summary>
public const int ERROR_CODE_NO_CLIENT_TOKEN = -3;
/// <summary>
/// No data was returned from the server.
/// </summary>
public const int ERROR_CODE_NO_DATA_FROM_SERVER = -4;
/// <summary>
/// Invalid data was returned from the server.
/// </summary>
public const int ERROR_CODE_INVALID_DATA_FROM_SERVER = -5;
/// <summary>
/// Request was aborted
/// </summary>
public const int ERROR_CODE_ABORTED = -6;
/// <summary>
/// Request to the server timeed out
/// </summary>
public const int ERROR_CODE_TIMEOUT = -7;
public const string URI_SCHEME = "https";
public const string URI_AUTHORITY = "api.wit.ai";
public const string WIT_API_VERSION = "20210928";
public const string WIT_SDK_VERSION = "0.0.25";
public const string WIT_ENDPOINT_SPEECH = "speech";
public const string WIT_ENDPOINT_MESSAGE = "message";
public const string WIT_ENDPOINT_ENTITIES = "entities";
public const string WIT_ENDPOINT_INTENTS = "intents";
public const string WIT_ENDPOINT_TRAITS = "traits";
public const string WIT_ENDPOINT_APPS = "apps";
public const string WIT_ENDPOINT_UTTERANCES = "utterances";
private WitConfiguration configuration;
private Stream activeStream;
private string command;
private string path;
public QueryParam[] queryParams;
private HttpWebRequest request;
private HttpWebResponse response;
private WitResponseNode responseData;
private bool isActive;
private bool responseStarted;
public byte[] postData;
public string postContentType;
private object streamLock = new object();
/// <summary>
/// Callback called when a response is received from the server
/// </summary>
public Action<WitRequest> onResponse;
/// <summary>
/// Callback called when the server is ready to receive data from the WitRequest's input
/// stream. See WitRequest.Write()
/// </summary>
public Action<WitRequest> onInputStreamReady;
/// <summary>
/// Returns the raw string response that was received before converting it to a JSON object.
///
/// NOTE: This response comes back on a different thread. Do not attempt ot set UI control
/// values or other interactions from this callback. This is intended to be used for demo
/// and test UI, not for regular use.
/// </summary>
public Action<string> onRawResponse;
/// <summary>
/// Returns a partial utterance from an in process request
///
/// NOTE: This response comes back on a different thread.
/// </summary>
public Action<string> onPartialTranscription;
/// <summary>
/// Returns a full utterance from a completed request
///
/// NOTE: This response comes back on a different thread.
/// </summary>
public Action<string> onFullTranscription;
public delegate Uri OnCustomizeUriEvent(UriBuilder uriBuilder);
/// <summary>
/// Provides an opportunity to customize the url just before a request executed
/// </summary>
public OnCustomizeUriEvent onCustomizeUri;
public delegate Dictionary<string, string> OnProvideCustomHeadersEvent();
/// <summary>
/// Provides an opportunity to provide custom headers for the request just before it is
/// executed.
/// </summary>
public OnProvideCustomHeadersEvent onProvideCustomHeaders;
/// <summary>
/// Returns true if a request is pending. Will return false after data has been populated
/// from the response.
/// </summary>
public bool IsActive => isActive;
/// <summary>
/// JSON data that was received as a response from the server after onResponse has been
/// called
/// </summary>
public WitResponseNode ResponseData => responseData;
/// <summary>
/// Encoding settings for audio based requests
/// </summary>
public AudioEncoding audioEncoding = new AudioEncoding();
private int statusCode;
public int StatusCode => statusCode;
private string statusDescription;
private bool isRequestStreamActive;
public bool IsRequestStreamActive => IsActive && isRequestStreamActive;
public bool HasResponseStarted => responseStarted;
private bool isServerAuthRequired;
public string StatusDescription => statusDescription;
public int Timeout => configuration ? configuration.timeoutMS : 10000;
private static string operatingSystem;
private static string deviceModel;
private static string deviceName;
private static string appIdentifier;
private bool configurationRequired;
private string serverToken;
private string callingStackTrace;
private DateTime requestStartTime;
private ConcurrentQueue<byte[]> writeBuffer = new ConcurrentQueue<byte[]>();
public override string ToString()
{
return path;
}
public WitRequest(WitConfiguration configuration, string path,
params QueryParam[] queryParams)
{
if (!configuration) throw new ArgumentException("Configuration is not set.");
configurationRequired = true;
this.configuration = configuration;
this.command = path.Split('/').First();
this.path = path;
this.queryParams = queryParams;
if (null == operatingSystem) operatingSystem = SystemInfo.operatingSystem;
if (null == deviceModel) deviceModel = SystemInfo.deviceModel;
if (null == deviceName) deviceName = SystemInfo.deviceName;
if (null == appIdentifier) appIdentifier = Application.identifier;
}
public WitRequest(WitConfiguration configuration, string path, bool isServerAuthRequired,
params QueryParam[] queryParams)
{
if (!isServerAuthRequired && !configuration)
throw new ArgumentException("Configuration is not set.");
configurationRequired = true;
this.configuration = configuration;
this.isServerAuthRequired = isServerAuthRequired;
this.command = path.Split('/').First();
this.path = path;
this.queryParams = queryParams;
if (isServerAuthRequired)
{
serverToken = WitAuthUtility.GetAppServerToken(configuration?.application?.id);
}
}
public WitRequest(string serverToken, string path, params QueryParam[] queryParams)
{
configurationRequired = false;
this.isServerAuthRequired = true;
this.command = path.Split('/').First();
this.path = path;
this.queryParams = queryParams;
this.serverToken = serverToken;
}
/// <summary>
/// Key value pair that is sent as a query param in the Wit.ai uri
/// </summary>
public class QueryParam
{
public string key;
public string value;
}
/// <summary>
/// Start the async request for data from the Wit.ai servers
/// </summary>
public void Request()
{
responseStarted = false;
UriBuilder uriBuilder = new UriBuilder();
var endpointConfig = WitEndpointConfig.GetEndpointConfig(configuration);
uriBuilder.Scheme = endpointConfig.UriScheme;
uriBuilder.Host = endpointConfig.Authority;
var api = endpointConfig.WitApiVersion;
if (endpointConfig.port > 0)
{
uriBuilder.Port = endpointConfig.port;
}
uriBuilder.Query = $"v={api}";
uriBuilder.Path = path;
callingStackTrace = Environment.StackTrace;
if (queryParams.Any())
{
var p = queryParams.Select(par =>
$"{par.key}={Uri.EscapeDataString(par.value)}");
uriBuilder.Query += "&" + string.Join("&", p);
}
var uri = null == onCustomizeUri ? uriBuilder.Uri : onCustomizeUri(uriBuilder);
StartRequest(uri);
}
private void StartRequest(Uri uri)
{
if (!configuration && configurationRequired)
{
statusDescription = "Configuration is not set. Cannot start request.";
Debug.LogError(statusDescription);
statusCode = ERROR_CODE_NO_CONFIGURATION;
SafeInvoke(onResponse);
return;
}
if (!isServerAuthRequired && string.IsNullOrEmpty(configuration.clientAccessToken))
{
statusDescription = "Client access token is not defined. Cannot start request.";
Debug.LogError(statusDescription);
statusCode = ERROR_CODE_NO_CLIENT_TOKEN;
SafeInvoke(onResponse);
return;
}
request = (HttpWebRequest) WebRequest.Create(uri);
if (isServerAuthRequired)
{
request.Headers["Authorization"] =
$"Bearer {serverToken}";
}
else
{
request.Headers["Authorization"] =
$"Bearer {configuration.clientAccessToken.Trim()}";
}
if (null != postContentType)
{
request.Method = "POST";
request.ContentType = postContentType;
request.ContentLength = postData.Length;
}
// Configure additional headers
if (WitEndpointConfig.GetEndpointConfig(configuration).Speech == command)
{
request.ContentType = audioEncoding.ToString();
request.Method = "POST";
request.SendChunked = true;
}
var configId = "not-yet-configured";
#if UNITY_EDITOR
if (configuration)
{
if (string.IsNullOrEmpty(configuration.configId))
{
configuration.configId = Guid.NewGuid().ToString();
EditorUtility.SetDirty(configuration);
}
configId = configuration.configId;
}
#endif
request.UserAgent = $"voice-sdk-37.0.0.112.109,wit-unity-{WIT_SDK_VERSION},{operatingSystem},{deviceModel},{configId},{appIdentifier}";
#if UNITY_EDITOR
request.UserAgent += ",Editor";
#else
request.UserAgent += ",Runtime";
#endif
requestStartTime = DateTime.UtcNow;
isActive = true;
statusCode = 0;
statusDescription = "Starting request";
request.Timeout = configuration ? configuration.timeoutMS : 10000;
WatchMainThreadCallbacks();
if (null != onProvideCustomHeaders)
{
foreach (var header in onProvideCustomHeaders())
{
request.Headers[header.Key] = header.Value;
}
}
if (request.Method == "POST")
{
var getRequestTask = request.BeginGetRequestStream(HandleRequestStream, request);
ThreadPool.RegisterWaitForSingleObject(getRequestTask.AsyncWaitHandle,
HandleTimeoutTimer, request, Timeout, true);
}
else
{
StartResponse();
}
}
private void StartResponse()
{
var result = request.BeginGetResponse(HandleResponse, request);
ThreadPool.RegisterWaitForSingleObject(result.AsyncWaitHandle, HandleTimeoutTimer,
request, Timeout, true);
}
private void HandleTimeoutTimer(object state, bool timedout)
{
if (!timedout) return;
// Clean up the current request if it is still going
var request = (HttpWebRequest) state;
if (null != this.request)
{
Debug.Log("Request timed out after " + (DateTime.UtcNow - requestStartTime));
request.Abort();
}
isActive = false;
// Close any open stream resources and clean up streaming state flags
CloseRequestStream();
// Update the error state to indicate the request timed out
statusCode = ERROR_CODE_TIMEOUT;
statusDescription = "Request timed out.";
SafeInvoke(onResponse);
}
private void HandleResponse(IAsyncResult ar)
{
string stringResponse = "";
responseStarted = true;
try
{
response = (HttpWebResponse) request.EndGetResponse(ar);
statusCode = (int) response.StatusCode;
statusDescription = response.StatusDescription;
try
{
var responseStream = response.GetResponseStream();
if (response.Headers["Transfer-Encoding"] == "chunked")
{
byte[] buffer = new byte[10240];
int bytes = 0;
int offset = 0;
int totalRead = 0;
while ((bytes = responseStream.Read(buffer, offset, buffer.Length - offset)) > 0)
{
totalRead += bytes;
stringResponse = Encoding.UTF8.GetString(buffer, 0, totalRead);
if (stringResponse.Length > 0)
{
try
{
responseData = WitResponseJson.Parse(stringResponse);
offset = 0;
totalRead = 0;
if (null != responseData)
{
var transcription = responseData["text"];
if (!string.IsNullOrEmpty(transcription))
{
MainThreadCallback(() => onPartialTranscription?.Invoke(transcription));
}
}
}
catch (JSONParseException e)
{
// TODO: t105419819 Update the protocol to better handle this issue.
// This is a bit of a hack to get around an issue with a full
// socket buffer or partial server response. We will need to
// address this server side to make sure we're reading all data
// rather than relying on a json parse exception to catch this.
// Test case: Utterance with multiple entity responses pushing
// final data > 1024 bytes.
offset = bytes;
Debug.LogWarning("Received what appears to be a partial response or invalid json. Attempting to continue reading. Parsing error: " + e.Message);
}
}
}
if (stringResponse.Length > 0 && null != responseData)
{
MainThreadCallback(() => onFullTranscription?.Invoke(responseData["text"]));
MainThreadCallback(() => onRawResponse?.Invoke(stringResponse));
}
}
else
{
using (StreamReader reader = new StreamReader(responseStream))
{
stringResponse = reader.ReadToEnd();
MainThreadCallback(() => onRawResponse?.Invoke(stringResponse));
responseData = WitResponseJson.Parse(stringResponse);
}
}
responseStream.Close();
}
catch (JSONParseException e)
{
Debug.LogError("Server returned invalid data: " + e.Message + "\n" +
stringResponse);
statusCode = ERROR_CODE_INVALID_DATA_FROM_SERVER;
statusDescription = "Server returned invalid data.";
}
catch (Exception e)
{
Debug.LogError(
$"{e.Message}\nRequest Stack Trace:\n{callingStackTrace}\nResponse Stack Trace:\n{e.StackTrace}");
statusCode = ERROR_CODE_GENERAL;
statusDescription = e.Message;
}
response.Close();
}
catch (WebException e)
{
statusCode = (int) e.Status;
if (e.Response is HttpWebResponse errorResponse)
{
statusCode = (int) errorResponse.StatusCode;
try
{
var stream = errorResponse.GetResponseStream();
if (null != stream)
{
using (StreamReader reader = new StreamReader(stream))
{
stringResponse = reader.ReadToEnd();
MainThreadCallback(() => onRawResponse?.Invoke(stringResponse));
responseData = WitResponseJson.Parse(stringResponse);
}
}
}
catch (JSONParseException)
{
// Response wasn't encoded error, ignore it.
}
catch (Exception errorResponseError)
{
// We've already caught that there is an error, we'll ignore any errors
// reading error response data and use the status/original error for validation
Debug.LogWarning(errorResponseError);
}
}
statusDescription = e.Message;
if (e.Status != WebExceptionStatus.RequestCanceled)
{
Debug.LogError(
$"Http Request Failed [{statusCode}]: {e.Message}\nRequest Stack Trace:\n{callingStackTrace}\nResponse Stack Trace:\n{e.StackTrace}");
}
}
finally
{
isActive = false;
}
CloseRequestStream();
if (null != responseData)
{
var error = responseData["error"];
if (!string.IsNullOrEmpty(error))
{
statusDescription = $"Error: {responseData["code"]}. {error}";
statusCode = statusCode == 200 ? ERROR_CODE_GENERAL : statusCode;
}
}
else if (statusCode == 200)
{
statusCode = ERROR_CODE_NO_DATA_FROM_SERVER;
statusDescription = "Server did not return a valid json response.";
Debug.LogWarning(
"No valid data was received from the server even though the request was successful. Actual potential response data: \n" +
stringResponse);
}
SafeInvoke(onResponse);
}
private void HandleRequestStream(IAsyncResult ar)
{
StartResponse();
var stream = request.EndGetRequestStream(ar);
if (null != postData)
{
stream.Write(postData, 0, postData.Length);
CloseRequestStream();
}
else
{
if (null == onInputStreamReady)
{
CloseRequestStream();
}
else
{
isRequestStreamActive = true;
SafeInvoke(onInputStreamReady);
}
}
new Thread(ExecuteWriteThread).Start(stream);
}
private void ExecuteWriteThread(object obj)
{
Stream stream = (Stream) obj;
try
{
while (isRequestStreamActive)
{
FlushBuffer(stream);
Thread.Yield();
}
FlushBuffer(stream);
stream.Close();
}
catch (ObjectDisposedException)
{
// Handling edge case where stream is closed remotely
// This problem occurs when the Web server resets or closes the connection after
// the client application sends the HTTP header.
// https://support.microsoft.com/en-us/topic/fix-you-receive-a-system-objectdisposedexception-exception-when-you-try-to-access-a-stream-object-that-is-returned-by-the-endgetrequeststream-method-in-the-net-framework-2-0-bccefe57-0a61-517a-5d5f-2dce0cc63265
Debug.LogWarning(
"Stream already disposed. It is likely the server reset the connection before streaming started.");
}
catch (IOException e)
{
Debug.LogWarning(e.Message);
}
catch (Exception e)
{
Debug.LogError(e);
}
}
private void FlushBuffer(Stream stream)
{
while (writeBuffer.Count > 0)
{
if (writeBuffer.TryDequeue(out var buffer))
{
stream.Write(buffer, 0, buffer.Length);
}
}
}
private void SafeInvoke(Action<WitRequest> action)
{
MainThreadCallback(() =>
{
// We want to allow each invocation to run even if there is an exception thrown by one
// of the callbacks in the invocation list. This protects shared invocations from
// clients blocking things like UI updates from other parts of the sdk being invoked.
foreach (var responseDelegate in action.GetInvocationList())
{
try
{
responseDelegate.DynamicInvoke(this);
}
catch (Exception e)
{
Debug.LogError(e);
}
}
});
}
public void AbortRequest()
{
CloseRequestStream();
Debug.Log("Abort");
request.Abort();
statusCode = ERROR_CODE_ABORTED;
statusDescription = "Request was aborted";
isActive = false;
}
/// <summary>
/// Method to close the input stream of data being sent during the lifecycle of this request
///
/// If a post method was used, this will need to be called before the request will complete.
/// </summary>
public void CloseRequestStream()
{
lock (streamLock)
{
isRequestStreamActive = false;
}
}
/// <summary>
/// Write request data to the Wit.ai post's body input stream
///
/// Note: If the stream is not open (IsActive) this will throw an IOException.
/// Data will be written synchronously. This should not be called from the main thread.
/// </summary>
/// <param name="data"></param>
/// <param name="offset"></param>
/// <param name="length"></param>
public void Write(byte[] data, int offset, int length)
{
// TODO: This is going to cause additional allocations, we can probably improve this
var buffer = new byte[data.Length];
Array.Copy(data, offset, buffer, 0, length);
writeBuffer.Enqueue(buffer);
}
#region CALLBACKS
// Check performing
private bool _performing = false;
// All actions
private ConcurrentQueue<Action> _mainThreadCallbacks = new ConcurrentQueue<Action>();
// Called from background thread
private void MainThreadCallback(Action action)
{
_mainThreadCallbacks.Enqueue(action);
}
// While active, perform any sent callbacks
private void WatchMainThreadCallbacks()
{
// Ifnore if already performing
if (_performing)
{
return;
}
// Check callbacks every frame (editor or runtime)
CoroutineUtility.StartCoroutine(PerformMainThreadCallbacks());
}
// Every frame check for callbacks & perform any found
private System.Collections.IEnumerator PerformMainThreadCallbacks()
{
// Begin performing
_performing = true;
// While checking, continue
while (HasMainThreadCallbacks())
{
// Wait for frame
yield return new WaitForEndOfFrame();
// Perform if possible
while (_mainThreadCallbacks.Count > 0 && _mainThreadCallbacks.TryDequeue(out var result))
{
result();
}
}
// Done performing
_performing = false;
}
// Check actions
private bool HasMainThreadCallbacks()
{
return IsActive || isRequestStreamActive || _mainThreadCallbacks.Count > 0;
}
#endregion
}
}

Some files were not shown because too many files have changed in this diff Show More