From 2c7ce5e5c98c1222cd26425db457ef1c5eedda92 Mon Sep 17 00:00:00 2001 From: Fabio Manganiello Date: Sun, 31 Jan 2021 13:07:59 +0100 Subject: [PATCH] Migrated 9th article --- static/img/voice-1.jpg | Bin 0 -> 47870 bytes static/pages/Build-custom-voice-assistants.md | 742 ++++++++++++++++++ 2 files changed, 742 insertions(+) create mode 100644 static/img/voice-1.jpg create mode 100644 static/pages/Build-custom-voice-assistants.md diff --git a/static/img/voice-1.jpg b/static/img/voice-1.jpg new file mode 100644 index 0000000000000000000000000000000000000000..0f1059565f1f37a7e1e920d03b8b7e170c3c5a1e GIT binary patch literal 47870 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 +[//]: # (published: 2020-03-08) + +I wrote [an article](https://blog.platypush.tech/article/Build-your-customizable-voice-assistant-with-Platypush) a while +ago that describes how to make your own Google-based voice assistant using just a RaspberryPi, Platypush, a speaker and +a microphone. + +It also showed how to make your own custom hotword model that triggers the assistant if you don’t want to say “Ok +Google,” or if you want distinct hotwords to trigger different assistants in different languages. It also showed how to +hook your own custom logic and scripts when certain phrases are recognized, without writing any code. + +Since I wrote that article, a few things have changed: + +- When I wrote the article, Platypush only supported the Google Assistant as a voice back end. In the meantime, I’ve + worked on [supporting Alexa as well](https://github.com/BlackLight/platypush/issues/80). Feel free to use the + `assistant.echo` integration in Platypush if you’re an Alexa fan, but bear in mind that it’s more limited than the + existing Google Assistant based options — there are limitations in the AVS (Amazon Voice Service). For example, it + won’t provide the transcript of the detected text, which means it’s not possible to insert custom hooks or the + transcript of the rendered response because the AVS mostly works with audio files as input and provides audio as + output. It could also experience some minor audio glitches, at least on RasbperryPi. + +- Although deprecated, a new release of the Google Assistant + Library [has been made available](https://github.com/googlesamples/assistant-sdk-python/releases/tag/0.6.0) to fix the + segmentation fault issue on RaspberryPi 4. I’ve buzzed the developers often over the past year and I’m glad that it’s + been done! It’s good news because the Assistant library has the best engine for hotword detection I’ve seen. No other + SDK I’ve tried — Snowboy, DeepSpeech, or PicoVoice — comes close to the native “Ok Google” hotword detection accuracy + and performance. The news isn’t all good, however: The library is still deprecated, with no alternative is currently + on the horizon. The new release was mostly made in response to user requests to fix things on the new RaspberryPi. But + at least one of the best options out there to build a voice assistant will still work for a while. Those interested in + building a custom voice assistant that acts 100% like a native Google Assistant can read my previous article. + +- In the meantime, the shaky situation of the official voice assistant SDK has motivated me to research more + state-of-art alternatives. I’ve been a long-time fan of [Snowboy](https://snowboy.kitt.ai/), which has a + well-supported platypush integration, and I’ve used it as a hotword engine to trigger other assistant integrations for + a long time. However, when it comes to accuracy in real-time scenarios, even its best models aren’t that satisfactory. + I’ve also experimented with + [Mozilla DeepSpeech](https://github.com/mozilla/DeepSpeech) and [PicoVoice](https://github.com/Picovoice) products, + for voice detection and built integrations in Platypush. In this article, I’ll try to provide a comprehensive overview + of what’s currently possible with DIY voice assistants and a comparison of the integrations I’ve built. + +- **EDIT January 2021**: Unfortunately, as of Dec 31st, + 2020 [Snowboy has been officially shut down](https://github.com/Kitt-AI/snowboy/). The GitHub repository is still + there, you can still clone it and either use the example models provided under `resources/models`, train a model + using the Python API or use any of your previously trained model. However, the repo is no longer maintained, and the + website that could be used to browse and generate user models is no longer available. It's really a shame - the user + models provided by Snowboy were usually quite far from perfect, but it was a great example of crowd-trained + open-source project, and it just shows how difficult it is to keep such projects alive without anybody funding the + time invested by the developers in them. Anyway, most of the Snowboy examples reported in this article will still work + if you download and install the code from the repo. + +## The Case for DIY Voice Assistants + +Why would anyone bother to build their own voice assistant when cheap Google or Alexa assistants can be found anywhere? Despite how pervasive these products have become, I decided to power my whole house with several DIY assistants for a number of reasons: + +- **Privacy**. The easiest one to guess! I’m not sure if a microphone in the house, active 24/7, connected to a private + company through the internet is a proportionate price to pay for between five and ten interactions a day to toggle the + lightbulbs, turn on the thermostat, or play a Spotify playlist. I’ve built the voice assistant integrations in + platypush with the goal of giving people the option of voice-enabled services without sending all of the daily voice + interactions over a privately-owned channel through a privately-owned box. + +- **Compatibility**. A Google Assistant device will only work with devices that support Google Assistant. The same goes + for Alexa-powered devices. Some devices may lose some of their voice-enabled capabilities — either temporarily, + depending on the availability of the cloud connections, or permanently, because of hardware or software deprecation or + other commercial factors. My dream voice assistant works natively with any device, as long as it has an SDK or API to + interact with, and does not depend on business decisions. + +- **Flexibility**. Even when a device works with your assistant, you’re still bound to the features that have been + agreed and implemented by the two parties. Implementing more complex routines over voice commands is usually tricky. + In most cases, it involves creating code that will run on the cloud (either in the form of Actions or Lambdas, or + IFTTT rules), not in your own network, which limits the actual possibilities. My dream assistant must have the ability + to run whichever logic I want on whichever device I want, using whichever custom shortcut I want (even with regex + matching), regardless of the complexity. I also aimed to build an assistant that can provide multiple services ( + Google, Alexa, Siri etc.) in multiple languages on the same device, simply by using different hotwords. + +- **Hardware constraints**. I’ve never understood the case for selling plastic boxes that embed a microphone and a speaker + in order to enter the world of voice services. That was a good way to showcase the idea. After a couple of years of + experiments, it’s probably time to expect the industry to provide a voice assistant experience that can run on any + device, as long as it has a microphone and a controller unit that can process code. As for compatibility, there should + be no case for Google-compatible or Alexa-compatible devices. Any device should be compatible with any assistant, as + long as that device has a way to communicate with the outside world. The logic to control that device should be able + to run on the same network that the device belongs to. + +- **Cloud vs. local processing**. Most of the commercial voice assistants operate by regularly capturing streams of + audio, scanning for the hotword in the audio chunks through their cloud -provided services, and opening another + connection to their cloud services once the hotword is detected, to parse the speech and to provide the response. In + some cases, even the hotword detection is, at least partly, run in the cloud. In other words, most of the voice + assistants are dumb terminals intended to communicate with cloud providers that actually do most of the job, and they + exchange a huge amount of information over the internet in order to operate. This may be sensible when your targets + are low-power devices that operate within a fast network and you don’t need much flexibility. But if you can afford to + process the audio on a more capable CPU, or if you want to operate on devices with limited connectivity, or if you + want to do things that you usually can’t do with off-the-shelf solutions, you may want to process as much as possible + of the load on your device. I understand the case for a cloud-oriented approach when it comes to voice assistants but, + regardless of the technology, we should always be provided with a choice between decentralized and centralized + computing. My dream assistant must have the ability to run the hotword and speech detection logic either on-device or + on-cloud, depending on the use case and depending on the user’s preference. + +- **Scalability**. If I need a new voice assistant in another room or house, I just grab a RaspberryPi, flash the copy + of my assistant-powered OS image to the SD card, plug in a microphone and a speaker, and it’s done. Without having to + buy a new plastic box. If I need a voice-powered music speaker, I just take an existing speaker and plug it into a + RaspberryPi. If I need a voice-powered display, I just take an existing display and plug it to a RaspberryPi. If I + need a voice-powered switch, I just write a rule for controlling it on voice command directly on my RaspberryPi, + without having to worry about whether it’s supported in my Google Home or Alexa app. Any device should be given the + possibility of becoming a smart device. + +## Overview of the voice assistant integrations + +A voice assistant usually consists of two components: + +- An **audio recorder** that captures frames from an audio input device +- A **speech engine** that keeps track of the current context. + +There are then two main categories of speech engines: hotword detectors, which scan the audio input for the presence of +specific hotwords (like “Ok Google” or “Alexa”), and speech detectors, which instead do proper speech-to-text +transcription using acoustic and language models. As you can imagine, continuously running a full speech detection has a +far higher overhead than just running hotword detection, which only has to compare the captured speech against the, +usually short, list of stored hotword models. Then there are speech-to-intent engines, like PicoVoice’s Rhino. Instead +of providing a text transcription as output, these provide a structured breakdown of the speech intent. For example, if +you say *“Can I have a small double-shot espresso with a lot of sugar and some milk”* they may return something like `{" +type":"espresso", “size”:”small", “numberOfShots":2, “sugar":"a lot", “milk":"some"}`). + +In Platypush, I’ve built integrations to provide users with a wide choice when it comes to speech-to-text processors and +engines. Let’s go through some of the available integrations, and evaluate their pros and cons. + +## Native Google Assistant library + +### Integrations + +- [`assistant.google`](https://platypush.readthedocs.io/en/latest/platypush/plugins/assistant.google.html) plugin (to + programmatically start/stop conversations) + and [`assistant.google`](https://platypush.readthedocs.io/en/latest/platypush/backend/assistant.google.html) backend + (for continuous hotword detection). + +### Configuration + +- Create a Google project and download the `credentials.json` file from + the [Google developers console](https://console.cloud.google.com/apis/credentials). + +- Install the `google-oauthlib-tool`: + +```shell +[sudo] pip install --upgrade 'google-auth-oauthlib[tool]' +``` + +- Authenticate to use the `assistant-sdk-prototype` scope: + +```shell +export CREDENTIALS_FILE=~/.config/google-oauthlib-tool/credentials.json + +google-oauthlib-tool --scope https://www.googleapis.com/auth/assistant-sdk-prototype \ + --scope https://www.googleapis.com/auth/gcm \ + --save --headless --client-secrets $CREDENTIALS_FILE +``` + +- Install Platypush with the HTTP backend and Google Assistant library support: + +```shell +[sudo] pip install 'platypush[http,google-assistant-legacy]' +``` + +- Create or add the lines to `~/.config/platypush/config.yaml` to enable the webserver and the assistant integration: + +```yaml +backend.http: + enabled: True + +backend.assistant.google: + enabled: True + +assistant.google: + enabled: True +``` + +- Start Platypush, say “Ok Google” and enjoy your assistant. On the web panel on `http://your-rpi:8008` you should be + able to see your voice interactions in real-time. + +### Features + +- *Hotword detection*: **YES** (“Ok Google” or “Hey Google). +- *Speech detection*: **YES** (once the hotword is detected). +- *Detection runs locally*: **NO** (hotword detection [seems to] run locally, but once it's detected a channel is open + with Google servers for the interaction). + +### Pros + +- It implements most of the features that you’d find in any Google Assistant products. That includes native support for + timers, calendars, customized responses on the basis of your profile and location, native integration with the devices + configured in your Google Home, and so on. For more complex features, you’ll have to write your custom platypush hooks + on e.g. speech detected or conversation start/end events. + +- Both hotword detection and speech detection are rock solid, as they rely on the Google cloud capabilities. + +- Good performance even on older RaspberryPi models (the library isn’t available for the Zero model or other arm6-based + devices though), because most of the processing duties actually happen in the cloud. The audio processing thread takes + around 2–3% of the CPU on a RaspberryPi 4. + +### Cons + +- The Google Assistant library used as a backend by the integration has + been [deprecated by Google](https://developers.google.com/assistant/sdk/reference/library/python). It still works on + most of the devices I’ve tried, as long as the latest version is used, but keep in mind that it’s no longer maintained + by Google and it could break in the future. Unfortunately, I’m still waiting for an official alternative. + +- If your main goal is to operate voice-enabled services within a secure environment with no processing happening on + someone else’s cloud, then this is not your best option. The assistant library makes your computer behave more or less + like a full Google Assistant device, included capturing audio and sending it to Google servers for processing and, + potentially, review. + +## Google Assistant Push-To-Talk Integration + +### Integrations + +- [`assistant.google.pushtotalk`](https://platypush.readthedocs.io/en/latest/platypush/plugins/assistant.google.pushtotalk.html) + plugin. + +### Configuration + +- Create a Google project and download the `credentials.json` file from + the [Google developers console](https://console.cloud.google.com/apis/credentials). + +- Install the `google-oauthlib-tool`: + +```shell +[sudo] pip install --upgrade 'google-auth-oauthlib[tool]' +``` + +- Authenticate to use the `assistant-sdk-prototype` scope: + +```shell +export CREDENTIALS_FILE=~/.config/google-oauthlib-tool/credentials.json + +google-oauthlib-tool --scope https://www.googleapis.com/auth/assistant-sdk-prototype \ + --scope https://www.googleapis.com/auth/gcm \ + --save --headless --client-secrets $CREDENTIALS_FILE +``` + +- Install Platypush with the HTTP backend and Google Assistant SDK support: + +```shell +[sudo] pip install 'platypush[http,google-assistant]' +``` + +- Create or add the lines to `~/.config/platypush/config.yaml` to enable the webserver and the assistant integration: + +```yaml +backend.http: + enabled: True + +assistant.google.pushtotalk: + language: en-US +``` + +- Start Platypush. Unlike the native Google library integration, the push-to-talk plugin doesn’t come with a hotword + detection engine. You can initiate or end conversations programmatically through e.g. Platypush event hooks, + procedures, or through the HTTP API: + +```shell +curl -XPOST -H 'Content-Type: application/json' -d ' +{ + "type":"request", + "action":"assistant.google.pushtotalk.start_conversation" +}' -a 'username:password' http://your-rpi:8008/execute +``` + +### Features + +- *Hotword detection*: **NO** (call `start_conversation` or `stop_conversation` from your logic or from the context of a + hotword integration like Snowboy, DeepSpeech or PicoVoice to trigger or stop the assistant). + +- *Speech detection*: **YES**. + +- *Detection runs locally*: **NO** (you can customize the hotword engine and how to trigger the assistant, but once a + conversation is started a channel is opened with Google servers). + +### Pros + +- It implements many of the features you’d find in any Google Assistant product out there, even though hotword detection + isn’t available and some of the features currently available on the assistant library aren’t provided (like timers or + alarms). + +- Rock-solid speech detection, using the same speech model used by Google Assistant products. + +- Relatively good performance even on older RaspberryPi models. It’s also available for arm6 architecture, which makes + it suitable also for RaspberryPi Zero or other low-power devices. No hotword engine running means that it uses + resources only when you call `start_conversation`. + +- It provides the benefits of the Google Assistant speech engine with no need to have a 24/7 open connection between + your mic and Google’s servers. The connection is only opened upon `start_conversation`. This makes it a good option if + privacy is a concern, or if you want to build more flexible assistants that can be triggered through different hotword + engines (or even build assistants that are triggered in different languages depending on the hotword that you use), or + assistants that aren’t triggered by a hotword at all — for example, you can call start_conversation upon button press, + motion sensor event or web call. + +### Cons + +- I’ve built this integration after the deprecation of the Google Assistant library occurred with no official + alternatives being provided. I’ve built it by refactoring the poorly refined code provided by Google in its samples ( + [`pushtotalk.py`](https://github.com/googlesamples/assistant-sdk-python/blob/master/google-assistant-sdk/googlesamples/assistant/grpc/pushtotalk.py)) + and making a proper plugin out of it. It works, but keep in mind that it’s based on some ugly code that’s waiting to + be replaced by Google. + +- No hotword support. You’ll have to hook it up to Snowboy, PicoVoice or DeepSpeech if you want hotword support. + +## Alexa Integration + +### Integrations + +- [`assistant.echo`](https://platypush.readthedocs.io/en/latest/platypush/plugins/assistant.echo.html) plugin. + +### Configuration + +- Install Platypush with the HTTP backend and Alexa support: + +```shell +[sudo] pip install 'platypush[http,alexa]' +``` + +- Run `alexa-auth`. It will start a local web server on your machine on `http://your-rpi:3000`. Open it in your browser + and authenticate with your Amazon account. A credentials file should be generated under `~/.avs.json`. + +- Create or add the lines to your `~/.config/platypush/config.yaml` to enable the webserver and the assistant + integration: + +```yaml +backend.http: + enabled: True + +assistant.echo: + enabled: True +``` + +- Start Platypush. The Alexa integration doesn’t come with a hotword detection engine. You can initiate or end + conversations programmatically through e.g. Platypush event hooks, procedures, or through the HTTP API: + +```shell +curl -XPOST -H 'Content-Type: application/json' -d ' +{ + "type":"request", + "action":"assistant.echo.start_conversation" +}' -a 'username:password' http://your-rpi:8008/execute +``` + +### Features + +- *Hotword detection*: **NO** (call `start_conversation` or `stop_conversation` from your logic or from the context of a + hotword integration like Snowboy or PicoVoice to trigger or stop the assistant). + +- *Speech detection*: **YES** (although limited: transcription of the processed audio won’t be provided). + +- *Detection runs locally*: **NO**. + +### Pros + +- It implements many of the features that you’d find in any Alexa product out there, even though hotword detection isn’t + available. Also, the support for skills or media control may be limited. + +- Good speech detection capabilities, although inferior to the Google Assistant when it comes to accuracy. + +- Good performance even on low-power devices. No hotword engine running means it uses resources only when you call + start_conversation. + +- It provides some of the benefits of an Alexa device but with no need for a 24/7 open connection between your mic and + Amazon’s servers. The connection is only opened upon start_conversation. + +### Cons + +- The situation is extremely fragmented when it comes to Alexa voice SDKs. Amazon eventually re-released the AVS (Alexa + Voice Service), mostly with commercial uses in mind, but its features are still quite limited compared to the Google + assistant products. The biggest limitation is the fact that the AVS works on raw audio input and spits back raw audio + responses. It means that text transcription, either for the request or the response, won’t be available. That limits + what you can build with it. For example, you won’t be able to capture custom requests through event hooks. + +- No hotword support. You’ll have to hook it up to Snowboy, PicoVoice or DeepSpeech if you want hotword support. + +## Snowboy Integration + +### Integrations + +- [`assistant.snowboy`](https://platypush.readthedocs.io/en/latest/platypush/backend/assistant.snowboy.html) backend. + +### Configuration + +- Install Platypush with the HTTP backend and Snowboy support: + +```shell +[sudo] pip install 'platypush[http,snowboy]' +``` + +- Choose your hotword model(s). Some are available under `SNOWBOY_INSTALL_DIR/resources/models`. Otherwise, you can + train or download models from the [Snowboy website](https://snowboy.kitt.ai/). + +- Create or add the lines to your `~/.config/platypush/config.yaml` to enable the webserver and the assistant + integration: + +```yaml +backend.http: + enabled: True + +backend.assistant.snowboy: + audio_gain: 1.2 + models: + # Trigger the Google assistant in Italian when I say "computer" + computer: + voice_model_file: ~/models/computer.umdl + assistant_plugin: assistant.google.pushtotalk + assistant_language: it-IT + detect_sound: ~/sounds/bell.wav + sensitivity: 0.4 + + # Trigger the Google assistant in English when I say "OK Google" + ok_google: + voice_model_file: ~/models/OK Google.pmdl + assistant_plugin: assistant.google.pushtotalk + assistant_language: en-US + detect_sound: ~/sounds/bell.wav + sensitivity: 0.4 + + # Trigger Alexa when I say "Alexa" + alexa: + voice_model_file: ~/models/Alexa.pmdl + assistant_plugin: assistant.echo + assistant_language: en-US + detect_sound: ~/sounds/bell.wav + sensitivity: 0.5 +``` + +- Start Platypush. Say the hotword associated with one of your models, check on the logs that the + [`HotwordDetectedEvent`](https://platypush.readthedocs.io/en/latest/platypush/events/assistant.html#platypush.message.event.assistant.HotwordDetectedEvent) + is triggered and, if there’s an assistant plugin associated with the hotword, the corresponding assistant is correctly + started. + +### Features + +- *Hotword detection*: **YES**. +- *Speech detection*: **NO**. +- *Detection runs locally*: **YES**. + +### Pros + +- I've been an early fan and supporter of the Snowboy project. I really like the idea of crowd-powered machine learning. + You can download any hotword models for free from their website, provided that you record three audio samples of you + saying that word in order to help improve the model. You can also create your custom hotword model, and if enough + people are interested in using it then they’ll contribute with their samples, and the model will become more robust + over time. I believe that more machine learning projects out there could really benefit from this “use it for free as + long as you help improve the model” paradigm. + +- Platypush was an early supporter of Snowboy, so its integration is well-supported and extensively documented. You can + natively configure custom assistant plugins to be executed when a certain hotword is detected, making it easy to make + a multi-language and multi-hotword voice assistant. + +- Good performance, even on low-power devices. I’ve used Snowboy in combination with the Google Assistant push-to-talk + integration for a while on single-core RaspberryPi Zero devices, and the CPU usage from hotword processing never + exceeded 20–25%. + +- The hotword detection runs locally, on models that are downloaded locally. That means no need for a network connection + to run and no data exchanged with any cloud. + +### Cons + +- Even though the idea of crowd-powered voice models is definitely interesting and has plenty of potentials to scale up, + the most popular models on their website have been trained with at most 2000 samples. And (sadly as well as + expectedly) most of those voice samples belong to white, young-adult males, which makes many of these models perform + quite poorly with speech recorded from any individuals that don’t fit within that category (and also with people who + aren’t native English speakers). + +## Mozilla DeepSpeech + +### Integrations + +- [`stt.deepspeech`](https://platypush.readthedocs.io/en/latest/platypush/plugins/stt.deepspeech.html) plugin + and [`stt.deepspeech`](https://platypush.readthedocs.io/en/latest/platypush/backend/stt.deepspeech.html) backend (for + continuous detection). + +### Configuration + +- Install Platypush with the HTTP backend and Mozilla DeepSpeech support. Take note of the version of DeepSpeech that + gets installed: + +```shell +[sudo] pip install 'platypush[http,deepspeech]' +``` + +- Download the Tensorflow model files for the version of DeepSpeech that has been installed. This may take a while + depending on your connection: + +```shell +export MODELS_DIR=~/models +export DEEPSPEECH_VERSION=0.6.1 + +wget https://github.com/mozilla/DeepSpeech/releases/download/v$DEEPSPEECH_VERSION/deepspeech-$DEEPSPEECH_VERSION-models.tar.gz + +tar xvf deepspeech-$DEEPSPEECH_VERSION-models.tar.gz +x deepspeech-0.6.1-models/ +x deepspeech-0.6.1-models/lm.binary +x deepspeech-0.6.1-models/output_graph.pbmm +x deepspeech-0.6.1-models/output_graph.pb +x deepspeech-0.6.1-models/trie +x deepspeech-0.6.1-models/output_graph.tflite + +mv deepspeech-$DEEPSPEECH_VERSION-models $MODELS_DIR +``` + +- Create or add the lines to your `~/.config/platypush/config.yaml` to enable the webserver and the DeepSpeech + integration: + +```yaml +backend.http: + enabled: True + +stt.deepspeech: + model_file: ~/models/output_graph.pbmm + lm_file: ~/models/lm.binary + trie_file: ~/models/trie + + # Custom list of hotwords + hotwords: + - computer + - alexa + - hello + + conversation_timeout: 5 + +backend.stt.deepspeech: + enabled: True +``` + +- Start Platypush. Speech detection will start running on startup. + [`SpeechDetectedEvents`](https://platypush.readthedocs.io/en/latest/platypush/events/stt.html#platypush.message.event.stt.SpeechDetectedEvent) + will be triggered when you talk. + [`HotwordDetectedEvents`](https://platypush.readthedocs.io/en/latest/platypush/events/stt.html#platypush.message.event.stt.HotwordDetectedEvent) + will be triggered when you say one of the configured hotwords. + [`ConversationDetectedEvents`](https://platypush.readthedocs.io/en/latest/platypush/events/stt.html#platypush.message.event.stt.ConversationDetectedEvent) + will be triggered when you say something after a hotword, with speech provided as an argument. You can also disable the + continuous detection and only start it programmatically by calling `stt.deepspeech.start_detection` and + `stt.deepspeech.stop_detection`. You can also use it to perform offline speech transcription from audio files: + +```shell +curl -XPOST -H 'Content-Type: application/json' -d ' +{ + "type":"request", + "action":"stt.deepspeech.detect", + "args": { + "audio_file": "~/audio.wav" + } +}' -a 'username:password' http://your-rpi:8008/execute + +# Example response +{ + "type":"response", + "target":"http", + "response": { + "errors":[], + "output": { + "speech": "This is a test" + } + } +} +``` + +### Features + +- *Hotword detection*: **YES**. +- *Speech detection*: **YES**. +- *Detection runs locally*: **YES**. + +### Pros + +- I’ve been honestly impressed by the features of DeepSpeech and the progress they’ve made starting from the version + 0.6.0. Mozilla made it easy to run both hotword and speech detection on-device with no need for any third-party + services or network connection. The full codebase is open-source and the Tensorflow voice and language models are also + very good. It’s amazing that they’ve released the whole thing for free to the community. It also means that you can + easily extend the Tensorflow model by training it with your own samples. + +- Speech-to-text transcription of audio files can be a very useful feature. + +### Cons + +- DeepSpeech is quite demanding when it comes to CPU resources. It will run OK on a laptop or on a RaspberryPi 4 (but in + my tests it took 100% of a core on a RaspberryPi 4 for speech detection),. It may be too resource-intensive to run on + less powerful machines. + +- DeepSpeech has a bit more delay than other solutions. The engineers at Mozilla have worked a lot to make the model as + small and performant as possible, and they claim of having achieved real-time performance on a RaspberryPi 4. In + reality, all of my tests bear between 2 and 4 seconds of delay between speech capture and detection. + +- DeepSpeech is relatively good at detecting speech, but not at interpreting the semantic context (that’s something + where Google still wins hands down). If you say “this is a test,” the model may actually capture “these is a test.” + “This” and “these” do indeed sound almost the same in English, but the Google assistant has a better semantic engine + to detect the right interpretation of such ambiguous cases. DeepSpeech works quite well for speech-to-text + transcription purposes but, in such ambiguous cases, it lacks some semantic context. + +- Even though it’s possible to use DeepSpeech from Platypush as a hotword detection engine, keep in mind that it’s not + how the engine is intended to be used. Hotword engines usually run against smaller and more performant models only + intended to detect one or few words, not against a full-featured language model. The best usage of DeepSpeech is + probably either for offline text transcription, or with another hotword integration and leveraging DeepSpeech for the + speech detection part. + +## PicoVoice + +[PicoVoice](https://github.com/Picovoice/) is a very promising company that has released several products for performing +voice detection on-device. Among them: + +- [*Porcupine*](https://github.com/Picovoice/porcupine), a hotword engine. +- [*Leopard*](https://github.com/Picovoice/leopard), a speech-to-text offline transcription engine. +- [*Cheetah*](https://github.com/Picovoice/cheetah), a speech-to-text engine for real-time applications. +- [*Rhino*](https://github.com/Picovoice/rhino), a speech-to-intent engine. + +So far, Platypush provides integrations with Porcupine and Cheetah. + +### Integrations + +- *Hotword engine*: + [`stt.picovoice.hotword`](https://platypush.readthedocs.io/en/latest/platypush/plugins/stt.picovoice.hotword.html) + plugin and + [`stt.picovoice.hotword`](https://platypush.readthedocs.io/en/latest/platypush/backend/stt.picovoice.hotword.html) + backend (for continuous detection). + +- *Speech engine*: + [`stt.picovoice.speech`](https://platypush.readthedocs.io/en/latest/platypush/plugins/stt.picovoice.speech.html) + plugin and + [`stt.picovoice.speech`](https://platypush.readthedocs.io/en/latest/platypush/backend/stt.picovoice.speech.html) + backend (for continuous detection). + +### Configuration + +- Install Platypush with the HTTP backend and the PicoVoice hotword integration and/or speech integration: + +```shell +[sudo] pip install 'platypush[http,picovoice-hotword,picovoice-speech]' +``` + +- Create or add the lines to your `~/.config/platypush/config.yaml` to enable the webserver and the DeepSpeech + integration: + +```yaml +stt.picovoice.hotword: + # Custom list of hotwords + hotwords: + - computer + - alexa + - hello + +# Enable continuous hotword detection +backend.stt.picovoice.hotword: + enabled: True + +# Enable continuous speech detection +# backend.stt.picovoice.speech: +# enabled: True + +# Or start speech detection when a hotword is detected +event.hook.OnHotwordDetected: + if: + type: platypush.message.event.stt.HotwordDetectedEvent + then: + # Start a timer that stops the detection in 10 seconds + - action: utils.set_timeout + args: + seconds: 10 + name: StopSpeechDetection + actions: + - action: stt.picovoice.speech.stop_detection + + - action: stt.picovoice.speech.start_detection +``` + +- Start Platypush and enjoy your on-device voice assistant. + +### Features + +- *Hotword detection*: **YES**. +- *Speech detection*: **YES**. +- *Detection runs locally*: **YES**. + +### Pros + +- When it comes to on-device voice engines, PicoVoice products are probably the best solution out there. Their hotword + engine is far more accurate than Snowboy and it manages to be even less CPU-intensive. Their speech engine has much + less delay than DeepSpeech and it’s also much less power-hungry — it will still run well and with low latency even on + older models of RaspberryPi. + +### Cons + +- While PicoVoice provides Python SDKs, their native libraries are closed source. It means that I couldn’t dig much into + how they’ve solved the problem. + +- Their hotword engine (Porcupine) can be installed and run free of charge for personal use on any device, but if you + want to expand the set of keywords provided by default, or add more samples to train the existing models, then you’ll + have to go for a commercial license. Their speech engine (Cheetah) instead can only be installed and run free of + charge for personal use on Linux on x86_64 architecture. Any other architecture or operating system, as well as any + chance to extend the model or use a different model, is only possible through a commercial license. While I understand + their point and their business model, I’d have been super-happy to just pay for a license through a more friendly + process, instead of relying on the old-fashioned “contact us for a commercial license/we’ll reach back to you” + paradigm. + +- Cheetah’s speech engine still suffers from some of the issues of DeepSpeech when it comes to semantic context/intent + detection. The “this/these” ambiguity also happens here. However, these problems can be partially solved by using + Rhino, PicoVoice’s speech-to-intent engine, which will provide a structured representation of the speech intent + instead of a letter-by-letter transcription. However, I haven’t yet worked on integrating Rhino into platypush. + +## Conclusions + +The democratization of voice technology has long been dreamed about, and it’s finally (slowly) coming. The situation out +there is still quite fragmented though and some commercial SDKs may still get deprecated with short notice or no notice +at all. But at least some solutions are emerging to bring speech detection to all devices. + +I’ve built integrations in Platypush for all of these services because I believe that it’s up to users, not to +businesses, to decide how people should use and benefit from voice technology. Moreover, having so many voice +integrations in the same product — and especially having voice integrations that expose all the same API and generate +the same events — makes it very easy to write assistant-agnostic logic, and really decouple the tasks of speech +recognition from the business logic that can be run by voice commands. + +Check out +[my previous article](https://blog.platypush.tech/article/Build-your-customizable-voice-assistant-with-Platypush) to +learn how to write your own custom hooks in Platypush on speech detection, hotword detection and speech start/stop +events. + +To summarize my findings so far: + +- Use the native **Google Assistant** integration if you want to have a full Google experience, and if you’re ok with + Google servers processing your audio and the possibility that somewhere in the future the deprecated Google Assistant + library won’t work anymore. + +- Use the **Google push-to-talk** integration if you only want to have the assistant, without hotword detection, or you + want your assistant to be triggered by alternative hotwords. + +- Use the **Alexa** integration if you already have an Amazon-powered ecosystem and you’re ok with having less + flexibility when it comes to custom hooks because of the unavailability of speech transcript features in the AVS. + +- Use **Snowboy** if you want to use a flexible, open-source and crowd-powered engine for hotword detection that runs + on-device and/or use multiple assistants at the same time through different hotword models, even if the models may not + be that accurate. + +- Use **Mozilla DeepSpeech** if you want a fully on-device open-source engine powered by a robust Tensorflow model, even + if it takes more CPU load and a bit more latency. + +- Use **PicoVoice** solutions if you want a full voice solution that runs on-device and it’s both accurate and + performant, even though you’ll need a commercial license for using it on some devices or extend/change the model. + +Let me know your thoughts on these solutions and your experience with these integrations!