diff --git a/Assets/SpeechSDK.meta b/Assets/SpeechSDK.meta
new file mode 100644
index 00000000..e795f8bc
--- /dev/null
+++ b/Assets/SpeechSDK.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 70435ec2393e0a747907eb02931894e7
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/StreamingAssets.meta b/Assets/StreamingAssets.meta
new file mode 100644
index 00000000..3f782670
--- /dev/null
+++ b/Assets/StreamingAssets.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: d82af4f5510f642628ed817058679113
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/i5 Toolkit for Unity/Runtime/Speech Module.meta b/Assets/i5 Toolkit for Unity/Runtime/Speech Module.meta
new file mode 100644
index 00000000..03d81026
--- /dev/null
+++ b/Assets/i5 Toolkit for Unity/Runtime/Speech Module.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 498fc1e0aba47cb479ef3d06478de98d
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/i5 Toolkit for Unity/Runtime/Speech Module/README.md b/Assets/i5 Toolkit for Unity/Runtime/Speech Module/README.md
new file mode 100644
index 00000000..7e188db7
--- /dev/null
+++ b/Assets/i5 Toolkit for Unity/Runtime/Speech Module/README.md
@@ -0,0 +1,46 @@
+# Speech Module
+The speech module provides a extendable Speech-To-Text (Speech Recognition) and Text-To-Speech (Speech Synthesis) functionalities to Unity program on Windows Standalone, UWP, and Android platforms.
+
+## Components
+The speech module consists of three components: speech recognizers, speech synthesizers, and the `SpeechProvider`. You can implement your own recognizers and synthesizers if needed. All licenses of third-party libraries can be found in `THIRD-PARTY-NOTICES` under the `Third Party Plugins` folder.
+
+### Speech Recognizer
+All speech recognizers should implement the `ISpeechRecognizer` interface and realize its `StartRecordingAsync()` and `StopRecordingAsync()` methods, `Language` and `IsApplicable` properties, and `OnRecognitionResultReceived` event. It should also inherits ``MonoBehavior``. There are two pre-implemented instances in the module:
+ - `AzureSpeechRecognizer`, which uses the [Azure Congitive Service](https://azure.microsoft.com/en-us/services/cognitive-services/#overview) of Microsoft. It provides two modes: SingleShot and Continuous. In the SingleShot mode, it stops automatically when it detects silence, while in the Continuous mode, user must stop it manually. You needs a subscribtion key and service region to use the Azure service, and of course also internet conection. To use this recognizer, one needs the [SpeechSDK](https://docs.microsoft.com/en-us/azure/cognitive-services/speech-service/quickstarts/setup-platform?pivots=programming-language-csharp&tabs=windows%2Cubuntu%2Cunity%2Cjre%2Cmaven%2Cbrowser%2Cmac%2Cpypi).
+ - `NativeSpeechRecognizer`, which is neural-network based and can run offline on the device. It uses the [Vosk](https://alphacephei.com/vosk/index) library. Since it is neural-network based, one must specify neural-network models. They can be downloaded [here](https://alphacephei.com/vosk/models). It is suggested to download the small models, which are typically around 40 to 50MB. The models must be placed under `Assets/StreamingAssets` folder. On the inspector view, you need to specify the path of the model. If the model is placed under the `StreamingAssets` folder, the path is only the name of the model with ".zip" at the end. Basically you can add any language that has a model.
+
+The Speech SDK is not included in the package, you need to import them. See the next chapter for detail.
+### Speech Synthesizer
+All speech synthesizer should implement the `ISpeechSyntheizer` interface and realize its `StartSynthesizingAndSpeakingAsync()` method, `Language`, `IsApplicable` and `OutputForm` properties, and the `OnSynthesisResultReceived` event. The `OutputForm` has two values: `To Speaker` and `As Byte Stream`. Considering some APIs allow developers get the raw byte stream, we suggest you to use `As Byte Stream` if you need a spatial sound setting. In this case, the stream will be converted to an `Audio Clip` and played by an `Audio Source` on the attached `GameObject`. It is useful especially when you develop an agent, since the spatial sound make it more human-like. However, if the API that you want to call don't support this, you can then neglect this property, the `SpeechProvider` would take care of it.
+
+Again, there are two implemented instances:
+- `AzureSpeechSynthesizer`, which works similar to the `AzureSpeechRecognizer`.
+- `NativeSpeechSynthesizer`, which is an offline synthesizer. For Windows Standalone, it uses the [Microsoft Speech API (SAPI)](https://docs.microsoft.com/en-us/previous-versions/windows/desktop/ee125663(v=vs.85)) through the `interop.speechlib.dll`. For UWP, it uses the `Windows.Media.SpeechSynthesis` API through the `TextToSpeechUWP` script, which is a slightly modified version of the [`TextToSpeech` script in the `MixedRealityToolkit` of Microsoft](https://github.com/microsoft/MixedRealityToolkit-Unity/blob/main/Assets/MRTK/SDK/Features/Audio/TextToSpeech.cs). For Andorid, it uses the scripts and Android plugins from the GitHub repository [nir-takemi/UnityTTS](https://github.com/nir-takemi/UnityTTS). The native synthesizer only supports English on all platforms.
+
+All third-party libraries are not included, you need to import them. See the next chapter for detail.
+
+### Speech Provider
+The `SpeechProvider` requires at least one `ISpeechRecognizer` and one `ISpeechSynthesizer` on the same `GameObject`. The ones with higher priorities should be placed on top of other recognizers and synthesizers ON the inspector. It manages the `ISpeechRecognizer` and `ISpeechSynthesizer` and exposes their functionalities to users. So you only need to re-implement your own `ISpeechRecognizer` and `ISpeechSynthesizer` if needed, and don't need to care about the user-interaction aspects for each of them. There maybe also other settings (SerializeField) on the recognizers and synthesizers. In case of the selected recognizer or synthesizer is not applicable by checking their `IsApplicable` property, it would automatically find another applicable one. For synthesizers, it repeats the synthesis for the given text again. However, for recognizers, users must repeat what they said since the audio data is not buffered on the device. Moreover, by settings its properties and subcribing its events, the values would be propagated to all recognizers and synthesizers, so you don't need to set them one by one.
+
+## Import the Libraries for Pre-implemented Recognizers and Synthesizers
+In order to reduce the package size and not to force users to download external resources that they will even not use, the third-party libraries of the pre-implemented recognizers and synthesizers must be imported manually and some custom scripting symbols are defined for them, so that developers who use other modules in the i5 Toolkit but not the speech module don't need to download those resources. Note that the scripts themselves for the recognizers and synthesizers are contained in the package.
+
+To import the above introduced `AzureSpeechRecognizer/Synthesizer` and `NativeSpeechRecognizer/Synthesizer`, you need to navigate to the _i5 Toolkit - Import Speech Module_ on the menu bar at the top of Unity Editor. By clicking on a recognizer/synthesizer, an importer will automatically download all resources required and import them, then it will set the corresponding custom scripting symbol. All custom scripting symbols used here are:
+- I5_TOOLKIT_USE_AZURE_SPEECH_RECOGNIZER
+- I5_TOOLKIT_USE_AZURE_SPEECH_SYNTHESIZER
+- I5_TOOLKIT_USE_NATIVE_SPEECH_RECOGNIZER
+- I5_TOOLKIT_USE_NATIVE_SPEECH_SYNTHESIZER
+
+During the importing process, the importer will first download the required resources. A progress bar will be displayed on top of the editor window. However, you can still do other things during the download process. After that, the importer will import the downloaded package (except for the Vosk neural network models). There will be no pop-up window for importing so it will be done automatically. The imported packages are under `Assets/SpeechSDK` and `Assets/i5 Toolkit for Unity Speech Module Plugin` folder for Azure and native recognizers/synthesizers, respectively. After the importing, the package file will be deleted automatically.
+
+If you don't want to use a specific recognizer/synthesizer anymore, you should manually delete the corresponding scripting symbol from the custom scripting symbols after you deleted the third-party packages. You can find those symbols in _PlayerSettings - Other Settings - Scripting Define Symbols_.
+
+## What You Should Notice
+- Don't subscribe to the events or setting the properties of `SpeechProvider` in `Awake()`, since it might haven't initialized all recognizers and synthesizers due to the execution order of scripts.
+- Although some recognizer don't require a manually stop, e.g. the `AzureSpeechRecognizer` on the SingleShot mode, it is still a good choice to add a stop button on the UI and call the `StopRecordingAsync()` method of the `SpeechProvider`. When you implement such a recognizer, you can just leave the `StopRecordingAsync()` method empty.
+- If you are quite sure about your use cases and only want to use one recognizer/synthesizer, you can also omit the `SpeechProvider` and directly interact with the recognizer/synthesizer.
+- `PrimaryAudioOutputForm` and `Language` properties of `SpeechProvider` may not influence all recognizers or syntheisizers because they may not support them.
+- Although the methods for recognizing and synthesizing do have return values, they are not guaranteed to be meaningful. In facts, they are meaningless in most cases and should only be used for `await`. Instead, you should subscribe to the `OnRecognitionResultReceived` and `OnSynthesisResultReceived` events to deal with the results.
+- The neural-network models for the `NativeSpeechRecognizer` must be stored in the `StreamingAssets` folder, because they would be decompressed to the `PersistentDataPath` on the first start, so they need to be "as is" after build and shouldn't be compressed by Unity.
+- The `NativeSpeechSyntheizer` only works with `Mono` Backend and `.NET 4.x` API compatibility level on Windows Standalone. For Android, it must be built with an API level greater or equal than 21.
+- Although some third-party libraries contain plugins for other platforms, e.g. MacOS or IOS, they are removed, but you can still find them on the corresponding websites.
\ No newline at end of file
diff --git a/Assets/i5 Toolkit for Unity/Runtime/Speech Module/README.md.meta b/Assets/i5 Toolkit for Unity/Runtime/Speech Module/README.md.meta
new file mode 100644
index 00000000..7c5261f5
--- /dev/null
+++ b/Assets/i5 Toolkit for Unity/Runtime/Speech Module/README.md.meta
@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: 2aea3aa44aa4b9945a316d3f979cd2ca
+TextScriptImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/i5 Toolkit for Unity/Runtime/Speech Module/RocognizerAndSynthesizer.meta b/Assets/i5 Toolkit for Unity/Runtime/Speech Module/RocognizerAndSynthesizer.meta
new file mode 100644
index 00000000..90975c69
--- /dev/null
+++ b/Assets/i5 Toolkit for Unity/Runtime/Speech Module/RocognizerAndSynthesizer.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 3c4631c617515d749a141f223e253c7f
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/i5 Toolkit for Unity/Runtime/Speech Module/RocognizerAndSynthesizer/AzureSpeechRecognizer.cs b/Assets/i5 Toolkit for Unity/Runtime/Speech Module/RocognizerAndSynthesizer/AzureSpeechRecognizer.cs
new file mode 100644
index 00000000..ad42932b
--- /dev/null
+++ b/Assets/i5 Toolkit for Unity/Runtime/Speech Module/RocognizerAndSynthesizer/AzureSpeechRecognizer.cs
@@ -0,0 +1,171 @@
+using UnityEngine;
+using System.Threading.Tasks;
+using System;
+
+#if I5_TOOLKIT_USE_AZURE_SPEECH_RECOGNIZER
+using Microsoft.CognitiveServices.Speech;
+using Microsoft.CognitiveServices.Speech.Audio;
+#endif
+
+namespace i5.Toolkit.Core.SpeechModule
+{
+ ///
+ /// A speech recognizer (Speech-To-Text) using Azure Congitive Service and Speech SDK. See https://docs.microsoft.com/en-us/azure/cognitive-services/speech-service/index-speech-to-text
+ /// Need subscribtion key and service region.
+ ///
+ public class AzureSpeechRecognizer : MonoBehaviour, ISpeechRecognizer
+ {
+ [Tooltip("You can find your subscription key on Azure Portal.")]
+ [SerializeField] private string subscriptionKey;
+ [Tooltip("You can find your service region on Azure Portal.")]
+ [SerializeField] private string serviceRegion;
+ [Tooltip("The Single Shot mode receives a silence as a stop symbol and only supports audio up to 15 seconds. The Continuous mode requires a manually stop.")]
+ [SerializeField] private AzureRecognitionMode mode;
+
+#if I5_TOOLKIT_USE_AZURE_SPEECH_RECOGNIZER
+ private SpeechRecognizer speechRecognizer;
+ private SpeechConfig speechConfig;
+#endif
+ void Start() {
+#if I5_TOOLKIT_USE_AZURE_SPEECH_RECOGNIZER
+ speechConfig = SpeechConfig.FromSubscription(subscriptionKey, serviceRegion);
+#else
+ Debug.LogError("The required Speech SDK for AzureSpeechRecognizer cannot be found, or the I5_TOOLKIT_USE_AZURE_SPEECH_RECOGNIZER directive is not defined on current platform.");
+#endif
+ }
+
+ ///
+ /// Fires when the recognizer receives the result.
+ /// Please subscribe the OnRecognitionResultReceived event and avoid using the return value, because the result is empty for successful continuous recognition.
+ ///
+ public event Action OnRecognitionResultReceived;
+
+ ///
+ /// Supported Language. You may also add any language you want.
+ ///
+ public Language Language { get; set; }
+
+ /// Applicable if the component is enabled and there is an internet connection.
+ public bool IsApplicable => enabled && Application.internetReachability != NetworkReachability.NotReachable;
+
+ ///
+ /// Start recording and recognizing according to the recognition mode.
+ /// Please subscribe the OnRecognitionResultReceived event and avoid using the return value, because the result is empty for successful continuous recognition.
+ /// Note that the continuous recognition is running in another thread, so you cannot call Unity APIs in the OnRecognitionResultReceived event.
+ /// However, you can use a Queue to enable it.
+ ///
+ /// The result of the recognition.
+ public async Task StartRecordingAsync() {
+#if I5_TOOLKIT_USE_AZURE_SPEECH_RECOGNIZER
+ RecognitionResult result;
+ SourceLanguageConfig sourceLanguageConfig;
+ switch (Language) {
+ case Language.en_US:
+ sourceLanguageConfig = SourceLanguageConfig.FromLanguage("en-US");
+ break;
+ case Language.de_DE:
+ sourceLanguageConfig = SourceLanguageConfig.FromLanguage("de-DE");
+ break;
+ default:
+ sourceLanguageConfig = SourceLanguageConfig.FromLanguage("en-US");
+ break;
+ }
+ var audioConfig = AudioConfig.FromDefaultMicrophoneInput();
+ speechRecognizer = new SpeechRecognizer(speechConfig, sourceLanguageConfig, audioConfig);
+ if (mode == AzureRecognitionMode.SingleShot) {
+ result = await StartSingleShotRecordingAsync();
+ }
+ else {
+ result = await StartContinuousRecordingAsync();
+ }
+ return result;
+#else
+ await Task.Run(() => Debug.LogError("The required Speech SDK for AzureSpeechRecognizer cannot be found, or the I5_TOOLKIT_USE_AZURE_SPEECH_RECOGNIZER directive is not defined on current platform."));
+ return RecognitionResult.RequiredModulesNotFoundResult;
+#endif
+ }
+
+ ///
+ /// Stop recording. Only used for countinuous recognition.
+ ///
+ public async Task StopRecordingAsync() {
+#if I5_TOOLKIT_USE_AZURE_SPEECH_RECOGNIZER
+ if (mode == AzureRecognitionMode.Countinuous) {
+ await speechRecognizer.StopContinuousRecognitionAsync();
+ }
+#else
+ await Task.Run(() => Debug.LogError("The required Speech SDK for AzureSpeechRecognizer cannot be found, or the I5_TOOLKIT_USE_AZURE_SPEECH_RECOGNIZER directive is not defined on current platform."));
+#endif
+ }
+
+#if I5_TOOLKIT_USE_AZURE_SPEECH_RECOGNIZER
+ private async Task StartSingleShotRecordingAsync() {
+ Debug.Log("Speak into your microphone.");
+ var speechRecognitionResult = await speechRecognizer.RecognizeOnceAsync();
+ RecognitionResult result = ParseAzureRecognitionResult(speechRecognitionResult);
+ OnRecognitionResultReceived?.Invoke(result);
+ Debug.Log("Recognition Stopped.");
+ return result;
+ }
+
+ private async Task StartContinuousRecordingAsync() {
+ Debug.Log("Speak into your microphone. Stop recording when finished.");
+ RecognitionResult result = new RecognitionResult();
+ var stopRecognition = new TaskCompletionSource();
+ speechRecognizer.Recognizing += (s, e) => Debug.Log($"RECOGNIZING: Text={e.Result.Text}");
+
+ speechRecognizer.Recognized += (s, e) => OnRecognitionResultReceived?.Invoke(ParseAzureRecognitionResult(e.Result));
+
+ speechRecognizer.Canceled += (s, e) =>
+ {
+ stopRecognition.TrySetResult(0);
+ result = ParseAzureRecognitionResult(e.Result);
+ };
+
+ speechRecognizer.SessionStopped += (s, e) =>
+ {
+ Debug.Log("Recognition Stopped");
+ stopRecognition.TrySetResult(0);
+ };
+ await speechRecognizer.StartContinuousRecognitionAsync();
+ return result;
+
+ }
+
+ //Parse the SpeechRecognitionResult of Azure to our RecognitionResult.
+ private RecognitionResult ParseAzureRecognitionResult(SpeechRecognitionResult speechRecognitionResult) {
+ RecognitionResult result = new RecognitionResult();
+ switch (speechRecognitionResult.Reason) {
+ case ResultReason.RecognizedSpeech:
+ result.State = ResultState.Succeeded;
+ result.Text = speechRecognitionResult.Text;
+ result.Message = "Recognition Succeeded." + $" Text: {result.Text}";
+ break;
+ case ResultReason.NoMatch:
+ result.State = ResultState.NoMatch;
+ result.Message = "No Match: Speech could not be recognized.";
+ break;
+ case ResultReason.Canceled:
+ var cancellation = CancellationDetails.FromResult(speechRecognitionResult);
+ result.State = ResultState.Failed;
+ result.Message = $"Failed: Reason: {cancellation.Reason}";
+ if (cancellation.Reason == CancellationReason.Error) {
+ result.Message += $" AzureErrorCode={cancellation.ErrorCode}.\nDid you set the speech resource key and region values?";
+ }
+ break;
+ default:
+ result.Message = result.Text;
+ break;
+ }
+ Debug.Log(result.Message);
+ return result;
+ }
+#endif
+
+ private enum AzureRecognitionMode
+ {
+ SingleShot,
+ Countinuous
+ }
+ }
+}
diff --git a/Assets/i5 Toolkit for Unity/Runtime/Speech Module/RocognizerAndSynthesizer/AzureSpeechRecognizer.cs.meta b/Assets/i5 Toolkit for Unity/Runtime/Speech Module/RocognizerAndSynthesizer/AzureSpeechRecognizer.cs.meta
new file mode 100644
index 00000000..5f4ec275
--- /dev/null
+++ b/Assets/i5 Toolkit for Unity/Runtime/Speech Module/RocognizerAndSynthesizer/AzureSpeechRecognizer.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: f7088ab75fcc2674e841fd04bb53f009
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/i5 Toolkit for Unity/Runtime/Speech Module/RocognizerAndSynthesizer/AzureSpeechSynthesizer.cs b/Assets/i5 Toolkit for Unity/Runtime/Speech Module/RocognizerAndSynthesizer/AzureSpeechSynthesizer.cs
new file mode 100644
index 00000000..5904386a
--- /dev/null
+++ b/Assets/i5 Toolkit for Unity/Runtime/Speech Module/RocognizerAndSynthesizer/AzureSpeechSynthesizer.cs
@@ -0,0 +1,111 @@
+using System.Threading.Tasks;
+using UnityEngine;
+
+#if I5_TOOLKIT_USE_AZURE_SPEECH_SYNTHESIZER
+using Microsoft.CognitiveServices.Speech;
+#endif
+
+namespace i5.Toolkit.Core.SpeechModule
+{
+ ///
+ /// A speech synthesizer (Text-To-Speech) using Azure Congitive Service. See https://docs.microsoft.com/en-us/azure/cognitive-services/speech-service/index-text-to-speech.
+ /// Need subscribtion key and service region.
+ ///
+ public class AzureSpeechSynthesizer : MonoBehaviour, ISpeechSynthesizer
+ {
+ [Tooltip("You can find your subscription key on Azure Portal.")]
+ [SerializeField] private string subscriptionKey;
+ [Tooltip("You can find your service region on Azure Portal.")]
+ [SerializeField] private string serviceRegion;
+ [Tooltip("Specify the output form of the speech.")]
+ [SerializeField] private AudioDataOutputForm outputForm;
+#if I5_TOOLKIT_USE_AZURE_SPEECH_SYNTHESIZER
+ private SpeechSynthesizer speechSynthesizer;
+ private SpeechConfig speechConfig;
+#endif
+
+ ///
+ /// Fires when the synthesis is complete.
+ ///
+ public event System.Action OnSynthesisResultReceived;
+
+ public Language Language { get; set; }
+
+ ///
+ /// Applicable if the component is enabled and there is an internet connection.
+ ///
+ public bool IsApplicable => enabled && Application.internetReachability != NetworkReachability.NotReachable;
+
+ ///
+ /// Whether directly output to speaker or as byte stream. Speech provider will convert a byte stream to an audio clip and play it via an audio source as spaital sound.
+ ///
+ public AudioDataOutputForm OutputForm { get => outputForm; set => outputForm = value; }
+
+ void Start() {
+#if I5_TOOLKIT_USE_AZURE_SPEECH_SYNTHESIZER
+ speechConfig = SpeechConfig.FromSubscription(subscriptionKey, serviceRegion);
+#else
+ Debug.LogError("The required Speech SDK for AzureSpeechRecognizer cannot be found, or you didn't define the I5_TOOLKIT_USE_AZURE_SPEECH_SYNTHESIZER directive on your platform.");
+#endif
+ }
+
+ public async Task StartSynthesizingAndSpeakingAsync(string text) {
+#if I5_TOOLKIT_USE_AZURE_SPEECH_SYNTHESIZER
+ // The language of the voice that speaks.
+ switch (Language) {
+ case Language.en_US:
+ speechConfig.SpeechSynthesisVoiceName = "en-US-JennyNeural";
+ break;
+ case Language.de_DE:
+ speechConfig.SpeechSynthesisVoiceName = "de-DE-ConradNeural";
+ break;
+ default:
+ speechConfig.SpeechSynthesisVoiceName = "en-US-JennyNeural";
+ break;
+ }
+ if (outputForm == AudioDataOutputForm.ToSpeaker) {
+ //Directly speaking the synthesized audio
+ speechSynthesizer = new SpeechSynthesizer(speechConfig);
+ }
+ else {
+ //"Speak" the audio to a memory stream
+ speechSynthesizer = new SpeechSynthesizer(speechConfig, null);
+ }
+ var speechSynthesisResult = await speechSynthesizer.SpeakTextAsync(text);
+ SynthesisResult result = ParseAzureSynthesisResult(speechSynthesisResult, text);
+ OnSynthesisResultReceived?.Invoke(result);
+ return result;
+#else
+ await Task.Run(() => Debug.LogError("The required Speech SDK for AzureSpeechRecognizer cannot be found, or the I5_TOOLKIT_USE_AZURE_SPEECH_SYNTHESIZER directive is not defined on current platform."));
+ return SynthesisResult.RequiredModulesNotFoundResult;
+#endif
+ }
+#if I5_TOOLKIT_USE_AZURE_SPEECH_SYNTHESIZER
+ //Parse the SpeechSynthesisResult of Azure to our SynthesisResult.
+ private SynthesisResult ParseAzureSynthesisResult(SpeechSynthesisResult speechSynthesisResult, string inputText) {
+
+ SynthesisResult result = new SynthesisResult();
+ switch (speechSynthesisResult.Reason) {
+ case ResultReason.SynthesizingAudioCompleted:
+ result.State = ResultState.Succeeded;
+ result.AudioData = speechSynthesisResult.AudioData;
+ result.Message = $"Speech synthesized for text: {inputText}";
+ break;
+ case ResultReason.Canceled:
+ var cancellation = SpeechSynthesisCancellationDetails.FromResult(speechSynthesisResult);
+ result.State = ResultState.Failed;
+ result.Message = $"Failed: Reason: {cancellation.Reason}";
+ if (cancellation.Reason == CancellationReason.Error) {
+ result.Message += $" ErrorCode: {cancellation.ErrorCode}. Did you set the speech resource key and region values?";
+ }
+ break;
+ default:
+ break;
+ }
+ Debug.Log(result.Message);
+ return result;
+
+ }
+#endif
+ }
+}
diff --git a/Assets/i5 Toolkit for Unity/Runtime/Speech Module/RocognizerAndSynthesizer/AzureSpeechSynthesizer.cs.meta b/Assets/i5 Toolkit for Unity/Runtime/Speech Module/RocognizerAndSynthesizer/AzureSpeechSynthesizer.cs.meta
new file mode 100644
index 00000000..c011a6a4
--- /dev/null
+++ b/Assets/i5 Toolkit for Unity/Runtime/Speech Module/RocognizerAndSynthesizer/AzureSpeechSynthesizer.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 4400c8d107b381d419dfc91ce4d31c8d
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/i5 Toolkit for Unity/Runtime/Speech Module/RocognizerAndSynthesizer/ISpeechRecognizer.cs b/Assets/i5 Toolkit for Unity/Runtime/Speech Module/RocognizerAndSynthesizer/ISpeechRecognizer.cs
new file mode 100644
index 00000000..c0973bd8
--- /dev/null
+++ b/Assets/i5 Toolkit for Unity/Runtime/Speech Module/RocognizerAndSynthesizer/ISpeechRecognizer.cs
@@ -0,0 +1,45 @@
+using System.Collections;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using System;
+using UnityEngine;
+
+namespace i5.Toolkit.Core.SpeechModule
+{
+ public interface ISpeechRecognizer
+ {
+
+ ///
+ /// Preffered Language, one recognizer may not supports some of the selectable languages.
+ ///
+ Language Language { get; set; }
+
+ ///
+ /// Check if the recognizer is currently applicable. For example, cloud service are not applicable without internet connection.
+ ///
+ bool IsApplicable { get;}
+
+ ///
+ /// Start recording and return the result.
+ /// Please subscribe the OnRecognitionResultReceived event and avoid using the return value of StartRecordingAsync(),
+ /// because the result is empty for successful continuous recognition for some (Azure) recognizers.
+ ///
+ /// The recognition result, avoid using it.
+ Task StartRecordingAsync();
+
+ ///
+ /// Only use to stop recording but return nothing.
+ /// For some use cases that the recording stops automatically (e.g. when silence is detected), you can leave it empty.
+ ///
+ Task StopRecordingAsync();
+
+
+
+ ///
+ /// Fires when the recognizer receives the result.
+ /// Please subscribe the OnRecognitionResultReceived event and avoid using the return value of StartRecordingAsync(),
+ /// because the result is empty for successful continuous recognition for some (Azure) recognizers.
+ ///
+ event Action OnRecognitionResultReceived;
+ }
+}
diff --git a/Assets/i5 Toolkit for Unity/Runtime/Speech Module/RocognizerAndSynthesizer/ISpeechRecognizer.cs.meta b/Assets/i5 Toolkit for Unity/Runtime/Speech Module/RocognizerAndSynthesizer/ISpeechRecognizer.cs.meta
new file mode 100644
index 00000000..1a2ee883
--- /dev/null
+++ b/Assets/i5 Toolkit for Unity/Runtime/Speech Module/RocognizerAndSynthesizer/ISpeechRecognizer.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 4b5d0596f7398454d9016072b7937925
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/i5 Toolkit for Unity/Runtime/Speech Module/RocognizerAndSynthesizer/ISpeechSynthesizer.cs b/Assets/i5 Toolkit for Unity/Runtime/Speech Module/RocognizerAndSynthesizer/ISpeechSynthesizer.cs
new file mode 100644
index 00000000..09810a9e
--- /dev/null
+++ b/Assets/i5 Toolkit for Unity/Runtime/Speech Module/RocognizerAndSynthesizer/ISpeechSynthesizer.cs
@@ -0,0 +1,40 @@
+using System.Collections;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using System;
+using UnityEngine;
+
+namespace i5.Toolkit.Core.SpeechModule
+{
+ public interface ISpeechSynthesizer
+ {
+ ///
+ /// Preffered Language, one recognizer may not supports some of the selectable languages.
+ ///
+ Language Language { get; set; }
+
+ ///
+ /// If it is set to ToSpeaker, the synthesizer should speak the audio out directly when it gets the result.
+ /// If it is set to AsByteStream, the synthesizer should pass the raw audio data as Byte[] to the SynthesisResult for downstream modules, e.g. converting to an audio clip.
+ /// Except for compatibility consideration, as a byte stream allows the audio to be played by an Audio Source and especially as a 3D audio clip.
+ ///
+ AudioDataOutputForm OutputForm { get; set; }
+
+ ///
+ /// Check if the synthesizer is currently applicable. For example, cloud service are not applicable without internet connection.
+ /// It is encouraged to have a native solution which works without internet connection.
+ ///
+ bool IsApplicable { get; }
+
+ ///
+ /// Synthesizing the given text and speaking the audio out.
+ ///
+ /// The text to synthesize
+ Task StartSynthesizingAndSpeakingAsync(string text);
+
+ ///
+ /// Fires when the synthesis is complete.
+ ///
+ event Action OnSynthesisResultReceived;
+ }
+}
diff --git a/Assets/i5 Toolkit for Unity/Runtime/Speech Module/RocognizerAndSynthesizer/ISpeechSynthesizer.cs.meta b/Assets/i5 Toolkit for Unity/Runtime/Speech Module/RocognizerAndSynthesizer/ISpeechSynthesizer.cs.meta
new file mode 100644
index 00000000..8a5e2c73
--- /dev/null
+++ b/Assets/i5 Toolkit for Unity/Runtime/Speech Module/RocognizerAndSynthesizer/ISpeechSynthesizer.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: edb75e2d164c450498e066d723697897
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/i5 Toolkit for Unity/Runtime/Speech Module/RocognizerAndSynthesizer/NativeSpeechRecognizer.cs b/Assets/i5 Toolkit for Unity/Runtime/Speech Module/RocognizerAndSynthesizer/NativeSpeechRecognizer.cs
new file mode 100644
index 00000000..0d3e713e
--- /dev/null
+++ b/Assets/i5 Toolkit for Unity/Runtime/Speech Module/RocognizerAndSynthesizer/NativeSpeechRecognizer.cs
@@ -0,0 +1,134 @@
+using System;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using UnityEngine;
+
+#if I5_TOOLKIT_USE_NATIVE_SPEECH_RECOGNIZER
+using Vosk;
+#endif
+
+namespace i5.Toolkit.Core.SpeechModule
+{
+ ///
+ /// A cross-platform native speech recognizer using the Vosk library. https://alphacephei.com/vosk/index
+ /// The models can also be found on the website.
+ /// It may crash on the first time because of decompressing issue of the models.
+ ///
+ public class NativeSpeechRecognizer : MonoBehaviour, ISpeechRecognizer
+ {
+ [Tooltip("The path of the English model. It should be just the name with extension of the zip file, which located under the Assets/StreamingAssets folder.")]
+ [SerializeField] private string voskModelPathEn_US = "vosk-model-small-en-us-0.15.zip";
+ [Tooltip("The path of the German model. It should be just the name with extension of the zip file, which located under the Assets/StreamingAssets folder.")]
+ [SerializeField] private string voskModelPathDe_DE = "vosk-model-small-de-0.15.zip";
+#if I5_TOOLKIT_USE_NATIVE_SPEECH_RECOGNIZER
+ private VoskSpeechToText recognizer;
+#endif
+ ///
+ /// The supported Language, depends on the downloaded models. You can add any language and corresponding model on Vosk's website.
+ ///
+ public Language Language { get; set; }
+
+ ///
+ /// The native recognizer is applicable if the component is active.
+ ///
+ public bool IsApplicable => enabled && true;
+
+ public event Action OnRecognitionResultReceived;
+
+ // Start is called before the first frame update
+ void Start()
+ {
+#if I5_TOOLKIT_USE_NATIVE_SPEECH_RECOGNIZER
+ VoiceProcessor voiceProcesser = gameObject.AddComponent();
+ recognizer = gameObject.AddComponent();
+ recognizer.VoiceProcessor = voiceProcesser;
+ recognizer.OnTranscriptionResult += GetVoskRecognitionResult;
+ recognizer.OnStatusUpdated += OnStatusUpdated;
+ recognizer.MaxAlternatives = 1;
+ recognizer.MaxRecordLength = 15;
+ switch (Language) {
+ case Language.en_US:
+ recognizer.ModelPath = voskModelPathEn_US;
+ break;
+ case Language.de_DE:
+ recognizer.ModelPath = voskModelPathDe_DE;
+ break;
+ default:
+ recognizer.ModelPath = voskModelPathEn_US;
+ break;
+ }
+ recognizer.StartVoskStt();
+#else
+ Debug.LogError("The required Vosk models for NativeSpeechRecognizer cannot be found, or the I5_TOOLKIT_USE_NATIVE_SPEECH_RECOGNIZER directive is not defined on current platform.");
+#endif
+ }
+
+ ///
+ /// Start recording. There is no return value and we must subscribe the OnTranscriptionResultEvent.
+ ///
+ public Task StartRecordingAsync() {
+#if I5_TOOLKIT_USE_NATIVE_SPEECH_RECOGNIZER
+ recognizer.StartRecording();
+ RecognitionResult result = new RecognitionResult
+ {
+ State = ResultState.Succeeded,
+ Text = "The native recognizer (Vosk) has no return value, please subscribe to the OnRecognitionResultReceived event"
+ };
+ return Task.FromResult(result);
+#else
+ Debug.LogError("The required Vosk models for NativeSpeechRecognizer cannot be found, or the I5_TOOLKIT_USE_NATIVE_SPEECH_RECOGNIZER directive is not defined on current platform.");
+ return Task.FromResult(RecognitionResult.RequiredModulesNotFoundResult);
+#endif
+ }
+
+ ///
+ /// Stop recording and return a CompletedTask.
+ ///
+ public Task StopRecordingAsync() {
+#if I5_TOOLKIT_USE_NATIVE_SPEECH_RECOGNIZER
+ recognizer.StopRecording();
+#else
+ Debug.LogError("The required Vosk models for NativeSpeechRecognizer cannot be found, or the I5_TOOLKIT_USE_NATIVE_SPEECH_RECOGNIZER directive is not defined on current platform.");
+#endif
+ return Task.CompletedTask;
+ }
+
+#if I5_TOOLKIT_USE_NATIVE_SPEECH_RECOGNIZER
+ //Callback for the vosk recognizer.
+ private void GetVoskRecognitionResult(string recognizedText) {
+ RecognitionResult result = ParseVoskRecognitionResult(recognizedText);
+ OnRecognitionResultReceived?.Invoke(result);
+ }
+
+ private void OnStatusUpdated(string status) {
+ Debug.Log($"Vosk Recognizer: {status}");
+ }
+
+ private RecognitionResult ParseVoskRecognitionResult(string recognizedText) {
+ RecognitionResult result = new RecognitionResult();
+ result.State = ResultState.Succeeded;
+ RecognizedTextJson recognizedTextJson = JsonUtility.FromJson(recognizedText);
+ result.Text = recognizedTextJson.alternatives[0].text;
+ result.Message = "Recognition Succeeded." + $" Text={result.Text}";
+ Debug.Log(result.Message);
+ return result;
+ }
+#endif
+ //Json objects for parsing the recognition result.
+ [Serializable]
+ private class RecognizedTextJson
+ {
+ public List alternatives;
+ }
+
+ [Serializable]
+ private class AlternativesJson
+ {
+ public string confidence = null;
+ public string result = null;
+ public string text = null;
+ }
+
+ }
+
+}
diff --git a/Assets/i5 Toolkit for Unity/Runtime/Speech Module/RocognizerAndSynthesizer/NativeSpeechRecognizer.cs.meta b/Assets/i5 Toolkit for Unity/Runtime/Speech Module/RocognizerAndSynthesizer/NativeSpeechRecognizer.cs.meta
new file mode 100644
index 00000000..d91bee89
--- /dev/null
+++ b/Assets/i5 Toolkit for Unity/Runtime/Speech Module/RocognizerAndSynthesizer/NativeSpeechRecognizer.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 80f33da8c1fccbf4fa89d63e11f5480c
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/i5 Toolkit for Unity/Runtime/Speech Module/RocognizerAndSynthesizer/NativeSpeechSynthesizer.cs b/Assets/i5 Toolkit for Unity/Runtime/Speech Module/RocognizerAndSynthesizer/NativeSpeechSynthesizer.cs
new file mode 100644
index 00000000..ab9bb6b0
--- /dev/null
+++ b/Assets/i5 Toolkit for Unity/Runtime/Speech Module/RocognizerAndSynthesizer/NativeSpeechSynthesizer.cs
@@ -0,0 +1,111 @@
+using System.Threading.Tasks;
+using UnityEngine;
+using System;
+
+#if I5_TOOLKIT_USE_NATIVE_SPEECH_SYNTHESIZER
+using SpeechLib;
+using ylib.Services;
+using Microsoft.MixedReality.Toolkit.Audio;
+#endif
+namespace i5.Toolkit.Core.SpeechModule
+{
+ ///
+ /// A native speech synthesizer for Windows and Andorid, only supports English.
+ /// On Windows Standalone and Unity Editor (only with Mono backend and .NET 4.x), it uses the Microsoft SAPI, see https://docs.microsoft.com/en-us/previous-versions/windows/desktop/ee125663(v=vs.85).
+ /// On Universal Windows Platform, it uses the TextToSpeech from the HoloToolkit, see https://github.com/microsoft/MixedRealityToolkit-Unity/blob/htk_release/Assets/HoloToolkit/Utilities/Scripts/TextToSpeech.cs.
+ /// On Android, it uses the repository https://github.com/nir-takemi/UnityTTS, it also supports IOS but we discarded it.
+ ///
+ public class NativeSpeechSynthesizer : MonoBehaviour, ISpeechSynthesizer
+ {
+#if I5_TOOLKIT_USE_NATIVE_SPEECH_SYNTHESIZER
+
+#if UNITY_EDITOR || UNITY_STANDALONE_WIN
+ private SpVoiceClass windowsSynthesizer;
+#endif
+#if !UNITY_EDITOR && UNITY_WSA
+ private TextToSpeechUWP synthesizer;
+#endif
+#endif
+ ///
+ /// The native synthesizer only supports English on both Windows and Android.
+ /// The synthesizer's language on Windows actually depends on the system language, and we can only use the voice tokens that are installed on users' devices.
+ /// Since we don't know it for sure, we'd better only use English. All languages support English TTS.
+ /// The tokens on your windows computer can be found in the Registry Editor (regedit) under HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Speech\Voices\Tokens
+ ///
+ public Language Language { get; set; }
+
+ ///
+ /// The native synthesizer can only output the audio to speakers.
+ ///
+ public AudioDataOutputForm OutputForm { get; set; }
+
+ ///
+ /// The native recognizer is applicable if the component is active.
+ ///
+ public bool IsApplicable => enabled && true;
+
+ public event Action OnSynthesisResultReceived;
+
+ // Start is called before the first frame update
+ void Start() {
+#if I5_TOOLKIT_USE_NATIVE_SPEECH_SYNTHESIZER
+#if UNITY_EDITOR || UNITY_STANDALONE_WIN
+ windowsSynthesizer = new SpVoiceClass();
+#elif UNITY_WSA
+ synthesizer = gameObject.AddComponent();
+#elif UNITY_ANDROID
+ UnityTTS.Init();
+#endif
+#else
+ Debug.LogError("The required libraries for NativeSpeechRecognizer cannot be found, or you didn't define the I5_TOOLKIT_USE_NATIVE_SPEECH_SYNTHESIZER directive on your platform.");
+#endif
+ }
+
+ void OnDestroy() {
+#if I5_TOOLKIT_USE_NATIVE_SPEECH_SYNTHESIZER
+#if UNITY_EDITOR || UNITY_STANDALONE_WIN
+ windowsSynthesizer = null;
+#endif
+#endif
+ }
+
+ ///
+ /// Start TTS. Please subscribe the OnSynthesisResultReceived Event because the return value is meaningless.
+ ///
+ public Task StartSynthesizingAndSpeakingAsync(string text) {
+#if I5_TOOLKIT_USE_NATIVE_SPEECH_SYNTHESIZER
+ SynthesisResult result;
+#if UNITY_EDITOR || UNITY_STANDALONE_WIN
+ windowsSynthesizer.Speak(text, SpeechVoiceSpeakFlags.SVSFlagsAsync);
+#elif UNITY_WSA
+ //Only support output to stream. But directly play it, not via SpeechProvider.
+ synthesizer.StartSpeaking(text);
+#elif UNITY_ANDROID
+ //Only support output to speaker.
+ UnityTTS.Speech(text);
+#endif
+ result = ParseNativeSynthesisResult(text);
+ OnSynthesisResultReceived?.Invoke(result);
+ return Task.FromResult(result);
+#else
+ Debug.LogError("The required libraries for NativeSpeechRecognizer cannot be found, or you didn't define the I5_TOOLKIT_USE_NATIVE_SPEECH_SYNTHESIZER directive on your platform.");
+ return Task.FromResult(SynthesisResult.RequiredModulesNotFoundResult);
+#endif
+ }
+
+#if I5_TOOLKIT_USE_NATIVE_SPEECH_SYNTHESIZER
+ private SynthesisResult ParseNativeSynthesisResult(string text)
+ {
+ SynthesisResult result = new SynthesisResult();
+ result.State = ResultState.Succeeded;
+#if UNITY_EDITOR || UNITY_STANDALONE_WIN || UNITY_WSA
+ result.Message = $"Speech synthesized for text: {text} using Windows native synthesizer";
+#elif UNITY_ANDROID
+ result.Message = $"Speech synthesized for text: {text} using Android native synthesizer";
+#endif
+ Debug.Log(result.Message);
+ return result;
+ }
+#endif
+ }
+}
diff --git a/Assets/i5 Toolkit for Unity/Runtime/Speech Module/RocognizerAndSynthesizer/NativeSpeechSynthesizer.cs.meta b/Assets/i5 Toolkit for Unity/Runtime/Speech Module/RocognizerAndSynthesizer/NativeSpeechSynthesizer.cs.meta
new file mode 100644
index 00000000..823e0465
--- /dev/null
+++ b/Assets/i5 Toolkit for Unity/Runtime/Speech Module/RocognizerAndSynthesizer/NativeSpeechSynthesizer.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: e560cad3f15bcf44995a0dd54893f2a0
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/i5 Toolkit for Unity/Runtime/Speech Module/SpeechProvider.cs b/Assets/i5 Toolkit for Unity/Runtime/Speech Module/SpeechProvider.cs
new file mode 100644
index 00000000..87e3ed88
--- /dev/null
+++ b/Assets/i5 Toolkit for Unity/Runtime/Speech Module/SpeechProvider.cs
@@ -0,0 +1,235 @@
+using System.Collections;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using UnityEngine;
+using System;
+
+namespace i5.Toolkit.Core.SpeechModule
+{
+ ///
+ /// The SpeechProvider requires at least one ISpeechRecognizer and one ISpeechSynthesizer on the same gameObject.
+ /// The ones with higher priorities should be placed on top of other recognizers and synthesizers in the inspector.
+ /// It manages the ISpeechRecognizer and ISpeechSynthesizer and exposes their functionalities to users.
+ /// So developers only need to re-implement their own ISpeechRecognizer and ISpeechSynthesizer if needed, and don't need to care about the user-interaction aspects.
+ /// There maybe also other settings (SerializeField) on the recognizer and synthesizer.
+ ///
+ [RequireComponent(typeof(ISpeechRecognizer))]
+ [RequireComponent(typeof(ISpeechSynthesizer))]
+ public class SpeechProvider : MonoBehaviour
+ {
+ [Tooltip("Prefered Language, some might not be supported by some recognizers or synthesizers.")]
+ [SerializeField] private Language language;
+ [Tooltip("Set the primary audio data output form. It will be propagated to all synthesizers. But it may not have any effects due to synthesizer's implementation, " +
+ "e.g. some may not support byte stream as the output format. " +
+ "For \"AsByteStream\", the output will be converted to an audio clip and can be played by an Audio Source.")]
+ [SerializeField] private AudioDataOutputForm primaryAudioOutputForm;
+
+ private ISpeechRecognizer[] recognizers;
+ private ISpeechRecognizer currentRecognizer;
+ private ISpeechSynthesizer[] synthesizers;
+ private ISpeechSynthesizer currentSynthesizer;
+
+ private AudioSource audioSource;
+
+ ///
+ /// By subscribing, it distributed the subscribtion to all recognizers.
+ /// Don't subscribe in Awake();
+ ///
+ public event Action OnRecognitionResultReceived
+ {
+ add
+ {
+ foreach (var recognizer in recognizers) {
+ recognizer.OnRecognitionResultReceived += value;
+ }
+ }
+ remove
+ {
+ foreach (var recognizer in recognizers) {
+ recognizer.OnRecognitionResultReceived -= value;
+ }
+ }
+ }
+
+ ///
+ /// By subscribing, it distributed the subscribtion to all synthesizers.
+ /// Don't subscribe in Awake();
+ ///
+ public event Action OnSynthesisResultReceived
+ {
+ add
+ {
+ foreach(var synthesizer in synthesizers) {
+ synthesizer.OnSynthesisResultReceived += value;
+ }
+ }
+ remove
+ {
+ foreach(var synthesizer in synthesizers) {
+ synthesizer.OnSynthesisResultReceived -= value;
+ }
+ }
+ }
+
+ ///
+ /// Current Language. By setting this property, it sets the Language property of all recognizers and synthesizers.
+ /// Don't set in Awake();
+ ///
+ public Language Language
+ {
+ get => language;
+ set
+ {
+ language = value;
+ foreach(ISpeechRecognizer recognizer in recognizers) {
+ recognizer.Language = value;
+ }
+ foreach(ISpeechSynthesizer synthesizer in synthesizers) {
+ synthesizer.Language = value;
+ }
+ }
+ }
+
+ ///
+ /// Primary audio output form. By setting this property, it sets this property of all recognizers and synthesizers.
+ /// Don't set in Awake();
+ ///
+ public AudioDataOutputForm PrimaryAudioOutputForm
+ {
+ get => primaryAudioOutputForm;
+ set
+ {
+ primaryAudioOutputForm = value;
+ foreach (ISpeechSynthesizer synthesizer in synthesizers) {
+ synthesizer.OutputForm = value;
+ }
+ }
+ }
+
+ private void Awake() {
+ InitializeRecognizerAndSynthesizer();
+ }
+
+ // Start is called before the first frame update
+ void Start() {
+ //We just add a new one because even if the gameObject has already an AudioSource, it can be used in other purpose.
+ audioSource = gameObject.AddComponent();
+ audioSource.spatialBlend = 1;
+ }
+
+ ///
+ /// Start recording and recognizing the user's voice.
+ /// If the current recognizer is not applicable, it will try to switch to another applicable one but not start re-recognizing since the sound data is not buffered locally.
+ /// User may restart recording.
+ ///
+ public async Task StartRecordingAsync() {
+ RecognitionResult result = await currentRecognizer.StartRecordingAsync();
+ if(result.State == ResultState.Failed && !currentRecognizer.IsApplicable) {
+ SwitchToApplicableRecognizer();
+ }
+ return result;
+ }
+
+ public async Task StopRecordingAsync() {
+ await currentRecognizer.StopRecordingAsync();
+ }
+
+ ///
+ /// Synthesizing the given text and speaking the audio out.
+ /// If the current synthesizer is not applicable, it will try to switch to another applicable one and automatically restart synthesizing.
+ ///
+ ///
+ public async Task StartSynthesizingAndSpeakingAsync(string inputText) {
+ SynthesisResult result = await currentSynthesizer.StartSynthesizingAndSpeakingAsync(inputText);
+ if(result.State == ResultState.Failed && !currentSynthesizer.IsApplicable) {
+ if (SwitchToApplicableSynthesizer()) {
+ await currentSynthesizer.StartSynthesizingAndSpeakingAsync(inputText);
+ }
+ }
+ else {
+ //If the speech will not be played directly and the current synthesizer supports byte stream.
+ //If result.AudioData is null, then current synthesizer doesn't support AsByteStream and the audio should be played directly, so we don't create the audio clip.
+ if (currentSynthesizer.OutputForm == AudioDataOutputForm.AsByteStream && result.AudioData != null && result.AudioData.Length > 0) {
+ float[] clipData = BytesToFloat(result.AudioData);
+ audioSource.clip = AudioClip.Create("SynthesizedSpeech", 16000 * 10, 1, 16000, false);
+ audioSource.clip.SetData(clipData, 0);
+ audioSource.Play();
+ }
+ }
+ return result;
+ }
+
+ ///
+ /// Update all recognizers and try to find one applicable, return if any.
+ /// Can be called when an error occurs.
+ ///
+ /// If there is an applicable recognizer
+ public bool SwitchToApplicableRecognizer() {
+ Debug.Log("Current recognizer is not applicable, trying to switch to another applicable recognizer.");
+ foreach (ISpeechRecognizer recognizer in recognizers) {
+ if (recognizer != currentRecognizer && recognizer.IsApplicable) {
+ currentRecognizer = recognizer;
+ Debug.Log("Applicable alternative recognizer found. Please start the recording again.");
+ return true;
+ }
+ }
+ Debug.LogWarning("No applicable recognizer found.");
+ return false;
+ }
+
+ public bool SwitchToApplicableSynthesizer() {
+ Debug.Log("Current synthesizer is not applicable, trying to switch to another appicable synthesizer.");
+ foreach (ISpeechSynthesizer synthesizer in synthesizers) {
+ if (synthesizer != currentSynthesizer && synthesizer.IsApplicable) {
+ currentSynthesizer = synthesizer;
+ Debug.Log("Applicable alternative synthesizer found. Please try again.");
+ return true;
+ }
+ }
+ Debug.LogWarning("No applicable synthesizer found.");
+ return false;
+ }
+
+ #region Private Methods
+
+ //Initialize all recognizers and synthesizers and choose the applicables with highest priority.
+ private void InitializeRecognizerAndSynthesizer() {
+ recognizers = GetComponents();
+ foreach (ISpeechRecognizer speechRecognizer in recognizers) {
+ speechRecognizer.Language = Language;
+ if (speechRecognizer.IsApplicable) {
+ currentRecognizer = speechRecognizer;
+ break;
+ }
+ }
+ synthesizers = GetComponents();
+ foreach (ISpeechSynthesizer speechSynthesizer in synthesizers) {
+ speechSynthesizer.Language = Language;
+ speechSynthesizer.OutputForm = primaryAudioOutputForm;
+ if (speechSynthesizer.IsApplicable) {
+ currentSynthesizer = speechSynthesizer;
+ break;
+ }
+ }
+ }
+
+ // convert two bytes to one float in the range -1 to 1
+ private float BytesToFloat(byte firstByte, byte secondByte) {
+ // convert two bytes to one short (little endian)
+ short s = (short)((secondByte << 8) | firstByte);
+ // convert to range from -1 to (just below) 1
+ return s / 32768.0F;
+ }
+
+ private float[] BytesToFloat(byte[] byteStream) {
+ float[] soundData = new float[byteStream.Length / 2];
+ for (int i = 0; i < soundData.Length; i++) {
+ soundData[i] = BytesToFloat(byteStream[i * 2], byteStream[i * 2 + 1]);
+ }
+ return soundData;
+ }
+
+ #endregion
+ }
+
+}
diff --git a/Assets/i5 Toolkit for Unity/Runtime/Speech Module/SpeechProvider.cs.meta b/Assets/i5 Toolkit for Unity/Runtime/Speech Module/SpeechProvider.cs.meta
new file mode 100644
index 00000000..18b70586
--- /dev/null
+++ b/Assets/i5 Toolkit for Unity/Runtime/Speech Module/SpeechProvider.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 7f6b85ba30b09b442aa7c2779f2a2875
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/i5 Toolkit for Unity/Runtime/Speech Module/THIRD-PARTY-NOTICES.txt b/Assets/i5 Toolkit for Unity/Runtime/Speech Module/THIRD-PARTY-NOTICES.txt
new file mode 100644
index 00000000..a130363b
--- /dev/null
+++ b/Assets/i5 Toolkit for Unity/Runtime/Speech Module/THIRD-PARTY-NOTICES.txt
@@ -0,0 +1,629 @@
+Below are licenses for all third party open-source libraries/repositories
+that are used in the i5-Toolkit-for-Unity-Speech-Module-Plugin.
+
+In the event that we accidentally failed to list a required notice, please
+bring it to our attention by creating a pull-request or filing a github
+issue.
+
+1) License Notice for the Vosk library and models.
+-------------------------------------------------
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+
+2) License Notice for the DotNetZip Library
+--------------------------------------
+Microsoft Public License (Ms-PL)
+1. Definitions
+The terms "reproduce," "reproduction," "derivative works," and "distribution" have the
+same meaning here as under U.S. copyright law.
+
+A "contribution" is the original software, or any additions or changes to the software.
+
+A "contributor" is any person that distributes its contribution under this license.
+
+"Licensed patents" are a contributor's patent claims that read directly on its contribution.
+
+2. Grant of Rights
+
+(A) Copyright Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free copyright license to reproduce its contribution, prepare derivative works of its contribution, and distribute its contribution or any derivative works that you create.
+
+(B) Patent Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free license under its licensed patents to make, have made, use, sell, offer for sale, import, and/or otherwise dispose of its contribution in the software or derivative works of the contribution in the software.
+
+3. Conditions and Limitations
+
+(A) No Trademark License- This license does not grant you rights to use any contributors' name, logo, or trademarks.
+
+(B) If you bring a patent claim against any contributor over patents that you claim are infringed by the software, your patent license from such contributor to the software ends automatically.
+
+(C) If you distribute any portion of the software, you must retain all copyright, patent, trademark, and attribution notices that are present in the software.
+
+(D) If you distribute any portion of the software in source code form, you may do so only under this license by including a complete copy of this license with your distribution. If you distribute any portion of the software in compiled or object code form, you may only do so under a license that complies with this license.
+
+(E) The software is licensed "as-is." You bear the risk of using it. The contributors give no express warranties, guarantees or conditions. You may have additional consumer rights under your local laws which this license cannot change. To the extent permitted under your local laws, the contributors exclude the implied warranties of merchantability, fitness for a particular purpose and non-infringement.
+
+
+4) License Notice for the VoiceProcessor Script
+------------------------------------------
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+5) License Notice for the scripts in the Babilinski/vosk-stt-unity repository
+-----------------------------------------------------------------------------
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+6) License Notice for scripts in the nir-takemi/UnityTTS repository
+-------------------------------------------------------------------
+
+The MIT License (MIT)
+
+Copyright (c) 2012-2017 Markus Göbel (Bunny83)
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+7) License Notice for MixedRealityToolkit of Mircosoft
+--------------------------------------------------------------------------
+
+The MIT License (MIT)
+
+Copyright (c) 2012-2017 Markus Göbel (Bunny83)
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
\ No newline at end of file
diff --git a/Assets/i5 Toolkit for Unity/Runtime/Speech Module/THIRD-PARTY-NOTICES.txt.meta b/Assets/i5 Toolkit for Unity/Runtime/Speech Module/THIRD-PARTY-NOTICES.txt.meta
new file mode 100644
index 00000000..ff95f892
--- /dev/null
+++ b/Assets/i5 Toolkit for Unity/Runtime/Speech Module/THIRD-PARTY-NOTICES.txt.meta
@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: ec49e23566b1ce74ab393715b4c10364
+TextScriptImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/i5 Toolkit for Unity/Runtime/Speech Module/Utilities.cs b/Assets/i5 Toolkit for Unity/Runtime/Speech Module/Utilities.cs
new file mode 100644
index 00000000..34f44b5b
--- /dev/null
+++ b/Assets/i5 Toolkit for Unity/Runtime/Speech Module/Utilities.cs
@@ -0,0 +1,345 @@
+using System.Collections;
+using System.Collections.Generic;
+using UnityEngine;
+using UnityEditor;
+using UnityEngine.Networking;
+using System.IO;
+using i5.Toolkit.Core.Utilities.Async;
+using System.Threading.Tasks;
+
+namespace i5.Toolkit.Core.SpeechModule
+{
+ public enum Language
+ {
+ en_US,
+ de_DE
+ }
+ public enum ResultState
+ {
+ Succeeded,
+ NoMatch,
+ Failed
+ }
+
+ public enum AudioDataOutputForm {
+ ToSpeaker,
+ AsByteStream
+ }
+
+ public class RecognitionResult {
+ public ResultState State;
+ ///
+ /// The recognized text.
+ ///
+ public string Text;
+ ///
+ /// The success message or error message.
+ ///
+ public string Message;
+
+ public static RecognitionResult RequiredModulesNotFoundResult
+ {
+ get
+ {
+ RecognitionResult result = new RecognitionResult();
+ result.State = ResultState.Failed;
+ result.Text = "Required modules for the current Recognizer cannot be found, or corresponding directive is not set on the current platform.";
+ result.Message = "Required modules for the current Recognizer cannot be found, or corresponding directive is not set on the current platform.";
+ return result;
+ }
+ }
+ }
+
+ public class SynthesisResult {
+ public ResultState State;
+ ///
+ /// The synthesized audio in bytes, only assigned when the output form is "AsByteStream".
+ ///
+ public byte[] AudioData;
+ ///
+ /// The success message or error message.
+ ///
+ public string Message;
+
+ public static SynthesisResult RequiredModulesNotFoundResult
+ {
+ get
+ {
+ SynthesisResult result = new SynthesisResult();
+ result.State = ResultState.Failed;
+ result.Message = "Required modules for the current Recognizer cannot be found, or corresponding directive is not set on the current platform.";
+ return result;
+ }
+ }
+ }
+
+#if UNITY_EDITOR
+ public static class LibraryImporter {
+
+ private const string AzureRecognizerDefine = "I5_TOOLKIT_USE_AZURE_SPEECH_RECOGNIZER";
+ private const string AzureSynthesizerDefine = "I5_TOOLKIT_USE_AZURE_SPEECH_SYNTHESIZER";
+ private const string NativeRecognizerDefine = "I5_TOOLKIT_USE_NATIVE_SPEECH_RECOGNIZER";
+ private const string NativeSynthesizerDefine = "I5_TOOLKIT_USE_NATIVE_SPEECH_SYNTHESIZER";
+ private const string ProgressBarTitle = "i5 Toolkit Speech Module Importer";
+ private const string AzureSpeechSDKURL = "https://aka.ms/csspeech/unitypackage";
+ private const string NativeRecognizerAssemblyName = "i5.Toolkit.Core.Runtime.SpeechModulePlugin.NativeRecognizer";
+ private const string NativeRecognizerURL = "https://github.com/rwth-acis/i5-Toolkit-for-Unity-SpeechModulePlugin/releases/download/v1.0.0/NativeSpeechRecognizerPlugin.unitypackage";
+ private const string VoskEnglishModelURL = "https://alphacephei.com/vosk/models/vosk-model-small-en-us-0.15.zip";
+ private const string VoskEnglishModelName = "vosk-model-small-en-us-0.15.zip";
+ private const string VoskEnglishModelNameWithoutExtension = "vosk-model-small-en-us-0.15";
+ private const string VoskGermanModelURL = "https://alphacephei.com/vosk/models/vosk-model-small-de-0.15.zip";
+ private const string VoskGermanModelName = "vosk-model-small-de-0.15.zip";
+ private const string VoskGermanModelNameWithoutExtension = "vosk-model-small-de-0.15";
+ private const string NativeSynthesizerAssemblyName = "i5.Toolkit.Core.Runtime.SpeechModulePlugin.NativeSynthesizer";
+ private const string NativeSynthesizerURL = "https://github.com/rwth-acis/i5-Toolkit-for-Unity-SpeechModulePlugin/releases/download/v1.0.0/NativeSpeechSynthesizerPlugin.unitypackage";
+
+ public static List TargetPlatform = new List{BuildTargetGroup.Standalone, BuildTargetGroup.WSA, BuildTargetGroup.Android};
+
+ //We only need to check on one platform
+ private static bool AzureSpeechRecognizerImported
+ {
+ get
+ {
+ string define = PlayerSettings.GetScriptingDefineSymbolsForGroup(BuildTargetGroup.Standalone);
+ if (define.Contains(AzureRecognizerDefine)) {
+ Debug.Log($"{AzureRecognizerDefine} already defined.");
+ return true;
+ }
+ else {
+ return false;
+ }
+ }
+ }
+ private static bool AzureSpeechSynthesizerImported
+ {
+ get
+ {
+ string define = PlayerSettings.GetScriptingDefineSymbolsForGroup(BuildTargetGroup.Standalone);
+ if (define.Contains(AzureSynthesizerDefine)) {
+ Debug.Log($"{AzureSynthesizerDefine} already defined.");
+ return true;
+ }
+ else {
+ return false;
+ }
+ }
+ }
+ private static bool NativeSpeechRecognizerImported
+ {
+ get
+ {
+ string define = PlayerSettings.GetScriptingDefineSymbolsForGroup(BuildTargetGroup.Standalone);
+ if (define.Contains(NativeRecognizerDefine)) {
+ Debug.Log($"{NativeRecognizerDefine} already defined.");
+ return true;
+ }
+ else {
+ return false;
+ }
+ }
+ }
+ private static bool NativeSpeechSynthesizerImported
+ {
+ get
+ {
+ string define = PlayerSettings.GetScriptingDefineSymbolsForGroup(BuildTargetGroup.Standalone);
+ if (define.Contains(NativeSynthesizerDefine)) {
+ Debug.Log($"{NativeSynthesizerDefine} already defined.");
+ return true;
+ }
+ else {
+ return false;
+ }
+ }
+ }
+
+ [MenuItem("i5 Toolkit/Import Speech Module/Azure Recognizer")]
+ public static async void ImportAzureSpeechRecognizerAsync() {
+ if (!AzureSpeechRecognizerImported) {
+ string[] assetGUIDs = AssetDatabase.FindAssets("SpeechSDK");
+ if (assetGUIDs.Length > 0) {
+ Debug.Log($"Found Speech SDK in {AssetDatabase.GUIDToAssetPath(assetGUIDs[0])}, skip download.");
+ }
+ else {
+ //Download the speech SDK and show the progress
+ Debug.Log("Starting to download SpeechSDK...");
+ if (AssetDatabase.FindAssets($"StreamingAssets").Length == 0) {
+ AssetDatabase.CreateFolder("Assets", "StreamingAssets");
+ }
+ string path = Application.streamingAssetsPath + "/SpeechSDK.unitypackage";
+ string progressBarMessage = "Downloading SpeechSDK...";
+ await DownloadResourceAsync(AzureSpeechSDKURL, path, progressBarMessage);
+ //Import the package
+ AssetDatabase.ImportPackage(path, false);
+ File.Delete(path);
+ }
+ Dictionary scriptingSymbols = GetScriptingSymbolsForTargetPlatforms();
+ foreach (BuildTargetGroup targetGroup in TargetPlatform) {
+ PlayerSettings.SetScriptingDefineSymbolsForGroup(targetGroup, $"{scriptingSymbols[targetGroup]};{AzureRecognizerDefine}");
+ }
+ Debug.Log("Azure Speech Recognizer imported.");
+ EditorUtility.ClearProgressBar();
+ }
+ else {
+ Debug.Log("Azure Speech Recognizer already imported.");
+ }
+ }
+
+ [MenuItem("i5 Toolkit/Import Speech Module/Azure Synthesizer")]
+ public static async void ImportAzureSpeechSynthesizerAsync() {
+ if (!AzureSpeechSynthesizerImported) {
+ string[] assetGUIDs = AssetDatabase.FindAssets("SpeechSDK");
+ if (assetGUIDs.Length > 0) {
+ Debug.Log($"Found Speech SDK in {AssetDatabase.GUIDToAssetPath(assetGUIDs[0])}, skip download.");
+ }
+ else {
+ //Download the speech SDK and show the progress
+ Debug.Log("Starting to download SpeechSDK...");
+ if (AssetDatabase.FindAssets($"StreamingAssets").Length == 0) {
+ AssetDatabase.CreateFolder("Assets", "StreamingAssets");
+ }
+ string path = Application.streamingAssetsPath + "/SpeechSDK.unitypackage";
+ string progressBarMessage = "Downloading SpeechSDK...";
+ await DownloadResourceAsync(AzureSpeechSDKURL, path, progressBarMessage);
+ //Import the package
+ AssetDatabase.ImportPackage(path, false);
+ File.Delete(path);
+ }
+ Dictionary scriptingSymbols = GetScriptingSymbolsForTargetPlatforms();
+ foreach (BuildTargetGroup targetGroup in TargetPlatform) {
+ PlayerSettings.SetScriptingDefineSymbolsForGroup(targetGroup, $"{scriptingSymbols[targetGroup]};{AzureSynthesizerDefine}");
+ }
+ Debug.Log("Azure Speech Synthesizer imported.");
+ }
+ else {
+ Debug.Log("Azure Speech Synthesizer already imported.");
+ }
+ }
+
+ [MenuItem("i5 Toolkit/Import Speech Module/Native Recognizer")]
+ public static async void ImportNativeSpeechRecognizerAsync() {
+ if (!NativeSpeechRecognizerImported) {
+ // Download the (latest) English model (small).
+ if (AssetDatabase.FindAssets(VoskEnglishModelNameWithoutExtension, new string[1] { "Assets/StreamingAssets/" }).Length > 0) {
+ Debug.Log("Found Vosk English model (small) in the StreamingAssets folder, skip download.");
+ }
+ else {
+ Debug.Log("Starting to download Vosk English model (small)...");
+ string path = $"{Application.streamingAssetsPath}/{VoskEnglishModelName}";
+ string progressBarMessage = "Downloading Vosk English model (small)...";
+ // Check if the StreamingAssets folder exists
+ if (AssetDatabase.FindAssets($"StreamingAssets").Length == 0) {
+ AssetDatabase.CreateFolder("Assets", "StreamingAssets");
+ }
+ await DownloadResourceAsync(VoskEnglishModelURL, path, progressBarMessage);
+ Debug.Log("Vosk English model (small) downloaded in the StreamingAssets folder.");
+ }
+
+ // Download the (latest) German model (small).
+ if (AssetDatabase.FindAssets(VoskGermanModelNameWithoutExtension, new string[1] { "Assets/StreamingAssets/" }).Length > 0) {
+ Debug.Log("Found Vosk German model (small) in StreamingAssets folder, skip download.");
+ }
+ else {
+ Debug.Log("Starting to download Vosk German model (small)...");
+ string path = $"{Application.streamingAssetsPath}/{VoskGermanModelName}";
+ string progressBarMessage = "Downloading Vosk German model (small)...";
+ // Check if the StreamingAssets folder exists
+ if (AssetDatabase.FindAssets($"StreamingAssets").Length == 0) {
+ AssetDatabase.CreateFolder("Assets", "StreamingAssets");
+ }
+ await DownloadResourceAsync(VoskGermanModelURL, path, progressBarMessage);
+ Debug.Log("Vosk German model (small) downloaded in the StreamingAssets folder.");
+ }
+
+ // Download the scripts as package and import.
+ string[] folderGUIDs = AssetDatabase.FindAssets(NativeRecognizerAssemblyName);
+ if (folderGUIDs.Length > 0) {
+ Debug.Log($"Found the native recognizer assembly definition in {AssetDatabase.GUIDToAssetPath(folderGUIDs[0])}, skip download.");
+ }
+ else {
+ string path = Application.streamingAssetsPath + "/NativeRecognizer.unitypackage";
+ string progressBarMessage = "Downloading native recognizer scripts...";
+ if (AssetDatabase.FindAssets($"StreamingAssets").Length == 0) {
+ AssetDatabase.CreateFolder("Assets", "StreamingAssets");
+ }
+ await DownloadResourceAsync(NativeRecognizerURL, path, progressBarMessage);
+ //Import the package
+ AssetDatabase.ImportPackage(path, false);
+ File.Delete(path);
+ }
+ Dictionary scriptingSymbols = GetScriptingSymbolsForTargetPlatforms();
+ foreach (BuildTargetGroup targetGroup in TargetPlatform) {
+ PlayerSettings.SetScriptingDefineSymbolsForGroup(targetGroup, $"{scriptingSymbols[targetGroup]};{NativeRecognizerDefine}");
+ }
+ Debug.Log("Native Speech Recognizer imported in \"i5 Toolkit for Unity Speech Module Plugin\".");
+ }
+ else {
+ Debug.Log("Native Speech Recognizer already imported.");
+ }
+ }
+
+ [MenuItem("i5 Toolkit/Import Speech Module/Native Synthesizer")]
+ public static async void ImportNativeSpeechSynthesizerAsync() {
+ if (!NativeSpeechSynthesizerImported) {
+ string[] folderGUIDs = AssetDatabase.FindAssets(NativeSynthesizerAssemblyName);
+ if (folderGUIDs.Length > 0) {
+ Debug.Log($"Found the native synthesizer assembly definition in {AssetDatabase.GUIDToAssetPath(folderGUIDs[0])}, skip download.");
+ }
+ else {
+ string path = Application.streamingAssetsPath + "/NativeSynthesizer.unitypackage";
+ string progressBarMessage = "Downloading native synthesizer scripts...";
+ if (AssetDatabase.FindAssets($"StreamingAssets").Length == 0) {
+ AssetDatabase.CreateFolder("Assets", "StreamingAssets");
+ }
+ await DownloadResourceAsync(NativeSynthesizerURL, path, progressBarMessage);
+ //Import the package
+ AssetDatabase.ImportPackage(path, false);
+ File.Delete(path);
+ }
+ Dictionary scriptingSymbols = GetScriptingSymbolsForTargetPlatforms();
+ foreach (BuildTargetGroup targetGroup in TargetPlatform) {
+ PlayerSettings.SetScriptingDefineSymbolsForGroup(targetGroup, $"{scriptingSymbols[targetGroup]};{NativeSynthesizerDefine}");
+ }
+ Debug.Log("Native Speech Synthesizer imported in \"i5 Toolkit for Unity Speech Module Plugin\".");
+ }
+ else {
+ Debug.Log("Native Speech Synthesizer already imported.");
+ }
+ }
+
+ private static Dictionary GetScriptingSymbolsForTargetPlatforms() {
+ Dictionary scriptingSymbols = new Dictionary();
+ foreach(BuildTargetGroup targetGroup in TargetPlatform) {
+ scriptingSymbols.Add(targetGroup, PlayerSettings.GetScriptingDefineSymbolsForGroup(targetGroup));
+ }
+ return scriptingSymbols;
+ }
+
+ private static void ShowDownloadProgress(UnityWebRequest downloadRequest, string progressBarInfo) {
+ if (downloadRequest != null) {
+ EditorUtility.DisplayProgressBar(ProgressBarTitle, progressBarInfo, downloadRequest.downloadProgress);
+ }
+ }
+
+ private static void ShowImportingPackageProgress() {
+ EditorUtility.DisplayProgressBar(ProgressBarTitle, "Importing Package...", 1);
+ }
+
+ // Download the resources from url to path.
+ private static async Task DownloadResourceAsync(string url, string path, string progressBarMessage) {
+ UnityWebRequest downloadRequest = UnityWebRequest.Get(url);
+ EditorApplication.update += () => ShowDownloadProgress(downloadRequest, progressBarMessage);
+ await downloadRequest.SendWebRequest();
+ EditorApplication.update -= () => ShowDownloadProgress(downloadRequest, progressBarMessage);
+ byte[] data = downloadRequest.downloadHandler.data;
+ using (FileStream fs = new FileStream(path, FileMode.Create)) {
+ fs.Write(data, 0, data.Length);
+ fs.Dispose();
+ }
+ EditorUtility.ClearProgressBar();
+ downloadRequest.Dispose();
+ downloadRequest = null;
+ }
+ }
+#endif
+}
diff --git a/Assets/i5 Toolkit for Unity/Runtime/Speech Module/Utilities.cs.meta b/Assets/i5 Toolkit for Unity/Runtime/Speech Module/Utilities.cs.meta
new file mode 100644
index 00000000..7e282414
--- /dev/null
+++ b/Assets/i5 Toolkit for Unity/Runtime/Speech Module/Utilities.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 4394a5a5d7040ef40bc8040ec0f0c1cf
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/i5 Toolkit for Unity/Runtime/i5.Toolkit.Core.Runtime.asmdef b/Assets/i5 Toolkit for Unity/Runtime/i5.Toolkit.Core.Runtime.asmdef
index cd4b6915..b222b494 100644
--- a/Assets/i5 Toolkit for Unity/Runtime/i5.Toolkit.Core.Runtime.asmdef
+++ b/Assets/i5 Toolkit for Unity/Runtime/i5.Toolkit.Core.Runtime.asmdef
@@ -1,7 +1,9 @@
{
"name": "i5.Toolkit.Core.Runtime",
"references": [
- "Unity.TextMeshPro"
+ "Unity.TextMeshPro",
+ "i5.Toolkit.Core.SpeechModule.NativeRecognizer",
+ "i5.Toolkit.Core.Runtime.SpeechModule.NativeSynthesizer"
],
"includePlatforms": [],
"excludePlatforms": [],
diff --git a/Assets/i5 Toolkit for Unity/Samples~/RocketChatClient.meta b/Assets/i5 Toolkit for Unity/Samples~/RocketChat Client.meta
similarity index 100%
rename from Assets/i5 Toolkit for Unity/Samples~/RocketChatClient.meta
rename to Assets/i5 Toolkit for Unity/Samples~/RocketChat Client.meta
diff --git a/Assets/i5 Toolkit for Unity/Samples~/RocketChatClient/ClientBootstrapper.cs b/Assets/i5 Toolkit for Unity/Samples~/RocketChat Client/ClientBootstrapper.cs
similarity index 95%
rename from Assets/i5 Toolkit for Unity/Samples~/RocketChatClient/ClientBootstrapper.cs
rename to Assets/i5 Toolkit for Unity/Samples~/RocketChat Client/ClientBootstrapper.cs
index 855af45c..bac89a95 100644
--- a/Assets/i5 Toolkit for Unity/Samples~/RocketChatClient/ClientBootstrapper.cs
+++ b/Assets/i5 Toolkit for Unity/Samples~/RocketChat Client/ClientBootstrapper.cs
@@ -1,161 +1,161 @@
-using i5.Toolkit.Core.RocketChatClient;
-using i5.Toolkit.Core.ServiceCore;
-using i5.Toolkit.Core.Utilities;
-using TMPro;
-using UnityEngine;
-using UnityEngine.UI;
-
-public class ClientBootstrapper : BaseServiceBootstrapper
-{
-
- public GameObject DemoCanvas;
- public string HostAddress = "";
- public string Username = "";
- public string Password = "";
-
- private RocketChatService client;
- private TMP_InputField hostAddress;
- private TMP_InputField username;
- private TMP_InputField password;
- private Button login;
- private Button getChannelList;
- private Button getGroupList;
- private TMP_InputField roomID;
- private TMP_InputField messageToPost;
- private Button postMessage;
- private Button subscribe;
- private Button unsubscribe;
-
- // Can be freely chosen.
- private string subscribtionUniqueID = "1";
-
- void Awake()
- {
- hostAddress = DemoCanvas.transform.Find("HostAddress").GetComponent();
- username = DemoCanvas.transform.Find("Username").GetComponent();
- password = DemoCanvas.transform.Find("Password").GetComponent();
- login = DemoCanvas.transform.Find("Login").GetComponent