mixedreality/com.microsoft.mixedreality..../SDK/Features/Input/Handlers/SpeechInputHandler.cs

156 lines
6.1 KiB
C#

// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using Microsoft.MixedReality.Toolkit.UI;
using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
namespace Microsoft.MixedReality.Toolkit.Input
{
/// <summary>
/// This component handles the speech input events raised form the <see cref="IMixedRealityInputSystem"/>.
/// </summary>
[DisallowMultipleComponent]
[AddComponentMenu("Scripts/MRTK/SDK/SpeechInputHandler")]
public class SpeechInputHandler : BaseInputHandler, IMixedRealitySpeechHandler
{
/// <summary>
/// The keywords to be recognized and optional keyboard shortcuts.
/// </summary>
public KeywordAndResponse[] Keywords => keywords;
[SerializeField]
[Tooltip("The keywords to be recognized and optional keyboard shortcuts.")]
private KeywordAndResponse[] keywords = Array.Empty<KeywordAndResponse>();
[SerializeField]
[Tooltip("Keywords are persistent across all scenes. This Speech Input Handler instance will not be destroyed when loading a new scene.")]
private bool persistentKeywords = false;
[SerializeField]
[Tooltip("Assign SpeechConfirmationTooltip.prefab here to display confirmation label. Optional.")]
private SpeechConfirmationTooltip speechConfirmationTooltipPrefab = null;
/// <summary>
/// Tooltip prefab used to display confirmation label. Optional.
/// </summary>
public SpeechConfirmationTooltip SpeechConfirmationTooltipPrefab
{
get { return speechConfirmationTooltipPrefab; }
set { speechConfirmationTooltipPrefab = value; }
}
private SpeechConfirmationTooltip speechConfirmationTooltipPrefabInstance = null;
private readonly Dictionary<string, UnityEvent> responses = new Dictionary<string, UnityEvent>();
#region MonoBehaviour Implementation
protected override void Start()
{
base.Start();
if (persistentKeywords)
{
Debug.Assert(gameObject.transform.parent == null, "Persistent keyword GameObject must be at the root level of the scene hierarchy.");
DontDestroyOnLoad(gameObject);
}
// Convert the struct array into a dictionary, with the keywords and the methods as the values.
// This helps easily link the keyword recognized to the UnityEvent to be invoked.
int keywordCount = keywords.Length;
for (int index = 0; index < keywordCount; index++)
{
KeywordAndResponse keywordAndResponse = keywords[index];
string keyword = keywordAndResponse.Keyword.ToLower();
if (responses.ContainsKey(keyword))
{
Debug.LogError($"Duplicate keyword \'{keyword}\' specified in \'{gameObject.name}\'.");
}
else
{
responses.Add(keyword, keywordAndResponse.Response);
}
}
}
#endregion MonoBehaviour Implementation
#region InputSystemGlobalHandlerListener Implementation
protected override void RegisterHandlers()
{
CoreServices.InputSystem?.RegisterHandler<IMixedRealitySpeechHandler>(this);
}
protected override void UnregisterHandlers()
{
CoreServices.InputSystem?.UnregisterHandler<IMixedRealitySpeechHandler>(this);
}
#endregion InputSystemGlobalHandlerListener Implementation
#region SpeechInputHandler public methods
public void AddResponse(string keyword, UnityAction action)
{
string lowerKeyword = keyword.ToLower();
if (!responses.ContainsKey(lowerKeyword))
{
responses[lowerKeyword] = new UnityEvent();
}
responses[lowerKeyword].AddListener(action);
}
public void RemoveResponse(string keyword, UnityAction action)
{
string lowerKeyword = keyword.ToLower();
if (responses.ContainsKey(lowerKeyword))
{
responses[lowerKeyword].RemoveListener(action);
}
}
#endregion SpeechInputHandler public methods
#region IMixedRealitySpeechHandler Implementation
void IMixedRealitySpeechHandler.OnSpeechKeywordRecognized(SpeechEventData eventData)
{
// early return if the script is already heading for destruction
if (this.IsNull())
{
return;
}
// Check to make sure the recognized keyword exists in the methods dictionary, then invoke the corresponding method.
if (enabled && responses.TryGetValue(eventData.Command.Keyword.ToLower(), out UnityEvent keywordResponse))
{
keywordResponse.Invoke();
eventData.Use();
// Instantiate the Speech Confirmation Tooltip prefab if assigned
// Ignore "Select" keyword since OS will display the tooltip.
if (SpeechConfirmationTooltipPrefab != null
&& speechConfirmationTooltipPrefabInstance == null
&& !eventData.Command.Keyword.Equals("select", StringComparison.CurrentCultureIgnoreCase))
{
speechConfirmationTooltipPrefabInstance = Instantiate(speechConfirmationTooltipPrefab);
// Update the text label with recognized keyword
speechConfirmationTooltipPrefabInstance.SetText(eventData.Command.Keyword);
// Trigger animation of the Speech Confirmation Tooltip prefab
speechConfirmationTooltipPrefabInstance.TriggerConfirmedAnimation();
// Tooltip prefab instance will be destroyed on animation complete
// by DestroyOnAnimationComplete.cs in the SpeechConfirmationTooltip.prefab
}
}
}
#endregion IMixedRealitySpeechHandler Implementation
}
}