Initial Commit

This commit is contained in:
2025-07-04 20:33:06 +03:00
commit 8f09347ae0
9219 changed files with 2447903 additions and 0 deletions
@@ -0,0 +1,11 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
* All rights reserved.
*
* This source code is licensed under the license found in the
* LICENSE file in the root directory of this source tree.
*/
using System.Runtime.CompilerServices;
[assembly:InternalsVisibleTo("Meta.WitAi.Tests")]
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: d1e74ebbbf864e009ea70c1e3ac18477
timeCreated: 1654203305
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 4145be28f213451fbd89e767e85d9ba7
timeCreated: 1680127664
@@ -0,0 +1,22 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
* All rights reserved.
*
* This source code is licensed under the license found in the
* LICENSE file in the root directory of this source tree.
*/
using UnityEngine;
namespace Meta.WitAi.Attributes
{
public class TooltipBoxAttribute : PropertyAttribute
{
public string Text { get; private set; }
public TooltipBoxAttribute(string text)
{
Text = text;
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: ef7ac601451d4e869a7f05b23c59c842
timeCreated: 1680127686
@@ -0,0 +1,58 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
* All rights reserved.
*
* This source code is licensed under the license found in the
* LICENSE file in the root directory of this source tree.
*/
using System;
using Meta.WitAi.Data;
namespace Meta.WitAi
{
public class AudioDurationTracker
{
private readonly String _requestId;
private double _bytesCaptured = 0.0;
private readonly int _bytesPerSample;
private readonly AudioEncoding _audioEncoding;
private long _finalizeTimeStamp;
private double _audioDurationMs;
public AudioDurationTracker(string requestId, AudioEncoding audioEncoding)
{
_requestId = requestId;
_audioEncoding = audioEncoding;
_bytesPerSample = _audioEncoding.bits / 8;
}
public void AddBytes(long bytes)
{
_bytesCaptured += bytes;
}
public void FinalizeAudio()
{
_finalizeTimeStamp = (DateTime.UtcNow.Ticks / TimeSpan.TicksPerMillisecond);
_audioDurationMs =
(_bytesCaptured / (_audioEncoding.samplerate * _audioEncoding.numChannels * _bytesPerSample)) * 1000.0;
}
public long GetFinalizeTimeStamp()
{
return _finalizeTimeStamp;
}
public double GetAudioDuration()
{
return _audioDurationMs;
}
public string GetRequestId()
{
return _requestId;
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: a325b28834054db2a96b89ac4354c880
timeCreated: 1670629377
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: d054f5543f909634a88982d2b2dc8e55
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,22 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
* All rights reserved.
*
* 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 Meta.WitAi.CallbackHandlers
{
[Serializable]
public class ConfidenceRange
{
public float minConfidence;
public float maxConfidence;
public UnityEvent onWithinConfidenceRange = new UnityEvent();
public UnityEvent onOutsideConfidenceRange = new UnityEvent();
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 8a9a869d94ca2214fb1f2f5c3aeef455
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,53 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
* All rights reserved.
*
* This source code is licensed under the license found in the
* LICENSE file in the root directory of this source tree.
*/
using Meta.WitAi.Attributes;
using Meta.WitAi.Json;
using Meta.WitAi.Utilities;
using UnityEngine;
using Utilities;
namespace Meta.WitAi.CallbackHandlers
{
/// <summary>
/// Triggers an event when no intents were recognized in an utterance.
/// </summary>
[AddComponentMenu("Wit.ai/Response Matchers/Out Of Domain")]
public class OutOfScopeUtteranceHandler : WitResponseHandler
{
[Tooltip("If set to a value greater than zero, any intent that returns with a confidence lower than this value will be treated as out of domain/scope.")]
[Range(0, 1f)]
[SerializeField] private float confidenceThreshold = 0.0f;
[Space(WitRuntimeStyles.HeaderPaddingTop)]
[TooltipBox("Triggered when a activation on the associated AppVoiceExperience does not return any intents.")]
[SerializeField] private StringEvent onOutOfDomain = new StringEvent();
protected override string OnValidateResponse(WitResponseNode response, bool isEarlyResponse)
{
if (response == null)
{
return "Response is null";
}
if (response["intents"].Count > 0)
{
if (response.GetFirstIntent()["confidence"].AsFloat < confidenceThreshold)
{
return string.Empty;
}
return "Intents found";
}
return string.Empty;
}
protected override void OnResponseInvalid(WitResponseNode response, string error) {}
protected override void OnResponseSuccess(WitResponseNode response)
{
onOutOfDomain?.Invoke(response.GetTranscription());
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 960c102c70216114fa5c3800d4172d67
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,64 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
* All rights reserved.
*
* This source code is licensed under the license found in the
* LICENSE file in the root directory of this source tree.
*/
using System;
using Meta.WitAi.Json;
using UnityEngine;
using UnityEngine.Events;
namespace Meta.WitAi.CallbackHandlers
{
[AddComponentMenu("Wit.ai/Response Matchers/Simple Intent Handler")]
public class SimpleIntentHandler : WitIntentMatcher
{
[SerializeField] private UnityEvent onIntentTriggered = new UnityEvent();
[Tooltip("Confidence ranges are executed in order. If checked, all confidence values will be checked instead of stopping on the first one that matches.")]
[SerializeField] public bool allowConfidenceOverlap;
#if UNITY_2021_3_2 || UNITY_2021_3_3 || UNITY_2021_3_4 || UNITY_2021_3_5
[NonReorderable]
#endif
[SerializeField] public ConfidenceRange[] confidenceRanges;
public UnityEvent OnIntentTriggered => onIntentTriggered;
protected override void OnResponseSuccess(WitResponseNode response)
{
onIntentTriggered.Invoke();
UpdateRanges(response);
}
protected override void OnResponseInvalid(WitResponseNode response, string error)
{
UpdateRanges(response);
}
private void UpdateRanges(WitResponseNode response)
{
// Find intents if possible
var intents = response?.GetIntents();
if (intents == null)
{
return;
}
// Iterate intents
foreach (var intentData in intents)
{
if (string.Equals(intent, intentData.name, StringComparison.CurrentCultureIgnoreCase))
{
// Found intent
RefreshConfidenceRange(intentData.confidence, confidenceRanges, allowConfidenceOverlap);
return;
}
}
// Not matched
RefreshConfidenceRange(0, confidenceRanges, allowConfidenceOverlap);
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 8d2d3ff93ff48bd40ab5bca3cf4e6d2c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,60 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
* All rights reserved.
*
* This source code is licensed under the license found in the
* LICENSE file in the root directory of this source tree.
*/
using System;
using Meta.WitAi.Json;
using UnityEngine;
using UnityEngine.Events;
namespace Meta.WitAi.CallbackHandlers
{
[AddComponentMenu("Wit.ai/Response Matchers/Simple String Entity Handler")]
public class SimpleStringEntityHandler : WitIntentMatcher
{
[SerializeField] public string entity;
[SerializeField] public string format;
[SerializeField] private StringEntityMatchEvent onIntentEntityTriggered
= new StringEntityMatchEvent();
public StringEntityMatchEvent OnIntentEntityTriggered => onIntentEntityTriggered;
protected override string OnValidateResponse(WitResponseNode response, bool isEarlyResponse)
{
// Return base
string result = base.OnValidateResponse(response, isEarlyResponse);
if (!string.IsNullOrEmpty(result))
{
return result;
}
// Not found
var entityValue = response.GetFirstEntityValue(entity);
if (string.IsNullOrEmpty(entityValue))
{
return $"Missing required entity: {entity}";
}
// Fail
return string.Empty;
}
protected override void OnResponseInvalid(WitResponseNode response, string error) { }
protected override void OnResponseSuccess(WitResponseNode response)
{
var entityValue = response.GetFirstEntityValue(entity);
if (!string.IsNullOrEmpty(format))
{
onIntentEntityTriggered.Invoke(format.Replace("{value}", entityValue));
}
else
{
onIntentEntityTriggered.Invoke(entityValue);
}
}
}
[Serializable]
public class StringEntityMatchEvent : UnityEvent<string> {}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 77dceeee73817ec4c844564714e05c77
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,81 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
* All rights reserved.
*
* This source code is licensed under the license found in the
* LICENSE file in the root directory of this source tree.
*/
using System;
using Meta.Conduit;
using Meta.WitAi.Json;
using Meta.WitAi.Data.Intents;
using UnityEngine;
using UnityEngine.Serialization;
namespace Meta.WitAi.CallbackHandlers
{
// Abstract class to share confidence handling
public abstract class WitIntentMatcher : WitResponseHandler
{
/// <summary>
/// Intent name to be matched
/// </summary>
[Header("Intent Settings")]
[SerializeField] public string intent;
/// <summary>
/// Confidence threshold
/// </summary>
[FormerlySerializedAs("confidence")]
[Range(0, 1f), SerializeField] public float confidenceThreshold = .6f;
// Handle simple intent validation
protected override string OnValidateResponse(WitResponseNode response, bool isEarlyResponse)
{
// No response
if (response == null)
{
return "No response";
}
// Check against all intents
WitIntentData[] intents = response.GetIntents();
if (intents == null || intents.Length == 0)
{
return "No intents found";
}
// Find intent
WitIntentData found = null;
foreach (var intentData in intents)
{
if (string.Equals(intent, intentData.name, StringComparison.CurrentCultureIgnoreCase))
{
found = intentData;
break;
}
}
if (found == null)
{
return $"Missing required intent '{intent}'";
}
// Check confidence
if (found.confidence < confidenceThreshold)
{
return $"Required intent '{intent}' confidence too low: {found.confidence:0.000}\nRequired: {confidenceThreshold:0.000}";
}
return string.Empty;
}
protected override void OnEnable()
{
Manifest.WitResponseMatcherIntents.Add(intent);
base.OnEnable();
}
protected override void OnDisable()
{
Manifest.WitResponseMatcherIntents.Remove(intent);
base.OnDisable();
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 9dca9f31f9df1bd4d9cda88e81bf3082
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,163 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
* All rights reserved.
*
* This source code is licensed under the license found in the
* LICENSE file in the root directory of this source tree.
*/
using System;
using Meta.WitAi.Data;
using Meta.WitAi.Json;
using Meta.WitAi.Requests;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.Serialization;
namespace Meta.WitAi.CallbackHandlers
{
[Serializable]
public class WitResponseEvent : UnityEvent<WitResponseNode> {}
[Serializable]
public class WitResponseErrorEvent : UnityEvent<WitResponseNode, string> {}
public abstract class WitResponseHandler : MonoBehaviour
{
[FormerlySerializedAs("wit")]
[SerializeField] public VoiceService Voice;
[SerializeField] public bool ValidateEarly = false;
// Whether validated early
private bool _validated = false;
private void OnValidate()
{
if (!Voice) Voice = FindObjectOfType<VoiceService>();
}
protected virtual void OnEnable()
{
if (!Voice) Voice = FindObjectOfType<VoiceService>();
if (!Voice)
{
VLog.E($"VoiceService not found in scene.\nDisabling {GetType().Name} on {gameObject.name}");
enabled = false;
}
else
{
Voice.VoiceEvents.OnSend.AddListener(OnRequestSend);
Voice.VoiceEvents.OnValidatePartialResponse.AddListener(HandleValidateEarlyResponse);
Voice.VoiceEvents.OnResponse.AddListener(HandleFinalResponse);
}
}
protected virtual void OnDisable()
{
if (Voice)
{
Voice.VoiceEvents.OnSend.RemoveListener(OnRequestSend);
Voice.VoiceEvents.OnValidatePartialResponse.RemoveListener(HandleValidateEarlyResponse);
Voice.VoiceEvents.OnResponse.RemoveListener(HandleFinalResponse);
}
}
protected virtual void OnRequestSend(VoiceServiceRequest request)
{
_validated = false;
}
protected virtual void HandleValidateEarlyResponse(VoiceSession session)
{
if (ValidateEarly && !_validated)
{
string invalidReason = OnValidateResponse(session.response, true);
if (string.IsNullOrEmpty(invalidReason))
{
_validated = true;
OnResponseSuccess(session.response);
session.validResponse = true;
}
}
}
protected virtual void HandleFinalResponse(WitResponseNode response)
{
// Not yet handled
if (!_validated)
{
string invalidReason = OnValidateResponse(response, false);
if (!string.IsNullOrEmpty(invalidReason))
{
OnResponseInvalid(response, invalidReason);
}
else
{
OnResponseSuccess(response);
}
_validated = true;
}
}
// Handle validation
protected abstract string OnValidateResponse(WitResponseNode response, bool isEarlyResponse);
// Handle invalid
protected abstract void OnResponseInvalid(WitResponseNode response, string error);
// Handle valid
protected abstract void OnResponseSuccess(WitResponseNode response);
#if UNITY_EDITOR
// For tests
public void HandleResponse(WitResponseNode response) => HandleFinalResponse(response);
#endif
/// <summary>
/// Refresh confidence range
/// </summary>
/// <param name="confidence"></param>
/// <param name="confidenceRanges"></param>
/// <param name="allowConfidenceOverlap"></param>
/// <returns></returns>
public static bool RefreshConfidenceRange(float confidence, ConfidenceRange[] confidenceRanges, bool allowConfidenceOverlap)
{
// Whether found
bool foundIn = false;
bool foundOut = false;
// Iterate confidences
for (int i = 0; null != confidenceRanges && i < confidenceRanges.Length; i++)
{
var range = confidenceRanges[i];
// Within Range
if (confidence >= range.minConfidence &&
confidence <= range.maxConfidence)
{
// Ignore overlap
if (!allowConfidenceOverlap && foundIn)
{
continue;
}
// Within delegate
range.onWithinConfidenceRange?.Invoke();
foundIn = true;
}
// Out of Range
else
{
// Ignore overlap
if (!allowConfidenceOverlap && foundOut)
{
continue;
}
// Outside delegate
range.onOutsideConfidenceRange?.Invoke();
foundOut = true;
}
}
// Return if found within
return foundIn;
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 097381abbd7a3364f80ac4460551a2cb
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,358 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
* All rights reserved.
*
* 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 Meta.WitAi.Attributes;
using Meta.WitAi.Data;
using Meta.WitAi.Json;
using Meta.WitAi.Utilities;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.Serialization;
namespace Meta.WitAi.CallbackHandlers
{
[AddComponentMenu("Wit.ai/Response Matchers/Response Matcher")]
public class WitResponseMatcher : WitIntentMatcher
{
[FormerlySerializedAs("valuePaths")]
[Header("Value Matching")]
#if UNITY_2021_3_2 || UNITY_2021_3_3 || UNITY_2021_3_4 || UNITY_2021_3_5
[NonReorderable]
#endif
[SerializeField] public ValuePathMatcher[] valueMatchers;
[Header("Output")]
#if UNITY_2021_3_2 || UNITY_2021_3_3 || UNITY_2021_3_4 || UNITY_2021_3_5
[NonReorderable]
#endif
[SerializeField] private FormattedValueEvents[] formattedValueEvents;
[SerializeField] private MultiValueEvent onMultiValueEvent = new MultiValueEvent();
[TooltipBox("Triggered if the matching conditions did not match. The parameter will be the transcription that was received. This will only trigger if there were values for intents or entities, but those values didn't match this matcher.")]
[SerializeField] private StringEvent onDidNotMatch = new StringEvent();
[TooltipBox("Triggered if a request was checked and no intents were found. This will still trigger if entities match and only applies to intents. The parameter will be the transcription.")]
[SerializeField] private StringEvent onOutOfDomain = new StringEvent();
private static Regex valueRegex = new Regex(Regex.Escape("{value}"), RegexOptions.Compiled);
// Handle validation
protected override string OnValidateResponse(WitResponseNode response, bool isEarlyResponse)
{
// Return base
string result = base.OnValidateResponse(response, isEarlyResponse);
if (!string.IsNullOrEmpty(result))
{
return result;
}
// Only check value matches on early
if (isEarlyResponse && !ValueMatches(response))
{
return "No value matches";
}
// Success
return string.Empty;
}
// Ignore for mismatched intent
protected override void OnResponseInvalid(WitResponseNode response, string error)
{
if (response.GetIntents().Length > 0 || response.EntityCount() > 0)
{
onDidNotMatch?.Invoke(response.GetTranscription());
}
if (response.GetIntents().Length == 0)
{
onOutOfDomain?.Invoke(response.GetTranscription());
}
}
// Handle valid callback
protected override void OnResponseSuccess(WitResponseNode response)
{
// Check value matches
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);
}
}
}
else
{
onDidNotMatch?.Invoke(response.GetTranscription());
}
// Get all values & perform multi value event
List<string> values = new List<string>();
foreach (var matcher in valueMatchers)
{
// Add value
var value = matcher.Reference.GetStringValue(response);
values.Add(value);
// Refresh confidence
if (matcher.ConfidenceReference != null)
{
float confidenceValue = ValueMatches(response, matcher)
? matcher.ConfidenceReference.GetFloatValue(response)
: 0f;
RefreshConfidenceRange(confidenceValue, matcher.confidenceRanges, matcher.allowConfidenceOverlap);
}
}
onMultiValueEvent.Invoke(values.ToArray());
}
private bool ValueMatches(WitResponseNode response)
{
bool matches = true;
for (int i = 0; i < valueMatchers.Length && matches; i++)
{
matches &= ValueMatches(response, valueMatchers[i]);
}
return matches;
}
private bool ValueMatches(WitResponseNode response, ValuePathMatcher matcher)
{
var value = matcher.Reference.GetStringValue(response);
bool result = !matcher.contentRequired || !string.IsNullOrEmpty(value);
switch (matcher.matchMethod)
{
case MatchMethod.RegularExpression:
result &= Regex.Match(value, matcher.matchValue).Success;
break;
case MatchMethod.Text:
result &= value == matcher.matchValue;
break;
case MatchMethod.IntegerComparison:
result &= CompareInt(value, matcher);
break;
case MatchMethod.FloatComparison:
result &= CompareFloat(value, matcher);
break;
case MatchMethod.DoubleComparison:
result &= CompareDouble(value, matcher);
break;
}
return result;
}
private bool CompareDouble(string value, ValuePathMatcher matcher)
{
// This one is freeform based on the input so we will retrun false if it is not parsable
if (!double.TryParse(value, out double 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)
{
// This one is freeform based on the input so we will retrun false if it is not parsable
if (!float.TryParse(value, out float 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)
{
// This one is freeform based on the input so we will retrun false if it is not parsable
if (!int.TryParse(value, out int 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;
}
}
[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;
[Tooltip("Confidence ranges are executed in order. If checked, all confidence values will be checked instead of stopping on the first one that matches.")]
[SerializeField] public bool allowConfidenceOverlap;
[Tooltip("The confidence levels to handle for this value.\nNOTE: The selected node must have a confidence sibling node.")]
public ConfidenceRange[] confidenceRanges;
private WitResponseReference pathReference;
private WitResponseReference confidencePathReference;
public WitResponseReference ConfidenceReference
{
get
{
if (null != confidencePathReference) return confidencePathReference;
var confidencePath = Reference?.path;
if (!string.IsNullOrEmpty(confidencePath))
{
confidencePath = confidencePath.Substring(0, confidencePath.LastIndexOf("."));
confidencePath += ".confidence";
confidencePathReference = WitResultUtilities.GetWitResponseReference(confidencePath);
}
return confidencePathReference;
}
}
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
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 591c3d6f017c11b4faa41506d75635b9
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,76 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
* All rights reserved.
*
* 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 Meta.WitAi.Json;
using Meta.WitAi.Utilities;
using UnityEngine;
namespace Meta.WitAi.CallbackHandlers
{
[AddComponentMenu("Wit.ai/Response Matchers/Utterance Matcher")]
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 string OnValidateResponse(WitResponseNode response, bool isEarlyResponse)
{
var text = response["text"].Value;
if (!IsMatch(text))
{
return "Required utterance does not match";
}
return "";
}
protected override void OnResponseInvalid(WitResponseNode response, string error){}
protected override void OnResponseSuccess(WitResponseNode response)
{
var text = response["text"].Value;
onUtteranceMatched?.Invoke(text);
}
private bool IsMatch(string text)
{
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)
{
return true;
}
else
{
return true;
}
}
}
else if (exactMatch && text.ToLower() == searchText.ToLower())
{
return true;
}
else if (text.ToLower().Contains(searchText.ToLower()))
{
return true;
}
return false;
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d0a93535f06eabe47bf93b4b504873c8
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 5c9bc9136c8441e48996d814209d4c2e
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,230 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
* All rights reserved.
*
* This source code is licensed under the license found in the
* LICENSE file in the root directory of this source tree.
*/
using System.Collections;
using System.Collections.Generic;
using Meta.WitAi.Events;
using Meta.WitAi.Interfaces;
using Meta.WitAi.Lib;
using UnityEngine;
namespace Meta.WitAi.Data
{
public class AudioBuffer : MonoBehaviour
{
#region Singleton
private static AudioBuffer _instance;
public static AudioBuffer Instance
{
get
{
if (!_instance && Application.isPlaying)
{
_instance = FindObjectOfType<AudioBuffer>();
if (!_instance)
{
var audioBufferObject = new GameObject("AudioBuffer");
_instance = audioBufferObject.AddComponent<AudioBuffer>();
}
}
return _instance;
}
}
#endregion
[SerializeField] private bool alwaysRecording;
[SerializeField] private AudioBufferConfiguration audioBufferConfiguration = new AudioBufferConfiguration();
[SerializeField] private AudioBufferEvents events = new AudioBufferEvents();
public AudioBufferEvents Events => events;
public IAudioInputSource MicInput
{
get
{
if (_micInput == null && Application.isPlaying)
{
// Check this gameobject & it's children for audio input
_micInput = gameObject.GetComponentInChildren<IAudioInputSource>();
// Check all roots for Mic Input JIC
if (_micInput == null)
{
foreach (var root in gameObject.scene.GetRootGameObjects())
{
_micInput = root.GetComponentInChildren<IAudioInputSource>();
if (_micInput != null)
{
break;
}
}
}
// Use default mic script
if (_micInput == null)
{
_micInput = gameObject.AddComponent<Mic>();
}
}
return _micInput;
}
}
private IAudioInputSource _micInput;
private RingBuffer<byte> _micDataBuffer;
private byte[] _byteDataBuffer;
private HashSet<Component> _waitingRecorders = new HashSet<Component>();
private HashSet<Component> _activeRecorders = new HashSet<Component>();
public bool IsRecording(Component component) => _waitingRecorders.Contains(component) || _activeRecorders.Contains(component);
public bool IsInputAvailable => MicInput != null && MicInput.IsInputAvailable;
public void CheckForInput() => MicInput.CheckForInput();
public AudioEncoding AudioEncoding => MicInput.AudioEncoding;
private void Awake()
{
_instance = this;
InitializeMicDataBuffer();
}
private void OnEnable()
{
MicInput.OnSampleReady += OnMicSampleReady;
if (alwaysRecording) StartRecording(this);
}
// Remove mic delegates
private void OnDisable()
{
MicInput.OnSampleReady -= OnMicSampleReady;
if (alwaysRecording) StopRecording(this);
}
// Callback for mic sample ready
private void OnMicSampleReady(int sampleCount, float[] sample, float levelMax)
{
events.OnMicLevelChanged.Invoke(levelMax);
var marker = CreateMarker();
Convert(sample);
if (null != events.OnByteDataReady)
{
marker.Clone().ReadIntoWriters(events.OnByteDataReady.Invoke);
}
events.OnSampleReady?.Invoke(marker, levelMax);
}
// Generate mic data buffer if needed
private void InitializeMicDataBuffer()
{
if (null == _micDataBuffer && audioBufferConfiguration.micBufferLengthInSeconds > 0)
{
var bufferSize = (int) Mathf.Ceil(2 *
audioBufferConfiguration
.micBufferLengthInSeconds * 1000 *
audioBufferConfiguration.sampleLengthInMs);
if (bufferSize <= 0)
{
bufferSize = 1024;
}
_micDataBuffer = new RingBuffer<byte>(bufferSize);
}
}
// Convert
private void Convert(float[] samples)
{
var sampleCount = samples.Length;
const int rescaleFactor = 32767; //to convert float to Int16
for (int i = 0; i < sampleCount; i++)
{
short data = (short) (samples[i] * rescaleFactor);
_micDataBuffer.Push((byte) data);
_micDataBuffer.Push((byte) (data >> 8));
}
}
public RingBuffer<byte>.Marker CreateMarker()
{
return _micDataBuffer.CreateMarker();
}
/// <summary>
/// Creates a marker with an offset
/// </summary>
/// <param name="offset">Number of seconds to offset the marker by</param>
/// <returns></returns>
public RingBuffer<byte>.Marker CreateMarker(float offset)
{
var samples = (int) (AudioEncoding.samplerate * offset);
return _micDataBuffer.CreateMarker(samples);
}
public void StartRecording(Component component)
{
StartCoroutine(WaitForMicToStart(component));
}
private IEnumerator WaitForMicToStart(Component component)
{
// Wait for mic
_waitingRecorders.Add(component);
yield return new WaitUntil(() => null != MicInput && MicInput.IsInputAvailable);
if (!_waitingRecorders.Contains(component))
{
yield break;
}
_waitingRecorders.Remove(component);
// Add component
_activeRecorders.Add(component);
// Start mic
if (!MicInput.IsRecording)
{
MicInput.StartRecording(audioBufferConfiguration.sampleLengthInMs);
}
// On Start Listening
if (component is IVoiceEventProvider v)
{
v.VoiceEvents.OnStartListening?.Invoke();
}
}
public void StopRecording(Component component)
{
// Remove waiting recorder
if (_waitingRecorders.Contains(component))
{
_waitingRecorders.Remove(component);
return;
}
// Ignore unless active
if (!_activeRecorders.Contains(component))
{
return;
}
// Remove active recorder
_activeRecorders.Remove(component);
// Stop recording if last active recorder
if (_activeRecorders.Count == 0)
{
MicInput.StopRecording();
}
// On Stop Listening
if (component is IVoiceEventProvider v)
{
v.VoiceEvents.OnStoppedListening?.Invoke();
}
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f7b07c59a0c163b4d932f1b4a11b24f6
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,27 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
* All rights reserved.
*
* 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 Meta.WitAi.Data
{
[Serializable]
public class AudioBufferConfiguration
{
[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;
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 5dddc67743ac75b4f83ba080cafe1299
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: d99f42b1387bffe4b845b1bf9f9cf5cb
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,293 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
* All rights reserved.
*
* 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.IO;
using System.Linq;
using System.Reflection;
using Meta.WitAi.Configuration;
using Meta.WitAi.Data.Info;
using UnityEngine;
using UnityEngine.Serialization;
#if UNITY_EDITOR
using UnityEditor;
#endif
namespace Meta.WitAi.Data.Configuration
{
public class WitConfiguration : ScriptableObject, IWitRequestConfiguration
{
/// <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")]
[FormerlySerializedAs("clientAccessToken")]
[SerializeField] private string _clientAccessToken;
/// <summary>
/// Application info
/// </summary>
[FormerlySerializedAs("application")]
[SerializeField] private WitAppInfo _appInfo; //to be replaced by _configData
/// <summary>
/// Configuration data about the app.
/// </summary>
[SerializeField] private WitConfigurationAssetData[] _configData;
/// <summary>
/// Configuration id
/// </summary>
[FormerlySerializedAs("configId")]
[HideInInspector] [SerializeField] private string _configurationId;
[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();
/// <summary>
/// True if this configuration should not show up in the demo list
/// </summary>
[SerializeField] public bool isDemoOnly;
/// <summary>
/// When set to true, will use Conduit to dispatch voice commands.
/// </summary>
[Tooltip("Conduit enables manifest-based dispatching to invoke callbacks with native types directly without requiring manual parsing.")]
[SerializeField] public bool useConduit = true;
/// <summary>
/// The path to the Conduit manifest.
/// </summary>
[SerializeField] private string _manifestLocalPath;
/// <summary>
/// The assemblies that we want to exclude from Conduit.
/// </summary>
[SerializeField] public List<string> excludedAssemblies = new List<string>
{
"Oculus.Voice.Demo, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null",
"Meta.WitAi.Samples, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"
};
[Tooltip("When true, Conduit will attempt to match incoming requests by type when no exact matches are found. This increases tolerance but reduces runtime performance.")]
[SerializeField] public bool relaxedResolution;
/// <summary>
/// Safe access of local path
/// </summary>
public string ManifestLocalPath
{
get
{
#if UNITY_EDITOR
if (string.IsNullOrEmpty(_manifestLocalPath))
{
_manifestLocalPath = $"ConduitManifest-{Guid.NewGuid()}.json";
SaveConfiguration();
}
#endif
return _manifestLocalPath;
}
}
#if UNITY_EDITOR
/// <summary>
/// Returns manifest full editor path
/// </summary>
public string GetManifestEditorPath()
{
if (string.IsNullOrEmpty(_manifestLocalPath)) return string.Empty;
string lookup = Path.GetFileNameWithoutExtension(_manifestLocalPath);
string[] guids = UnityEditor.AssetDatabase.FindAssets(lookup);
if (guids != null && guids.Length > 0)
{
return UnityEditor.AssetDatabase.GUIDToAssetPath(guids[0]);
}
return string.Empty;
}
#endif
/// <summary>
/// Reset all data
/// </summary>
public void ResetData()
{
_configurationId = null;
_appInfo = new WitAppInfo();
endpointConfiguration = new WitEndpointConfig();
}
/// <summary>
/// Refreshes the individual data components of the configuration.
/// </summary>
public void UpdateDataAssets()
{
#if UNITY_EDITOR
RefreshPlugins();
#endif
foreach (WitConfigurationAssetData data in _configData)
{
data.Refresh(this);
}
}
// Logger invalid warnings
private const string INVALID_APP_ID_NO_CLIENT_TOKEN = "App Info Not Set - No Client Token";
private const string INVALID_APP_ID_WITH_CLIENT_TOKEN =
"App Info Not Set - Has Client Token";
public string GetLoggerAppId()
{
// Get application id
string applicationId = GetApplicationId();
if (String.IsNullOrEmpty(applicationId))
{
// NOTE: If a dev only provides a client token we may not have the application id.
string clientAccessToken = GetClientAccessToken();
if (!string.IsNullOrEmpty(clientAccessToken))
{
return INVALID_APP_ID_WITH_CLIENT_TOKEN;
}
return INVALID_APP_ID_NO_CLIENT_TOKEN;
}
return applicationId;
}
#region IWitRequestConfiguration
/// <summary>
/// Returns unique configuration guid
/// </summary>
public string GetConfigurationId()
{
#if UNITY_EDITOR
// Ensure configuration id is generated
if (string.IsNullOrEmpty(_configurationId))
{
_configurationId = Guid.NewGuid().ToString();
}
#endif
// Return configuration id
return _configurationId;
}
/// <summary>
/// Returns unique application id
/// </summary>
public string GetApplicationId() => _appInfo.id;
/// <summary>
/// Returns application info
/// </summary>
public WitAppInfo GetApplicationInfo() => _appInfo;
/// <summary>
/// Returns all the configuration data for this app.
/// </summary>
/// <returns></returns>
public WitConfigurationAssetData[] GetConfigData()
{
if (_configData == null)
{
_configData = Array.Empty<WitConfigurationAssetData>();
}
return _configData;
}
/// <summary>
/// Return endpoint override
/// </summary>
public IWitRequestEndpointInfo GetEndpointInfo() => endpointConfiguration;
/// <summary>
/// Returns client access token
/// </summary>
public string GetClientAccessToken()
{
return _clientAccessToken;
}
#if UNITY_EDITOR
/// <summary>
/// Editor only setter
/// </summary>
public void SetClientAccessToken(string newToken)
{
_clientAccessToken = newToken;
SaveConfiguration();
}
/// <summary>
/// Returns server access token (Editor Only)
/// </summary>
/// <returns></returns>
/// <exception cref="NotImplementedException"></exception>
public string GetServerAccessToken()
{
return WitAuthUtility.GetAppServerToken(GetApplicationId());
}
/// <summary>
/// Set application info
/// </summary>
public void SetApplicationInfo(WitAppInfo newInfo)
{
_appInfo = newInfo;
SaveConfiguration();
}
/// <summary>
/// Saves the plugin-specific data for this WitConfiguration
/// </summary>
public void SetConfigData(WitConfigurationAssetData[] configData)
{
_configData = configData;
}
// Save this configuration asset
private void SaveConfiguration()
{
EditorUtility.SetDirty(this);
#if UNITY_2021_3_OR_NEWER
AssetDatabase.SaveAssetIfDirty(this);
#else
AssetDatabase.SaveAssets();
#endif
}
private void RefreshPlugins()
{
Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
// Find all derived data types
List<Type> dataPlugins = typeof(WitConfigurationAssetData).GetSubclassTypes();
// Create instances of the types and register them
List<WitConfigurationAssetData> newConfigs = new List<WitConfigurationAssetData>();
var configurationAssetPath = AssetDatabase.GetAssetPath(this);
foreach (Type dataType in dataPlugins)
{
// Grab existing if present
var plugin = (WitConfigurationAssetData)AssetDatabase.LoadAssetAtPath(configurationAssetPath, dataType);
// Generate instance & add to asset
if (plugin == null)
{
plugin = (WitConfigurationAssetData)CreateInstance(dataType);
plugin.name = dataType.Name;
AssetDatabase.AddObjectToAsset(plugin, configurationAssetPath);
AssetDatabase.ImportAsset(AssetDatabase.GetAssetPath(plugin));
plugin = (WitConfigurationAssetData)AssetDatabase.LoadAssetAtPath(configurationAssetPath, dataType);
}
newConfigs.Add(plugin);
}
SetConfigData(newConfigs.ToArray());
AssetDatabase.SaveAssets();
}
#endif
#endregion
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ae5a46bda3295124c99b0e6537ac7252
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,107 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
* All rights reserved.
*
* 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.Serialization;
using Meta.WitAi.Data.Configuration;
namespace Meta.WitAi.Configuration
{
[Serializable]
public class WitEndpointConfig : IWitRequestEndpointInfo
{
/// <summary>
/// Customized uri scheme (Ex. https)
/// </summary>
[SerializeField] [FormerlySerializedAs("uriScheme")]
private string _uriScheme;
public string UriScheme => string.IsNullOrEmpty(_uriScheme) ? WitConstants.URI_SCHEME : _uriScheme;
/// <summary>
/// Customized host location (Ex. api.wit.ai)
/// </summary>
[SerializeField] [FormerlySerializedAs("authority")]
private string _authority;
public string Authority =>
string.IsNullOrEmpty(_authority) ? WitConstants.URI_AUTHORITY : _authority;
/// <summary>
/// Customized host port (Ex. api.wit.ai)
/// </summary>
[SerializeField] [FormerlySerializedAs("port")]
private int _port;
public int Port => _port <= 0 ? WitConstants.URI_DEFAULT_PORT : _port;
/// <summary>
/// API version to be used for this endpoint. Defaults to sdk default version
/// </summary>
[SerializeField] [FormerlySerializedAs("witApiVersion")]
private string _witApiVersion;
public string WitApiVersion => string.IsNullOrEmpty(_witApiVersion)
? WitConstants.API_VERSION
: _witApiVersion;
/// <summary>
/// Endpoint used for text based voice command. Defaults to 'message'
/// </summary>
[SerializeField] [FormerlySerializedAs("message")]
private string _message;
public string Message =>
string.IsNullOrEmpty(_message) ? WitConstants.ENDPOINT_MESSAGE : _message;
/// <summary>
/// Endpoint used for audio based voice command. Defaults to 'speech'
/// </summary>
[SerializeField] [FormerlySerializedAs("speech")]
private string _speech;
public string Speech =>
string.IsNullOrEmpty(_speech) ? WitConstants.ENDPOINT_SPEECH : _speech;
/// <summary>
/// Endpoint used for audio based transcription. Defaults to 'dictation'
/// </summary>
[SerializeField] [FormerlySerializedAs("dictation")]
private string _dictation;
public string Dictation => string.IsNullOrEmpty(_dictation) ? WitConstants.ENDPOINT_DICTATION : _dictation;
/// <summary>
/// Endpoint used for Text-To-Speech. Defaults to 'synthesize'
/// </summary>
[SerializeField]
private string _synthesize;
public string Synthesize => string.IsNullOrEmpty(_synthesize) ? WitConstants.ENDPOINT_TTS : _synthesize;
/// <summary>
/// Endpoint used for Composer text requests. Defaults to 'event'
/// </summary>
[SerializeField]
private string _event;
public string Event => string.IsNullOrEmpty(_event) ? WitConstants.ENDPOINT_COMPOSER_MESSAGE : _event;
/// <summary>
/// Endpoint used for Composer audio requests. Defaults to 'converse'
/// </summary>
[SerializeField]
private string _converse;
public string Converse => string.IsNullOrEmpty(_converse) ? WitConstants.ENDPOINT_COMPOSER_SPEECH : _converse;
// Default endpoint data
private static WitEndpointConfig defaultEndpointConfig = new WitEndpointConfig();
/// <summary>
/// Generates a configuration using a preset if possible
/// </summary>
public static WitEndpointConfig GetEndpointConfig(WitConfiguration witConfig)
{
return witConfig && null != witConfig.endpointConfiguration
? witConfig.endpointConfiguration
: defaultEndpointConfig;
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 61a0415bfdfa4d64e89801fbc0b30ec7
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,60 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
* All rights reserved.
*
* 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 Meta.WitAi.Json;
using Meta.WitAi.Requests;
using Meta.WitAi.Interfaces;
namespace Meta.WitAi.Configuration
{
public class WitRequestOptions : VoiceServiceRequestOptions
{
/// <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;
/// <summary>
/// The tag for snapshot
/// </summary>
public string tag;
/// <summary>
/// Formerly used for request id
/// </summary>
[Obsolete("Use 'RequestId' property instead")] [JsonIgnore]
public string requestID => RequestId;
/// <summary>
/// Callback for completion
/// </summary>
public Action<WitRequest> onResponse;
// Get json string. Used to get the payload for PI.
// PI will reparse these parameters and construct it's own request.
public string ToJsonString()
{
Dictionary<string, string> parameters = new Dictionary<string, string>();
parameters["nBestIntents"] = nBestIntents.ToString();
parameters["tag"] = tag;
parameters["requestID"] = RequestId;
foreach (var key in QueryParams.Keys)
{
parameters[key] = QueryParams[key];
}
return JsonConvert.SerializeObject(parameters);
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a6834fd3c55fb41a1885302e6043f5d6
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,79 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
* All rights reserved.
*
* This source code is licensed under the license found in the
* LICENSE file in the root directory of this source tree.
*/
using System;
using Meta.WitAi.Data.Configuration;
using Meta.WitAi.Interfaces;
using Meta.WitAi.Utilities;
using UnityEngine;
using UnityEngine.Serialization;
namespace Meta.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]
[DynamicRange("RecordingTimeRange")]
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]
[DynamicRange("RecordingTimeRange")]
public float minTranscriptionKeepAliveTimeInSeconds = 1f;
[Tooltip("The maximum amount of time in seconds the mic will stay active")]
[SerializeField]
[DynamicRange("RecordingTimeRange")]
public float maxRecordingTime = 20;
protected virtual Vector2 RecordingTimeRange => new Vector2(0, 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;
[Tooltip("The maximum amount of concurrent requests that can occur")]
[Range(1, 10)] [SerializeField] public int maxConcurrentRequests = 5;
[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;
[Tooltip("If always record is set the mic will fill the mic data buffer as long as the component is enabled in the scene.")]
public bool alwaysRecord;
[Tooltip("The preferred number of seconds to offset from the time the activation happens. A negative value here could help to catch any words that may have been cut off at the beginning of an activation (assuming input is already being read into the buffer)")]
public float preferredActivationOffset = -.5f;
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: bd8d40c9f75b44b5940fcc21769d6af0
timeCreated: 1629397017
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: d92a289db3a8408dba9dd973090b1ade
timeCreated: 1632287743
@@ -0,0 +1,28 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
* All rights reserved.
*
* This source code is licensed under the license found in the
* LICENSE file in the root directory of this source tree.
*/
using Meta.WitAi.Interfaces;
using UnityEngine;
namespace Meta.WitAi.Data.Entities
{
public class DynamicEntityDataProvider : MonoBehaviour, IDynamicEntitiesProvider
{
[SerializeField] internal WitDynamicEntitiesData[] entitiesDefinition;
public WitDynamicEntities GetDynamicEntities()
{
WitDynamicEntities entities = new WitDynamicEntities();
foreach (var entity in entitiesDefinition)
{
entities.Merge(entity);
}
return entities;
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 4b4e835dcc764fee99862c3c535ab5fa
timeCreated: 1646775302
@@ -0,0 +1,23 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
* All rights reserved.
*
* This source code is licensed under the license found in the
* LICENSE file in the root directory of this source tree.
*/
using Meta.WitAi.Interfaces;
using UnityEngine;
namespace Meta.WitAi.Data.Entities
{
public class DynamicEntityProvider : MonoBehaviour, IDynamicEntitiesProvider
{
[SerializeField] protected WitDynamicEntities entities;
public WitDynamicEntities GetDynamicEntities()
{
return entities;
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b789a872764a60545972d2f4505ead38
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 6b9f5c6540348cd41a3f48d0e20057db
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,68 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
* All rights reserved.
*
* This source code is licensed under the license found in the
* LICENSE file in the root directory of this source tree.
*/
using UnityEngine;
using Meta.WitAi.Data.Info;
using Meta.WitAi.Interfaces;
namespace Meta.WitAi.Data.Entities
{
/// <summary>
/// Singleton registry for tracking any objects owned defined in entities in
/// a scene
/// </summary>
public class DynamicEntityKeywordRegistry : MonoBehaviour, IDynamicEntitiesProvider
{
private static DynamicEntityKeywordRegistry instance;
private WitDynamicEntities entities = new WitDynamicEntities();
public static bool HasDynamicEntityRegistry => Instance;
/// <summary>
/// Gets the instance in the scene if there is one
/// </summary>
public static DynamicEntityKeywordRegistry Instance
{
get
{
if (!instance)
{
instance = FindObjectOfType<DynamicEntityKeywordRegistry>();
}
return instance;
}
}
private void OnEnable()
{
instance = this;
}
private void OnDisable()
{
instance = null;
}
public void RegisterDynamicEntity(string entity, WitEntityKeywordInfo keyword)
{
entities.AddKeyword(entity, keyword);
}
public void UnregisterDynamicEntity(string entity, WitEntityKeywordInfo keyword)
{
entities.RemoveKeyword(entity, keyword);
}
public WitDynamicEntities GetDynamicEntities()
{
return entities;
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 7faff460c1b46b34481ecb99327be2b0
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,51 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
* All rights reserved.
*
* This source code is licensed under the license found in the
* LICENSE file in the root directory of this source tree.
*/
using UnityEngine;
using Meta.WitAi.Data.Info;
namespace Meta.WitAi.Data.Entities
{
/// <summary>
/// A configured dynamic entity meant to be placed on dynamic objects.
/// when the object is enabled this entity will be registered with active
/// voice services on activation.
/// </summary>
public class RegisteredDynamicEntityKeyword : MonoBehaviour
{
[SerializeField] private string entity;
[SerializeField] private WitEntityKeywordInfo keyword;
private void OnEnable()
{
if (string.IsNullOrEmpty(keyword.keyword)) return;
if (string.IsNullOrEmpty(entity)) return;
if (DynamicEntityKeywordRegistry.HasDynamicEntityRegistry)
{
DynamicEntityKeywordRegistry.Instance.RegisterDynamicEntity(entity, keyword);
}
else
{
VLog.W($"Cannot register {name}: No dynamic entity registry present in the scene." +
$"Please add one and try again.");
}
}
private void OnDisable()
{
if (string.IsNullOrEmpty(keyword.keyword)) return;
if (string.IsNullOrEmpty(entity)) return;
if (DynamicEntityKeywordRegistry.HasDynamicEntityRegistry)
{
DynamicEntityKeywordRegistry.Instance.UnregisterDynamicEntity(entity, keyword);
}
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 314acb2f0d34f4d4b9a72bed29e990d4
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,116 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
* All rights reserved.
*
* 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;
using System.Collections.Generic;
using Meta.WitAi.Interfaces;
using Meta.WitAi.Json;
using Meta.WitAi.Data.Info;
using UnityEngine;
namespace Meta.WitAi.Data.Entities
{
[Serializable]
public class WitDynamicEntities : IDynamicEntitiesProvider, IEnumerable<WitDynamicEntity>
{
public List<WitDynamicEntity> entities = new List<WitDynamicEntity>();
public WitDynamicEntities()
{
}
public WitDynamicEntities(IEnumerable<WitDynamicEntity> entity)
{
entities.AddRange(entity);
}
public WitDynamicEntities(params WitDynamicEntity[] entity)
{
entities.AddRange(entity);
}
public WitResponseClass AsJson
{
get
{
WitResponseClass json = new WitResponseClass();
foreach (var entity in entities)
{
json.Add(entity.entity, entity.AsJson);
}
return json;
}
}
public override string ToString()
{
return AsJson.ToString();
}
public IEnumerator<WitDynamicEntity> GetEnumerator() => entities.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
public WitDynamicEntities GetDynamicEntities()
{
return this;
}
public void Merge(IDynamicEntitiesProvider provider)
{
if (null == provider) return;
entities.AddRange(provider.GetDynamicEntities());
}
public void Merge(IEnumerable<WitDynamicEntity> mergeEntities)
{
if (null == mergeEntities) return;
entities.AddRange(mergeEntities);
}
public void Add(WitDynamicEntity dynamicEntity)
{
int index = entities.FindIndex(e => e.entity == dynamicEntity.entity);
if(index < 0) entities.Add(dynamicEntity);
else VLog.W($"Cannot add entity, registry already has an entry for {dynamicEntity.entity}");
}
public void Remove(WitDynamicEntity dynamicEntity)
{
entities.Remove(dynamicEntity);
}
public void AddKeyword(string entityName, WitEntityKeywordInfo keyword)
{
var entity = entities.Find(e => entityName == e.entity);
if (null == entity)
{
entity = new WitDynamicEntity(entityName);
entities.Add(entity);
}
entity.keywords.Add(keyword);
}
public void RemoveKeyword(string entityName, WitEntityKeywordInfo keyword)
{
int index = entities.FindIndex(e => e.entity == entityName);
if (index >= 0)
{
entities[index].keywords.Remove(keyword);
if(entities[index].keywords.Count == 0) entities.RemoveAt(index);
}
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a296282c67dc244dd974baca4699c554
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,22 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
* All rights reserved.
*
* This source code is licensed under the license found in the
* LICENSE file in the root directory of this source tree.
*/
using Meta.WitAi.Interfaces;
using UnityEngine;
namespace Meta.WitAi.Data.Entities
{
public class WitDynamicEntitiesData : ScriptableObject, IDynamicEntitiesProvider
{
public WitDynamicEntities entities;
public WitDynamicEntities GetDynamicEntities()
{
return entities;
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 86f432dec1a4be34792c2afdea52bb2d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,80 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
* All rights reserved.
*
* 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 Meta.WitAi.Interfaces;
using Meta.WitAi.Json;
using Meta.WitAi.Data.Info;
namespace Meta.WitAi.Data.Entities
{
[Serializable]
public class WitDynamicEntity : IDynamicEntitiesProvider
{
public string entity;
public List<WitEntityKeywordInfo> keywords = new List<WitEntityKeywordInfo>();
public WitDynamicEntity()
{
}
public WitDynamicEntity(string entity, WitEntityKeywordInfo keyword)
{
this.entity = entity;
this.keywords.Add(keyword);
}
public WitDynamicEntity(string entity, params string[] keywords)
{
this.entity = entity;
foreach (var keyword in keywords)
{
this.keywords.Add(new WitEntityKeywordInfo()
{
keyword = keyword,
synonyms = new List<string>(new string[] { keyword })
});
}
}
public WitDynamicEntity(string entity, Dictionary<string, List<string>> keywordsToSynonyms)
{
this.entity = entity;
foreach (var synonym in keywordsToSynonyms)
{
keywords.Add(new WitEntityKeywordInfo()
{
keyword = synonym.Key,
synonyms = synonym.Value
});
}
}
public WitResponseArray AsJson
{
get
{
return JsonConvert.SerializeToken(keywords).AsArray;
}
}
public WitDynamicEntities GetDynamicEntities()
{
return new WitDynamicEntities()
{
entities = new List<WitDynamicEntity>
{
this
}
};
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: efe24d6c4ed424d25a19396c7a2eec38
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,145 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
* All rights reserved.
*
* This source code is licensed under the license found in the
* LICENSE file in the root directory of this source tree.
*/
using System;
using Meta.WitAi.Json;
using UnityEngine;
using UnityEngine.Scripting;
namespace Meta.WitAi.Data.Entities
{
public abstract class WitEntityDataBase<T>
{
public WitResponseNode responseNode;
public string id;
public string name;
public string role;
public int start;
public int end;
public string type;
public string body;
public T value;
public float confidence;
public bool hasData;
public WitResponseArray entities;
[Preserve]
public WitEntityDataBase<T> FromEntityWitResponseNode(WitResponseNode node)
{
responseNode = node;
WitEntityDataBase<T> result = this;
JsonConvert.DeserializeIntoObject(ref result, node);
return result;
}
public override string ToString()
{
return value.ToString();
}
}
public class WitEntityData : WitEntityDataBase<string>
{
[Preserve]
public WitEntityData() {}
[Preserve]
public WitEntityData(WitResponseNode node)
{
FromEntityWitResponseNode(node);
}
public static implicit operator bool(WitEntityData data) => null != data && !string.IsNullOrEmpty(data.value);
public static implicit operator string(WitEntityData data) => data.value;
public static bool operator ==(WitEntityData data, object value) => Equals(data?.value, value);
public static bool operator !=(WitEntityData data, object value) => !Equals(data?.value, value);
public override bool Equals(object obj)
{
if (obj is string s) return s == value;
return base.Equals(obj);
}
public override int GetHashCode()
{
return base.GetHashCode();
}
}
public class WitEntityFloatData : WitEntityDataBase<float>
{
[Preserve]
public WitEntityFloatData() {}
[Preserve]
public WitEntityFloatData(WitResponseNode node)
{
FromEntityWitResponseNode(node);
}
public static implicit operator bool(WitEntityFloatData data) =>
null != data && data.hasData;
public bool Approximately(float v, float tolerance = .001f) => Math.Abs(v - value) < tolerance;
public static bool operator ==(WitEntityFloatData data, float value) => data?.value == value;
public static bool operator !=(WitEntityFloatData data, float value) => !(data == value);
public static bool operator ==(WitEntityFloatData data, int value) => data?.value == value;
public static bool operator !=(WitEntityFloatData data, int value) => !(data == value);
public static bool operator ==(float value, WitEntityFloatData data) => data?.value == value;
public static bool operator !=(float value, WitEntityFloatData data) => !(data == value);
public static bool operator ==(int value, WitEntityFloatData data) => data?.value == value;
public static bool operator !=(int value, WitEntityFloatData data) => !(data == value);
public override bool Equals(object obj)
{
if (obj is float f) return f == value;
return base.Equals(obj);
}
public override int GetHashCode()
{
return base.GetHashCode();
}
}
public class WitEntityIntData : WitEntityDataBase<int>
{
[Preserve]
public WitEntityIntData() {}
[Preserve]
public WitEntityIntData(WitResponseNode node)
{
FromEntityWitResponseNode(node);
}
public static implicit operator bool(WitEntityIntData data) =>
null != data && data.hasData;
public static bool operator ==(WitEntityIntData data, int value) => data?.value == value;
public static bool operator !=(WitEntityIntData data, int value) => !(data == value);
public static bool operator ==(int value, WitEntityIntData data) => data?.value == value;
public static bool operator !=(int value, WitEntityIntData data) => !(data == value);
public override bool Equals(object obj)
{
if (obj is int i) return i == value;
return base.Equals(obj);
}
public override int GetHashCode()
{
return base.GetHashCode();
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 50471c3e54af4dc1adc8cab506b1e28e
timeCreated: 1646062595
@@ -0,0 +1,26 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
* All rights reserved.
*
* This source code is licensed under the license found in the
* LICENSE file in the root directory of this source tree.
*/
using Meta.WitAi.Interfaces;
using UnityEngine;
namespace Meta.WitAi.Data.Entities
{
public class WitSimpleDynamicEntity : MonoBehaviour, IDynamicEntitiesProvider
{
[SerializeField] private string entityName;
[SerializeField] private string[] keywords;
public WitDynamicEntities GetDynamicEntities()
{
var entity = new WitDynamicEntity(entityName, keywords);
var entities = new WitDynamicEntities(entity);
return entities;
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e15f31f7f3b6c49fb96d0553d125fe22
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 7de07ce5d8884e15924606bffef7ed67
timeCreated: 1632287863
@@ -0,0 +1,35 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
* All rights reserved.
*
* This source code is licensed under the license found in the
* LICENSE file in the root directory of this source tree.
*/
using Meta.WitAi.Json;
namespace Meta.WitAi.Data.Intents
{
public class WitIntentData
{
public WitResponseNode responseNode;
public string id;
public string name;
public float confidence;
public WitIntentData() {}
public WitIntentData(WitResponseNode node)
{
FromIntentWitResponseNode(node);
}
public WitIntentData FromIntentWitResponseNode(WitResponseNode node)
{
WitIntentData result = this;
JsonConvert.DeserializeIntoObject(ref result, node);
return result;
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: fa212f2366174f8a900d2f10b9402ac9
timeCreated: 1646062501
@@ -0,0 +1,292 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
* All rights reserved.
*
* 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 Meta.WitAi.Data
{
public class RingBuffer<T>
{
public delegate void OnDataAdded(T[] data, int offset, int length);
public delegate void ByteDataWriter(T[] buffer, int offset, int length);
public OnDataAdded OnDataAddedEvent;
private readonly T[] buffer;
private int bufferIndex;
private long bufferDataLength;
public int Capacity => buffer.Length;
public int GetBufferArrayIndex(long bufferDataIndex)
{
if (bufferDataLength <= bufferDataIndex) return -1;
if (bufferDataLength - bufferDataIndex > buffer.Length) return -1;
var endOffset = bufferDataLength - bufferDataIndex;
var index = bufferIndex - endOffset;
if (index < 0) index = buffer.Length + index;
return (int) index;
}
public T this[long bufferDataIndex] => buffer[GetBufferArrayIndex(bufferDataIndex)];
public void Clear(bool eraseData = false)
{
bufferIndex = 0;
bufferDataLength = 0;
if (eraseData)
{
for (int i = 0; i < buffer.Length; i++)
{
buffer[i] = default;
}
}
}
public class Marker
{
private long bufferDataIndex;
private int index;
private readonly RingBuffer<T> ringBuffer;
public RingBuffer<T> RingBuffer => ringBuffer;
public Marker(RingBuffer<T> ringBuffer, long markerPosition, int bufIndex)
{
this.ringBuffer = ringBuffer;
bufferDataIndex = markerPosition;
index = bufIndex;
}
public bool IsValid => ringBuffer.bufferDataLength - bufferDataIndex <= ringBuffer.Capacity;
public long AvailableByteCount => Math.Min(ringBuffer.Capacity, RequestedByteCount);
public long RequestedByteCount => ringBuffer.bufferDataLength - bufferDataIndex;
public long CurrentBufferDataIndex => bufferDataIndex;
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 void ReadIntoWriters(params ByteDataWriter[] writers)
{
if (!IsValid && ringBuffer.bufferDataLength > ringBuffer.Capacity)
{
bufferDataIndex = ringBuffer.bufferDataLength - ringBuffer.Capacity;
}
index = ringBuffer.GetBufferArrayIndex(bufferDataIndex);
var length = (int) (ringBuffer.bufferDataLength - bufferDataIndex);
if (IsValid && length > 0)
{
for (int i = 0; i < writers.Length; i++)
{
ringBuffer.WriteFromBuffer(writers[i], index, length);
}
}
bufferDataIndex += length;
index = ringBuffer.GetBufferArrayIndex(bufferDataIndex);
}
public Marker Clone()
{
return new Marker(ringBuffer, bufferDataIndex, index);
}
public void Offset(int amount)
{
bufferDataIndex += amount;
if (bufferDataIndex < 0) bufferDataIndex = 0;
if (bufferDataIndex > ringBuffer.bufferDataLength)
{
bufferDataIndex = ringBuffer.bufferDataLength;
}
index = ringBuffer.GetBufferArrayIndex(bufferDataIndex);
}
}
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;
}
}
}
public void WriteFromBuffer(ByteDataWriter writer, long bufferIndex, int length)
{
lock (buffer)
{
if (bufferIndex + length < buffer.Length)
{
writer(buffer, (int) bufferIndex, length);
}
else
{
if (length > bufferDataLength)
{
length = (int) (bufferDataLength - bufferIndex);
}
if (length > buffer.Length)
{
length = buffer.Length;
}
var l = Math.Min(buffer.Length, length);
int endChunkLength = (int) (buffer.Length - bufferIndex);
int wrappedChunkLength = l - endChunkLength;
writer(buffer, (int) bufferIndex, endChunkLength);
writer(buffer, 0, wrappedChunkLength);
}
}
}
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 void Push(T data)
{
lock (buffer)
{
buffer[bufferIndex++] = data;
if (bufferIndex >= buffer.Length)
{
bufferIndex = 0;
}
bufferDataLength++;
}
}
public int Read(T[] data, int offset, int length, long bufferDataIndex)
{
if (bufferIndex == 0 && bufferDataLength == 0) // The ring buffer has been cleared.
{
return 0;
}
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 -= buffer.Length;
}
var marker = new Marker(this, markerPosition, bufIndex);
return marker;
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: e01ee0aa0d794f7abb30675495fd7ca9
timeCreated: 1626392023
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 982d60138e5147b88a5117adc9ded942
timeCreated: 1632287691
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: eba9bfef0d8049398230af05b25af0e6
timeCreated: 1680299500
@@ -0,0 +1,34 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
* All rights reserved.
*
* This source code is licensed under the license found in the
* LICENSE file in the root directory of this source tree.
*/
namespace Meta.WitAi.Data.ValueReferences
{
using UnityEngine;
[System.Serializable]
public class StringReference<T> : IStringReference where T : ScriptableObject, IStringReference
{
[SerializeField] private string stringValue;
[SerializeField] private T stringObject;
public string Value
{
get => stringObject ? stringObject.Value : stringValue;
set
{
stringObject = null;
stringValue = value;
}
}
}
public interface IStringReference
{
string Value { get; set; }
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: d93d804b221245ecada6cf7bdf41d751
timeCreated: 1680299563
@@ -0,0 +1,30 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
* All rights reserved.
*
* This source code is licensed under the license found in the
* LICENSE file in the root directory of this source tree.
*/
using System;
using Meta.WitAi.Json;
namespace Meta.WitAi.Data
{
[Serializable]
public class VoiceSession
{
/// <summary>
/// Voice service being used
/// </summary>
public VoiceService service;
/// <summary>
/// Voice service response data
/// </summary>
public WitResponseNode response;
/// <summary>
/// Session response data is valid & can be deactivated if true
/// </summary>
public bool validResponse = false;
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 4abc542ed39ac304cbfaa6f82eab7cba
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,44 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
* All rights reserved.
*
* This source code is licensed under the license found in the
* LICENSE file in the root directory of this source tree.
*/
using System;
using Meta.WitAi.Json;
using UnityEngine;
namespace Meta.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);
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: ab8864132457433b8281c87f23398770
timeCreated: 1624318241
@@ -0,0 +1,40 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
* All rights reserved.
*
* This source code is licensed under the license found in the
* LICENSE file in the root directory of this source tree.
*/
using Meta.WitAi.Json;
namespace Meta.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);
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 02641aecd6e142a1b375ed2fb84b8303
timeCreated: 1624318209
@@ -0,0 +1,35 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
* All rights reserved.
*
* This source code is licensed under the license found in the
* LICENSE file in the root directory of this source tree.
*/
using Meta.WitAi.Json;
namespace Meta.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);
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 872feba58ea14c069669a1d25959b22c
timeCreated: 1624318051
@@ -0,0 +1,41 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
* All rights reserved.
*
* This source code is licensed under the license found in the
* LICENSE file in the root directory of this source tree.
*/
using Meta.WitAi.Json;
using UnityEngine;
namespace Meta.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);
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d14d7c8a51621ce419693992b114e2ce
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 401db7761c526c3448f1af1d549ec9f9
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,28 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
* All rights reserved.
*
* This source code is licensed under the license found in the
* LICENSE file in the root directory of this source tree.
*/
using System;
using Meta.WitAi.Data;
using UnityEngine;
namespace Meta.WitAi.Events
{
[Serializable]
public class AudioBufferEvents
{
public delegate void OnSampleReadyEvent(RingBuffer<byte>.Marker marker, float levelMax);
public OnSampleReadyEvent OnSampleReady;
[Tooltip("Called when the volume level of the mic input has changed")]
public WitMicLevelChangedEvent OnMicLevelChanged = new WitMicLevelChangedEvent();
[Header("Data")]
public WitByteDataEvent OnByteDataReady = new WitByteDataEvent();
public WitByteDataEvent OnByteDataSent = new WitByteDataEvent();
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: bd7c64da99e7e054c83dea7647d723b2
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,16 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
* All rights reserved.
*
* This source code is licensed under the license found in the
* LICENSE file in the root directory of this source tree.
*/
using UnityEngine.Events;
namespace Meta.WitAi.Events
{
public class AudioDurationTrackerFinishedEvent : UnityEvent<long, double>
{
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: f8dfb56f054f4aaead7cf9a29003a1d1
timeCreated: 1670631381
@@ -0,0 +1,24 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
* All rights reserved.
*
* 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 Meta.WitAi.Events
{
[AttributeUsage(AttributeTargets.Field, AllowMultiple = true)]
public class EventCategoryAttribute : PropertyAttribute
{
public readonly string Category;
public EventCategoryAttribute(string category = "")
{
Category = category;
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 3c098352b8ac4407a48e88eec765e7c5
timeCreated: 1658192444
@@ -0,0 +1,56 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
* All rights reserved.
*
* 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 UnityEngine;
namespace Meta.WitAi.Events
{
public class EventRegistry
{
[SerializeField]
private List<string> _overriddenCallbacks = new List<string>();
private HashSet<string> _overriddenCallbacksHash;
public HashSet<string> OverriddenCallbacks
{
get
{
if (_overriddenCallbacksHash == null)
{
_overriddenCallbacksHash = new HashSet<string>(_overriddenCallbacks);
}
return _overriddenCallbacksHash;
}
}
public void RegisterOverriddenCallback(string callback)
{
if (!_overriddenCallbacks.Contains(callback))
{
_overriddenCallbacks.Add(callback);
_overriddenCallbacksHash.Add(callback);
}
}
public void RemoveOverriddenCallback(string callback)
{
if (_overriddenCallbacks.Contains(callback))
{
_overriddenCallbacks.Remove(callback);
_overriddenCallbacksHash.Remove(callback);
}
}
public bool IsCallbackOverridden(string callback)
{
return OverriddenCallbacks.Contains(callback);
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: bb347bc947a144a4895c273b88f7baff
timeCreated: 1658874536
@@ -0,0 +1,16 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
* All rights reserved.
*
* This source code is licensed under the license found in the
* LICENSE file in the root directory of this source tree.
*/
namespace Meta.WitAi.Events
{
public class TelemetryEvents
{
public AudioDurationTrackerFinishedEvent OnAudioTrackerFinished =
new AudioDurationTrackerFinishedEvent();
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 671577dd5928458da0dfb66db09c5b95
timeCreated: 1670875696
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: e46f5b77b81ea435ea3274e9d3ddceaa
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,59 @@
using Meta.WitAi.Interfaces;
using UnityEngine;
using UnityEngine.Events;
namespace Meta.WitAi.Events.UnityEventListeners
{
[RequireComponent(typeof(IAudioEventProvider))]
public class AudioEventListener : MonoBehaviour, IAudioInputEvents
{
[SerializeField] private WitMicLevelChangedEvent onMicAudioLevelChanged = new WitMicLevelChangedEvent();
[SerializeField] private UnityEvent onMicStartedListening = new UnityEvent();
[SerializeField] private UnityEvent onMicStoppedListening = new UnityEvent();
public WitMicLevelChangedEvent OnMicAudioLevelChanged => onMicAudioLevelChanged;
public UnityEvent OnMicStartedListening => onMicStartedListening;
public UnityEvent OnMicStoppedListening => onMicStoppedListening;
private IAudioInputEvents _events;
private IAudioInputEvents AudioInputEvents
{
get
{
if (null == _events)
{
var eventProvider = GetComponent<IAudioEventProvider>();
if (null != eventProvider)
{
_events = eventProvider.AudioEvents;
}
}
return _events;
}
}
private void OnEnable()
{
var events = AudioInputEvents;
if (null != events)
{
events.OnMicAudioLevelChanged.AddListener(onMicAudioLevelChanged.Invoke);
events.OnMicStartedListening.AddListener(onMicStartedListening.Invoke);
events.OnMicStoppedListening.AddListener(onMicStoppedListening.Invoke);
}
}
private void OnDisable()
{
var events = AudioInputEvents;
if (null != events)
{
events.OnMicAudioLevelChanged.RemoveListener(onMicAudioLevelChanged.Invoke);
events.OnMicStartedListening.RemoveListener(onMicStartedListening.Invoke);
events.OnMicStoppedListening.RemoveListener(onMicStoppedListening.Invoke);
}
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 9b7e57a219bf949418f86fc9056e38ec
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,55 @@
using Meta.WitAi.Interfaces;
using UnityEngine;
namespace Meta.WitAi.Events.UnityEventListeners
{
public class TranscriptionEventListener : MonoBehaviour, ITranscriptionEvent
{
[SerializeField] private WitTranscriptionEvent onPartialTranscription = new
WitTranscriptionEvent();
[SerializeField] private WitTranscriptionEvent onFullTranscription = new
WitTranscriptionEvent();
public WitTranscriptionEvent OnPartialTranscription => onPartialTranscription;
public WitTranscriptionEvent OnFullTranscription => onFullTranscription;
private ITranscriptionEvent _events;
private ITranscriptionEvent TranscriptionEvents
{
get
{
if (null == _events)
{
var eventProvider = GetComponent<ITranscriptionEventProvider>();
if (null != eventProvider)
{
_events = eventProvider.TranscriptionEvents;
}
}
return _events;
}
}
private void OnEnable()
{
var events = TranscriptionEvents;
if (null != events)
{
events.OnPartialTranscription.AddListener(onPartialTranscription.Invoke);
events.OnFullTranscription.AddListener(onFullTranscription.Invoke);
}
}
private void OnDisable()
{
var events = TranscriptionEvents;
if (null != events)
{
events.OnPartialTranscription.RemoveListener(onPartialTranscription.Invoke);
events.OnFullTranscription.RemoveListener(onFullTranscription.Invoke);
}
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d4753c88442c94393b915563ef1b41cf
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,36 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
* All rights reserved.
*
* 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.Serialization;
namespace Meta.WitAi.Events
{
[Serializable]
public class VoiceEvents : SpeechEvents
{
private const string EVENT_CATEGORY_DATA_EVENTS = "Data Events";
[EventCategory(EVENT_CATEGORY_DATA_EVENTS)]
[FormerlySerializedAs("OnByteDataReady")] [SerializeField] [HideInInspector]
private WitByteDataEvent _onByteDataReady = new WitByteDataEvent();
public WitByteDataEvent OnByteDataReady => _onByteDataReady;
[EventCategory(EVENT_CATEGORY_DATA_EVENTS)]
[FormerlySerializedAs("OnByteDataSent")] [SerializeField] [HideInInspector]
private WitByteDataEvent _onByteDataSent = new WitByteDataEvent();
public WitByteDataEvent OnByteDataSent => _onByteDataSent;
[EventCategory(EVENT_CATEGORY_ACTIVATION_RESPONSE)]
[Tooltip("Called after an on partial response to validate data. If data.validResponse is true, service will deactivate & use the partial data as final")]
[FormerlySerializedAs("OnValidatePartialResponse")] [SerializeField]
private WitValidationEvent _onValidatePartialResponse = new WitValidationEvent();
public WitValidationEvent OnValidatePartialResponse => _onValidatePartialResponse;
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 371044f8a8a85b34aa78547a31c99aa0
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,17 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
* All rights reserved.
*
* This source code is licensed under the license found in the
* LICENSE file in the root directory of this source tree.
*/
using System;
using Meta.WitAi.Requests;
using UnityEngine.Events;
namespace Meta.WitAi.Events
{
[Serializable]
public class VoiceServiceRequestEvent : UnityEvent<VoiceServiceRequest> { }
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 5a88978da4d44ed4f962061566872b83
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,24 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
* All rights reserved.
*
* This source code is licensed under the license found in the
* LICENSE file in the root directory of this source tree.
*/
using System;
namespace Meta.WitAi.Events
{
[Flags]
public enum VoiceState
{
MicOff = 1, //000001
MicOn = 2, //000010
Listening = 4,//000100
StartProcessing = 8,//001000
Response = 16,//010000
Error = 32, //100000
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f3a30f8f7e33c754484eaa264d133f25
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,26 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
* All rights reserved.
*
* 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 Meta.WitAi.Events
{
[Serializable]
public class WitByteDataEvent : UnityEvent<byte[], int, int> { }
public interface IWitByteDataReadyHandler
{
void OnWitDataReady(byte[] data, int offset, int length);
}
public interface IWitByteDataSentHandler
{
void OnWitDataSent(byte[] data, int offset, int length);
}
}

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