Example #1
0
        /// <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));
        }
Example #2
0
        /// <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();
        }
Example #3
0
        /// <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);
        }
Example #4
0
        /// <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."));
            }
        }
Example #5
0
        /// <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."));
            }
        }
Example #6
0
        /// <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();
        }
Example #7
0
        /// <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();
        }