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,174 @@
/*
* 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;
using Meta.WitAi.Data;
using Meta.WitAi.Interfaces;
using UnityEngine;
public class AudioClipAudioSource : MonoBehaviour, IAudioInputSource
{
[SerializeField] private AudioSource _audioSource;
[SerializeField] private List<AudioClip> _audioClips;
[Tooltip("If true, the associated clips will be played again from the beginning with multiple requests after the clip queue has been exhausted.")]
[SerializeField] private bool _loopRequests;
private bool _isRecording;
private Queue<int> _audioQueue = new Queue<int>();
private int clipIndex = 0;
private float[] activeClip;
private int activeClipIndex;
private float[] activeClipBuffer = new float[0];
private List<float[]> clipData = new List<float[]>();
private void Start()
{
foreach (var clip in _audioClips)
{
AddClipData(clip);
VLog.D($"Added {clip.name} to queue");
}
}
private void SendSample(float[] sample)
{
var len = Math.Min(_audioQueue.Dequeue(), activeClip.Length - activeClipIndex);
try
{
VLog.D("Sending length: " + len);
activeClipBuffer = new float[len];
Array.Copy(activeClip, activeClipIndex, activeClipBuffer, 0, len);
activeClipIndex += len;
}
catch (ArgumentException e)
{
VLog.D(e);
}
var max = float.MinValue;
foreach (var f in activeClipBuffer)
{
max = Mathf.Max(f, max);
}
OnSampleReady?.Invoke(activeClipBuffer.Length, activeClipBuffer, max);
}
public event Action OnStartRecording;
public event Action OnStartRecordingFailed;
public event Action<int, float[], float> OnSampleReady;
public event Action OnStopRecording;
public void StartRecording(int sampleLen)
{
if (_isRecording)
{
OnStartRecordingFailed?.Invoke();
return;
}
_isRecording = true;
if (clipIndex >= _audioClips.Count && _loopRequests)
{
clipIndex = 0;
}
if (clipIndex < _audioClips.Count)
{
activeClip = clipData[clipIndex];
activeClipIndex = 0;
VLog.D($"Starting clip {clipIndex}");
_isRecording = true;
VLog.D($"Playing {_audioClips[clipIndex].name}");
_audioSource.PlayOneShot(_audioClips[clipIndex]);
StartCoroutine(ProcessClip(_audioClips[clipIndex], clipData[clipIndex]));
OnStartRecording?.Invoke();
}
else
{
OnStartRecordingFailed?.Invoke();
}
}
private IEnumerator ProcessClip(AudioClip clip, float[] clipData)
{
int chunkSize = 0;
for (int index = 0; index < clipData.Length; index += chunkSize)
{
chunkSize = (int) (16000 * Time.deltaTime);
int len = Math.Min(chunkSize, clipData.Length - index);
var data = new float[chunkSize];
Array.Copy(clipData, index, data, 0, len);
var max = float.MinValue;
foreach (var f in data)
{
max = Mathf.Max(f, max);
}
VLog.D($"Sending {index}/{clipData.Length} [{data.Length}] samples with a max volume of {max}");
OnSampleReady?.Invoke(data.Length, data, max);
yield return null;
}
StopRecording();
clipIndex++;
}
public void StopRecording()
{
_isRecording = false;
OnStopRecording?.Invoke();
}
public bool IsRecording => _isRecording;
public AudioEncoding AudioEncoding => new AudioEncoding();
public bool IsInputAvailable => true;
public void CheckForInput()
{
}
public bool SetActiveClip(string clipName)
{
int index = _audioClips.FindIndex(0, (AudioClip clip) => {
if (clip.name == clipName)
{
return true;
}
return false;
});
if (index < 0 || index >= _audioClips.Count)
{
VLog.D($"Couldn't find clip {clipName}");
return false;
}
clipIndex = index;
return true;
}
public void AddClip(AudioClip clip)
{
_audioClips.Add(clip);
AddClipData(clip);
VLog.D($"Clip added {clip.name}");
}
private void AddClipData(AudioClip clip)
{
var samples = new float[clip.samples];
clip.GetData(samples, 0);
clipData.Add(samples);
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 4b79645f07e0044b397f62aa9d6cbce7
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.Collections.Generic;
namespace Meta.WitAi.Utilities
{
public class DictionaryList<T, U>
{
private Dictionary<T, List<U>> dictionary = new Dictionary<T, List<U>>();
public void Add(T key, U value)
{
if (!TryGetValue(key, out var values))
{
dictionary[key] = values;
}
values.Add(value);
}
public void RemoveAt(T key, int index)
{
if (TryGetValue(key, out var values)) values.RemoveAt(index);
}
public void Remove(T key, U value)
{
if (TryGetValue(key, out var values)) values.Remove(value);
}
#region Getters
public List<U> this[T key]
{
get
{
List<U> values;
if (!TryGetValue(key, out values))
{
values = new List<U>();
dictionary[key] = values;
}
return values;
}
set => dictionary[key] = value;
}
public bool TryGetValue(T key, out List<U> values)
{
if (!dictionary.TryGetValue(key, out values))
{
values = new List<U>();
return false;
}
return true;
}
#endregion
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: bdab2e04b3d44a0c8abfa43a51c2dba6
timeCreated: 1646061057
@@ -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 UnityEngine;
namespace Meta.WitAi.Utilities
{
public class DynamicRangeAttribute : PropertyAttribute
{
public string RangeProperty { get; private set; }
public float DefaultMin { get; private set; }
public float DefaultMax { get; private set; }
public DynamicRangeAttribute(string rangeProperty, float defaultMin = float.MinValue, float defaultMax = float.MaxValue)
{
DefaultMin = defaultMin;
DefaultMax = defaultMax;
RangeProperty = rangeProperty;
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ebe9cc040d3f14698b96d8e5d7155c44
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.Attributes;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.Serialization;
using Utilities;
namespace Meta.WitAi.Utilities
{
[AddComponentMenu("Wit.ai/Utilities/Conversions/Float to String")]
public class FloatToStringEvent : MonoBehaviour
{
[FormerlySerializedAs("format")]
[Tooltip("The format value to be used on the float")]
[SerializeField] private string _floatFormat;
[Tooltip("The format of the string itself. {0} will represent the float value provided")]
[SerializeField] private string _stringFormat;
[Space(WitRuntimeStyles.HeaderPaddingTop)]
[TooltipBox("Triggered when ConvertFloatToString(float) is called. The string in this event will be formatted based on the format fields.")]
[SerializeField] private StringEvent onFloatToString = new StringEvent();
/// <summary>
/// Converts a float to a string using the component format values and emits an onFloatToString event.
/// </summary>
/// <param name="value"></param>
public void ConvertFloatToString(float value)
{
string floatStringValue;
if (string.IsNullOrEmpty(_floatFormat))
{
floatStringValue = value.ToString();
}
else
{
floatStringValue = value.ToString(_floatFormat);
}
if (string.IsNullOrEmpty(_stringFormat))
{
onFloatToString?.Invoke(floatStringValue);
}
else
{
onFloatToString?.Invoke(string.Format(_stringFormat, floatStringValue));
}
}
}
[Serializable]
public class StringEvent : UnityEvent<string> {}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: c63293fa01c74b159135d721528a5c47
timeCreated: 1626731148
@@ -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 System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
namespace Meta.WitAi.Utilities
{
public static class GameObjectSearchUtility
{
/// <summary>
/// Finds the first available scene scripts of a specific object type
/// </summary>
/// <param name="includeInactive">Whether inactive GameObjects should be searched</param>
/// <typeparam name="T">Script type being searched</typeparam>
/// <returns>The first found script matching the specified type</returns>
public static T FindSceneObject<T>(bool includeInactive = true) where T : UnityEngine.Object
{
T[] results = FindSceneObjects<T>(includeInactive, true);
return results == null || results.Length == 0 ? null : results[0];
}
/// <summary>
/// Finds all scene scripts of a specific object type
/// </summary>
/// <param name="includeInactive">Whether inactive GameObjects should be searched</param>
/// <param name="returnImmediately">Whether the method should return as soon as a matching script is found</param>
/// <typeparam name="T">Script type being searched</typeparam>
/// <returns>All scripts matching the specified type</returns>
public static T[] FindSceneObjects<T>(bool includeInactive = true, bool returnImmediately = false) where T : UnityEngine.Object
{
// Use default functionality
if (!includeInactive)
{
return GameObject.FindObjectsOfType<T>();
}
// Get results
List<T> results = new List<T>();
// Iterate loaded scenes
for (int s = 0; s < SceneManager.sceneCount; s++)
{
// Iterate root
foreach (var rootGameObject in SceneManager.GetSceneAt(s).GetRootGameObjects())
{
T[] children = rootGameObject.GetComponentsInChildren<T>(includeInactive);
if (children != null && children.Length > 0)
{
results.AddRange(children);
if (returnImmediately)
{
return results.ToArray();
}
}
}
}
// Return all
return results.ToArray();
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 56bfa5a57ef40774c8506e7cebc7c154
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,96 @@
/*
* 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.IO;
using UnityEngine;
namespace Meta.WitAi.Utilities
{
public static class IOUtility
{
// Log error
private static void LogError(string error)
{
VLog.E($"IO Utility - {error}");
}
/// <summary>
/// Creates a directory recursively if desired and returns true if successful
/// </summary>
/// <param name="directoryPath">The directory to be created</param>
/// <param name="recursively">Will traverse parent directories if needed</param>
/// <returns>Returns true if the directory exists</returns>
public static bool CreateDirectory(string directoryPath, bool recursively = true)
{
// Null
if (string.IsNullOrEmpty(directoryPath))
{
return false;
}
// Already exists
if (Directory.Exists(directoryPath))
{
return true;
}
// Check parent
if (recursively)
{
string parentDirectoryPath = Path.GetDirectoryName(directoryPath);
if (!string.IsNullOrEmpty(parentDirectoryPath) && !CreateDirectory(parentDirectoryPath, true))
{
return false;
}
}
try
{
Directory.CreateDirectory(directoryPath);
}
catch (Exception e)
{
LogError($"Create Directory Exception\nDirectory Path: {directoryPath}\n{e}");
return false;
}
// Successfully created
return true;
}
/// <summary>
/// Deletes a directory and returns true if the directory no longer exists
/// </summary>
/// <param name="directoryPath">The directory to be created</param>
/// <param name="forceIfFilled">Whether to force a deletion if the directory contains contents</param>
/// <returns>Returns true if the directory does not exist</returns>
public static bool DeleteDirectory(string directoryPath, bool forceIfFilled = true)
{
// Already gone
if (!Directory.Exists(directoryPath))
{
return true;
}
try
{
Directory.Delete(directoryPath, forceIfFilled);
}
catch (Exception e)
{
LogError($"Delete Directory Exception\nDirectory Path: {directoryPath}\n{e}");
return false;
}
// Successfully deleted
return true;
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 61d1f1da493566e4db1372a37e97fc02
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 Meta.WitAi.Attributes;
using UnityEngine;
using Utilities;
namespace Meta.WitAi.Utilities
{
[AddComponentMenu("Wit.ai/Utilities/Conversions/String to String")]
public class StringToStringEvent : MonoBehaviour
{
[Tooltip("The string format string that will be used to reformat input strings. Ex: I don't know how to respond to {0}")]
[SerializeField] private string _format;
[Space(WitRuntimeStyles.HeaderPaddingTop)]
[TooltipBox("Triggered when FormatString(float) is called. The string in this event will be formatted based on the format field.")]
[SerializeField] public StringEvent onStringEvent = new StringEvent();
/// <summary>
/// Trigger an onStringEvent with a provided format.
/// </summary>
/// <param name="format">The string format to use in the event</param>
/// <param name="value">The value that will get populated in {0} in the format string.</param>
public void FormatString(string format, string value)
{
if (string.IsNullOrEmpty(format))
{
onStringEvent?.Invoke(value);
}
else
{
onStringEvent?.Invoke(string.Format(format, value));
}
}
/// <summary>
/// Trigger an onStringEvent with the built in format value
/// </summary>
/// <param name="value">The text to insert into {0} in the format value.</param>
public void FormatString(string value)
{
FormatString(_format, value);
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: cba129c809a34fc5a35b03548ee4efc7
timeCreated: 1680109375
@@ -0,0 +1,104 @@
/*
* 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.Data;
using UnityEngine;
namespace Meta.WitAi.Lib
{
/// <summary>
/// Applies the current Voice Mic mic output to an audio
/// source clip for use with external lip sync scripts such
/// as OVRAvatarLipSyncContext.
/// </summary>
public class VoiceLipSyncMic : MonoBehaviour
{
[Tooltip("Audio desired sample size for lipsync. The mic frequency will be adjusted to match this.")]
public int AudioSampleRate = 48000;
[Tooltip("Manual specification of Audio Source. Default will use any attached to the same object.")]
public AudioSource AudioSource;
// Obtain audio source & generate clip
private void Awake()
{
// Setup audio source
if (!AudioSource)
{
AudioSource = GetComponent<AudioSource>();
if (!AudioSource)
{
AudioSource = gameObject.AddComponent<AudioSource>();
}
}
AudioSource.loop = true;
AudioSource.playOnAwake = false;
if (AudioSource.isPlaying)
{
AudioSource.Stop();
}
// Get mic from audio buffer & set sample rate
if (AudioBuffer.Instance?.MicInput is Mic mic)
{
mic.AudioClipSampleRate = AudioSampleRate;
}
else
{
Debug.LogError("VoiceMicLipSync only works with Mic script.");
}
}
// Enable audio buffer recording
private void OnEnable()
{
// Get buffer
AudioBuffer buffer = AudioBuffer.Instance;
if (buffer == null)
{
return;
}
// Get mic from audio buffer & get audio clip
if (AudioBuffer.Instance?.MicInput is Mic mic)
{
AudioSource.clip = mic.AudioClip;
}
buffer.Events.OnSampleReady += OnMicSampleReady;
buffer.StartRecording(this);
}
// Begin playback if possible
private void OnMicSampleReady(RingBuffer<byte>.Marker marker, float levelMax)
{
if (!AudioSource.isPlaying && AudioSource.clip != null)
{
AudioSource.Play();
}
}
// Stop audio buffer recording
private void OnDisable()
{
// Stop playback
if (AudioSource.isPlaying)
{
AudioSource.Stop();
}
AudioSource.clip = null;
// Breakdown buffer
AudioBuffer buffer = AudioBuffer.Instance;
if (buffer == null)
{
return;
}
buffer.StopRecording(this);
buffer.Events.OnSampleReady -= OnMicSampleReady;
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 69e476f7fc7baaf46b09dc40dbfecd38
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,90 @@
/*
* 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;
#if UNITY_EDITOR
using UnityEditor;
#endif
namespace Meta.WitAi.Utilities
{
[Serializable]
public struct VoiceServiceReference
{
[SerializeField] internal VoiceService voiceService;
public VoiceService VoiceService
{
get
{
if (!voiceService)
{
VoiceService[] services = Resources.FindObjectsOfTypeAll<VoiceService>();
if (services != null)
{
// Set as first instance that isn't a prefab
voiceService = Array.Find(services, (o) => o.gameObject.scene.rootCount != 0);
}
}
return voiceService;
}
}
}
#if UNITY_EDITOR
[CustomPropertyDrawer(typeof(VoiceServiceReference))]
public class VoiceServiceReferenceDrawer : PropertyDrawer
{
public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
{
return EditorGUIUtility.singleLineHeight;
}
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
var refProp = property.FindPropertyRelative("voiceService");
var reference = refProp.objectReferenceValue as VoiceService;
var voiceServices = GameObject.FindObjectsOfType<VoiceService>();
var voiceServiceNames = new string[voiceServices.Length + 1];
int index = 0;
voiceServiceNames[0] = "Autodetect";
if (voiceServices.Length == 1)
{
voiceServiceNames[0] = $"{voiceServiceNames[0]} - {voiceServices[0].name}";
}
for (int i = 0; i < voiceServices.Length; i++)
{
voiceServiceNames[i + 1] = voiceServices[i].name;
if (voiceServices[i] == reference)
{
index = i + 1;
}
}
EditorGUI.BeginProperty(position, label, property);
var updatedIndex = EditorGUI.Popup(position, index, voiceServiceNames);
if (index != updatedIndex)
{
if (updatedIndex > 0)
{
refProp.objectReferenceValue = voiceServices[updatedIndex - 1];
}
else
{
refProp.objectReferenceValue = null;
}
property.serializedObject.ApplyModifiedProperties();
}
EditorGUI.EndProperty();
}
}
#endif
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b69aa53204096d540b373b139a54546b
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.
*/
namespace Utilities
{
public class WitRuntimeStyles
{
// Header padding
public const float HeaderPaddingTop = 8f;
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: c91c720c1ebf4a51b8a3f173beb23f46
timeCreated: 1680201046