AppVoiceExperience.cs 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453
  1. /*
  2. * Copyright (c) Meta Platforms, Inc. and affiliates.
  3. * All rights reserved.
  4. *
  5. * Licensed under the Oculus SDK License Agreement (the "License");
  6. * you may not use the Oculus SDK except in compliance with the License,
  7. * which is provided at the time of installation or download, or which
  8. * otherwise accompanies this software in either electronic or hard copy form.
  9. *
  10. * You may obtain a copy of the License at
  11. *
  12. * https://developer.oculus.com/licenses/oculussdk/
  13. *
  14. * Unless required by applicable law or agreed to in writing, the Oculus SDK
  15. * distributed under the License is distributed on an "AS IS" BASIS,
  16. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  17. * See the License for the specific language governing permissions and
  18. * limitations under the License.
  19. */
  20. using System;
  21. using System.Globalization;
  22. using Meta.Voice;
  23. using Meta.WitAi;
  24. using Meta.WitAi.Configuration;
  25. using Meta.WitAi.Data;
  26. using Meta.WitAi.Data.Configuration;
  27. using Meta.WitAi.Interfaces;
  28. using Meta.WitAi.Json;
  29. using Meta.WitAi.Requests;
  30. using Oculus.Voice.Bindings.Android;
  31. using Oculus.Voice.Core.Bindings.Android.PlatformLogger;
  32. using Oculus.Voice.Core.Bindings.Interfaces;
  33. using Oculus.Voice.Interfaces;
  34. using Oculus.VoiceSDK.Utilities;
  35. using UnityEngine;
  36. namespace Oculus.Voice
  37. {
  38. [HelpURL("https://developer.oculus.com/experimental/voice-sdk/tutorial-overview/")]
  39. public class AppVoiceExperience : VoiceService, IWitRuntimeConfigProvider, IWitConfigurationProvider
  40. {
  41. [SerializeField] private WitRuntimeConfiguration witRuntimeConfiguration;
  42. [Tooltip("Uses platform services to access wit.ai instead of accessing wit directly from within the application.")]
  43. [SerializeField] private bool usePlatformServices;
  44. [Tooltip("Enables logs related to the interaction to be displayed on console")]
  45. [SerializeField] private bool enableConsoleLogging;
  46. public WitRuntimeConfiguration RuntimeConfiguration
  47. {
  48. get => witRuntimeConfiguration;
  49. set => witRuntimeConfiguration = value;
  50. }
  51. public WitConfiguration Configuration => witRuntimeConfiguration?.witConfiguration;
  52. private IPlatformVoiceService platformService;
  53. private IVoiceService voiceServiceImpl;
  54. private IVoiceSDKLogger voiceSDKLoggerImpl;
  55. #if UNITY_ANDROID && !UNITY_EDITOR
  56. // This version is auto-updated for a release build
  57. private readonly string PACKAGE_VERSION = "54.0.0.135.284";
  58. #endif
  59. private bool Initialized => null != voiceServiceImpl;
  60. public event Action OnInitialized;
  61. #region Voice Service Properties
  62. public override bool Active => base.Active || (null != voiceServiceImpl && voiceServiceImpl.Active);
  63. public override bool IsRequestActive => base.IsRequestActive || (null != voiceServiceImpl && voiceServiceImpl.IsRequestActive);
  64. public override ITranscriptionProvider TranscriptionProvider
  65. {
  66. get => voiceServiceImpl?.TranscriptionProvider;
  67. set
  68. {
  69. if (voiceServiceImpl != null)
  70. {
  71. voiceServiceImpl.TranscriptionProvider = value;
  72. }
  73. }
  74. }
  75. public override bool MicActive => null != voiceServiceImpl && voiceServiceImpl.MicActive;
  76. protected override bool ShouldSendMicData => witRuntimeConfiguration.sendAudioToWit ||
  77. null == TranscriptionProvider;
  78. #endregion
  79. #if UNITY_ANDROID && !UNITY_EDITOR
  80. public bool HasPlatformIntegrations => usePlatformServices && voiceServiceImpl is VoiceSDKImpl;
  81. #else
  82. public bool HasPlatformIntegrations => false;
  83. #endif
  84. public bool EnableConsoleLogging => enableConsoleLogging;
  85. public bool UsePlatformIntegrations
  86. {
  87. get => usePlatformServices;
  88. set
  89. {
  90. // If we're trying to turn on platform services and they're not currently active we
  91. // will forcibly reinit and try to set the state.
  92. if (usePlatformServices != value || HasPlatformIntegrations != value)
  93. {
  94. usePlatformServices = value;
  95. #if UNITY_ANDROID && !UNITY_EDITOR
  96. Debug.Log($"{(usePlatformServices ? "Enabling" : "Disabling")} platform integration.");
  97. InitVoiceSDK();
  98. #endif
  99. }
  100. }
  101. }
  102. #region Voice Service Text Methods
  103. public override bool CanSend()
  104. {
  105. return base.CanSend() && voiceServiceImpl.CanSend();
  106. }
  107. public override VoiceServiceRequest Activate(string text, WitRequestOptions requestOptions, VoiceServiceRequestEvents requestEvents)
  108. {
  109. if (CanSend())
  110. {
  111. voiceSDKLoggerImpl.LogInteractionStart(requestOptions.RequestId, "message");
  112. LogRequestConfig();
  113. return voiceServiceImpl.Activate(text, requestOptions, requestEvents);
  114. }
  115. return null;
  116. }
  117. #endregion
  118. #region Voice Service Audio Methods
  119. public override bool CanActivateAudio()
  120. {
  121. return base.CanActivateAudio() && voiceServiceImpl.CanActivateAudio();
  122. }
  123. protected override string GetActivateAudioError()
  124. {
  125. if (!HasPlatformIntegrations && !AudioBuffer.Instance.IsInputAvailable)
  126. {
  127. return "No Microphone(s)/recording devices found. You will be unable to capture audio on this device.";
  128. }
  129. return string.Empty;
  130. }
  131. public override VoiceServiceRequest Activate(WitRequestOptions requestOptions, VoiceServiceRequestEvents requestEvents)
  132. {
  133. if (CanActivateAudio() && CanSend())
  134. {
  135. voiceSDKLoggerImpl.LogInteractionStart(requestOptions.RequestId, "speech");
  136. LogRequestConfig();
  137. return voiceServiceImpl.Activate(requestOptions, requestEvents);
  138. }
  139. return null;
  140. }
  141. public override VoiceServiceRequest ActivateImmediately(WitRequestOptions requestOptions, VoiceServiceRequestEvents requestEvents)
  142. {
  143. if (CanActivateAudio() && CanSend())
  144. {
  145. voiceSDKLoggerImpl.LogInteractionStart(requestOptions.RequestId, "speech");
  146. LogRequestConfig();
  147. return voiceServiceImpl.ActivateImmediately(requestOptions, requestEvents);
  148. }
  149. return null;
  150. }
  151. public override void Deactivate()
  152. {
  153. voiceServiceImpl.Deactivate();
  154. }
  155. public override void DeactivateAndAbortRequest()
  156. {
  157. voiceServiceImpl.DeactivateAndAbortRequest();
  158. }
  159. #endregion
  160. private void InitVoiceSDK()
  161. {
  162. // Clean up if we're switching to native C# wit impl
  163. if (!UsePlatformIntegrations)
  164. {
  165. if (voiceServiceImpl is VoiceSDKImpl)
  166. {
  167. ((VoiceSDKImpl) voiceServiceImpl).Disconnect();
  168. }
  169. if (voiceSDKLoggerImpl is VoiceSDKPlatformLoggerImpl)
  170. {
  171. try
  172. {
  173. ((VoiceSDKPlatformLoggerImpl)voiceSDKLoggerImpl).Disconnect();
  174. }
  175. catch (Exception e)
  176. {
  177. VLog.E($"Disconnection error: {e.Message}");
  178. }
  179. }
  180. }
  181. #if UNITY_ANDROID && !UNITY_EDITOR
  182. var loggerImpl = new VoiceSDKPlatformLoggerImpl();
  183. loggerImpl.Connect(PACKAGE_VERSION);
  184. voiceSDKLoggerImpl = loggerImpl;
  185. if (UsePlatformIntegrations)
  186. {
  187. Debug.Log("Checking platform capabilities...");
  188. var platformImpl = new VoiceSDKImpl(this);
  189. platformImpl.OnServiceNotAvailableEvent += () => RevertToWitUnity();
  190. platformImpl.Connect(PACKAGE_VERSION);
  191. platformImpl.SetRuntimeConfiguration(RuntimeConfiguration);
  192. if (platformImpl.PlatformSupportsWit)
  193. {
  194. voiceServiceImpl = platformImpl;
  195. if (voiceServiceImpl is Wit wit)
  196. {
  197. wit.RuntimeConfiguration = witRuntimeConfiguration;
  198. }
  199. voiceServiceImpl.VoiceEvents = VoiceEvents;
  200. voiceServiceImpl.TelemetryEvents = TelemetryEvents;
  201. voiceSDKLoggerImpl.IsUsingPlatformIntegration = true;
  202. }
  203. else
  204. {
  205. Debug.Log("Platform registration indicated platform support is not currently available.");
  206. RevertToWitUnity();
  207. }
  208. }
  209. else
  210. {
  211. RevertToWitUnity();
  212. }
  213. #else
  214. voiceSDKLoggerImpl = new VoiceSDKConsoleLoggerImpl();
  215. RevertToWitUnity();
  216. #endif
  217. voiceSDKLoggerImpl.WitApplication = RuntimeConfiguration?.witConfiguration?.GetLoggerAppId();
  218. voiceSDKLoggerImpl.ShouldLogToConsole = EnableConsoleLogging;
  219. OnInitialized?.Invoke();
  220. }
  221. private void RevertToWitUnity()
  222. {
  223. Wit w = GetComponent<Wit>();
  224. if (null == w)
  225. {
  226. w = gameObject.AddComponent<Wit>();
  227. w.hideFlags = HideFlags.HideInInspector;
  228. }
  229. voiceServiceImpl = w;
  230. if (voiceServiceImpl is Wit wit)
  231. {
  232. wit.RuntimeConfiguration = witRuntimeConfiguration;
  233. }
  234. voiceServiceImpl.VoiceEvents = VoiceEvents;
  235. voiceServiceImpl.TelemetryEvents = TelemetryEvents;
  236. voiceSDKLoggerImpl.IsUsingPlatformIntegration = false;
  237. }
  238. protected override void OnEnable()
  239. {
  240. base.OnEnable();
  241. if (MicPermissionsManager.HasMicPermission())
  242. {
  243. InitVoiceSDK();
  244. }
  245. else
  246. {
  247. MicPermissionsManager.RequestMicPermission();
  248. }
  249. #if UNITY_ANDROID && !UNITY_EDITOR
  250. platformService?.SetRuntimeConfiguration(witRuntimeConfiguration);
  251. #endif
  252. // Logging
  253. VoiceEvents.OnResponse?.AddListener(OnWitResponseListener);
  254. VoiceEvents.OnStartListening?.AddListener(OnStartedListening);
  255. VoiceEvents.OnMinimumWakeThresholdHit?.AddListener(OnMinimumWakeThresholdHit);
  256. VoiceEvents.OnStoppedListening?.AddListener(OnStoppedListening);
  257. VoiceEvents.OnMicDataSent?.AddListener(OnMicDataSent);
  258. VoiceEvents.OnSend?.AddListener(OnSend);
  259. VoiceEvents.OnPartialTranscription?.AddListener(OnPartialTranscription);
  260. VoiceEvents.OnFullTranscription?.AddListener(OnFullTranscription);
  261. VoiceEvents.OnStoppedListeningDueToTimeout?.AddListener(OnStoppedListeningDueToTimeout);
  262. VoiceEvents.OnStoppedListeningDueToInactivity?.AddListener(OnStoppedListeningDueToInactivity);
  263. VoiceEvents.OnStoppedListeningDueToDeactivation?.AddListener(OnStoppedListeningDueToDeactivation);
  264. VoiceEvents.OnComplete?.AddListener(OnRequestComplete);
  265. TelemetryEvents.OnAudioTrackerFinished?.AddListener(OnAudioDurationTrackerFinished);
  266. }
  267. protected override void OnDisable()
  268. {
  269. base.OnDisable();
  270. #if UNITY_ANDROID
  271. if (voiceServiceImpl is VoiceSDKImpl platformImpl)
  272. {
  273. platformImpl.Disconnect();
  274. }
  275. if (voiceSDKLoggerImpl is VoiceSDKPlatformLoggerImpl loggerImpl)
  276. {
  277. loggerImpl.Disconnect();
  278. }
  279. #endif
  280. voiceServiceImpl = null;
  281. voiceSDKLoggerImpl = null;
  282. // Logging
  283. VoiceEvents.OnResponse?.RemoveListener(OnWitResponseListener);
  284. VoiceEvents.OnStartListening?.RemoveListener(OnStartedListening);
  285. VoiceEvents.OnMinimumWakeThresholdHit?.RemoveListener(OnMinimumWakeThresholdHit);
  286. VoiceEvents.OnStoppedListening?.RemoveListener(OnStoppedListening);
  287. VoiceEvents.OnMicDataSent?.RemoveListener(OnMicDataSent);
  288. VoiceEvents.OnSend?.RemoveListener(OnSend);
  289. VoiceEvents.OnPartialTranscription?.RemoveListener(OnPartialTranscription);
  290. VoiceEvents.OnFullTranscription?.RemoveListener(OnFullTranscription);
  291. VoiceEvents.OnStoppedListeningDueToTimeout?.RemoveListener(OnStoppedListeningDueToTimeout);
  292. VoiceEvents.OnStoppedListeningDueToInactivity?.RemoveListener(OnStoppedListeningDueToInactivity);
  293. VoiceEvents.OnStoppedListeningDueToDeactivation?.RemoveListener(OnStoppedListeningDueToDeactivation);
  294. VoiceEvents.OnComplete?.RemoveListener(OnRequestComplete);
  295. TelemetryEvents.OnAudioTrackerFinished?.RemoveListener(OnAudioDurationTrackerFinished);
  296. }
  297. private void OnApplicationFocus(bool hasFocus)
  298. {
  299. if (enabled && hasFocus && !Initialized)
  300. {
  301. if (MicPermissionsManager.HasMicPermission())
  302. {
  303. InitVoiceSDK();
  304. }
  305. }
  306. }
  307. #region Event listeners for logging
  308. void OnWitResponseListener(WitResponseNode witResponseNode)
  309. {
  310. var tokens = witResponseNode?["speech"]?["tokens"];
  311. if (tokens != null)
  312. {
  313. int speechTokensLength = tokens.Count;
  314. string speechLength = witResponseNode["speech"]["tokens"][speechTokensLength - 1]?["end"]?.Value;
  315. voiceSDKLoggerImpl.LogAnnotation("audioLength", speechLength);
  316. }
  317. }
  318. void OnStartedListening()
  319. {
  320. voiceSDKLoggerImpl.LogInteractionPoint("startedListening");
  321. }
  322. void OnMinimumWakeThresholdHit()
  323. {
  324. voiceSDKLoggerImpl.LogInteractionPoint("minWakeThresholdHit");
  325. }
  326. void OnStoppedListening()
  327. {
  328. voiceSDKLoggerImpl.LogInteractionPoint("stoppedListening");
  329. }
  330. void OnStoppedListeningDueToTimeout()
  331. {
  332. voiceSDKLoggerImpl.LogInteractionPoint("stoppedListeningTimeout");
  333. }
  334. void OnStoppedListeningDueToInactivity()
  335. {
  336. voiceSDKLoggerImpl.LogInteractionPoint("stoppedListeningInactivity");
  337. }
  338. void OnStoppedListeningDueToDeactivation()
  339. {
  340. voiceSDKLoggerImpl.LogInteractionPoint("stoppedListeningDeactivate");
  341. }
  342. void OnMicDataSent()
  343. {
  344. voiceSDKLoggerImpl.LogInteractionPoint("micDataSent");
  345. }
  346. void OnSend(VoiceServiceRequest request)
  347. {
  348. voiceSDKLoggerImpl.LogInteractionPoint("witRequestCreated");
  349. if (request != null)
  350. {
  351. voiceSDKLoggerImpl.LogAnnotation("requestIdOverride", request.Options?.RequestId);
  352. }
  353. }
  354. void OnAudioDurationTrackerFinished(long timestamp, double audioDuration)
  355. {
  356. voiceSDKLoggerImpl.LogAnnotation("adt_duration", audioDuration.ToString(CultureInfo.InvariantCulture));
  357. voiceSDKLoggerImpl.LogAnnotation("adt_finished", timestamp.ToString());
  358. }
  359. void OnPartialTranscription(string text)
  360. {
  361. voiceSDKLoggerImpl.LogFirstTranscriptionTime();
  362. }
  363. void OnFullTranscription(string text)
  364. {
  365. voiceSDKLoggerImpl.LogInteractionPoint("fullTranscriptionTime");
  366. }
  367. void OnRequestComplete(VoiceServiceRequest request)
  368. {
  369. if (request.State == VoiceRequestState.Failed)
  370. {
  371. VLog.E($"Request Failed\nError: {request.Results.Message}");
  372. voiceSDKLoggerImpl.LogInteractionEndFailure(request.Results.Message);
  373. }
  374. else if (request.State == VoiceRequestState.Canceled)
  375. {
  376. VLog.W($"Request Canceled\nMessage: {request.Results.Message}");
  377. voiceSDKLoggerImpl.LogInteractionEndFailure("aborted");
  378. }
  379. else
  380. {
  381. VLog.D($"Request Success");
  382. voiceSDKLoggerImpl.LogInteractionEndSuccess();
  383. }
  384. }
  385. void LogRequestConfig()
  386. {
  387. #if UNITY_ANDROID && !UNITY_EDITOR
  388. voiceSDKLoggerImpl.LogAnnotation("clientSDKVersion", PACKAGE_VERSION);
  389. #endif
  390. voiceSDKLoggerImpl.LogAnnotation("minWakeThreshold",
  391. RuntimeConfiguration?.soundWakeThreshold.ToString(CultureInfo.InvariantCulture));
  392. voiceSDKLoggerImpl.LogAnnotation("minKeepAliveTimeSec",
  393. RuntimeConfiguration?.minKeepAliveTimeInSeconds.ToString(CultureInfo.InvariantCulture));
  394. voiceSDKLoggerImpl.LogAnnotation("minTranscriptionKeepAliveTimeSec",
  395. RuntimeConfiguration?.minTranscriptionKeepAliveTimeInSeconds.ToString(CultureInfo.InvariantCulture));
  396. voiceSDKLoggerImpl.LogAnnotation("maxRecordingTime",
  397. RuntimeConfiguration?.maxRecordingTime.ToString(CultureInfo.InvariantCulture));
  398. }
  399. #endregion
  400. }
  401. }