Permalink
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
250 lines (208 sloc) 8.35 KB
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See LICENSE in the project root for license information.
using System;
using UnityEngine;
#if UNITY_WSA || UNITY_STANDALONE_WIN
using UnityEngine.Windows.Speech;
#endif
namespace HoloToolkit.Unity.InputModule
{
/// <summary>
/// SpeechInputSource allows you to specify keywords and methods in the Unity
/// Inspector, instead of registering them explicitly in code.
/// This also includes a setting to either automatically start the
/// keyword recognizer or allow your code to start it.
///
/// IMPORTANT: Please make sure to add the microphone capability in your app, in Unity under
/// Edit -> Project Settings -> Player -> Settings for Windows Store -> Publishing Settings -> Capabilities
/// or in your Visual Studio Package.appxmanifest capabilities.
/// </summary>
public partial class SpeechInputSource : BaseInputSource
{
/// <summary>
/// Keywords are persistent across all scenes. This Speech Input Source instance will not be destroyed when loading a new scene.
/// </summary>
[Tooltip("Keywords are persistent across all scenes. This Speech Input Source instance will not be destroyed when loading a new scene.")]
public bool PersistentKeywords;
// This enumeration gives the manager two different ways to handle the recognizer. Both will
// set up the recognizer and add all keywords. The first causes the recognizer to start
// immediately. The second allows the recognizer to be manually started at a later time.
public enum RecognizerStartBehavior { AutoStart, ManualStart }
[Tooltip("Whether the recognizer should be activated on start.")]
public RecognizerStartBehavior RecognizerStart;
[Tooltip("The keywords to be recognized and optional keyboard shortcuts.")]
public KeywordAndKeyCode[] Keywords;
#if UNITY_WSA || UNITY_STANDALONE_WIN
[Tooltip("The confidence level for the keyword recognizer.")]
// The serialized data of this field will be lost when switching between platforms and re-serializing this class.
[SerializeField]
private ConfidenceLevel recognitionConfidenceLevel = ConfidenceLevel.Medium;
private KeywordRecognizer keywordRecognizer;
#region Unity Methods
protected virtual void Start()
{
if (PersistentKeywords)
{
gameObject.DontDestroyOnLoad();
}
int keywordCount = Keywords.Length;
if (keywordCount > 0)
{
var keywords = new string[keywordCount];
for (int index = 0; index < keywordCount; index++)
{
keywords[index] = Keywords[index].Keyword;
}
keywordRecognizer = new KeywordRecognizer(keywords, recognitionConfidenceLevel);
keywordRecognizer.OnPhraseRecognized += KeywordRecognizer_OnPhraseRecognized;
if (RecognizerStart == RecognizerStartBehavior.AutoStart)
{
keywordRecognizer.Start();
}
}
else
{
Debug.LogError("Must have at least one keyword specified in the Inspector on " + gameObject.name + ".");
}
}
protected virtual void Update()
{
if (keywordRecognizer != null && keywordRecognizer.IsRunning)
{
ProcessKeyBindings();
}
}
protected virtual void OnDestroy()
{
if (keywordRecognizer != null)
{
StopKeywordRecognizer();
keywordRecognizer.OnPhraseRecognized -= KeywordRecognizer_OnPhraseRecognized;
keywordRecognizer.Dispose();
}
}
protected virtual void OnDisable()
{
if (keywordRecognizer != null)
{
StopKeywordRecognizer();
}
}
protected virtual void OnEnable()
{
if (keywordRecognizer != null && RecognizerStart == RecognizerStartBehavior.AutoStart)
{
StartKeywordRecognizer();
}
}
#endregion // Unity Methods
#region Event Callbacks
private void KeywordRecognizer_OnPhraseRecognized(PhraseRecognizedEventArgs args)
{
OnPhraseRecognized(args.confidence, args.phraseDuration, args.phraseStartTime, args.semanticMeanings, args.text);
}
#endregion // Event Callbacks
/// <summary>
/// Make sure the keyword recognizer is off, then start it.
/// Otherwise, leave it alone because it's already in the desired state.
/// </summary>
public void StartKeywordRecognizer()
{
if (keywordRecognizer != null && !keywordRecognizer.IsRunning)
{
keywordRecognizer.Start();
}
}
private void ProcessKeyBindings()
{
for (int index = Keywords.Length; --index >= 0;)
{
if (Input.GetKeyDown(Keywords[index].KeyCode))
{
OnPhraseRecognized(recognitionConfidenceLevel, TimeSpan.Zero, DateTime.Now, null, Keywords[index].Keyword);
}
}
}
/// <summary>
/// Make sure the keyword recognizer is on, then stop it.
/// Otherwise, leave it alone because it's already in the desired state.
/// </summary>
public void StopKeywordRecognizer()
{
if (keywordRecognizer != null && keywordRecognizer.IsRunning)
{
keywordRecognizer.Stop();
}
}
protected void OnPhraseRecognized(ConfidenceLevel confidence, TimeSpan phraseDuration, DateTime phraseStartTime, SemanticMeaning[] semanticMeanings, string text)
{
InputManager.Instance.RaiseSpeechKeywordPhraseRecognized(this, 0, confidence, phraseDuration, phraseStartTime, semanticMeanings, text);
}
#endif
#region Base Input Source Methods
public override bool TryGetSourceKind(uint sourceId, out InteractionSourceInfo sourceKind)
{
sourceKind = InteractionSourceInfo.Voice;
return true;
}
public override bool TryGetPointerPosition(uint sourceId, out Vector3 position)
{
position = Vector3.zero;
return false;
}
public override bool TryGetPointerRotation(uint sourceId, out Quaternion rotation)
{
rotation = Quaternion.identity;
return false;
}
public override bool TryGetPointingRay(uint sourceId, out Ray pointingRay)
{
pointingRay = default(Ray);
return false;
}
public override bool TryGetGripPosition(uint sourceId, out Vector3 position)
{
position = Vector3.zero;
return false;
}
public override bool TryGetGripRotation(uint sourceId, out Quaternion rotation)
{
rotation = Quaternion.identity;
return false;
}
public override SupportedInputInfo GetSupportedInputInfo(uint sourceId)
{
return SupportedInputInfo.None;
}
public override bool TryGetThumbstick(uint sourceId, out bool isPressed, out Vector2 position)
{
isPressed = false;
position = Vector2.zero;
return false;
}
public override bool TryGetTouchpad(uint sourceId, out bool isPressed, out bool isTouched, out Vector2 position)
{
isPressed = false;
isTouched = false;
position = Vector2.zero;
return false;
}
public override bool TryGetSelect(uint sourceId, out bool isPressed, out double pressedAmount)
{
isPressed = false;
pressedAmount = 0.0;
return false;
}
public override bool TryGetGrasp(uint sourceId, out bool isPressed)
{
isPressed = false;
return false;
}
public override bool TryGetMenu(uint sourceId, out bool isPressed)
{
isPressed = false;
return false;
}
#endregion // Base Input Source Methods
}
}