/// <summary> /// Gets inference results from Rhino. If the spoken command was understood, it includes the specific intent name /// that was inferred, and (if applicable) slot keys and specific slot values. Should only be called after the /// process function returns true, otherwise Rhino has not yet reached an inference conclusion. /// </summary> /// <returns> /// An immutable Inference object with `.IsUnderstood`, '.Intent` , and `.Slots` getters. /// </returns> public Inference GetInference() { if (!_isFinalized) { throw RhinoStatusToException(RhinoStatus.INVALID_STATE); } bool isUnderstood; string intent; Dictionary <string, string> slots; RhinoStatus status = pv_rhino_is_understood(_libraryPointer, out isUnderstood); if (status != RhinoStatus.SUCCESS) { throw RhinoStatusToException(status); } if (isUnderstood) { IntPtr intentPtr, slotKeysPtr, slotValuesPtr; int numSlots; status = pv_rhino_get_intent(_libraryPointer, out intentPtr, out numSlots, out slotKeysPtr, out slotValuesPtr); if (status != RhinoStatus.SUCCESS) { throw RhinoStatusToException(status); } intent = Marshal.PtrToStringAnsi(intentPtr); int elementSize = Marshal.SizeOf(typeof(IntPtr)); slots = new Dictionary <string, string>(); for (int i = 0; i < numSlots; i++) { string slotKey = Marshal.PtrToStringAnsi(Marshal.ReadIntPtr(slotKeysPtr, i * elementSize)); string slotValue = Marshal.PtrToStringAnsi(Marshal.ReadIntPtr(slotValuesPtr, i * elementSize)); slots[slotKey] = slotValue; } status = pv_rhino_free_slots_and_values(_libraryPointer, slotKeysPtr, slotValuesPtr); if (status != RhinoStatus.SUCCESS) { throw RhinoStatusToException(status); } } else { intent = null; slots = new Dictionary <string, string>(); } status = pv_rhino_reset(_libraryPointer); if (status != RhinoStatus.SUCCESS) { throw RhinoStatusToException(status); } return(new Inference(isUnderstood, intent, slots)); }
/// <summary> /// Creates an instance of the Rhino wake word engine. /// </summary> /// <param name="accessKey">AccessKey obtained from Picovoice Console (https://console.picovoice.ai/).</param> /// <param name="contextPath"> /// Absolute path to file containing context model (file with `.rhn` extension. A context represents the set of /// expressions(spoken commands), intents, and intent arguments(slots) within a domain of interest. /// </param> /// <param name="modelPath"> /// Absolute path to the file containing model parameters. If not set it will be set to the /// default location. /// </param> /// <param name="sensitivity"> /// Inference sensitivity expressed as floating point value within [0,1]. A higher sensitivity value results in fewer misses /// at the cost of (potentially) increasing the erroneous inference rate. /// </param> /// <param name="requireEndpoint"> /// If set to `true`, Rhino requires an endpoint (chunk of silence) before finishing inference. /// </param> private Rhino( string accessKey, string modelPath, string contextPath, float sensitivity = 0.5f, bool requireEndpoint = true) { if (string.IsNullOrEmpty(accessKey)) { throw new RhinoInvalidArgumentException("No AccessKey provided to Rhino"); } if (!File.Exists(modelPath)) { throw new RhinoIOException($"Couldn't find model file at '{modelPath}'"); } if (!File.Exists(contextPath)) { throw new RhinoIOException($"Couldn't find context file at '{contextPath}'"); } if (sensitivity < 0 || sensitivity > 1) { throw new RhinoInvalidArgumentException("Sensitivity value should be within [0, 1]."); } RhinoStatus status = pv_rhino_init( accessKey, modelPath, contextPath, sensitivity, requireEndpoint, out _libraryPointer); if (status != RhinoStatus.SUCCESS) { throw RhinoStatusToException(status); } IntPtr contextInfoPtr; status = pv_rhino_context_info(_libraryPointer, out contextInfoPtr); if (status != RhinoStatus.SUCCESS) { throw RhinoStatusToException(status, "Rhino init failed."); } ContextInfo = Marshal.PtrToStringAnsi(contextInfoPtr); Version = Marshal.PtrToStringAnsi(pv_rhino_version()); SampleRate = pv_sample_rate(); FrameLength = pv_rhino_frame_length(); }
/// <summary> /// Processes a frame of audio and emits a flag indicating if the inference is finalized. When finalized, /// `pv_rhino_is_understood()` should be called to check if the spoken command is considered valid. /// </summary> /// <param name="pcm"> /// A frame of audio samples. The number of samples per frame can be found by calling `.FrameLength`. /// The incoming audio needs to have a sample rate equal to `.SampleRate` and be 16-bit linearly-encoded. /// Rhino operates on single-channel audio. /// </param> /// <returns> /// Flag indicating if the inference is finalized. /// </returns> public bool Process(short[] pcm) { if (pcm.Length != FrameLength) { throw new ArgumentException($"Input audio frame size ({pcm.Length}) was not the size specified by Rhino engine ({FrameLength}). " + $"Use rhino.FrameLength to get the correct size."); } RhinoStatus status = pv_rhino_process(_libraryPointer, pcm, out _isFinalized); if (status != RhinoStatus.SUCCESS) { throw RhinoStatusToException(status); } return(_isFinalized); }
/// <summary> /// Coverts status codes to relavent .NET exceptions /// </summary> /// <param name="status">Picovoice library status code.</param> /// <returns>.NET exception</returns> private static Exception RhinoStatusToException(RhinoStatus status) { switch (status) { case RhinoStatus.OUT_OF_MEMORY: return(new OutOfMemoryException()); case RhinoStatus.IO_ERROR: return(new IOException()); case RhinoStatus.INVALID_ARGUMENT: return(new ArgumentException()); case RhinoStatus.INVALID_STATE: return(new Exception("Rhino reported an invalid state.")); default: return(new Exception("Unmapped error code returned from Rhino.")); } }
/// <summary> /// Coverts status codes to relavent .NET exceptions /// </summary> /// <param name="status">Picovoice library status code.</param> /// <returns>.NET exception</returns> private static Exception RhinoStatusToException(RhinoStatus status, string message = "") { switch (status) { case RhinoStatus.OUT_OF_MEMORY: return(new RhinoMemoryException(message)); case RhinoStatus.IO_ERROR: return(new RhinoIOException(message)); case RhinoStatus.INVALID_ARGUMENT: return(new RhinoInvalidArgumentException(message)); case RhinoStatus.STOP_ITERATION: return(new RhinoStopIterationException(message)); case RhinoStatus.KEY_ERROR: return(new RhinoKeyException(message)); case RhinoStatus.INVALID_STATE: return(new RhinoInvalidStateException(message)); case RhinoStatus.RUNTIME_ERROR: return(new RhinoRuntimeException(message)); case RhinoStatus.ACTIVATION_ERROR: return(new RhinoActivationException(message)); case RhinoStatus.ACTIVATION_LIMIT_REACHED: return(new RhinoActivationLimitException(message)); case RhinoStatus.ACTIVATION_THROTTLED: return(new RhinoActivationThrottledException(message)); case RhinoStatus.ACTIVATION_REFUSED: return(new RhinoActivationRefusedException(message)); default: return(new RhinoException("Unmapped error code returned from Rhino.")); } }
/// <summary> /// Creates an instance of the Rhino wake word engine. /// </summary> /// <param name="modelPath">Absolute path to file containing model parameters. /// <param name="contextPath"> /// Absolute path to file containing context parameters. A context represents the set of /// expressions(spoken commands), intents, and intent arguments(slots) within a domain of interest. /// </param> /// <param name="sensitivity"> /// Inference sensitivity. It should be a number within [0, 1]. A higher sensitivity value /// results in fewer misses at the cost of(potentially) increasing the erroneous inference rate. /// </param> public Rhino(string modelPath, string contextPath, float sensitivity = 0.5f) { if (!File.Exists(modelPath)) { throw new IOException($"Couldn't find model file at '{modelPath}'"); } if (!File.Exists(contextPath)) { throw new IOException($"Couldn't find context file at '{contextPath}'"); } if (sensitivity < 0 || sensitivity > 1) { throw new ArgumentException("Sensitivity value should be within [0, 1]."); } RhinoStatus status = pv_rhino_init(modelPath, contextPath, sensitivity, out _libraryPointer); if (status != RhinoStatus.SUCCESS) { throw RhinoStatusToException(status); } IntPtr contextInfoPtr; status = pv_rhino_context_info(_libraryPointer, out contextInfoPtr); if (status != RhinoStatus.SUCCESS) { throw RhinoStatusToException(status); } ContextInfo = Marshal.PtrToStringAnsi(contextInfoPtr); Version = Marshal.PtrToStringAnsi(pv_rhino_version()); SampleRate = pv_sample_rate(); FrameLength = pv_rhino_frame_length(); }
/// <summary> /// Creates an instance of the Rhino wake word engine. /// </summary> /// <param name="accessKey">AccessKey obtained from Picovoice Console (https://console.picovoice.ai/).</param> /// <param name="modelPath">Absolute path to file containing model parameters. /// <param name="contextPath"> /// Absolute path to file containing context parameters. A context represents the set of /// expressions(spoken commands), intents, and intent arguments(slots) within a domain of interest. /// </param> /// <param name="sensitivity"> /// <param name="requireEndpoint"> /// Boolean variable to indicate if Rhino should wait for a chunk of silence before finishing inference. /// </param> /// Inference sensitivity. It should be a number within [0, 1]. A higher sensitivity value /// results in fewer misses at the cost of(potentially) increasing the erroneous inference rate. /// </param> private Rhino(string accessKey, string modelPath, string contextPath, float sensitivity, bool requireEndpoint) { if (string.IsNullOrEmpty(accessKey)) { throw new RhinoInvalidArgumentException("No AccessKey provided to Rhino"); } if (string.IsNullOrEmpty(contextPath)) { throw new RhinoInvalidArgumentException("No contextPath provided to Rhino"); } if (!File.Exists(modelPath)) { #if !UNITY_EDITOR && UNITY_ANDROID try { modelPath = ExtractResource(modelPath); } catch { throw new RhinoIOException($"Couldn't find model file at '{modelPath}'"); } #else throw new RhinoIOException($"Couldn't find model file at '{modelPath}'"); #endif } if (!File.Exists(contextPath)) { #if !UNITY_EDITOR && UNITY_ANDROID try { contextPath = ExtractResource(contextPath); } catch (Exception e) { throw new RhinoIOException($"Couldn't find context file at '{contextPath}'"); } #else throw new RhinoIOException($"Couldn't find context file at '{contextPath}'"); #endif } if (sensitivity < 0 || sensitivity > 1) { throw new RhinoInvalidArgumentException("Sensitivity value should be within [0, 1]."); } RhinoStatus status = pv_rhino_init(accessKey, modelPath, contextPath, sensitivity, requireEndpoint, out _libraryPointer); if (status != RhinoStatus.SUCCESS) { throw RhinoStatusToException(status); } IntPtr contextInfoPtr; status = pv_rhino_context_info(_libraryPointer, out contextInfoPtr); if (status != RhinoStatus.SUCCESS) { throw RhinoStatusToException(status); } ContextInfo = Marshal.PtrToStringAnsi(contextInfoPtr); Version = Marshal.PtrToStringAnsi(pv_rhino_version()); SampleRate = pv_sample_rate(); FrameLength = pv_rhino_frame_length(); }