{"id":21423926,"url":"https://github.com/picovoice/rhino","last_synced_at":"2025-05-14T05:10:57.043Z","repository":{"id":37791121,"uuid":"155038855","full_name":"Picovoice/rhino","owner":"Picovoice","description":"On-device Speech-to-Intent engine powered by deep learning","archived":false,"fork":false,"pushed_at":"2025-05-08T21:49:54.000Z","size":284952,"stargazers_count":661,"open_issues_count":0,"forks_count":91,"subscribers_count":15,"default_branch":"master","last_synced_at":"2025-05-08T22:33:50.202Z","etag":null,"topics":["entity-resolution","intent-inference","natural-language-understanding","nlu","on-device","slot-filling","slu","speech-recognition","spoken-language-understanding","voice-assistant","voice-command","voice-command-control","voice-commands","voice-control","voice-recognition","voice-ui","voice-user-interface","vui"],"latest_commit_sha":null,"homepage":"https://picovoice.ai/","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Picovoice.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2018-10-28T05:34:00.000Z","updated_at":"2025-05-08T21:49:56.000Z","dependencies_parsed_at":"2022-07-10T02:16:54.522Z","dependency_job_id":"b461c6d5-58f3-4e5f-bf23-56f5671888b6","html_url":"https://github.com/Picovoice/rhino","commit_stats":{"total_commits":749,"total_committers":21,"mean_commits":"35.666666666666664","dds":0.7089452603471296,"last_synced_commit":"cc600ee943999ac4928d7fe96fd604c619968065"},"previous_names":[],"tags_count":40,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Picovoice%2Frhino","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Picovoice%2Frhino/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Picovoice%2Frhino/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Picovoice%2Frhino/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Picovoice","download_url":"https://codeload.github.com/Picovoice/rhino/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254076850,"owners_count":22010611,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["entity-resolution","intent-inference","natural-language-understanding","nlu","on-device","slot-filling","slu","speech-recognition","spoken-language-understanding","voice-assistant","voice-command","voice-command-control","voice-commands","voice-control","voice-recognition","voice-ui","voice-user-interface","vui"],"created_at":"2024-11-22T21:18:54.730Z","updated_at":"2025-05-14T05:10:52.019Z","avatar_url":"https://github.com/Picovoice.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Rhino\n\n[![GitHub release](https://img.shields.io/github/release/Picovoice/Rhino.svg)](https://github.com/Picovoice/Rhino/releases)\n[![GitHub](https://img.shields.io/github/license/Picovoice/rhino)](https://github.com/Picovoice/rhino/)\n\n[![Crates.io](https://img.shields.io/crates/v/pv_rhino)](https://crates.io/crates/pv_rhino)\u003c!-- markdown-link-check-disable-line --\u003e\n[![Maven Central](https://img.shields.io/maven-central/v/ai.picovoice/rhino-android?label=maven-central%20%5Bandroid%5D)](https://repo1.maven.org/maven2/ai/picovoice/rhino-android/)\n[![Maven Central](https://img.shields.io/maven-central/v/ai.picovoice/rhino-java?label=maven%20central%20%5Bjava%5D)](https://repo1.maven.org/maven2/ai/picovoice/rhino-java/)\n[![npm](https://img.shields.io/npm/v/@picovoice/rhino-node?label=npm%20%5Bnode%5D)](https://www.npmjs.com/package/@picovoice/rhino-node)\n[![npm](https://img.shields.io/npm/v/@picovoice/rhino-react?label=npm%20%5Breact%5D)](https://www.npmjs.com/package/@picovoice/rhino-react)\n[![npm](https://img.shields.io/npm/v/@picovoice/rhino-react-native?label=npm%20%5Breact-native%5D)](https://www.npmjs.com/package/@picovoice/rhino-react-native)\n[![npm](https://img.shields.io/npm/v/@picovoice/rhino-web?label=npm%20%5Bweb%5D)](https://www.npmjs.com/package/@picovoice/rhino-web)\n[![Nuget](https://img.shields.io/nuget/v/rhino)](https://www.nuget.org/packages/Rhino/)\n[![CocoaPods](https://img.shields.io/cocoapods/v/Rhino-iOS)](https://cocoapods.org/pods/Rhino-iOS)\n[![Pub Version](https://img.shields.io/pub/v/rhino_flutter)](https://pub.dev/packages/rhino_flutter)\n[![PyPI](https://img.shields.io/pypi/v/pvrhino)](https://pypi.org/project/pvrhino/)\n\nMade in Vancouver, Canada by [Picovoice](https://picovoice.ai)\n\n[![Twitter URL](https://img.shields.io/twitter/url?label=%40AiPicovoice\u0026style=social\u0026url=https%3A%2F%2Ftwitter.com%2FAiPicovoice)](https://twitter.com/AiPicovoice)\u003c!-- markdown-link-check-disable-line --\u003e\n[![YouTube Channel Views](https://img.shields.io/youtube/channel/views/UCAdi9sTCXLosG1XeqDwLx7w?label=YouTube\u0026style=social)](https://www.youtube.com/channel/UCAdi9sTCXLosG1XeqDwLx7w)\n\nRhino is Picovoice's Speech-to-Intent engine. It directly infers intent from spoken commands within a given context of\ninterest, in real-time. For example, given a spoken command:\n\n\u003e Can I have a small double-shot espresso?\n\nRhino infers what the user wants and emits the following inference result:\n\n```json\n{\n  \"isUnderstood\": \"true\",\n  \"intent\": \"orderBeverage\",\n  \"slots\": {\n    \"beverage\": \"espresso\",\n    \"size\": \"small\",\n    \"numberOfShots\": \"2\"\n  }\n}\n```\n\nRhino is:\n\n- using deep neural networks trained in real-world environments.\n- compact and computationally-efficient. It is perfect for IoT.\n- cross-platform:\n  - Arm Cortex-M, STM32, and Arduino\n  - Raspberry Pi\n  - Android and iOS\n  - Chrome, Safari, Firefox, and Edge\n  - Linux (x86_64), macOS (x86_64, arm64), and Windows (x86_64, arm64)\n- self-service. Developers can train custom contexts using [Picovoice Console](https://console.picovoice.ai/).\n\n## Table of Contents\n\n- [Rhino](#rhino)\n  - [Table of Contents](#table-of-contents)\n  - [Use Cases](#use-cases)\n  - [Try It Out](#try-it-out)\n  - [Language Support](#language-support)\n  - [Performance](#performance)\n  - [Terminology](#terminology)\n  - [Demos](#demos)\n    - [Python](#python-demos)\n    - [.NET](#net-demos)\n    - [Java](#java-demos)\n    - [Unity](#unity-demos)\n    - [Flutter](#flutter-demos)\n    - [React Native](#react-native-demos)\n    - [Android](#android-demos)\n    - [iOS](#ios-demos)\n    - [Web](#web-demos)\n      - [Vanilla JavaScript and HTML](#vanilla-javascript-and-html)\n      - [React](#react-demos)\n    - [Node.js](#nodejs-demos)\n    - [Rust](#rust-demos)\n    - [C](#c-demos)\n  - [SDKs](#sdks)\n    - [Python](#python)\n    - [.NET](#net)\n    - [Java](#java)\n    - [Unity](#unity)\n    - [Flutter](#flutter)\n    - [React Native](#react-native)\n    - [Android](#android)\n    - [iOS](#ios)\n    - [Web](#web)\n      - [Vanilla JavaScript and HTML (CDN Script Tag)](#vanilla-javascript-and-html-cdn-script-tag)\n      - [Vanilla JavaScript and HTML (ES Modules)](#vanilla-javascript-and-html-es-modules)\n      - [React](#react)\n    - [Node.js](#nodejs)\n    - [Rust](#rust)\n    - [C](#c)\n  - [Releases](#releases)\n  - [FAQ](#faq)\n\n## Use Cases\n\nRhino is the right choice if the domain of voice interactions is specific (limited).\n\n- If you want to create voice experiences similar to Alexa or Google, see the [Picovoice platform](https://github.com/Picovoice/picovoice).\n- If you need to recognize a few static (always listening) voice commands, see [Porcupine](https://github.com/Picovoice/porcupine).\n\n## Try It Out\n\n- [Interactive Web Demo](https://picovoice.ai/demos/barista/)\n\n- Rhino and [Porcupine](https://github.com/Picovoice/porcupine) on an ARM Cortex-M7\n\n[![Rhino in Action](https://img.youtube.com/vi/WadKhfLyqTQ/0.jpg)](https://www.youtube.com/watch?v=WadKhfLyqTQ)\n\n## Language Support\n\n- English, French, German, Italian, Japanese, Korean, Portuguese, and Spanish.\n- Support for [additional languages is available for commercial customers](https://picovoice.ai/consulting/) on a case-by-case basis.\n\n## Performance\n\nA comparison between the accuracy of Rhino and major cloud-based alternatives is provided\n[here](https://github.com/Picovoice/speech-to-intent-benchmark). Below is the summary of the benchmark:\n\n![](resources/.doc/benchmark.png)\n\n## Terminology\n\nRhino infers the user's intent from spoken commands within a domain of interest. We refer to such a specialized domain as\na `Context`. A context can be thought of a set of voice commands, each mapped to an intent:\n\n```yaml\nturnLightOff:\n  - Turn off the lights in the office\n  - Turn off all lights\nsetLightColor:\n  - Set the kitchen lights to blue\n```\n\nIn examples above, each voice command is called an `Expression`. Expressions are what we expect the user to utter\nto interact with our voice application.\n\nConsider the expression:\n\n\u003e Turn off the lights in the office\n\nWhat we require from Rhino is:\n\n1. To infer the intent (`turnLightOff`)\n2. Record the specific details from the utterance, in this case the location (`office`)\n\nWe can capture these details using slots by updating the expression:\n\n```yaml\nturnLightOff:\n  - Turn off the lights in the $location:lightLocation.\n```\n\n`$location:lightLocation` means that we expect a variable of type `location` to occur, and we want to capture its value\nin a variable named `lightLocation`. We call such variable a `Slot`. Slots give us the ability to capture details of the\nspoken commands. Each slot type is be defined as a set of phrases. For example:\n\n```yaml\nlightLocation:\n  - \"attic\"\n  - \"balcony\"\n  - \"basement\"\n  - \"bathroom\"\n  - \"bedroom\"\n  - \"entrance\"\n  - \"kitchen\"\n  - \"living room\"\n  - ...\n```\n\nYou can create custom contexts using the [Picovoice Console](https://console.picovoice.ai/).\n\nTo learn the complete expression syntax of Rhino, see the [Speech-to-Intent Syntax Cheat Sheet](https://picovoice.ai/docs/tips/syntax-cheat-sheet/).\n\n## Demos\n\nIf using SSH, clone the repository with:\n\n```console\ngit clone --recurse-submodules git@github.com:Picovoice/rhino.git\n```\n\nIf using HTTPS, clone the repository with:\n\n```console\ngit clone --recurse-submodules https://github.com/Picovoice/rhino.git\n```\n\n### Python Demos\n\nInstall the demo package:\n\n```console\nsudo pip3 install pvrhinodemo\n```\n\nWith a working microphone connected to your device run the following in the terminal:\n\n```\nrhino_demo_mic --access_key ${ACCESS_KEY} --context_path ${CONTEXT_PATH}\n```\n\nReplace `${CONTEXT_PATH}` with either a context file created using Picovoice Console or one within the repository.\n\nFor more information about Python demos, go to [demo/python](./demo/python).\n\n### .NET Demos\n\n[Rhino .NET demo](./demo/dotnet) is a command-line application that lets you choose between running Rhino on an audio\nfile or on real-time microphone input.\n\nMake sure there is a working microphone connected to your device. From [demo/dotnet/RhinoDemo](./demo/dotnet/RhinoDemo)\nrun the following in the terminal:\n\n```console\ndotnet run -c MicDemo.Release -- --access_key ${ACCESS_KEY} --context_path ${CONTEXT_FILE_PATH}\n```\n\nReplace `${ACCESS_KEY}` with your Picovoice `AccessKey` and `${CONTEXT_FILE_PATH}` with either a context file created using Picovoice Console or one within the repository.\n\nFor more information about .NET demos, go to [demo/dotnet](./demo/dotnet).\n\n### Java Demos\n\nThe [Rhino Java demo](./demo/java) is a command-line application that lets you choose between running Rhino on an\naudio file or on real-time microphone input.\n\nTo try the real-time demo, make sure there is a working microphone connected to your device. Then invoke the following commands from the terminal:\n\n```console\ncd demo/java\n./gradlew build\ncd build/libs\njava -jar rhino-mic-demo.jar -a ${ACCESS_KEY} -c ${CONTEXT_FILE_PATH}\n```\n\nReplace `${CONTEXT_FILE_PATH}` with either a context file created using Picovoice Console or one within the repository.\n\nFor more information about Java demos go to [demo/java](./demo/java).\n\n### Unity Demos\n\n\u003e Unity SDKs will no longer be maintained after **December 15, 2025**. If you plan to use the Rhino Speech-to-Intent Unity SDK for commercial purposes, please [contact us](https://picovoice.ai/contact/).\n\nTo run the Rhino Unity demo, import the [Rhino Unity package](./binding/unity/rhino-3.0.1.unitypackage) into your project, open the RhinoDemo scene and hit play. To run on other platforms or in the player, go to _File \u003e Build Settings_, choose your platform and hit the `Build and Run` button.\n/\nTo browse the demo source go to [demo/unity](./demo/unity).\n\n### Flutter Demos\n\nTo run the Rhino demo on Android or iOS with Flutter, you must have the [Flutter SDK](https://flutter.dev/docs/get-started/install) installed on your system. Once installed, you can run `flutter doctor` to determine any other missing requirements for your relevant platform. Once your environment has been set up, launch a simulator or connect an Android/iOS device.\n\nRun the `prepare_demo` script with a language code to set up the demo in the language of your\nchoice (e.g. `de` -\u003e German, `ko` -\u003e Korean). To see a list of available languages, run `prepare_demo` without a language code.\n\n```console\ncd demo/flutter\ndart scripts/prepare_demo.dart ${LANGUAGE}\n```\n\nRun the following command to build and deploy the demo to your device:\n\n```console\ncd demo/flutter\nflutter run\n```\n\nOnce the demo app has started, press the start button and utter a command to start inferring context. To see more details about the current context information, press the `Context Info` button on the top right corner in the app.\n\n### React Native Demos\n\nTo run the React Native Rhino demo app you will first need to set up your React Native environment. For this,\nplease refer to [React Native's documentation](https://reactnative.dev/docs/environment-setup). Once your environment has\nbeen set up, navigate to [demo/react-native](./demo/react-native) to run the following commands:\n\nFor Android:\n\n```console\nyarn android-install    # sets up environment\nyarn android-run        # builds and deploys to Android\n```\n\nFor iOS:\n\n```console\nyarn ios-install        # sets up environment\nyarn ios-run            # builds and deploys to iOS\n```\n\nBoth demos use a smart lighting context, which can understand commands such as:\n\n\u003e Turn off the lights.\n\nor\n\n\u003e Set the lights in the living room to purple.\n\n### Android Demos\n\nUsing Android Studio, open [demo/android/Activity](./demo/android/Activity) as an Android project and then run the\napplication.\n\nOnce the demo app has started, press the `Start` button and speak a command from the context to start inference. To see more details about\nthe current context information, press the `Show Context` button on the top right corner in the app.\n\nFor more information about Android demo, go to [demo/android](./demo/android).\n\n### iOS Demos\n\nTo run the application demo:\n\n1) From the [demo](./demo/ios) directory run:\n\n```console\npod install\n```\n\n2) Open `RhinoDemo.xcworkspace` in XCode.\n\n3) Replace `let accessKey = \"${YOUR_ACCESS_KEY_HERE}\"` in the file [ContentView.swift](./demo/ios/RhinoDemo/ContentView.swift) with your `AccessKey`.\n\n4) Go to `Product \u003e Scheme` and select the scheme for the language you would like to demo (e.g. `esDemo` -\u003e Spanish Demo, `deDemo` -\u003e German Demo)\n\n5) Run the demo with a simulator or connected iOS device.\n\n6) Once the demo app has started, press the `Start` button to infer audio within a context. To see more details about\nthe current context information, press the `Context Info` button on the top right corner in the app.\n\nFor more information about iOS demo, go to [demo/ios](./demo/ios).\n\n### Web Demos\n\n#### Vanilla JavaScript and HTML\n\nFrom [demo/web](./demo/web) use `yarn` or `npm` to install the dependencies, and the `start` script with a language code\nto start a local web server hosting the demo in the language of your choice (e.g. `pl` -\u003e Polish, `ko` -\u003e Korean).\nTo see a list of available languages, run `start` without a language code.\n\n```console\nyarn\nyarn start ${LANGUAGE}\n```\n\n(or)\n\n```console\nnpm install\nnpm run start ${LANGUAGE}\n```\n\nOpen `http://localhost:5000` in your browser to try the demo.\n\n#### React Demos\n\nFrom [demo/react](./demo/react) use `yarn` or `npm` to install the dependencies, and the `start` script with a language code\nto start a local web server hosting the demo in the language of your choice (e.g. `pl` -\u003e Polish, `ko` -\u003e Korean).\nTo see a list of available languages, run `start` without a language code.\n\n```console\nyarn\nyarn start ${LANGUAGE}\n```\n\n(or)\n\n```console\nnpm install\nnpm run start ${LANGUAGE}\n```\n\nOpen `http://localhost:3000` in your browser to try the demo.\n\n### Node.js Demos\n\nInstall the demo package:\n\n```console\nyarn global add @picovoice/rhino-node-demo\n```\n\nWith a working microphone connected to your device, run the following in the terminal:\n\n```console\nrhn-mic-demo --access_key ${ACCESS_KEY} --context_path ${CONTEXT_FILE_PATH}\n```\n\nReplace `${CONTEXT_FILE_PATH}` with either a context file created using Picovoice Console or one within the repository.\n\nFor more information about Node.js demos go to [demo/nodejs](./demo/nodejs).\n\n### Rust Demos\n\n\u003e Rust SDKs will no longer be maintained after **July 15, 2025**. If you plan to use the Rhino Speech-to-Intent Rust SDK for commercial purposes, please [contact us](https://picovoice.ai/contact/).\n\nThis demo opens an audio stream from a microphone and performs inference on spoken commands.\nFrom [demo/rust/micdemo](./demo/rust/micdemo) run the following:\n\n```console\ncargo run --release -- --access_key ${ACCESS_KEY} --context_path ${CONTEXT_FILE_PATH}\n```\n\nReplace `${CONTEXT_FILE_PATH}` with either a context file created using Picovoice Console or one within the repository.\n\nFor more information about Rust demos go to [demo/rust](./demo/rust).\n\n### C Demos\n\nThe C demo requires [CMake](https://cmake.org/) version 3.4 or higher.\n\n**Windows Requires [MinGW](http://mingw-w64.org) to build the demo.**\n\n#### Microphone Demo\n\nAt the root of the repository, build with:\n\n```console\ncmake -S demo/c/. -B demo/c/build \u0026\u0026 cmake --build demo/c/build --target rhino_demo_mic\n```\n\n##### Linux (x86_64), macOS (x86_64, arm64), and Raspberry Pi\n\nList input audio devices with:\n\n```console\n./demo/c/build/rhino_demo_mic --show_audio_devices\n```\n\nRun the demo using:\n\n```console\n./demo/c/build/rhino_demo_mic -l ${RHINO_LIBRARY_PATH} -m lib/common/rhino_params.pv \\\n-c resources/contexts/${PLATFORM}/smart_lighting_${PLATFORM}.rhn \\\n-d ${AUDIO_DEVICE_INDEX} -a ${ACCESS_KEY}\n```\n\nReplace `${LIBRARY_PATH}` with path to appropriate library available under [lib](/lib), `${PLATFORM}` with the\nname of the platform you are running on (`linux`, `raspberry-pi`, or `mac`), `${AUDIO_DEVICE_INDEX}` with\nthe index of your audio device and `${ACCESS_KEY}` with your Picovoice AccessKey.\n\n##### Windows\n\nList input audio devices with:\n\n```console\n.\\\\demo\\\\c\\\\build\\\\rhino_demo_mic.exe --show_audio_devices\n```\n\nRun the demo using:\n\n```console\n.\\\\demo\\\\c\\\\build\\\\rhino_demo_mic.exe -l lib/windows/amd64/libpv_rhino.dll -c lib/common/rhino_params.pv -c resources/contexts/windows/smart_lighting_windows.rhn -d ${AUDIO_DEVICE_INDEX} -a ${ACCESS_KEY}\n```\n\nReplace `${AUDIO_DEVICE_INDEX}` with the index of your audio device and `${ACCESS_KEY}` with your Picovoice AccessKey.\n\nThe demo opens an audio stream and infers your intent from spoken commands in the context of a smart lighting system.\nFor example, you can say:\n\n\u003e \"Turn on the lights in the bedroom.\"\n\n#### File Demo\n\nAt the root of the repository, build with:\n\n```console\ncmake -S demo/c/. -B demo/c/build \u0026\u0026 cmake --build demo/c/build --target rhino_demo_file\n```\n\n##### Linux (x86_64), macOS (x86_64, arm64), and Raspberry Pi\n\nRun the demo using:\n\n```console\n./demo/c/build/rhino_demo_file -l ${LIBRARY_PATH} -m lib/common/rhino_params.pv \\\n-c resources/contexts/${PLATFORM}/coffee_maker_${PLATFORM}.rhn -w resources/audio_samples/test_within_context.wav \\\n-a ${ACCESS_KEY}\n```\n\nReplace `${LIBRARY_PATH}` with path to appropriate library available under [lib](/lib), `${PLATFORM}` with the\nname of the platform you are running on (`linux`, `raspberry-pi`, or `mac`) and `${ACCESS_KEY}`\nwith your Picovoice AccessKey.\n\n##### Windows\n\nRun the demo using:\n\n```console\n.\\\\demo\\\\c\\\\build\\\\rhino_demo_file.exe -l lib/windows/amd64/libpv_rhino.dll -m lib/common/rhino_params.pv -c resources/contexts/windows/coffee_maker_windows.rhn -w resources/audio_samples/test_within_context.wav -a ${ACCESS_KEY}\n```\n\nReplace `${ACCESS_KEY}` with your Picovoice AccessKey.\n\nThe demo opens up the WAV file and infers the intent in the context of a coffee-maker system.\n\nFor more information about C demos go to [demo/c](./demo/c).\n\n## SDKs\n\n### Python\n\nInstall the Python SDK:\n\n```console\npip3 install pvrhino\n```\n\nThe SDK exposes a factory method to create instances of the engine:\n\n```python\nimport pvrhino\n\naccess_key = \"${ACCESS_KEY}\" # AccessKey obtained from Picovoice Console (https://console.picovoice.ai/)\n\nhandle = pvrhino.create(access_key=access_key, context_path='/absolute/path/to/context')\n```\n\nWhere `context_path` is the absolute path to the Speech-to-Intent context created either using Picovoice Console or one of\nthe default contexts available on Rhino's GitHub repository.\n\nWhen initialized, the required sample rate can be obtained using `rhino.sample_rate`. The expected frame length\n(number of audio samples in an input array) is provided by `rhino.frame_length`. The object can be used to infer intent from spoken\ncommands as below:\n\n```python\ndef get_next_audio_frame():\n    pass\n\nwhile True:\n    is_finalized = handle.process(get_next_audio_frame())\n\n    if is_finalized:\n        inference = handle.get_inference()\n        if not inference.is_understood:\n            # add code to handle unsupported commands\n            pass\n        else:\n            intent = inference.intent\n            slots = inference.slots\n            # add code to take action based on inferred intent and slot values\n```\n\nFinally, when done be sure to explicitly release the resources using `handle.delete()`.\n\n### .NET\n\nInstall the .NET SDK using NuGet or the dotnet CLI:\n\n```console\ndotnet add package Rhino\n```\n\nThe SDK exposes a factory method to create instances of the engine as below:\n\n```csharp\nusing Pv;\n\nconst string accessKey = \"${ACCESS_KEY}\";\nstring contextPath = \"/absolute/path/to/context.rhn\";\nRhino handle = Rhino.Create(accessKey, contextPath);\n```\n\nWhen initialized, the valid sample rate is given by `handle.SampleRate`. The expected frame length (number of audio samples\nin an input array) is `handle.FrameLength`. The engine accepts 16-bit linearly-encoded PCM and operates on\nsingle-channel audio.\n\n```csharp\nshort[] GetNextAudioFrame()\n{\n    // .. get audioFrame\n    return audioFrame;\n}\n\nwhile(true)\n{\n    bool isFinalized = handle.Process(GetNextAudioFrame());\n    if(isFinalized)\n    {\n        Inference inference = handle.GetInference();\n        if(inference.IsUnderstood)\n        {\n            string intent = inference.Intent;\n            Dictionary\u003cstring, string\u003e slots = inference.Slots;\n            // .. code to take action based on inferred intent and slot values\n        }\n        else\n        {\n            // .. code to handle unsupported commands\n        }\n    }\n}\n```\n\nRhino will have its resources freed by the garbage collector, but to have resources freed\nimmediately after use, wrap it in a `using` statement:\n\n```csharp\nusing(Rhino handle = Rhino.Create(accessKey, contextPath))\n{\n    // .. Rhino usage here\n}\n```\n\n### Java\n\nThe Rhino Java binding is available from the Maven Central Repository at `ai.picovoice:rhino-java:${version}`.\n\nThe SDK exposes a Builder that allows you to create an instance of the engine:\n\n```java\nimport ai.picovoice.rhino.*;\n\nfinal String accessKey = \"${ACCESS_KEY}\"; // AccessKey obtained from Picovoice Console (https://console.picovoice.ai/)\n\ntry{\n    Rhino handle = new Rhino.Builder()\n                    .setAccessKey(accessKey)\n                    .setContextPath(\"/absolute/path/to/context\")\n                    .build();\n} catch (RhinoException e) { }\n```\n\nWhen initialized, the valid sample rate is given by `handle.getSampleRate()`. The expected frame length (number of audio samples\nin an input array) is `handle.getFrameLength()`. The engine accepts 16-bit linearly-encoded PCM and operates on\nsingle-channel audio.\n\n```java\nshort[] getNextAudioFrame(){\n    // .. get audioFrame\n    return audioFrame;\n}\n\nwhile(true) {\n    boolean isFinalized = handle.process(getNextAudioFrame());\n    if(isFinalized){\n        RhinoInference inference = handle.getInference();\n        if(inference.getIsUnderstood()){\n            String intent = inference.getIntent();\n            Map\u003cstring, string\u003e slots = inference.getSlots();\n            // .. code to take action based on inferred intent and slot values\n        } else {\n            // .. code to handle unsupported commands\n        }\n    }\n}\n```\n\nOnce you are done with Rhino, ensure you release its resources explicitly:\n\n```java\nhandle.delete();\n```\n\n### Unity\n\n\u003e Unity SDKs will no longer be maintained after **December 15, 2025**. If you plan to use the Rhino Speech-to-Intent Unity SDK for commercial purposes, please [contact us](https://picovoice.ai/contact/).\n\nImport the [Rhino Unity Package](./binding/unity/rhino-3.0.1.unitypackage) into your Unity project.\n\nThe SDK provides two APIs:\n\n#### High-Level API\n\n[RhinoManager](./binding/unity/Assets/Rhino/RhinoManager.cs) provides a high-level API that takes care of audio recording. This class is the quickest way to get started.\n\nUsing the constructor `RhinoManager.Create` will create an instance of the RhinoManager using the provided context file.\n\n```csharp\nusing Pv.Unity;\n\nstring accessKey = \"${ACCESS_KEY}\"; // AccessKey obtained from Picovoice Console (https://console.picovoice.ai/)\n\ntry\n{\n    RhinoManager _rhinoManager = RhinoManager.Create(\n                                    accessKey,\n                                    \"/path/to/context/file.rhn\",\n                                    (inference) =\u003e {});\n}\ncatch (Exception ex)\n{\n    // handle rhino init error\n}\n```\n\nOnce you have instantiated a RhinoManager, you can start audio capture and intent inference by calling:\n\n```csharp\n_rhinoManager.Process();\n```\n\nAudio capture stops and Rhino resets once an inference result is returned via the inference callback. When you wish to result, call `.Process()` again.\n\nOnce the app is done with using an instance of RhinoManager, you can explicitly release the audio resources, and the resources allocated to Rhino:\n\n```csharp\n_rhinoManager.Delete();\n```\n\nThere is no need to deal with audio capture to enable intent inference with RhinoManager.\nThis is because it uses our\n[unity-voice-processor](https://github.com/Picovoice/unity-voice-processor/)\nUnity package to capture frames of audio and automatically pass it to the inference engine.\n\n#### Low-Level API\n\n[Rhino](./binding/unity/Assets/Rhino/Rhino.cs) provides low-level access to the inference engine for those who want to incorporate speech-to-intent into an already existing audio processing pipeline.\n\nTo create an instance of `Rhino`, use the `.Create` static constructor, and a context file.\n\n```csharp\nusing Pv.Unity;\n\nstring accessKey = \"${ACCESS_KEY}\"; // AccessKey obtained from Picovoice Console (https://console.picovoice.ai/)\n\ntry\n{\n    Rhino _rhino = Rhino.Create(accessKey, \"path/to/context/file.rhn\");\n}\ncatch (RhinoException ex)\n{\n    // handle rhino init error\n}\n```\n\nTo feed Rhino your audio, you must send it frames of audio to its `Process` function until it has made an inference.\n\n```csharp\nshort[] GetNextAudioFrame()\n{\n    // .. get audioFrame\n    return audioFrame;\n}\n\ntry\n{\n    bool isFinalized = _rhino.Process(GetNextAudioFrame());\n    if(isFinalized)\n    {\n        Inference inference = _rhino.GetInference();\n        if(inference.IsUnderstood)\n        {\n            string intent = inference.Intent;\n            Dictionary\u003cstring, string\u003e slots = inference.Slots;\n            // .. code to take action based on inferred intent and slot values\n        }\n        else\n        {\n            // .. code to handle unsupported commands\n        }\n    }\n}\ncatch (RhinoException ex)\n{\n    Debug.LogError(ex.ToString());\n}\n```\n\nFor process to work correctly, the audio data must be in the audio format required by Picovoice.\n\nRhino implements the `IDisposable` interface, so you can use Rhino in a `using` block. If you don't use a `using` block, resources will be released by the garbage collector automatically, or you can explicitly release the resources like so:\n\n```csharp\n_rhino.Dispose();\n```\n\n### Flutter\n\nAdd the [Rhino Flutter plugin](https://pub.dev/packages/rhino) to your pub.yaml.\n\n```yaml\ndependencies:\n  rhino_flutter: ^\u003cversion\u003e\n```\n\nThe SDK provides two APIs:\n\n#### High-Level API\n\n[RhinoManager](./binding/flutter/lib/rhino_manager.dart) provides a high-level API that takes care of audio recording. This class is the quickest way to get started.\n\nThe constructor `RhinoManager.create` will create an instance of the RhinoManager using a context file that you pass to it.\n\n```dart\nimport 'package:rhino_flutter/rhino_manager.dart';\nimport 'package:rhino_flutter/rhino_error.dart';\n\nfinal String accessKey = \"{ACCESS_KEY}\";  // AccessKey obtained from Picovoice Console (https://console.picovoice.ai/)\n\nvoid createRhinoManager() async {\n    try{\n        _rhinoManager = await RhinoManager.create(\n            accessKey,\n            \"/path/to/context/file.rhn\",\n            _inferenceCallback);\n    } on RhinoException catch (err) {\n        // handle rhino init error\n    }\n}\n```\n\nThe `inferenceCallback` parameter is a function that you want to execute when Rhino makes an inference.\nThe function should accept a `RhinoInference` instance that represents the inference result.\n\n```dart\nvoid _inference(RhinoInference inference) {\n    if(inference.isUnderstood!) {\n        String intent = inference.intent!;\n        Map\u003cString, String\u003e = inference.slots!;\n        // add code to take action based on inferred intent and slot values\n    }\n    else {\n        // add code to handle unsupported commands\n    }\n}\n```\n\nOnce you have instantiated a RhinoManager, you can start audio capture and intent inference using the `.process()` function.\nAudio capture stops and rhino resets once an inference result is returned via the inference callback.\n\n```dart\ntry {\n    await _rhinoManager.process();\n} on RhinoException catch (ex) { }\n```\n\nOnce your app is done with using RhinoManager, be sure you explicitly release the resources allocated for it:\n\n```dart\n_rhinoManager.delete();\n```\n\nOur [flutter_voice_processor](https://github.com/Picovoice/flutter-voice-processor/) Flutter plugin captures the frames of audio and automatically passes it to the speech-to-intent engine.\n\n#### Low-Level API\n\n[Rhino](./binding/flutter/lib/rhino.dart) provides low-level access to the inference engine for those who want to incorporate\nspeech-to-intent into an already existing audio processing pipeline.\n\n`Rhino` is created by passing a context file to its static constructor `create`:\n\n```dart\nimport 'package:rhino_flutter/rhino_manager.dart';\nimport 'package:rhino_flutter/rhino_error.dart';\n\nfinal String accessKey = \"{ACCESS_KEY}\"; // AccessKey obtained from Picovoice Console (https://console.picovoice.ai/)\n\nvoid createRhino() async {\n    try {\n        _rhino = await Rhino.create(accessKey, '/path/to/context/file.rhn');\n    } on RhinoException catch (err) {\n        // handle rhino init error\n    }\n}\n```\n\nTo deliver audio to the engine, you must send audio frames to its `process` function.\nEach call to `process` will return a `RhinoInference` instance with following variables:\n\n- isFinalized - true if Rhino has made an inference, false otherwise\n- isUnderstood - **null** if `isFinalized` is false, otherwise true if Rhino understood what it heard based on the context or false if it did not\n- intent - **null** if `isUnderstood` is not true, otherwise name of intent that were inferred\n- slots - **null** if `isUnderstood` is not true, otherwise the dictionary of slot keys and values that were inferred\n\n```dart\nList\u003cint\u003e buffer = getAudioFrame();\n\ntry {\n    RhinoInference inference = await _rhino.process(buffer);\n    if(inference.isFinalized) {\n        if(inference.isUnderstood!) {\n            String intent = inference.intent!;\n            Map\u003cString, String\u003e = inference.slots!;\n            // add code to take action based on inferred intent and slot values\n        }\n    }\n} on RhinoException catch (error) {\n    // handle error\n}\n\n// once you are done\nthis._rhino.delete();\n```\n\n### React Native\n\nInstall [@picovoice/react-native-voice-processor](https://www.npmjs.com/package/@picovoice/react-native-voice-processor) and\n[@picovoice/rhino-react-native](https://www.npmjs.com/package/@picovoice/rhino-react-native). The SDK provides two APIs:\n\n#### High-Level API\n\n[RhinoManager](./binding/react-native/src/rhino_manager.tsx) provides a high-level API that takes care of\naudio recording. This class is the quickest way to get started.\n\nThe constructor `RhinoManager.create` will create an instance of a RhinoManager using a context file that you pass to it.\n\n```javascript\nconst accessKey = \"${ACCESS_KEY}\"; // AccessKey obtained from Picovoice Console (https://console.picovoice.ai/)\n\nasync createRhinoManager(){\n    try{\n        this._rhinoManager = await RhinoManager.create(\n            accessKey,\n            '/path/to/context/file.rhn',\n            inferenceCallback);\n    } catch (err) {\n        // handle error\n    }\n}\n```\n\nOnce you have instantiated a RhinoManager, you can start/stop audio capture and intent inference by calling `.process()`.\nUpon receiving an inference callback, audio capture will stop automatically and Rhino will reset. To restart it you must\ncall `.process()` again.\n\n```javascript\nlet didStart = await this._rhinoManager.process();\n```\n\nWhen you are done using Rhino, you must explicitly release resources:\n\n```javascript\nthis._rhinoManager.delete();\n```\n\n[@picovoice/react-native-voice-processor](https://github.com/Picovoice/react-native-voice-processor/) handles\naudio capture and RhinoManager passes frames to the inference engine for you.\n\n#### Low-Level API\n\n[Rhino](./binding/react-native/src/rhino.tsx) provides low-level access to the inference engine for those\nwho want to incorporate speech-to-intent into an already existing audio processing pipeline.\n\n`Rhino` is created by passing a context file to its static constructor `create`:\n\n```javascript\nconst accessKey = \"${ACCESS_KEY}\"; // AccessKey obtained from Picovoice Console (https://console.picovoice.ai/)\n\nasync createRhino(){\n    try{\n        this._rhino = await Rhino.create(accessKey, '/path/to/context/file.rhn');\n    } catch (err) {\n        // handle error\n    }\n}\n```\n\nTo deliver audio to the engine, you must pass it audio frames\nusing the `process` function. The `RhinoInference` result that is returned from `process` will have up to four fields:\n\n- isFinalized - true if Rhino has made an inference, false otherwise\n- isUnderstood - **null** if `isFinalized` is false, otherwise true if Rhino understood what it heard based on the context or false if it did not\n- intent - **null** if `isUnderstood` is not true, otherwise name of intent that were inferred\n- slots - **null** if `isUnderstood` is not true, otherwise the dictionary of slot keys and values that were inferred\n\n```javascript\nlet buffer = getAudioFrame();\ntry {\n    let inference = await this._rhino.process(buffer);\n    // use result\n    // ..\n    }\n} catch (e) {\n    // handle error\n}\n\n// once you are done\nthis._rhino.delete();\n```\n\n### Android\n\nTo include the package in your Android project, ensure you have included `mavenCentral()` in your top-level `build.gradle` file and then add the following to your app's `build.gradle`:\n\n```groovy\ndependencies {\n    implementation 'ai.picovoice:rhino-android:${LATEST_VERSION}'\n}\n```\n\nThere are two possibilities for integrating Rhino into an Android application: the High-level API and the Low-level API.\n\n#### High-Level API\n\n[RhinoManager](binding/android/Rhino/rhino/src/main/java/ai/picovoice/rhino/RhinoManager.java) provides a high-level API\nfor integrating Rhino into Android applications. It manages all activities related to creating an input audio stream,\nfeeding it into Rhino, and invoking a user-provided inference callback. Context files (`.rhn`)\nshould be placed under the Android project assets folder (`src/main/assets/`).\n\n```java\nfinal String accessKey = \"${ACCESS_KEY}\"; // AccessKey obtained from Picovoice Console (https://console.picovoice.ai/)\nfinal String contextPath = \"/path/to/context.rhn\" // path relative to 'assets' folder\n\ntry {\n    RhinoManager rhinoManager = new RhinoManager.Builder()\n                        .setAccessKey(accessKey)\n                        .setContextPath(\"/path/to/context.rhn\")\n                        .setSensitivity(0.35f)\n                        .build(appContext, new RhinoManagerCallback() {\n                            @Override\n                            public void invoke(RhinoInference inference) {\n                                if (inference.getIsUnderstood()) {\n                                    final String intent = inference.getIntent();\n                                    final Map\u003cString, String\u003e slots = inference.getSlots();\n                                    // add code to take action based on inferred intent and slot values\n                                }\n                                else {\n                                    // add code to handle unsupported commands\n                                }\n                            }\n                        });\n} catch (RhinoException e) { }\n```\n\nThe `appContext` parameter is the Android application context - this is used to extract Rhino resources from the APK.\nSensitivity is the parameter that enables developers to trade miss rate for false alarm. It is a floating-point number within\n[0, 1]. A higher sensitivity reduces miss rate at cost of increased false alarm rate.\n\nWhen initialized, input audio can be processed using `manager.process()`. When done, be sure to release the resources\nusing `manager.delete()`.\n\n#### Low-Level API\n\nRhino provides a binding for Android using JNI. It can be initialized using:\n\n```java\nimport ai.picovoice.rhino.*;\n\nfinal String accessKey = \"${ACCESS_KEY}\"; // AccessKey obtained from Picovoice Console (https://console.picovoice.ai/)\n\ntry {\n    Rhino rhino = new Rhino.Builder()\n                        .setAccessKey(accessKey)\n                        .setContextPath(\"/path/to/context.rhn\")\n                        .build(appContext);\n} catch (RhinoException e) { }\n```\n\nOnce initialized, `handle` can be used for intent inference:\n\n```java\nprivate short[] getNextAudioFrame();\n\nwhile (!handle.process(getNextAudioFrame()));\n\nfinal RhinoInference inference = handle.getInference();\nif (inference.getIsUnderstood()) {\n    // logic to perform an action given the intent object.\n} else {\n    // logic for handling out of context or unrecognized command\n}\n```\n\nFinally, prior to exiting the application be sure to release resources acquired:\n\n```java\nhandle.delete()\n```\n\n### iOS\n\n\u003c!-- markdown-link-check-disable --\u003e\nThe Rhino iOS binding is available via [CocoaPods](https://cocoapods.org/pods/Rhino-iOS). To import it into your iOS project, add the following line to your Podfile and run `pod install`:\n\u003c!-- markdown-link-check-enable --\u003e\n\n```ruby\npod 'Rhino-iOS'\n```\n\nThere are two approaches for integrating Rhino into an iOS application: The high-level API and the low-level API.\n\n#### High-Level API\n\n[RhinoManager](./binding/ios/RhinoManager.swift) provides a high-level API\nfor integrating Rhino into iOS applications. It manages all activities related to creating an input audio stream, feeding it to the engine, and invoking a user-provided inference callback.\n```swift\nimport Rhino\n\nlet accessKey = \"${ACCESS_KEY}\" // Obtained from Picovoice Console (https://console.picovoice.ai)\ndo {\n    let manager = try RhinoManager(\n        accessKey: accessKey,\n        contextPath: \"/path/to/context/file.rhn\",\n        modelPath: \"/path/to/model/file.pv\",\n        sensitivity: 0.35,\n        onInferenceCallback: { inference in\n                if inference.isUnderstood {\n                    let intent:String = inference.intent\n                    let slots:Dictionary\u003cString,String\u003e = inference.slots\n                    // use inference results\n                }\n            })\n} catch { }\n```\n\nSensitivity is the parameter that enables developers to trade miss rate for false alarm. It is a floating-point number within\n[0, 1]. A higher sensitivity reduces miss rate at cost of increased false alarm rate.\n\nWhen initialized, input audio can be processed using `manager.process()`. When done, be sure to release the resources\nusing `manager.delete()`.\n\n#### Low-Level API\n\n[Rhino](./binding/ios/Rhino.swift) provides low-level access to the Speech-to-Intent engine for those who want to incorporate intent inference into an already existing audio processing pipeline.\n\n```swift\nimport Rhino\n\nlet accessKey = \"${ACCESS_KEY}\" // Obtained from Picovoice Console (https://console.picovoice.ai)\ndo {\n    let handle = try Rhino(\n      accessKey: accessKey,\n      contextPath: \"/path/to/context/file.rhn\")\n} catch { }\n```\n\nOnce initialized, `handle` can be used for intent inference:\n\n```swift\nfunc getNextAudioFrame() -\u003e [Int16] {\n    // .. get audioFrame\n    return audioFrame\n}\n\nwhile true {\n    do {\n        let isFinalized = try handle.process(getNextAudioFrame())\n        if isFinalized {\n            let inference = try handle.getInference()\n            if inference.isUnderstood {\n                let intent:String = inference.intent\n                let slots:Dictionary\u003cString, String\u003e = inference.slots\n                // add code to take action based on inferred intent and slot values\n            }\n        }\n    } catch { }\n}\n```\n\nFinally, prior to exiting the application be sure to release resources acquired:\n\n```swift\nhandle.delete()\n```\n\n### Web\n\nRhino is available on modern web browsers (i.e. not Internet Explorer) via [WebAssembly](https://webassembly.org/). Microphone audio is handled via the [Web Audio API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Audio_API) and is abstracted by the WebVoiceProcessor, which also handles downsampling to the correct format. Rhino is provided pre-packaged as a [Web Worker](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers).\n\n#### Vanilla JavaScript and HTML (CDN Script Tag)\n\n```html\n\u003c!DOCTYPE html\u003e\n\u003chtml lang=\"en\"\u003e\n  \u003chead\u003e\n    \u003cscript src=\"https://unpkg.com/@picovoice/rhino-web/dist/iife/index.js\"\u003e\u003c/script\u003e\n    \u003cscript src=\"https://unpkg.com/@picovoice/web-voice-processor/dist/iife/index.js\"\u003e\u003c/script\u003e\n    \u003cscript type=\"application/javascript\"\u003e\n      const RHINO_CONTEXT_BASE64 = /* Base64 representation of `.rhn` context file  */;\n      const RHINO_MODEL_BASE64 = /* Base64 representation of the `.pv` model file */;\n\n      let rhino = null;\n\n      function rhinoInferenceCallback(inference) {\n        if (inference.isFinalized) {\n          console.log(`Inference detected: ${JSON.stringify(inference)}`);\n          WebVoiceProcessor.WebVoiceProcessor.unsubscribe(rhino);\n          document.getElementById(\"push-to-talk\").disabled = false;\n          console.log(\"Press the 'Push to Talk' button to speak again.\");\n        }\n      }\n\n      async function startRhino() {\n        console.log(\"Rhino is loading. Please wait...\");\n        rhino = await RhinoWeb.RhinoWorker.create(\n            accessKey: \"${ACCESS_KEY}\",  // AccessKey obtained from Picovoice Console (https://console.picovoice.ai/)\n            { base64: RHINO_CONTEXT_BASE64 },\n            rhinoInferenceCallback,\n            { base64: RHINO_MODEL_BASE64 }\n        );\n\n        console.log(\"Rhino worker ready!\");\n        document.getElementById(\"push-to-talk\").disabled = false;\n        writeMessage(\"Press the 'Push to Talk' button to talk.\");\n      }\n\n      document.addEventListener(\"DOMContentLoaded\", function () {\n        document.getElementById(\"push-to-talk\").onclick = function (event) {\n          if (rhino) {\n            console.log(\"Rhino is listening for your commands ...\");\n            this.disabled = true;\n            WebVoiceProcessor.WebVoiceProcessor.subscribe(rhino);\n          }\n        };\n      });\n    \u003c/script\u003e\n  \u003c/head\u003e\n  \u003cbody\u003e\n    \u003cbutton id=\"push-to-talk\"\u003ePush to Talk\u003c/button\u003e\n  \u003c/body\u003e\n\u003c/html\u003e\n\n```\n\n#### Vanilla JavaScript and HTML (ES Modules)\n\n```console\nyarn add @picovoice/rhino-web @picovoice/web-voice-processor\n```\n\n(or)\n\n```console\nnpm install @picovoice/rhino-web @picovoice/web-voice-processor\n```\n\n```javascript\nimport { WebVoiceProcessor } from \"@picovoice/web-voice-processor\"\nimport { RhinoWorker } from \"@picovoice/rhino-web\";\n\nconst RHN_CONTEXT_BASE64 = /* Base64 representation of a `.rhn` context file */\nconst RHINO_MODEL_BASE64 = /* Base64 representation of the `.pv` model file*/;\n\nlet rhino = null\n\nfunction rhinoInferenceCallback(inference) {\n  if (inference.isFinalized) {\n    console.log(`Rhino inference: ${JSON.stringify(inference)}`);\n    WebVoiceProcessor.unsubscribe(rhino);\n  }\n}\n\nasync function startRhino() {\n  // Create a Rhino Worker to listen for commands in the specified context\n  rhino = await RhinoWorker.create(\n    accessKey: \"${ACCESS_KEY}\",  // AccessKey obtained from Picovoice Console (https://console.picovoice.ai/)\n    { base64: RHINO_CONTEXT_BASE64 },\n    rhinoInferenceCallback,\n    { base64: RHINO_MODEL_BASE64 }\n  );\n}\n\n// Start a voice interaction:\n// WebVoiceProcessor will request microphone permission.\n// n.b. This promise will reject if the user refuses permission! Make sure you handle that possibility.\nfunction pushToTalk() {\n  if (rhino) {\n    WebVoiceProcessor.subscribe(rhino);\n  }\n}\n\nstartRhino()\n\n...\n\n// Finished with Rhino? Release the WebVoiceProcessor and the worker.\nif (done) {\n  WebVoiceProcessor.unsubscribe(rhino);\n  rhino.release()\n  rhino.terminate()\n}\n```\n\n#### React\n\n```console\nyarn add @picovoice/rhino-react @picovoice/web-voice-processor\n```\n\n(or)\n\n```console\nnpm install @picovoice/rhino-react @picovoice/web-voice-processor\n```\n\n```javascript\nimport React, { useEffect } from 'react';\nimport { useRhino } from '@picovoice/rhino-react';\n\nconst RHINO_CONTEXT_BASE64 = /* Base64 representation of a Rhino context (.rhn) for WASM, omitted for brevity */\nconst RHN_MODEL_BASE64 = /* Base64 representation of a Rhino parameter model (.pv), omitted for brevity */\n\nfunction VoiceWidget(props) {\n  const {\n    inference,\n    contextInfo,\n    isLoaded,\n    isListening,\n    error,\n    init,\n    process,\n    release,\n  } = useRhino();\n\n  useEffect(() =\u003e {\n    if (!isLoaded) {\n      init(\n        \"${ACCESS_KEY}\", // AccessKey obtained from Picovoice Console (https://console.picovoice.ai/)\n        { base64: RHINO_CONTEXT_BASE64 },\n        { base64: RHN_MODEL_BASE64 }\n      );\n    }\n  }, [isLoaded])\n\nreturn (\n  \u003cdiv className=\"voice-widget\"\u003e\n    \u003cbutton onClick={() =\u003e process()} disabled={isListening || !isLoaded || error !== null}\u003e\n      Process\n    \u003c/button\u003e\n    \u003cp\u003e{JSON.stringify(inference)}\u003c/p\u003e\n  \u003c/div\u003e\n)\n```\n\n### Node.js\n\nInstall the Node.js SDK:\n\n```console\nyarn add @picovoice/rhino-node\n```\n\nCreate instances of the Rhino class by specifying the path to the context file:\n\n```javascript\nconst Rhino = require(\"@picovoice/rhino-node\");\nconst accessKey = \"${ACCESS_KEY}\" // Obtained from the Picovoice Console (https://console.picovoice.ai/)\nlet handle = new Rhino(accessKey, \"/path/to/context/file.rhn\");\n```\n\nWhen instantiated, `handle` can process audio via its `.process` method:\n\n```javascript\nlet getNextAudioFrame = function() {\n    ...\n};\n\nlet isFinalized = false;\nwhile (!isFinalized) {\n  isFinalized = handle.process(getNextAudioFrame());\n  if (isFinalized) {\n    let inference = engineInstance.getInference();\n    // Insert inference event callback\n  }\n}\n```\n\nWhen done, be sure to release resources acquired by WebAssembly using `release()`:\n\n```javascript\nhandle.release();\n```\n\n### Rust\n\n\u003e Rust SDKs will no longer be maintained after **July 15, 2025**. If you plan to use the Rhino Speech-to-Intent Rust SDK for commercial purposes, please [contact us](https://picovoice.ai/contact/).\n\nFirst you will need [Rust and Cargo](https://rustup.rs/) installed on your system.\n\nTo add the rhino library into your app, add `pv_rhino` to your apps `Cargo.toml` manifest:\n```toml\n[dependencies]\npv_rhino = \"*\"\n```\n\nTo create an instance of the engine you first create a `RhinoBuilder` instance with the configuration parameters for the speech to intent engine and then make a call to `.init()`:\n\n```rust\nuse rhino::RhinoBuilder;\n\nlet access_key = \"${ACCESS_KEY}\"; // AccessKey obtained from Picovoice Console (https://console.picovoice.ai/)\n\nlet rhino: Rhino = RhinoBuilder::new(access_key, \"/path/to/context/file.rhn\").init().expect(\"Unable to create Rhino\");\n```\n\nTo feed audio into Rhino, use the `process` function in your capture loop:\n```rust\nfn next_audio_frame() -\u003e Vec\u003ci16\u003e {\n    // get audio frame\n}\n\nloop {\n    if let Ok(is_finalized) = rhino.process(\u0026next_audio_frame()) {\n        if is_finalized {\n            if let Ok(inference) = rhino.get_inference() {\n                if inference.is_understood {\n                    let intent = inference.intent.unwrap();\n                    let slots = inference.slots;\n                    // add code to take action based on inferred intent and slot values\n                } else {\n                    // add code to handle unsupported commands\n                }\n            }\n        }\n    }\n}\n```\n\n### C\n\nRhino is implemented in ANSI C and therefore can be directly linked to C applications. The [pv_rhino.h](./include/pv_rhino.h)\nheader file contains relevant information. An instance of the Rhino object can be constructed as follows:\n\n```c\nconst char *access_key = \"${ACCESS_KEY}\" // obtained from the Picovoice Console (https://console.picovoice.ai/)\nconst char *model_path = ... // Available at lib/common/rhino_params.pv\nconst char *context_path = ... // absolute path to context file for the domain of interest\nconst float sensitivity = 0.5f;\nbool require_endpoint = false;\n\npv_rhino_t *handle = NULL;\nconst pv_status_t status = pv_rhino_init(access_key, model_path, context_path, sensitivity, require_endpoint, \u0026handle);\nif (status != PV_STATUS_SUCCESS) {\n    // add error handling code\n}\n```\n\nNow the `handle` can be used to infer intent from an incoming audio stream. Rhino accepts single channel, 16-bit PCM\naudio. The sample rate can be retrieved using `pv_sample_rate()`. Finally, Rhino accepts input audio in consecutive chunks\n(frames); the length of each frame can be retrieved using `pv_rhino_frame_length()`.\n\n```c\nextern const int16_t *get_next_audio_frame(void);\n\nwhile (true) {\n    const int16_t *pcm = get_next_audio_frame();\n\n    bool is_finalized = false;\n    pv_status_t status = pv_rhino_process(handle, pcm, \u0026is_finalized);\n    if (status != PV_STATUS_SUCCESS) {\n        // add error handling code\n    }\n\n    if (is_finalized) {\n        bool is_understood = false;\n        status = pv_rhino_is_understood(rhino, \u0026is_understood);\n        if (status != PV_STATUS_SUCCESS) {\n            // add error handling code\n        }\n\n        if (is_understood) {\n            const char *intent = NULL;\n            int32_t num_slots = 0;\n            const char **slots = NULL;\n            const char **values = NULL;\n            status = pv_rhino_get_intent(rhino, \u0026intent, \u0026num_slots, \u0026slots, \u0026values);\n            if (status != PV_STATUS_SUCCESS) {\n                // add error handling code\n            }\n\n            // add code to take action based on inferred intent and slot values\n\n            pv_rhino_free_slots_and_values(rhino, slots, values);\n        } else {\n            // add code to handle unsupported commands\n        }\n\n        pv_rhino_reset(rhino);\n    }\n}\n```\n\nWhen done, remember to release the resources acquired.\n\n```c\npv_rhino_delete(rhino);\n```\n\n## Releases\n\n### v3.0.0 - October 26th, 2023\n\n- Improvements to error reporting\n- Upgrades to authorization and authentication system\n- Added `reset()` function to API\n- Various bug fixes and improvements\n- Node min support bumped to 16\n- Unity editor min support bumped to 2021\n- Patches to .NET support\n\n### v2.2.0 - April 12th, 2023\n\n- Added language support for Arabic, Dutch, Hindi, Mandarin, Polish, Russian, Swedish and Vietnamese\n- Added support for .NET 7.0 and fixed support for .NET Standard 2.0\n- iOS minimum support moved to 11.0\n- Improved stability and performance\n\n### v2.1.0 - January 20th, 2022\n\n- Added macOS arm64 support for Java and Unity SDKs\n- Support added for non-English built-in slots\n- Support for Macros added\n- Various bug fixes and improvements\n\n### v2.0.0 - November 25th, 2021\n\n- Improved accuracy\n- Added Rust SDK\n- macOS arm64 support\n- Added NodeJS support for Windows, NVIDIA Jetson Nano, and BeagleBone\n- Added .NET support for NVIDIA Jetson Nano and BeagleBone\n- Runtime optimization\n\n### v1.6.0 - December 2nd, 2020\n\n- Added support for React Native\n- Added support for Java\n- Added support for .NET\n- Added support for NodeJS\n\n### v1.5.0 - June 4th, 2020\n\n- Accuracy improvements\n\n### v1.4.0 - April 13th, 2020\n\n- Accuracy improvements\n- Builtin slots\n\n### v1.3.0 - February 13th, 2020\n\n- Accuracy improvements\n- Runtime optimizations\n- Added support for Raspberry Pi 4\n- Added support for JavaScript\n- Added support for iOS\n- Updated documentation\n\n### v1.2.0 - April 26, 2019\n\n- Accuracy improvements\n- Runtime optimizations\n\n### v1.1.0 - December 23rd, 2018\n\n- Accuracy improvements\n- Open-sourced Raspberry Pi build\n\n### v1.0.0 - November 2nd, 2018\n\n- Initial Release\n\n## FAQ\n\nYou can find the FAQ [here](https://picovoice.ai/docs/faq/rhino/).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpicovoice%2Frhino","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpicovoice%2Frhino","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpicovoice%2Frhino/lists"}