179 Commits

Author SHA1 Message Date
Michael Hansen 0c37c88e61 Update README.md 2020-06-08 09:33:28 -04:00
Michael Hansen 50f05506a2 Some extra README info 2020-06-08 09:32:00 -04:00
Michael Hansen b62068da69 Can't use classes in divs in the README, I guess 2020-06-08 09:29:55 -04:00
Michael Hansen f69d7484ba Update README to point to 2.5 repo 2020-06-08 09:27:56 -04:00
Michael Hansen e478765b4e Merge branch 'master' of https://github.com/synesthesiam/rhasspy 2020-06-02 09:44:51 -04:00
Michael Hansen 8bf673c412 Merge pull request #236 from Mic92/intent-docs
Intent docs
2020-06-02 09:44:39 -04:00
Michael Hansen 8854f26dc1 Merge branch 'master' of https://github.com/synesthesiam/rhasspy 2020-06-02 09:40:21 -04:00
Michael Hansen cf8f2319b8 Merge pull request #234 from devkid/patch-1
Enable speech response for Home Assistant intent handler
2020-06-02 09:40:06 -04:00
Michael Hansen 782721fab3 Merge branch 'master' of https://github.com/synesthesiam/rhasspy 2020-06-02 09:39:10 -04:00
Michael Hansen 43dc7a07c9 Add mosquitto tools to Docker runtime image 2020-06-02 09:38:49 -04:00
Jörg Thalheim be42447620 docs: note that mycroft adapt does not convert integers back yet. 2020-06-01 23:45:32 +01:00
Alfred Krohmer 3a884888c4 Enable speech response for Home Assistant intent handler 2020-06-01 21:37:34 +02:00
Michael Hansen 3133666390 Merge pull request #230 from Romkabouter/master
Fix Google Wavenet TTS
2020-05-29 16:16:13 -04:00
Paul Romkes ff0aea4177 Fix Google Wavenet TTS 2020-05-29 20:49:31 +02:00
Michael Hansen fa418d3129 Ensure setuptools is in Docker runtime image 2020-05-26 13:39:59 -04:00
Michael Hansen d4314a85d9 Force pip in runtime Docker image 2020-05-26 13:38:19 -04:00
Michael Hansen 3d42551bfc Merge pull request #227 from kroka/patch-2
force download if deletion is enabled
2020-05-26 13:33:44 -04:00
kroka 6411924cf2 force download if deletion is enabled
ignore that file might exist already,
change motivated by #213
2020-05-22 14:33:24 +02:00
Michael Hansen 77d2196f00 Add /ssl prefix for Hass.IO SSL settings 2020-04-23 16:59:29 -04:00
Michael Hansen 3c53cd0236 Fix ssl_context 2020-04-20 08:54:33 -04:00
Michael Hansen 46a75ec83e Use ssl context in aiohttp 2020-04-19 15:03:50 -04:00
Michael Hansen 3e9c4bef68 Clean up docker file and docker run.sh 2020-04-19 11:39:46 -04:00
Michael Hansen 465d1e61e0 Add asoundrc to Hass.io config path in docker run 2020-04-19 09:19:30 -04:00
Michael Hansen 563fcfcd49 Merge pull request #207 from pheelee/master
fixes startup on hass.io
2020-04-19 09:10:25 -04:00
pheelee 0ff965a965 fixes startup on hass.io 2020-04-19 12:35:56 +02:00
Michael Hansen 998eff8708 Merge branch 'master' of pumpkin.lan:Mike/Projects/rhasspy 2020-04-15 16:48:35 -04:00
Michael Hansen 1dcf984669 Fix Docker tag 2020-04-15 16:48:18 -04:00
Michael Hansen 21c2b2ef9d Only use system Python pre-venv create 2020-04-15 16:47:37 -04:00
Michael Hansen 958c12cfb4 Merge branch 'master' of pumpkin.lan:Mike/Projects/rhasspy 2020-04-15 16:03:39 -04:00
Michael Hansen 04efe9f151 Update CHANGELOG 2020-04-10 16:53:38 -04:00
Michael Hansen 6dbc8dc35e Fix arm64 arch 2020-04-10 13:45:07 -04:00
Michael Hansen 2cac48292a Fix raw_value in MQTT messages 2020-04-10 13:25:26 -04:00
Michael Hansen a4528d40fb Porting Docker build to buildx for multi-platform 2020-04-10 13:17:15 -04:00
Michael Hansen 4cd8b307c7 Merge branch 'master' of https://github.com/synesthesiam/rhasspy 2020-04-10 13:08:26 -04:00
Michael Hansen de0824e49a Merge pull request #201 from ofekd/mqtt-tls-support
Support MQTT over TLS
2020-04-10 11:38:18 -04:00
Michael Hansen 74ba8c1c4a Bump version 2020-04-10 11:07:38 -04:00
Michael Hansen 9428abdd40 Softer error for porcupine missing files 2020-04-10 11:05:35 -04:00
Michael Hansen a6c425d65f Properly accept websocket connections 2020-04-10 11:05:27 -04:00
Michael Hansen 015c37fa5d Refactor Dockerfile 2020-04-10 11:05:18 -04:00
Michael Hansen 32f7e37657 Fix typo 2020-04-10 09:39:31 -04:00
Ofek 54063feb03 Support MQTT over TLS 2020-04-09 23:31:48 +03:00
Michael Hansen 3bc36f2fb1 Correct HA intent docs 2020-03-26 16:32:37 -04:00
Michael Hansen e0401d4c18 Move Google imports inside STT class 2020-03-12 17:19:09 -04:00
Michael Hansen 73124075ae Don't try to download Kaldi for armv6l 2020-03-12 17:18:56 -04:00
Michael Hansen c410f4eea7 Fix typo 2020-03-12 17:17:25 -04:00
Michael Hansen 3f0545ed0f Bump version 2020-03-04 11:52:18 -05:00
Michael Hansen 331138f300 Updated CHANGELOG 2020-03-04 11:52:03 -05:00
Michael Hansen 33b847b828 Merge branch 'master' of https://github.com/synesthesiam/rhasspy 2020-03-04 11:48:53 -05:00
Michael Hansen d770679373 Fix first entity bug in Rasa NLU training 2020-03-04 11:48:02 -05:00
Michael Hansen 86e695a7a4 Merge pull request #184 from daniele-athome/rasa-min-confidence
Rasa: min confidence parameter
2020-03-03 20:55:00 -05:00
Daniele Ricci 07d1cc4e43 Rasa: min confidence parameter
Signed-off-by: Daniele Ricci <daniele@casaricci.it>
2020-02-25 20:03:23 +01:00
Michael Hansen b68e5caf01 Merge pull request #182 from Tooa/follow_up_178
Prevent porcupine incompatibilities
2020-02-24 16:59:28 -05:00
Uli da4d994e75 Use tagged porcupine models
* This prevents incompatibilities with the Python wrapper
  in future.
2020-02-22 11:45:05 +01:00
Michael Hansen 627e6e8b3d Merge pull request #179 from daniele-athome/google-stt
Support for Google Cloud STT
2020-02-20 22:13:33 -05:00
Daniele Ricci f0ec0486f7 Google Cloud TTS documentation
Signed-off-by: Daniele Ricci <daniele@casaricci.it>
2020-02-18 20:04:03 +01:00
Daniele Ricci 1febc3d1d8 Support for Google Cloud STT
Signed-off-by: Daniele Ricci <daniele@casaricci.it>
2020-02-18 19:48:26 +01:00
Michael Hansen 2879802f2f Merge pull request #180 from Tooa/fix_178
Fixes Porcupine wrapper incompatibilities with models
2020-02-18 09:27:23 -05:00
Uli 21a2a8f9b4 Bump Porcupine Python wrapper 2020-02-16 18:51:01 +01:00
Michael Hansen a96f80237e Fix remote intent handler 2020-02-15 17:04:33 -05:00
Michael Hansen fc68d04f29 Fix siteId is null 2020-02-09 13:58:38 -05:00
Michael Hansen e00c1448cb Fix CHANGELOG date 2020-02-07 20:39:03 -05:00
Michael Hansen f04ad3bfeb Add more tutorials to docs 2020-02-07 17:14:01 -05:00
Michael Hansen eb11f90cab Add espeak arguments for text to speech 2020-02-07 17:00:33 -05:00
Michael Hansen 2c612ee669 Pocketsphinx wake keyphrase words added to dictionary 2020-02-07 16:39:06 -05:00
Michael Hansen dfe92f9d0e Fix STT casing outside of HTTP calls 2020-02-07 16:25:40 -05:00
Michael Hansen c59e7b42ab Update docs 2020-02-07 15:55:51 -05:00
Michael Hansen 948705a87b Add wake/text websocket endpoints 2020-02-07 15:45:37 -05:00
Michael Hansen 6b0b5c1799 Merge branch 'master' of https://github.com/synesthesiam/rhasspy 2020-02-06 16:50:51 -05:00
Michael Hansen 9553691e88 Working on wake websocket 2020-02-06 16:49:33 -05:00
Michael Hansen 997456631e Update /api/listen-for-wake to enable/disable wake word 2020-02-05 22:00:06 -05:00
Michael Hansen 104165198b Bump to rhasspy-nlu 0.1.5 2020-01-28 21:38:05 -05:00
Michael Hansen f405b827f4 Add Hass.IO change to CHANGELOG 2020-01-22 21:20:35 -05:00
Michael Hansen 089568cf9f Fix version in CHANGELOG 2020-01-22 21:14:49 -05:00
Michael Hansen a92d88ff8f Rename Add File button in web UI on sentences page 2020-01-22 17:03:21 -05:00
Michael Hansen c60030b48f Add download feedback to web UI 2020-01-22 17:02:26 -05:00
Michael Hansen b400d651f6 Add RHASSPY_LOG_LEVEL environment variable 2020-01-22 16:40:57 -05:00
Michael Hansen 2dfa9aa782 Fix _raw_text in Hass event being same as _text 2020-01-22 16:36:07 -05:00
Michael Hansen bd2c065415 Force slot programs to run each training cycle 2020-01-22 16:24:58 -05:00
Michael Hansen 1b95144b05 Add CHANGELOG and bump version 2020-01-21 15:56:09 -05:00
Michael Hansen 3f60936471 Add web button to play last recorded voice command 2020-01-21 15:55:01 -05:00
Michael Hansen 9a3c2f8a3f Move kaldi/custom_words.txt to kaldi_custom_words.txt 2020-01-21 15:39:22 -05:00
Michael Hansen 1fb75f24d7 Delete partial downloads of profile files 2020-01-21 15:39:08 -05:00
Michael Hansen 6c0187e606 Hide web notifications after 10 seconds 2020-01-21 15:38:52 -05:00
Michael Hansen 16262ec896 Keep slot substitution casing during training/recognition 2020-01-21 14:57:00 -05:00
Michael Hansen 9d1303ed21 Merge pull request #164 from alexkn/fix-device-preselection
fix microphone/sound device preselection
2020-01-20 08:38:57 -05:00
Michael Hansen a12e537110 Merge pull request #165 from alexkn/docs-picotts
update pico-tts languages
2020-01-20 08:37:39 -05:00
Alexander Knöbel 63fb3cf046 update pico-tts languages 2020-01-19 13:06:20 +01:00
Alexander Knöbel f5e6666931 fix microphone/sound device preselection 2020-01-19 01:04:02 +01:00
Michael Hansen 44a9c84bc7 Merge pull request #158 from drhirn/master
Added exclamation mark to shebang
2020-01-14 22:23:56 -05:00
Michael Hansen 9a076936c5 Merge pull request #160 from alexkn/docs-yarn-install
Add yarn install before build
2020-01-14 22:23:01 -05:00
Alexander Knöbel 102b29ecf6 Add yarn install before build 2020-01-14 19:02:08 +01:00
drhirn 51455bfd97 Added exclamation mark to shebang
The shebang in the code for the slot_program was missing an exclamation mark
2020-01-14 15:38:36 +01:00
Michael Hansen 5bf6086164 Merge pull request #156 from mzoeller/patch-3
Add missing space
2020-01-12 18:59:51 -05:00
mzoeller 08ebaf0914 Add missing space 2020-01-12 23:50:40 +01:00
Michael Hansen 4b3f26c12f Merge pull request #153 from daniele-athome/patch-1
Give a hint to lame about mp3 files
2020-01-12 13:29:34 -05:00
Michael Hansen 707c31e4d3 Merge pull request #155 from mzoeller/patch-2
Update intent-handling.md
2020-01-12 13:29:01 -05:00
Michael Hansen 509d47ea0f Merge pull request #154 from mzoeller/patch-1
Example command handler in python
2020-01-12 13:28:42 -05:00
mzoeller 1d2b08df6e Update intent-handling.md 2020-01-12 18:05:20 +01:00
mzoeller 0608443482 Example command handler in python 2020-01-12 17:56:00 +01:00
Daniele Ricci 9a1a41385c Give a hint to lame about mp3 files
Apparently, when given audio through stdin, lame can't detect the file type correctly some times. A quick fix is to add --mp3input to the command line (we have checked for file extension anyway, so...)
2020-01-12 17:24:43 +01:00
Michael Hansen 2d8095f0e1 Merge /media/hansenm/BAC6B44DC6B40C1F/rhasspy 2020-01-08 16:11:36 -05:00
Michael Hansen 8f3c1c5d61 Fix dictionary issue with multiple pronunciations 2020-01-07 21:08:17 -05:00
Michael Hansen deb742d768 Return WAV mimetype 2020-01-07 16:37:47 -05:00
Michael Hansen fa24588ea4 Removed flair intent recognition. Fixes for adapt/rasa. 2020-01-06 11:23:32 -05:00
Michael Hansen ed581ecf9d Fixing fuzzywuzzy and others with converters 2020-01-05 20:43:15 -05:00
Michael Hansen f8aedd4ef5 Update Docker update docs 2020-01-05 16:55:37 -05:00
Michael Hansen 14c1386496 Possible fix for threading issues 2020-01-05 16:46:14 -05:00
Michael Hansen 153b642057 Add Rpi Zero to docs 2020-01-05 15:14:45 -05:00
Michael Hansen dec32102dd Merge pull request #146 from esdeboer/freebsd
sed -i is not POSIX compliant, instead make a temp copy and rename to…
2020-01-05 14:50:38 -05:00
Michael Hansen f365c69265 Merge pull request #142 from maxbachmann/cleanup
code cleanup
2020-01-05 14:50:02 -05:00
Michael Hansen 1724c328b7 Trying to fix Docker image 2020-01-05 11:14:56 -05:00
Eric de Boer 6db4a8d341 sed -i is not POSIX compliant, instead make a temp copy and rename to original. 2020-01-05 16:53:38 +01:00
Michael Hansen b70e8a8569 Copying profiles in Docker 2020-01-04 22:41:04 -05:00
Michael Hansen 2e4828da06 Fix dockerignore 2020-01-04 22:34:15 -05:00
Michael Hansen 96cfe69753 Re-generated Dockerfile 2020-01-04 22:02:31 -05:00
maxbachmann 3e8e246c1c swap vars without temp var 2020-01-05 01:02:35 +01:00
Michael Hansen 80a5008b93 Copy built-in slots to Docker 2020-01-04 16:55:38 -05:00
Michael Hansen e26ecf82f1 Bump rhasspy-nly to 0.1.4.1 2020-01-04 16:41:10 -05:00
Michael Hansen e4db52f845 Merge pull request #138 from esdeboer/master
Install dependencies before running yarn build.
2020-01-04 16:38:41 -05:00
Michael Hansen 846313e236 Documented slot programs, number ranges, converters 2020-01-04 16:28:03 -05:00
Michael Hansen b68a3fac4a Add rhasspy/days and rhasspy/months slots 2020-01-04 16:15:34 -05:00
Michael Hansen 7459f0d9d9 Add rhasspy/number 2020-01-04 15:53:30 -05:00
Michael Hansen 617b789d89 Add locales to profiles 2020-01-04 15:53:13 -05:00
Michael Hansen bb20cd280b Transforming number ranges to rhasspy/number 2020-01-04 15:04:52 -05:00
Michael Hansen ce780feb74 Add en rhasspy/number and rhasspy/days slots 2020-01-04 12:31:46 -05:00
Michael Hansen 2225262a53 Add system slots/slot programs 2020-01-04 12:28:29 -05:00
Michael Hansen 5b5529339b Minor clean up in tutorial 2020-01-04 10:46:02 -05:00
Michael Hansen 90f5c5aef7 Touch up tutorials 2020-01-04 10:42:55 -05:00
Eric de Boer 78f263582d Install dependencies before running yarn build. 2020-01-04 09:44:02 +01:00
Michael Hansen 6c8608f1a1 Merge pull request #136 from esdeboer/master
Only download Kaldi when it is requested to be installed.
2020-01-03 17:21:57 -05:00
Michael Hansen 3e5437856b Merge pull request #137 from xLAva/Feature_Fuzzywuzzy_SpeedUp
Fuzzywuzzy: major speed improvement by disabling the debug log spam
2020-01-03 17:21:17 -05:00
Michael Hansen 15aaea2810 Support siteId in /api/text-to-speech 2020-01-03 17:19:27 -05:00
xLAva 61d8930e38 Fuzzywuzzy: major speed improvement by disabling the debug log spam 2020-01-03 22:43:26 +01:00
Eric de Boer 5748b2dc3a Only download Kaldi when it is requested to be installed. 2020-01-03 20:57:21 +01:00
Michael Hansen 8f7158f7cc Only allow a single hotword to be detected by snowboy (single_detection) 2020-01-03 14:11:01 -05:00
Michael Hansen 97226286e3 Fix phonetisaurus download link in build-from-source.sh 2020-01-03 11:46:41 -05:00
Michael Hansen 896b3ddfba Run isort 2020-01-03 11:18:45 -05:00
Michael Hansen 1772f6e740 Add slot programs 2020-01-03 11:17:42 -05:00
Michael Hansen b5dfd6518b Add converter args 2020-01-03 10:36:06 -05:00
Michael Hansen 2730c131d0 Merge pull request #133 from maxbachmann/cleanup
do some code cleanup
2020-01-03 09:37:41 -05:00
maxbachmann 05ded030c8 do some code cleanup 2020-01-03 08:52:49 +01:00
Michael Hansen 3b90383145 Trying to fix Jekyll build errors 2020-01-02 23:22:02 -05:00
Michael Hansen 1bb5462150 Merge pull request #130 from kroka/patch-1
add missing ASR fields for Hermes MQTT publishing
2020-01-02 23:13:26 -05:00
Michael Hansen 95a354e2a3 Merge pull request #132 from maxbachmann/master
correct spelling mistake
2020-01-02 23:13:08 -05:00
Michael Hansen d203a3ed75 Add custom converters (programs) 2020-01-02 17:16:34 -05:00
Michael Hansen 59d473b931 Add number ranges 2020-01-02 16:37:16 -05:00
Michael Hansen 17737f7fed Bump version 2020-01-02 16:22:29 -05:00
Michael Hansen 76cf173849 Doing int conversion with built-in number conversion 2020-01-02 16:20:05 -05:00
maxbachmann 55d1cfacdd correct spelling mistake 2020-01-02 18:52:14 +01:00
Michael Hansen 4f6d02169c Force casing on slot inputs 2020-01-02 11:40:04 -05:00
Michael Hansen 74761b942f Fix overwrite_all in slot params 2020-01-02 10:57:10 -05:00
Michael Hansen b88acb3a34 Parse JSON from requests with json5 2020-01-02 10:47:58 -05:00
kroka 7b323a08bb add missing fields for Hermes publishing
prevents a null pointer access in hermes-python
2020-01-02 16:42:28 +01:00
Michael Hansen ec55dbfa5b Remove --yes from apt-get install commands 2020-01-01 22:37:23 -05:00
Michael Hansen 15af0ae3c1 Merge pull request #121 from jthomasdewald/master
Home Assistant Template Example
2020-01-01 22:35:06 -05:00
Michael Hansen f8542f7ac1 Merge pull request #126 from Romkabouter/remove-x-hassio-key
Change X-HASSIO-KEY to Authorization
2020-01-01 09:06:14 -05:00
Paul Romkes de67b3318c Change X-HASSIO-KEY to Authorization 2020-01-01 11:32:09 +01:00
jthomasdewald b47dca03aa Update command-listener doc
Changed vad_mode description to match webrtcvad docs
2019-12-31 15:03:47 -08:00
Michael Hansen 89a1921c3e Merge 2019-12-31 13:00:48 -05:00
Michael Hansen e5fe2a31b3 Add mypy to check, code cleanup 2019-12-31 12:54:10 -05:00
Michael Hansen afdd241c57 Add awake webhook 2019-12-31 12:40:56 -05:00
Michael Hansen bea38cc64f Fix wakeword issues on TTS pause 2019-12-30 21:49:20 -05:00
Michael Hansen c2562aa674 Don't disable wake system by default with TTS 2019-12-30 17:09:52 -05:00
Michael Hansen 7dec472ec4 Add update instructions to docs 2019-12-28 20:51:07 -05:00
Michael Hansen 007ea4266e Bump version 2019-12-27 22:12:38 -05:00
Michael Hansen a627f8746c Code cleanup 2019-12-27 21:19:46 -05:00
Michael Hansen 13f183afd4 Reset PyAudio on error 2019-12-27 21:17:03 -05:00
Michael Hansen 130cbeb7a8 Consolidate actor events. Stop wake on TTS speak. 2019-12-27 21:07:00 -05:00
jthomasdewald 358e7b087e Home Assistant Template Example
Example files to get Rhasspy to control any light with only one automation and one script.
2019-12-27 15:25:36 -08:00
Michael Hansen 8e2d2f2352 Play error sound when intent not recognized 2019-12-27 11:16:38 -05:00
Michael Hansen ac3c92e24a Fix pop sound in pico2wave 2019-12-27 10:59:52 -05:00
Michael Hansen a501c52954 Add /api/speech-to-text endpoint to docs 2019-12-26 22:29:22 -05:00
Michael Hansen f8f0b48140 Setting text and raw_text when intent is not recognized 2019-12-26 22:11:58 -05:00
Michael Hansen 2a8972fb99 Merge branch 'master' of https://github.com/synesthesiam/rhasspy 2019-12-26 11:10:37 -05:00
Michael Hansen cbbfc23395 Merge pull request #118 from jthomasdewald/master
Clarify intent handling for sever / client setup
2019-12-25 18:04:10 -05:00
Michael Hansen 0c2a1931f6 Merge pull request #115 from frkos/patch-1
Porcupine optimizer tool is deprecated
2019-12-25 18:02:52 -05:00
jthomasdewald 414457f150 Clarify intent handling for sever / client setup 2019-12-25 10:08:18 -08:00
frkos 640be7b0ac Update wake-word.md 2019-12-24 13:02:26 +03:00
frkos 421f59518a Update Porcupine wake-word docs
Porcupine optimizer tool is retired, so the link doesn't work
Accroding to the link https://github.com/Picovoice/porcupine#picovoice-console :
The console succeeds the (now retired) optimizer tool, as it can be used to train custom wake-words (Porcupine .ppn files).
2019-12-24 12:58:52 +03:00
172 changed files with 4142 additions and 2883 deletions
+172 -22
View File
@@ -1,26 +1,176 @@
.git/
.venv/
node_modules/
__pycache__/
test/
tools/
etc/test/
download/precise-engine/
download/kaldi/
opt/
*
etc/homeassistant/config/.storage
examples/typical/home-assistant/config/.storage
examples/typical-intent/home-assistant/config/.storage
examples/client-server/home-assistant/config/.storage
examples/mqtt-hermes/home-assistant/config/.storage
!download/
profiles/*/base_dictionary.txt
profiles/*/base_language_model.txt
profiles/*/acoustic_model/
profiles/*/g2p.fst
!requirements.txt
!dist/
!etc/wav
!etc/shflags
!create-venv.sh
!download-dependencies.sh
profiles/en-kaldi/
profiles/en-zamia/
!docker/run.sh
!docker/rhasspy
profiles/*/download/
!profiles/defaults.json
!profiles/zh/profile.json
!profiles/zh/custom_words.txt
!profiles/zh/espeak_phonemes.txt
!profiles/zh/phoneme_examples.txt
!profiles/zh/frequent_words.txt
!profiles/zh/sentences.ini
!profiles/zh/stop_words.txt
!profiles/zh/slots
!profiles/zh/slot_programs
!profiles/hi/profile.json
!profiles/hi/custom_words.txt
!profiles/hi/espeak_phonemes.txt
!profiles/hi/phoneme_examples.txt
!profiles/hi/frequent_words.txt
!profiles/hi/sentences.ini
!profiles/hi/stop_words.txt
!profiles/hi/slots
!profiles/hi/slot_programs
!profiles/el/profile.json
!profiles/el/custom_words.txt
!profiles/el/espeak_phonemes.txt
!profiles/el/phoneme_examples.txt
!profiles/el/frequent_words.txt
!profiles/el/sentences.ini
!profiles/el/stop_words.txt
!profiles/el/slots
!profiles/el/slot_programs
!profiles/es/profile.json
!profiles/es/custom_words.txt
!profiles/es/espeak_phonemes.txt
!profiles/es/phoneme_examples.txt
!profiles/es/frequent_words.txt
!profiles/es/sentences.ini
!profiles/es/stop_words.txt
!profiles/es/slots
!profiles/es/slot_programs
!profiles/it/profile.json
!profiles/it/custom_words.txt
!profiles/it/espeak_phonemes.txt
!profiles/it/phoneme_examples.txt
!profiles/it/frequent_words.txt
!profiles/it/sentences.ini
!profiles/it/stop_words.txt
!profiles/it/slots
!profiles/it/slot_programs
!profiles/ru/profile.json
!profiles/ru/custom_words.txt
!profiles/ru/espeak_phonemes.txt
!profiles/ru/phoneme_examples.txt
!profiles/ru/frequent_words.txt
!profiles/ru/sentences.ini
!profiles/ru/stop_words.txt
!profiles/ru/slots
!profiles/ru/slot_programs
!profiles/pt/profile.json
!profiles/pt/custom_words.txt
!profiles/pt/espeak_phonemes.txt
!profiles/pt/phoneme_examples.txt
!profiles/pt/frequent_words.txt
!profiles/pt/sentences.ini
!profiles/pt/stop_words.txt
!profiles/pt/slots
!profiles/pt/slot_programs
!profiles/sv/profile.json
!profiles/sv/custom_words.txt
!profiles/sv/espeak_phonemes.txt
!profiles/sv/phoneme_examples.txt
!profiles/sv/frequent_words.txt
!profiles/sv/sentences.ini
!profiles/sv/stop_words.txt
!profiles/sv/slots
!profiles/sv/slot_programs
!profiles/vi/profile.json
!profiles/vi/custom_words.txt
!profiles/vi/espeak_phonemes.txt
!profiles/vi/phoneme_examples.txt
!profiles/vi/frequent_words.txt
!profiles/vi/sentences.ini
!profiles/vi/stop_words.txt
!profiles/vi/slots
!profiles/vi/slot_programs
!profiles/ca/profile.json
!profiles/ca/custom_words.txt
!profiles/ca/espeak_phonemes.txt
!profiles/ca/phoneme_examples.txt
!profiles/ca/frequent_words.txt
!profiles/ca/sentences.ini
!profiles/ca/stop_words.txt
!profiles/ca/slots
!profiles/ca/slot_programs
!profiles/nl/profile.json
!profiles/nl/custom_words.txt
!profiles/nl/espeak_phonemes.txt
!profiles/nl/phoneme_examples.txt
!profiles/nl/frequent_words.txt
!profiles/nl/sentences.ini
!profiles/nl/stop_words.txt
!profiles/nl/slots
!profiles/nl/slot_programs
!profiles/nl/kaldi/custom_words.txt
!profiles/nl/kaldi/espeak_phonemes.txt
!profiles/nl/kaldi/phoneme_examples.txt
!profiles/de/profile.json
!profiles/de/custom_words.txt
!profiles/de/espeak_phonemes.txt
!profiles/de/phoneme_examples.txt
!profiles/de/frequent_words.txt
!profiles/de/sentences.ini
!profiles/de/stop_words.txt
!profiles/de/slots
!profiles/de/slot_programs
!profiles/de/kaldi/custom_words.txt
!profiles/de/kaldi/espeak_phonemes.txt
!profiles/de/kaldi/phoneme_examples.txt
!profiles/fr/profile.json
!profiles/fr/custom_words.txt
!profiles/fr/espeak_phonemes.txt
!profiles/fr/phoneme_examples.txt
!profiles/fr/frequent_words.txt
!profiles/fr/sentences.ini
!profiles/fr/stop_words.txt
!profiles/fr/slots
!profiles/fr/slot_programs
!profiles/fr/kaldi/custom_words.txt
!profiles/fr/kaldi/espeak_phonemes.txt
!profiles/fr/kaldi/phoneme_examples.txt
!profiles/en/profile.json
!profiles/en/custom_words.txt
!profiles/en/espeak_phonemes.txt
!profiles/en/phoneme_examples.txt
!profiles/en/frequent_words.txt
!profiles/en/sentences.ini
!profiles/en/stop_words.txt
!profiles/en/slots
!profiles/en/slot_programs
!profiles/en/kaldi/custom_words.txt
!profiles/en/kaldi/espeak_phonemes.txt
!profiles/en/kaldi/phoneme_examples.txt
!rhasspy/profile_schema.json
!rhasspy/*.py
!rhasspy/train/*.py
!rhasspy/train/jsgf2fst/*.py
!*.py
!VERSION
!pip
+104
View File
@@ -0,0 +1,104 @@
## [2.4.20] - 2020 Apr 10
### Added
- libasound2-plugins to Docker image (for Hass.IO)
- MQTT TLS support (thanks https://github.com/ofekd)
- Mycroft Precise 0.3.0 added to Docker image
### Changed
- Properly accept websocket connections
- Don't error out on missing porcupine files
- Fix rawValue in MQTT messages
## [2.4.19] - 2020 Mar 04
### Added
- Support for Google Cloud speech to text
- Rasa NLU minimum confidence parameter
### Changed
- Using tagged version of porcupine wake models to avoid incompatibilities
- Fix Rasa NLU first entity only bug
- Fix siteId null bug
## [2.4.18] - 2020 Feb 07
### Added
- /api/listen-for-wake accepts "on" and "off" as POST data to enable/disable wake word
- /api/events/wake websocket endpoint reports wake up events
- /api/events/text websocket endpoint reports transcription events
- Rhasspy logo changes in web UI when wake word is detected
- espeak arguments list for text to speech
### Changed
- STT output casing is fixed outside of HTTP API calls
- All voice commands show up in web UI test page
- Play last voice command button in web UI works for any command
- Fixed commas in numbers with thousand separators
- Words from Pocketsphinx wake keyphrase are added to dictionary
- Pocketsphinx wake word keyphrase casing is fixed
## [2.4.17] - 2020 Jan 21
### Added
- Button to web UI to play last recorded voice command
- RHASSPY_LOG_LEVEL environment variable
- Web UI feedback during download
- Add "asoundrc" config option to Hass.IO add-on
### Changed
- Moved $profile/kaldi/custom_words.txt to $profile/kaldi_custom_words.txt
- Slot substitution casing is kept during training/recognition
- Fixed fuzzywuzzy and other intent recognizer training after addition of converters
- Fix thread max count issue
- Hide web UI alerts after 10 seconds
- Delete partially downloaded profile files
- Force slot programs to run each training cycle
- Fix _raw_text in Hass event being same as _text
### Removed
- Flair intent recognizer
## [2.4.16] - 2020 Jan 5
### Added
- Number ranges (0..100)
- Converters for transforming JSON values in intents (!int)
- Slot programs for generating slot values
- $rhasspy/days and $rhasspy/months built-in slots
## [2.4.15] - 2019 Dec 27
### Added
- Preliminary support for Raspberry Pi Zero (no Kaldi)
- Play error sound when intent not recognized
- _text and _raw_text to Home Assistant events
### Changed
- Disable wake word when TTS is speaking
- Use json5 library to parse profile
- Remove picotts pop sound
- Don't open/close microphone after wake-up
## [2.4.14] - 2019 Dec 19
### Added
- Ability to split sentences across multiple .ini file in intents directory
- Support (future) /api/intent for Home Assistant
- Support for Home Assistant TTS system
- Emulate MaryTTS /process API in web API
- Include wakeId/siteId in JSON intent (MQTT/Websocket)
- ?voice and ?language query parameters to /api/text-to-speech
+80
View File
@@ -0,0 +1,80 @@
FROM ubuntu:eoan as build
ARG TARGETPLATFORM
ARG TARGETARCH
ARG TARGETVARIANT
ENV LANG C.UTF-8
ENV RHASSPY_APP /usr/share/rhasspy
ENV RHASSPY_VENV ${RHASSPY_APP}/.venv
WORKDIR /
RUN apt-get update && \
apt-get install --no-install-recommends --yes \
python3 python3-dev python3-setuptools python3-pip python3-venv \
build-essential swig portaudio19-dev libatlas-base-dev
COPY etc/shflags ${RHASSPY_APP}/etc/
COPY download/rhasspy-tools_*.tar.gz \
download/kaldi_*.tar.gz \
download/pocketsphinx-python.tar.gz \
download/snowboy-1.3.0.tar.gz \
download/precise-engine_0.3.0_*.tar.gz \
${RHASSPY_APP}/download/
COPY create-venv.sh download-dependencies.sh requirements.txt ${RHASSPY_APP}/
RUN cd ${RHASSPY_APP} && ./create-venv.sh --nosystem --noweb
# -----------------------------------------------------------------------------
FROM ubuntu:eoan
ARG TARGETPLATFORM
ARG TARGETARCH
ARG TARGETVARIANT
ENV LANG C.UTF-8
ENV RHASSPY_APP /usr/share/rhasspy
ENV RHASSPY_VENV ${RHASSPY_APP}/.venv
WORKDIR /
COPY --from=build ${RHASSPY_VENV} ${RHASSPY_VENV}
COPY --from=build ${RHASSPY_APP}/opt/kaldi/ ${RHASSPY_APP}/opt/kaldi/
RUN apt-get update && \
apt-get install --no-install-recommends --yes \
python3 python3-dev python3-setuptools python3-pip python3-venv \
bash jq unzip curl perl \
libportaudio2 libatlas3-base \
libgfortran4 ca-certificates \
sox espeak flite libttspico-utils alsa-utils lame \
libasound2-plugins \
libfreetype6-dev libpng-dev pkg-config libffi-dev libssl-dev \
gstreamer1.0-tools gstreamer1.0-plugins-good \
mosquitto-clients
# Web interface
ADD download/rhasspy-web-dist.tar.gz ${RHASSPY_APP}/
RUN ldconfig
# Copy script to run
COPY docker/run.sh /run.sh
RUN chmod +x /run.sh
COPY profiles/ ${RHASSPY_APP}/profiles/
COPY profiles/defaults.json ${RHASSPY_APP}/profiles/
COPY docker/rhasspy ${RHASSPY_APP}/bin/
COPY dist/ ${RHASSPY_APP}/dist/
COPY etc/wav/* ${RHASSPY_APP}/etc/wav/
COPY rhasspy/profile_schema.json ${RHASSPY_APP}/rhasspy/
COPY rhasspy/train/jsgf2fst/*.py ${RHASSPY_APP}/rhasspy/train/jsgf2fst/
COPY rhasspy/train/*.py ${RHASSPY_APP}/rhasspy/train/
COPY *.py ${RHASSPY_APP}/
COPY rhasspy/*.py ${RHASSPY_APP}/rhasspy/
COPY VERSION ${RHASSPY_APP}/
ENV CONFIG_PATH /data/options.json
ENV KALDI_PREFIX ${RHASSPY_APP}/opt
ENTRYPOINT ["/run.sh"]
+10 -40
View File
@@ -1,47 +1,19 @@
.PHONY: web-dist docker manifest docs-uml g2p check
SHELL := bash
DOCKER_PLATFORMS = linux/amd64,linux/arm64,linux/arm/v7
all: docker
# -----------------------------------------------------------------------------
# Docker
# -----------------------------------------------------------------------------
docker: web-dist docker-amd64 docker-armhf docker-aarch64 docker-push manifest
docker-amd64:
docker build . -f docker/templates/dockerfiles/Dockerfile.prebuilt.alsa.all \
--build-arg BUILD_ARCH=amd64 \
--build-arg CPU_ARCH=x86_64 \
--build-arg BUILD_FROM=ubuntu:bionic \
-t synesthesiam/rhasspy-server:amd64
docker-armhf:
docker build . -f docker/templates/dockerfiles/Dockerfile.prebuilt.alsa.all \
--build-arg BUILD_ARCH=armhf \
--build-arg CPU_ARCH=armv7l \
--build-arg BUILD_FROM=arm32v7/ubuntu:bionic \
-t synesthesiam/rhasspy-server:armhf
docker-aarch64:
docker build . -f docker/templates/dockerfiles/Dockerfile.prebuilt.alsa.all \
--build-arg BUILD_ARCH=aarch64 \
--build-arg CPU_ARCH=arm64v8 \
--build-arg BUILD_FROM=arm64v8/ubuntu:bionic \
-t synesthesiam/rhasspy-server:aarch64
docker-push:
docker push synesthesiam/rhasspy-server:amd64
docker push synesthesiam/rhasspy-server:armhf
docker push synesthesiam/rhasspy-server:aarch64
manifest:
docker manifest push --purge synesthesiam/rhasspy-server:latest
docker manifest create --amend synesthesiam/rhasspy-server:latest \
synesthesiam/rhasspy-server:amd64 \
synesthesiam/rhasspy-server:armhf \
synesthesiam/rhasspy-server:aarch64
docker manifest annotate synesthesiam/rhasspy-server:latest synesthesiam/rhasspy-server:armhf --os linux --arch arm
docker manifest annotate synesthesiam/rhasspy-server:latest synesthesiam/rhasspy-server:aarch64 --os linux --arch arm64
docker manifest push synesthesiam/rhasspy-server:latest
docker: web-dist
docker buildx build . \
--platform $(DOCKER_PLATFORMS) \
--tag synesthesiam/rhasspy-server:latest \
--push
# -----------------------------------------------------------------------------
# Yarn (Vue)
@@ -81,9 +53,7 @@ g2p: $(G2P_MODELS)
# Testing
# -----------------------------------------------------------------------------
mypy:
mypy app.py rhasspy
check:
flake8 --exclude=lexconvert.py app.py test.py rhasspy/*.py
pylint --ignore=lexconvert.py app.py test.py rhasspy/*.py
mypy app.py test.py rhasspy/*.py
+10 -4
View File
@@ -1,8 +1,14 @@
![Rhasspy logo](docs/img/rhasspy.svg)
# Rhasspy has [moved and improved!](https://github.com/rhasspy/rhasspy)
Rhasspy (pronounced RAH-SPEE) is an offline, [multilingual](#supported-languages) voice assistant toolkit inspired by [Jasper](https://jasperproject.github.io/) that works well with [Home Assistant](https://www.home-assistant.io/), [Hass.io](https://www.home-assistant.io/hassio/), and [Node-RED](https://nodered.org).
This repository contains code for an older version of Rhasspy (2.4).
* [Documentation](https://rhasspy.readthedocs.io/)
---
Rhasspy (pronounced RAH-SPEE) is an offline voice assistant toolkit inspired by [Jasper](https://jasperproject.github.io/) that [supports many languages](#supported-languages). It works well with [Home Assistant](https://www.home-assistant.io/), [Hass.io](https://www.home-assistant.io/hassio/), and [Node-RED](https://nodered.org).
**A newer version of Rhasspy (2.5) is available at [https://github.com/rhasspy/rhasspy](https://github.com/rhasspy/rhasspy)**
* [Documentation](https://rhasspy.readthedocs.io/en/v2.4.20/)
* [Discussion](https://community.rhasspy.org)
* [Video Introduction](https://www.youtube.com/watch?v=ijKTR_GqWwA)
* [Hass.IO Add-On Repository](https://github.com/synesthesiam/hassio-addons)
@@ -58,7 +64,7 @@ The table below summarizes language support across the various supporting techno
| | [rasaNLU](https://rhasspy.readthedocs.io/en/latest/intent-recognition/#rasanlu) | *needs extra software* | &#x2713; | &#x2713; | &#x2713; | &#x2713; | &#x2713; | &#x2713; | &#x2713; | &#x2713; | &#x2713; | &#x2713; | &#x2713; | &#x2713; | &#x2713; | &#x2713; |
| **Text to Speech** | [espeak](https://rhasspy.readthedocs.io/en/latest/text-to-speech/#espeak) | &#x2713; | &#x2713; | &#x2713; | &#x2713; | &#x2713; | &#x2713; | &#x2713; | &#x2713; | &#x2713; | &#x2713; | &#x2713; | &#x2713; | &#x2713; | &#x2713; | &#x2713; |
| | [flite](https://rhasspy.readthedocs.io/en/latest/text-to-speech/#flite) | &#x2713; | &#x2713; | | | | | | | | &#x2713; | | | | | |
| | [picotts](https://rhasspy.readthedocs.io/en/latest/text-to-speech/#picotts) | &#x2713; | &#x2713; | | | | | | | | | | | | | |
| | [picotts](https://rhasspy.readthedocs.io/en/latest/text-to-speech/#picotts) | &#x2713; | &#x2713; | &#x2713; | &#x2713; | &#x2713; | &#x2713; | | | | | | | | | |
| | [marytts](https://rhasspy.readthedocs.io/en/latest/text-to-speech/#marytts) | &#x2713; | &#x2713; | &#x2713; | | &#x2713; | &#x2713; | | &#x2713; | | | | | | | |
| | [wavenet](https://rhasspy.readthedocs.io/en/latest/text-to-speech/#google-wavenet) | | &#x2713; | &#x2713; | &#x2713; | &#x2713; | &#x2713; | &#x2713; | &#x2713; | | &#x2713; | &#x2713; | | &#x2713; | &#x2713; | |
+1 -1
View File
@@ -1 +1 @@
2.4.14
2.4.20
+6
View File
@@ -0,0 +1,6 @@
defaults:
-
scope:
path: ""
values:
render_with_liquid: false
+214 -62
View File
@@ -2,16 +2,20 @@
import argparse
import asyncio
import atexit
import concurrent.futures
import json
import logging
import os
import re
import shutil
import time
from functools import wraps
from pathlib import Path
from typing import Any, Dict, List, Tuple, Union
from typing import Any, Dict, List, Optional, Set, Tuple, Union
from uuid import uuid4
import attr
import json5
from quart import (
Quart,
Response,
@@ -27,8 +31,13 @@ from swagger_ui import quart_api_doc
from rhasspy.actor import ActorSystem, ConfigureEvent, RhasspyActor
from rhasspy.core import RhasspyCore
from rhasspy.dialogue import ProfileTrainingFailed
from rhasspy.intent import IntentRecognized
from rhasspy.events import (
IntentRecognized,
ProfileTrainingFailed,
VoiceCommand,
WakeWordDetected,
WavTranscription,
)
from rhasspy.utils import (
FunctionLoggingHandler,
buffer_to_wav,
@@ -52,6 +61,10 @@ app = Quart("rhasspy")
app.secret_key = str(uuid4())
app = cors(app)
# WAV data from last voice command
last_voice_wav: Optional[bytes] = None
# -----------------------------------------------------------------------------
# Parse Arguments
# -----------------------------------------------------------------------------
@@ -90,8 +103,12 @@ parser.add_argument("--log-level", default="DEBUG", help="Set logging level")
args = parser.parse_args()
# Set log level
log_level = getattr(logging, args.log_level.upper())
logging.basicConfig(level=log_level)
if "RHASSPY_LOG_LEVEL" in os.environ:
log_level = os.environ["RHASSPY_LOG_LEVEL"]
else:
log_level = args.log_level
logging.basicConfig(level=getattr(logging, log_level.upper()))
logger.debug(args)
@@ -185,7 +202,7 @@ async def api_profiles() -> Response:
return jsonify(
{
"default_profile": core.profile.name,
"profiles": sorted(list(profile_names)),
"profiles": sorted(profile_names),
"downloaded": downloaded,
"missing_files": missing_files,
}
@@ -205,6 +222,14 @@ async def api_download_profile() -> str:
return "OK"
@app.route("/api/download-status", methods=["GET"])
async def api_download_status() -> str:
"""Get status of profile download"""
assert core is not None
return "\n".join(core.download_status)
# -----------------------------------------------------------------------------
@@ -255,8 +280,11 @@ async def api_speakers() -> Response:
async def api_listen_for_wake() -> str:
"""Make Rhasspy listen for a wake word"""
assert core is not None
core.listen_for_wake()
return "OK"
enabled_str = (await request.data).decode().strip().lower()
enabled = enabled_str not in ["false", "off"]
core.listen_for_wake(enabled)
return str(enabled)
# -----------------------------------------------------------------------------
@@ -277,6 +305,10 @@ async def api_listen_for_command() -> Response:
entity = request.args.get("entity")
value = request.args.get("value")
# Emulate wake
wake_json = json.dumps({"wakewordId": "default", "siteId": core.siteId})
await add_ws_event("wake", wake_json)
return jsonify(
await core.listen_for_command(
handle=(not no_hass), timeout=timeout, entity=entity, value=value
@@ -295,14 +327,14 @@ async def api_profile() -> Union[str, Response]:
if request.method == "POST":
# Ensure that JSON is valid
profile_json = await request.json
profile_json = json5.loads(await request.data)
recursive_remove(core.profile.system_json, profile_json)
profile_path = Path(core.profile.write_path("profile.json"))
with open(profile_path, "w") as profile_file:
json.dump(profile_json, profile_file, indent=4)
msg = "Wrote profile to %s" % profile_path
msg = f"Wrote profile to {profile_path}"
logger.debug(msg)
return msg
@@ -368,7 +400,7 @@ async def api_pronounce() -> Union[Response, str]:
if download:
# Return WAV
return Response(wav_data) # , mimetype="audio/wav")
return Response(wav_data, mimetype="audio/wav")
# Play through speakers
core.play_wav_data(wav_data)
@@ -441,7 +473,7 @@ async def api_sentences():
num_chars = 0
paths_written = []
sentences_dict = await request.json
sentences_dict = json5.loads(await request.data)
for sentences_path, sentences_text in sentences_dict.items():
# Path is relative to profile directory
sentences_path = Path(core.profile.write_path(sentences_path))
@@ -460,9 +492,7 @@ async def api_sentences():
logger.debug("Removing %s", sentences_path)
sentences_path.unlink()
return "Wrote {} char(s) to {}".format(
num_chars, [str(p) for p in paths_written]
)
return f"Wrote {num_chars} char(s) to {[str(p) for p in paths_written]}"
# Update sentences.ini only
sentences_path = Path(
@@ -472,7 +502,7 @@ async def api_sentences():
data = await request.data
with open(sentences_path, "wb") as sentences_file:
sentences_file.write(data)
return "Wrote {} byte(s) to {}".format(len(data), sentences_path)
return f"Wrote {len(data)} byte(s) to {sentences_path}"
# GET
sentences_path_rel = core.profile.read_path(
@@ -525,6 +555,26 @@ async def api_custom_words():
assert core is not None
speech_system = core.profile.get("speech_to_text.system", "pocketsphinx")
# Temporary fix for kaldi/custom_words -> kaldi_custom_words.txt
old_kaldi_words_path = Path(core.profile.read_path("kaldi/custom_words.txt"))
if old_kaldi_words_path.is_file():
new_kaldi_words_path = Path(
core.profile.write_path(
core.profile.get(
"speech_to_text.kaldi.custom_words", "custom_words.txt"
)
)
)
if (
new_kaldi_words_path != old_kaldi_words_path
and not new_kaldi_words_path.is_file()
):
logger.warning(
"Moving %s to %s", str(old_kaldi_words_path), str(new_kaldi_words_path)
)
shutil.move(old_kaldi_words_path, new_kaldi_words_path)
if request.method == "POST":
custom_words_path = Path(
core.profile.write_path(
@@ -547,7 +597,7 @@ async def api_custom_words():
print(line, file=custom_words_file)
lines_written += 1
return "Wrote %s line(s) to %s" % (lines_written, custom_words_path)
return f"Wrote {lines_written} line(s) to {custom_words_path}"
custom_words_path = Path(
core.profile.read_path(
@@ -619,6 +669,7 @@ async def api_restart() -> str:
@app.route("/api/speech-to-text", methods=["POST"])
async def api_speech_to_text() -> str:
"""Transcribe speech from WAV file."""
global last_voice_wav
no_header = request.args.get("noheader", "false").lower() == "true"
assert core is not None
@@ -628,10 +679,20 @@ async def api_speech_to_text() -> str:
# Wrap in WAV
wav_data = buffer_to_wav(wav_data)
last_voice_wav = wav_data
start_time = time.perf_counter()
result = await core.transcribe_wav(wav_data)
end_time = time.perf_counter()
# Send to websocket
await add_ws_event(
"transcription",
json.dumps(
{"text": result.text, "wakewordId": "default", "siteId": core.siteId}
),
)
if prefers_json():
return jsonify(
{
@@ -666,7 +727,7 @@ async def api_text_to_intent():
intent_json = json.dumps(intent)
logger.debug(intent_json)
await add_ws_event(WS_EVENT_INTENT, intent_json)
await add_ws_event("intent", intent_json)
if not no_hass:
# Send intent to Home Assistant
@@ -681,11 +742,13 @@ async def api_text_to_intent():
@app.route("/api/speech-to-intent", methods=["POST"])
async def api_speech_to_intent() -> Response:
"""Transcribe speech, recognize intent, and optionally handle."""
global last_voice_wav
assert core is not None
no_hass = request.args.get("nohass", "false").lower() == "true"
# Prefer 16-bit 16Khz mono, but will convert with sox if needed
wav_data = await request.data
last_voice_wav = wav_data
# speech -> text
start_time = time.time()
@@ -693,6 +756,12 @@ async def api_speech_to_intent() -> Response:
text = transcription.text
logger.debug(text)
# Send to websocket
await add_ws_event(
"transcription",
json.dumps({"text": text, "wakewordId": "default", "siteId": core.siteId}),
)
# text -> intent
intent = (await core.recognize_intent(text)).intent
intent["speech_confidence"] = transcription.confidence
@@ -702,7 +771,7 @@ async def api_speech_to_intent() -> Response:
intent_json = json.dumps(intent)
logger.debug(intent_json)
await add_ws_event(WS_EVENT_INTENT, intent_json)
await add_ws_event("intent", intent_json)
if not no_hass:
# Send intent to Home Assistant
@@ -727,6 +796,7 @@ async def api_start_recording() -> str:
@app.route("/api/stop-recording", methods=["POST"])
async def api_stop_recording() -> Response:
"""End recording voice command. Transcribe and handle."""
global last_voice_wav
assert core is not None
no_hass = request.args.get("nohass", "false").lower() == "true"
@@ -740,20 +810,43 @@ async def api_stop_recording() -> Response:
text = transcription.text
logger.debug(text)
# Send to websocket
await add_ws_event(
"transcription",
json.dumps({"text": text, "wakewordId": "default", "siteId": core.siteId}),
)
intent = (await core.recognize_intent(text)).intent
intent["speech_confidence"] = transcription.confidence
intent_json = json.dumps(intent)
logger.debug(intent_json)
await add_ws_event(WS_EVENT_INTENT, intent_json)
await add_ws_event("intent", intent_json)
if not no_hass:
# Send intent to Home Assistant
intent = (await core.handle_intent(intent)).intent
# Save last voice command WAV data
last_voice_wav = wav_data
return jsonify(intent)
@app.route("/api/play-recording", methods=["POST"])
async def api_play_recording() -> str:
"""Play last recorded voice command through the configured audio output system"""
global last_voice_wav
assert core is not None
if last_voice_wav:
# Play through speakers
logger.debug("Playing %s byte(s)", len(last_voice_wav))
core.play_wav_data(last_voice_wav)
return "OK"
# -----------------------------------------------------------------------------
@@ -794,19 +887,20 @@ async def api_text_to_speech() -> Union[bytes, str]:
play = request.args.get("play", "true").strip().lower() == "true"
language = request.args.get("language")
voice = request.args.get("voice")
siteId = request.args.get("siteId")
data = await request.data
sentence = last_sentence if repeat else data.decode().strip()
assert core is not None
result = await core.speak_sentence(
sentence, play=play, language=language, voice=voice
sentence, play=play, language=language, voice=voice, siteId=siteId
)
last_sentence = sentence
if not play:
# Return WAV data instead of speaking
return result.wav_data
return Response(result.wav_data, mimetype="audio/wav")
return sentence
@@ -821,7 +915,7 @@ async def api_slots() -> Union[str, Response]:
if request.method == "POST":
overwrite_all = request.args.get("overwrite_all", "false").lower() == "true"
new_slot_values = await request.json
new_slot_values = json5.loads(await request.data)
slots_dir = Path(
core.profile.write_path(
@@ -831,7 +925,7 @@ async def api_slots() -> Union[str, Response]:
if overwrite_all:
# Remote existing values first
for name in new_slot_values.keys():
for name in new_slot_values:
slots_path = safe_join(slots_dir, f"{name}")
if slots_path.is_file():
try:
@@ -849,15 +943,16 @@ async def api_slots() -> Union[str, Response]:
slots_path.parent.mkdir(parents=True, exist_ok=True)
# Merge with existing values
values = set(values)
values = {v.strip() for v in values}
if slots_path.is_file():
values.update(line for line in slots_path.read_text().splitlines())
values.update(
line.strip() for line in slots_path.read_text().splitlines()
)
# Write merged values
if values:
with open(slots_path, "w") as slots_file:
for value in values:
value = value.strip()
if value:
print(value, file=slots_file)
@@ -977,7 +1072,7 @@ def api_intents():
@app.route("/process", methods=["GET"])
async def marytts_process():
async def marytts_process() -> Response:
"""Emulate MaryTTS /process API"""
global last_sentence
@@ -989,7 +1084,7 @@ async def marytts_process():
sentence, play=False, voice=voice, language=locale
)
return spoken.wav_data
return Response(spoken.wav_data, mimetype="audio/wav")
# -----------------------------------------------------------------------------
@@ -1061,26 +1156,26 @@ async def swagger_yaml() -> Response:
# WebSocket API
# -----------------------------------------------------------------------------
WS_EVENT_INTENT = 0
WS_EVENT_LOG = 1
ws_queues: List[List[asyncio.Queue]] = [[], []]
ws_locks: List[asyncio.Lock] = [asyncio.Lock(), asyncio.Lock()]
user_queues: Set[asyncio.Queue] = set()
logging_queues: Set[asyncio.Queue] = set()
async def add_ws_event(event_type: int, text: str):
"""Send text out to all websockets for a specific event."""
async with ws_locks[event_type]:
for q in ws_queues[event_type]:
await q.put(text)
async def add_ws_event(message_type: str, text: str):
"""Send text out to all user websockets for a specific event."""
for q in user_queues:
await q.put((message_type, text))
async def log_ws_event(text: str):
"""Send logging message out to websockets."""
for q in logging_queues:
await q.put(text)
# Send logging messages out to websocket
logging.root.addHandler(
FunctionLoggingHandler(
lambda msg: asyncio.run_coroutine_threadsafe(
add_ws_event(WS_EVENT_LOG, msg), loop
)
lambda msg: asyncio.run_coroutine_threadsafe(log_ws_event(msg), loop)
)
)
@@ -1090,6 +1185,8 @@ class WebSocketObserver(RhasspyActor):
def in_started(self, message: Any, sender: RhasspyActor) -> None:
"""Handle messages in started state."""
global last_voice_wav
if isinstance(message, IntentRecognized):
# Add slots
intent_slots = {}
@@ -1101,49 +1198,101 @@ class WebSocketObserver(RhasspyActor):
# Convert to JSON
intent_json = json.dumps(message.intent)
self._logger.debug(intent_json)
asyncio.run_coroutine_threadsafe(
add_ws_event(WS_EVENT_INTENT, intent_json), loop
asyncio.run_coroutine_threadsafe(add_ws_event("intent", intent_json), loop)
elif isinstance(message, WakeWordDetected):
assert core is not None
wake_json = json.dumps({"wakewordId": message.name, "siteId": core.siteId})
asyncio.run_coroutine_threadsafe(add_ws_event("wake", wake_json), loop)
elif isinstance(message, WavTranscription):
assert core is not None
transcription_json = json.dumps(
{
"text": message.text,
"wakewordId": message.wakewordId,
"siteId": core.siteId,
}
)
asyncio.run_coroutine_threadsafe(
add_ws_event("transcription_json", transcription_json), loop
)
elif isinstance(message, VoiceCommand):
# Save last voice command
last_voice_wav = buffer_to_wav(message.data)
def api_websocket(func):
"""Wraps a websocket route to use a user websocket queue"""
@wraps(func)
async def wrapper(*_args, **kwargs):
global user_queues
queue = asyncio.Queue()
user_queues.add(queue)
try:
return await func(queue, *_args, **kwargs)
except Exception:
logger.exception("api_websocket")
finally:
user_queues.discard(queue)
return wrapper
@app.websocket("/api/events/intent")
async def api_events_intent() -> None:
@api_websocket
async def api_events_intent(queue) -> None:
"""Websocket endpoint to receive intents as JSON."""
# Add new queue for websocket
q: asyncio.Queue = asyncio.Queue()
async with ws_locks[WS_EVENT_INTENT]:
ws_queues[WS_EVENT_INTENT].append(q)
await websocket.accept()
try:
while True:
text = await q.get()
while True:
message_type, text = await queue.get()
if message_type == "intent":
await websocket.send(text)
except Exception:
logger.exception("api_events_intent")
# Remove queue
async with ws_locks[WS_EVENT_INTENT]:
ws_queues[WS_EVENT_INTENT].remove(q)
@app.websocket("/api/events/text")
@api_websocket
async def api_events_text(queue) -> None:
"""Websocket endpoint for transcriptions."""
await websocket.accept()
while True:
message_type, text = await queue.get()
if message_type == "transcription":
await websocket.send(text)
@app.websocket("/api/events/wake")
@api_websocket
async def api_events_wake(queue) -> None:
"""Websocket endpoint to report wake up."""
await websocket.accept()
while True:
message_type, text = await queue.get()
if message_type == "wake":
await websocket.send(text)
@app.websocket("/api/events/log")
async def api_events_log() -> None:
"""Websocket endpoint to receive logging messages as text."""
await websocket.accept()
# Add new queue for websocket
q: asyncio.Queue = asyncio.Queue()
async with ws_locks[WS_EVENT_LOG]:
ws_queues[WS_EVENT_LOG].append(q)
logging_queues.add(q)
try:
while True:
text = await q.get()
await websocket.send(text)
except Exception:
logger.exception("api_events_log")
except concurrent.futures.CancelledError:
pass
# Remove queue
async with ws_locks[WS_EVENT_LOG]:
ws_queues[WS_EVENT_LOG].remove(q)
logging_queues.discard(q)
# -----------------------------------------------------------------------------
@@ -1179,6 +1328,9 @@ loop.run_until_complete(start_rhasspy())
# -----------------------------------------------------------------------------
# Disable useless logging messages
logging.getLogger("wsproto").setLevel(logging.CRITICAL)
# Start web server
if args.ssl is not None:
logger.debug("Using SSL with certfile, keyfile = %s", args.ssl)
+7 -7
View File
@@ -36,7 +36,7 @@ def main():
# Load dictionary
word_dict = {}
logging.info("Loading dictionary from %s" % args.dictionary)
logging.info("Loading dictionary from %s", args.dictionary)
with open(args.dictionary, "r") as dict_file:
read_dict(dict_file, word_dict)
@@ -53,7 +53,7 @@ def main():
all_words.append(word)
assert len(phonemes) == len(phoneme_words), "Not enough words to cover phonemes"
logging.debug("Phonemes: %s" % ", ".join(phoneme_words.keys()))
logging.debug("Phonemes: %s", ", ".join(phoneme_words))
phoneme_hyps = defaultdict(lambda: defaultdict(float))
@@ -66,7 +66,7 @@ def main():
phoneme_hyps[phoneme][hyp] = count
# Sample words from the dictionary
logging.info("Starting %s sample(s)" % args.samples)
logging.info("Starting %s sample(s)", args.samples)
phoneme_futures = {}
with ProcessPoolExecutor() as executor:
# Schedule eSpeak word samples
@@ -80,7 +80,7 @@ def main():
for i, future in enumerate(as_completed(phoneme_futures)):
if i % len(phonemes) == 0:
logging.info(
"Sample %s of %s" % ((i // len(phonemes) + 1), args.samples)
"Sample %s of %s", (i // len(phonemes) + 1), args.samples
)
phoneme = phoneme_futures[future]
@@ -113,14 +113,14 @@ def main():
best = {}
todo = set(phonemes)
used = set()
while len(todo) > 0:
while todo:
for phoneme in list(todo):
best_to_worst = sorted(
phoneme_hyps[phoneme].items(), key=lambda kv: kv[1], reverse=True
)
for hyp, count in best_to_worst:
if not hyp in used:
if hyp not in used:
best[phoneme] = hyp
used.add(hyp)
todo.remove(phoneme)
@@ -165,7 +165,7 @@ def read_dict(dict_file, word_dict):
"""
for line in dict_file:
line = line.strip()
if len(line) == 0:
if not line:
continue
word, pronounce = re.split("[ ]+", line, maxsplit=1)
+36
View File
@@ -0,0 +1,36 @@
#!/usr/bin/env python3
import argparse
import calendar
import json
import locale
from pathlib import Path
def main():
parser = argparse.ArgumentParser("generate-slots")
parser.add_argument("profiles_dir")
args = parser.parse_args()
for profile_dir in Path(args.profiles_dir).glob("*"):
if not profile_dir.is_dir():
continue
with open(profile_dir / "profile.json", "r") as profile_file:
profile = json.load(profile_file)
locale_name = profile["locale"] + ".UTF-8"
locale.setlocale(locale.LC_ALL, locale_name)
print(locale_name)
slots_dir = profile_dir / "slots" / "rhasspy"
slots_dir.mkdir(parents=True, exist_ok=True)
# Day names
(slots_dir / "days").write_text('\n'.join(calendar.day_name))
# Month names
(slots_dir / "months").write_text('\n'.join(filter(None, calendar.month_name)))
# -----------------------------------------------------------------------------
if __name__ == "__main__":
main()
+1 -3
View File
@@ -38,9 +38,7 @@ def main():
if not os.path.exists(html_path):
# Download
url = "https://www.ezglot.com/most-frequently-used-words.php?l={0}&submit=Select".format(
language
)
url = f"https://www.ezglot.com/most-frequently-used-words.php?l={language}&submit=Select"
print(f"Downloading from {url}")
with open(html_path, "w") as html_file:
+3 -3
View File
@@ -26,7 +26,7 @@ def main():
with open(args.dictionary, "r") as dict_file:
for line in dict_file:
line = line.strip()
if len(line) == 0:
if not line:
continue
parts = re.split(r"[\t ]+", line)
@@ -44,11 +44,11 @@ def main():
# Pick unique example words for every phoneme
used_words = set()
for phoneme in sorted(examples.keys()):
for phoneme in sorted(examples):
# Choose the shortest, unused example word for this phoneme.
# Exclude words with 3 or fewer letters.
for word, pron in sorted(examples[phoneme], key=lambda kv: len(kv[0])):
if len(word) > 3 and (not word in used_words):
if len(word) > 3 and (word not in used_words):
# Output format is:
# phoneme word pronunciation
print(phoneme, word, " ".join(pron))
+5 -5
View File
@@ -31,7 +31,7 @@ def main():
with open(args.dictionary, "r") as dict_file:
for line in dict_file:
line = line.strip()
if len(line) == 0:
if not line:
continue
parts = re.split(r"[\t ]+", line)
@@ -70,7 +70,7 @@ def main():
with open(args.frequent_phones, "r") as freq_phones_file:
for line in freq_phones_file:
line = line.strip()
if len(line) == 0:
if not line:
continue
parts = re.split(r"[ ]+", line, maxsplit=1)
@@ -82,7 +82,7 @@ def main():
mappings = []
bad_espeak = (":", ";", "-", "#")
for word, espeak in freq_espeak.items():
if not word in freq_phonemes:
if word not in freq_phonemes:
# No pronunciation
continue
@@ -134,7 +134,7 @@ def main():
m = 4
for p in all_phonemes:
candidate_counts = [
(e, phoneme_counts[(cp, e)]) for (cp, e) in phoneme_counts.keys() if cp == p
(e, phoneme_counts[(cp, e)]) for (cp, e) in phoneme_counts if cp == p
]
candidate_counts = [ec for ec in candidate_counts if ec[1] > n]
candidate_counts = sorted(candidate_counts, key=lambda x: x[1], reverse=True)
@@ -213,7 +213,7 @@ assign(P, E) :- maybe_assign(P, E).
predicates = []
for line in proc.stdout.splitlines():
line = line.decode().strip()
if len(line) == 0:
if not line:
continue
elif line.startswith("OPTIMUM FOUND"):
break
+1 -1
View File
@@ -20,7 +20,7 @@ def main():
with open(dict_path, "r") as dict_file:
for line in dict_file:
line = line.strip()
if len(line) == 0:
if not line:
continue
parts = re.split(r"[ ]+", line)
+28
View File
@@ -0,0 +1,28 @@
#!/usr/bin/env python
import sys
import json
import random
import datetime
def speech(text):
global o
o["speech"] = {"text": text}
# get json from stdin and load into python dict
o = json.loads(sys.stdin.read())
intent = o["intent"]["name"]
if intent == "GetTime":
now = datetime.datetime.now()
speech("It's %s %d %s." % (now.strftime('%H'), now.minute, now.strftime('%p')))
elif intent == "Hello":
replies = ['Hi!', 'Hello!', 'Hey there!', 'Greetings.']
speech(random.choice(replies))
# convert dict to json and print to stdout
print(json.dumps(o))
+1 -1
View File
@@ -12,7 +12,7 @@ def main():
with open(sys.argv[1], "r") as dict_file:
for line in dict_file:
line = line.strip()
if len(line) == 0:
if not line:
continue
parts = re.split(r"[ ]+", line)
+18 -9
View File
@@ -128,7 +128,7 @@ if [[ -z "${no_system}" ]]; then
echo "Installing system dependencies"
run_sudo apt-get update
run_sudo apt-get install --no-install-recommends --yes \
run_sudo apt-get install --no-install-recommends \
python3 python3-pip python3-venv python3-dev \
python \
build-essential autoconf autoconf-archive libtool automake bison \
@@ -183,7 +183,7 @@ if [[ ! -d "${phonetisaurus_dir}/build" ]]; then
phonetisaurus_file="${download_dir}/phonetisaurus-2019.tar.gz"
if [[ ! -s "${phonetisaurus_file}" ]]; then
phonetisaurus_url='https://github.com/synesthesiam/phonetisaurus-2019/releases/download/v1.0/phonetisaurus-2019.tar.gz'
phonetisaurus_url='https://github.com/synesthesiam/docker-phonetisaurus/raw/master/download/phonetisaurus-2019.tar.gz'
echo "Downloading phonetisaurus (${phonetisaurus_url})"
maybe_download "${phonetisaurus_url}" "${phonetisaurus_file}"
fi
@@ -191,7 +191,7 @@ fi
# Kaldi
kaldi_dir="${this_dir}/opt/kaldi"
if [[ ! -d "${kaldi_dir}" ]]; then
if [[ -z "${no_kaldi}" && ! -d "${kaldi_dir}" ]]; then
install libatlas-base-dev libatlas3-base gfortran
run_sudo ldconfig
kaldi_file="${download_dir}/kaldi-2019.tar.gz"
@@ -329,31 +329,40 @@ case "${CPU_ARCH}" in
esac
requirements_file="${temp_dir}/requirements.txt"
temp_requirements_file="${temp_dir}/temp_requirements.txt"
cp "${this_dir}/requirements.txt" "${requirements_file}"
# Exclude requirements
if [[ -n "${no_flair}" ]]; then
echo "Excluding flair from virtual environment"
sed -i '/^flair/d' "${requirements_file}"
sed '/^flair/d' "${requirements_file}" > "${temp_requirements_file}" &&
mv "${temp_requirements_file}" "${requirements_file}"
fi
if [[ -n "${no_precise}" ]]; then
echo "Excluding Mycroft Precise from virtual environment"
sed -i '/^precise-runner/d' "${requirements_file}"
sed '/^precise-runner/d' "${requirements_file}" > "${temp_requirements_file}" &&
mv "${temp_requirements_file}" "${requirements_file}"
fi
if [[ -n "${no_adapt}" ]]; then
echo "Excluding Mycroft Adapt from virtual environment"
sed -i '/^adapt-parser/d' "${requirements_file}"
sed '/^adapt-parser/d' "${requirements_file}" > "${temp_requirements_file}" &&
mv "${temp_requirements_file}" "${requirements_file}"
fi
if [[ -n "${no_google}" ]]; then
echo "Excluding Google Text to Speech from virtual environment"
sed -i '/^google-cloud-texttospeech/d' "${requirements_file}"
sed '/^google-cloud-texttospeech/d' "${requirements_file}" > "${temp_requirements_file}" &&
mv "${temp_requirements_file}" "${requirements_file}"
fi
# Install everything except openfst first
sed -i '/^openfst/d' "${requirements_file}"
sed '/^openfst/d' "${requirements_file}" > "${temp_requirements_file}" &&
mv "${temp_requirements_file}" "${requirements_file}"
"${python}" -m pip install -r "${requirements_file}"
@@ -398,5 +407,5 @@ esac
if [[ -z "${no_web}" ]]; then
echo "Building web interface"
cd "${this_dir}" && yarn build
cd "${this_dir}" && yarn install && yarn build
fi
+45 -23
View File
@@ -11,10 +11,12 @@ DEFINE_string 'venv' "${this_dir}/.venv" 'Path to create virtual environment'
DEFINE_string 'download-dir' "${this_dir}/download" 'Directory to cache downloaded files'
DEFINE_boolean 'system' true 'Install system dependencies'
DEFINE_boolean 'flair' false 'Install flair'
DEFINE_boolean 'precise' false 'Install Mycroft Precise'
DEFINE_boolean 'precise' true 'Install Mycroft Precise'
DEFINE_boolean 'adapt' true 'Install Mycroft Adapt'
DEFINE_boolean 'google' false 'Install Google Text to Speech'
DEFINE_boolean 'google' true 'Install Google Text to Speech'
DEFINE_boolean 'kaldi' true 'Install Kaldi'
DEFINE_boolean 'tools' true 'Install Rhasspy tools'
DEFINE_boolean 'web' true 'Install web UI'
DEFINE_boolean 'offline' false "Don't download anything"
DEFINE_integer 'make-threads' 4 'Number of threads to use with make' 'j'
DEFINE_string 'python' '' 'Path to Python executable'
@@ -60,6 +62,14 @@ if [[ "${FLAGS_offline}" -eq "${FLAGS_TRUE}" ]]; then
offline='true'
fi
if [[ "${FLAGS_tools}" -eq "${FLAGS_FALSE}" ]]; then
no_tools='true'
fi
if [[ "${FLAGS_web}" -eq "${FLAGS_FALSE}" ]]; then
no_web='true'
fi
make_threads="${FLAGS_make_threads}"
# -----------------------------------------------------------------------------
@@ -95,7 +105,7 @@ function maybe_download {
if [[ -z "${no_system}" ]]; then
echo "Installing system dependencies"
sudo apt-get update
sudo apt-get install --no-install-recommends --yes \
sudo apt-get install --no-install-recommends \
python3 python3-pip python3-venv python3-dev \
python \
build-essential autoconf autoconf-archive libtool automake bison \
@@ -122,7 +132,7 @@ if [[ -z "${FLAGS_python}" ]]; then
PYTHON='python3.6'
else
echo "Installing Python 3.6 from source. This is going to take a LONG time."
sudo apt-get install --no-install-recommends --yes \
sudo apt-get install --no-install-recommends \
tk-dev libncurses5-dev libncursesw5-dev \
libreadline6-dev libdb5.3-dev libgdbm-dev \
libsqlite3-dev libssl-dev libbz2-dev \
@@ -160,7 +170,7 @@ case "${CPU_ARCH}" in
FRIENDLY_ARCH=armhf
;;
arm64v8)
aarch64|arm64v8)
FRIENDLY_ARCH=aarch64
;;
@@ -183,6 +193,14 @@ if [[ -n "${no_kaldi}" ]]; then
download_args+=('--nokaldi')
fi
if [[ -n "${no_tools}" ]]; then
download_args+=('--notools')
fi
if [[ -n "${no_web}" ]]; then
download_args+=('--noweb')
fi
bash download-dependencies.sh "${download_args[@]}"
# -----------------------------------------------------------------------------
@@ -202,10 +220,12 @@ echo "Creating new virtual environment"
mkdir -p "${venv}"
"${PYTHON}" -m venv "${venv}"
# Extract Rhasspy tools
rhasspy_tools_file="${download_dir}/rhasspy-tools_${FRIENDLY_ARCH}.tar.gz"
echo "Extracting tools (${rhasspy_tools_file})"
tar -C "${venv}" -xf "${rhasspy_tools_file}"
if [[ -z "${no_tools}" ]]; then
# Extract Rhasspy tools
rhasspy_tools_file="${download_dir}/rhasspy-tools_${FRIENDLY_ARCH}.tar.gz"
echo "Extracting tools (${rhasspy_tools_file})"
tar -C "${venv}" -xf "${rhasspy_tools_file}"
fi
# Force .venv/lib to be used
export LD_LIBRARY_PATH="${venv}/lib:${LD_LIBRARY_PATH}"
@@ -214,11 +234,11 @@ export LD_LIBRARY_PATH="${venv}/lib:${LD_LIBRARY_PATH}"
source "${venv}/bin/activate"
echo "Upgrading pip"
"${PYTHON}" -m pip install --upgrade pip
python3 -m pip install --upgrade pip
echo "Installing Python requirements"
"${PYTHON}" -m pip install wheel setuptools
"${PYTHON}" -m pip install requests
python3 -m pip install wheel setuptools
python3 -m pip install requests
# pytorch is not available on ARM
case "${CPU_ARCH}" in
@@ -255,18 +275,18 @@ sed -i '/^openfst/d' "${requirements_file}"
python3 -m pip install -r "${requirements_file}"
# Install Python openfst wrapper
"${PYTHON}" -m pip install \
--global-option=build_ext \
--global-option="-I${venv}/include" \
--global-option="-L${venv}/lib" \
-r <(grep '^openfst' "${this_dir}/requirements.txt")
python3 -m pip install \
--global-option=build_ext \
--global-option="-I${venv}/include" \
--global-option="-L${venv}/lib" \
-r <(grep '^openfst' "${this_dir}/requirements.txt")
# -----------------------------------------------------------------------------
# Pocketsphinx for Python
# -----------------------------------------------------------------------------
pocketsphinx_file="${download_dir}/pocketsphinx-python.tar.gz"
"${PYTHON}" -m pip install "${pocketsphinx_file}"
python3 -m pip install "${pocketsphinx_file}"
# -----------------------------------------------------------------------------
# Snowboy
@@ -275,7 +295,7 @@ pocketsphinx_file="${download_dir}/pocketsphinx-python.tar.gz"
case "${CPU_ARCH}" in
x86_64|armv7l)
snowboy_file="${download_dir}/snowboy-1.3.0.tar.gz"
"${PYTHON}" -m pip install "${snowboy_file}"
python3 -m pip install "${snowboy_file}"
;;
*)
@@ -288,7 +308,7 @@ esac
if [[ -z "${no_precise}" && -z "$(command -v precise-engine)" ]]; then
case "${CPU_ARCH}" in
x86_64|armv7l)
x86_64|armv7l|aarch64)
echo "Installing Mycroft Precise"
precise_file="${download_dir}/precise-engine_0.3.0_${CPU_ARCH}.tar.gz"
precise_install="${venv}/lib"
@@ -316,9 +336,11 @@ fi
# Web Interface
# -----------------------------------------------------------------------------
rhasspy_web_file="${download_dir}/rhasspy-web-dist.tar.gz"
echo "Extracting web interface (${rhasspy_web_file})"
tar -C "${this_dir}" -xf "${rhasspy_web_file}"
if [[ -z "${no_web}" ]]; then
rhasspy_web_file="${download_dir}/rhasspy-web-dist.tar.gz"
echo "Extracting web interface (${rhasspy_web_file})"
tar -C "${this_dir}" -xf "${rhasspy_web_file}"
fi
# -----------------------------------------------------------------------------
+26 -5
View File
@@ -1,5 +1,4 @@
#!/usr/bin/env bash
DIR="$( cd "$( dirname "$0" )" && pwd )"
# Try to detemine where Rhasspy is located
if [[ -z "${RHASSPY_APP}" ]]; then
@@ -16,13 +15,35 @@ if [[ -f "${CONFIG_PATH}" ]]; then
# Hass.IO configuration
profile_name="$(jq --raw-output '.profile_name' "${CONFIG_PATH}")"
profile_dir="$(jq --raw-output '.profile_dir' "${CONFIG_PATH}")"
RHASSPY_ARGS="--profile \"${profile_name}\" --user-profiles \"${profile_dir}\""
RHASSPY_ARGS=('--profile' "${profile_name}" '--user-profiles' "${profile_dir}")
# Copy user-defined asoundrc to root
asoundrc="$(jq --raw-output '.asoundrc' "${CONFIG_PATH}")"
if [[ ! -z "${asoundrc}" ]]; then
echo "${asoundrc}" > /root/.asoundrc
fi
# Add SSL settings
ssl="$(jq --raw-output '.ssl' "${CONFIG_PATH}")"
if [[ "${ssl}" == 'true' ]]; then
certfile="$(jq --raw-output '.certfile' "${CONFIG_PATH}")"
keyfile="$(jq --raw-output '.keyfile' "${CONFIG_PATH}")"
RHASSPY_ARGS+=('--ssl' "/ssl/${certfile}" "/ssl/${keyfile}")
fi
fi
cd "${RHASSPY_APP}"
RHASSPY_VENV="${RHASSPY_APP}/.venv"
if [[ -d "${RHASSPY_VENV}" ]]; then
source "${RHASSPY_VENV}/bin/activate"
if [[ -z "${RHASSPY_ARGS}" ]]; then
# Force .venv/lib to be used
export LD_LIBRARY_PATH="${RHASSPY_VENV}/lib:${LD_LIBRARY_PATH}"
fi
cd "${RHASSPY_APP}" || exit 1
if [[ -z "${RHASSPY_ARGS[*]}" ]]; then
python3 app.py "$@"
else
python3 app.py "${RHASSPY_ARGS}" "$@"
python3 app.py "${RHASSPY_ARGS[@]}" "$@"
fi
-52
View File
@@ -1,52 +0,0 @@
ARG BUILD_FROM
FROM $BUILD_FROM
LABEL maintainer="Michael Hansen <hansen.mike@gmail.com>"
ARG BUILD_ARCH
ARG CPU_ARCH
ENV LANG C.UTF-8
ARG MAKE_THREADS=4
WORKDIR /
QEMU
SYSTEM_DEPENDENCIES
RHASSPY_TOOLS
PYTHON_REQUIREMENTS
PYTHON_POCKETSPHINX
SNOWBOY
TTS
KALDI
RUN ldconfig
# Copy bw and mllr_solve to /usr/bin
RUN find / -name bw -exec cp '{}' /usr/bin/ \;
RUN find / -name mllr_solve -exec cp '{}' /usr/bin/ \;
GSTREAMER
PULSEAUDIO
# Copy script to run
COPY docker/run.sh /run.sh
RUN chmod +x /run.sh
RHASSPY_USER
PROFILES
RHASSPY_CODE
ENV CONFIG_PATH /data/options.json
ENV KALDI_PREFIX /opt
ENTRYPOINT ["/run.sh"]
-132
View File
@@ -1,132 +0,0 @@
COPY profiles/zh/profile.json \
profiles/zh/custom_words.txt \
profiles/zh/espeak_phonemes.txt \
profiles/zh/phoneme_examples.txt \
profiles/zh/frequent_words.txt \
profiles/zh/sentences.ini \
profiles/zh/stop_words.txt ${RHASSPY_APP}/profiles/zh/
COPY profiles/hi/ \
profiles/hi/profile.json \
profiles/hi/custom_words.txt \
profiles/hi/espeak_phonemes.txt \
profiles/hi/phoneme_examples.txt \
profiles/hi/frequent_words.txt \
profiles/hi/sentences.ini \
profiles/hi/stop_words.txt ${RHASSPY_APP}/profiles/hi/
COPY profiles/el/profile.json \
profiles/el/custom_words.txt \
profiles/el/espeak_phonemes.txt \
profiles/el/phoneme_examples.txt \
profiles/el/frequent_words.txt \
profiles/el/sentences.ini \
profiles/el/stop_words.txt ${RHASSPY_APP}/profiles/el/
COPY profiles/de/profile.json \
profiles/de/custom_words.txt \
profiles/de/espeak_phonemes.txt \
profiles/de/phoneme_examples.txt \
profiles/de/frequent_words.txt \
profiles/de/sentences.ini \
profiles/de/stop_words.txt ${RHASSPY_APP}/profiles/de/
COPY profiles/de/kaldi/custom_words.txt \
profiles/de/kaldi/espeak_phonemes.txt \
profiles/de/kaldi/phoneme_examples.txt \
${RHASSPY_APP}/profiles/de/kaldi/
COPY profiles/it/profile.json \
profiles/it/custom_words.txt \
profiles/it/espeak_phonemes.txt \
profiles/it/phoneme_examples.txt \
profiles/it/frequent_words.txt \
profiles/it/sentences.ini \
profiles/it/stop_words.txt ${RHASSPY_APP}/profiles/it/
COPY profiles/es/profile.json \
profiles/es/custom_words.txt \
profiles/es/espeak_phonemes.txt \
profiles/es/phoneme_examples.txt \
profiles/es/frequent_words.txt \
profiles/es/sentences.ini \
profiles/es/stop_words.txt ${RHASSPY_APP}/profiles/es/
COPY profiles/fr/profile.json \
profiles/fr/custom_words.txt \
profiles/fr/espeak_phonemes.txt \
profiles/fr/phoneme_examples.txt \
profiles/fr/frequent_words.txt \
profiles/fr/sentences.ini \
profiles/fr/stop_words.txt ${RHASSPY_APP}/profiles/fr/
COPY profiles/fr/kaldi/custom_words.txt \
profiles/fr/kaldi/espeak_phonemes.txt \
profiles/fr/kaldi/phoneme_examples.txt \
${RHASSPY_APP}/profiles/fr/kaldi/
COPY profiles/ru/profile.json \
profiles/ru/custom_words.txt \
profiles/ru/espeak_phonemes.txt \
profiles/ru/phoneme_examples.txt \
profiles/ru/frequent_words.txt \
profiles/ru/sentences.ini \
profiles/ru/stop_words.txt ${RHASSPY_APP}/profiles/ru/
COPY profiles/nl/profile.json \
profiles/nl/custom_words.txt \
profiles/nl/espeak_phonemes.txt \
profiles/nl/phoneme_examples.txt \
profiles/nl/frequent_words.txt \
profiles/nl/sentences.ini \
profiles/nl/stop_words.txt ${RHASSPY_APP}/profiles/nl/
COPY profiles/nl/kaldi/custom_words.txt \
profiles/nl/kaldi/espeak_phonemes.txt \
profiles/nl/kaldi/phoneme_examples.txt \
${RHASSPY_APP}/profiles/nl/kaldi/
COPY profiles/vi/profile.json \
profiles/vi/custom_words.txt \
profiles/vi/espeak_phonemes.txt \
profiles/vi/phoneme_examples.txt \
profiles/vi/frequent_words.txt \
profiles/vi/sentences.ini \
profiles/vi/stop_words.txt ${RHASSPY_APP}/profiles/vi/
COPY profiles/pt/profile.json \
profiles/pt/custom_words.txt \
profiles/pt/espeak_phonemes.txt \
profiles/pt/phoneme_examples.txt \
profiles/pt/frequent_words.txt \
profiles/pt/sentences.ini \
profiles/pt/stop_words.txt ${RHASSPY_APP}/profiles/pt/
COPY profiles/sv/profile.json \
profiles/sv/custom_words.txt \
profiles/sv/espeak_phonemes.txt \
profiles/sv/phoneme_examples.txt \
profiles/sv/frequent_words.txt \
profiles/sv/sentences.ini \
profiles/sv/stop_words.txt ${RHASSPY_APP}/profiles/sv/
COPY profiles/ca/profile.json \
profiles/ca/custom_words.txt \
profiles/ca/espeak_phonemes.txt \
profiles/ca/phoneme_examples.txt \
profiles/ca/frequent_words.txt \
profiles/ca/sentences.ini \
profiles/ca/stop_words.txt ${RHASSPY_APP}/profiles/ca/
COPY profiles/en/profile.json \
profiles/en/custom_words.txt \
profiles/en/espeak_phonemes.txt \
profiles/en/phoneme_examples.txt \
profiles/en/frequent_words.txt \
profiles/en/sentences.ini \
profiles/en/stop_words.txt ${RHASSPY_APP}/profiles/en/
COPY profiles/en/kaldi/custom_words.txt \
profiles/en/kaldi/espeak_phonemes.txt \
profiles/en/kaldi/phoneme_examples.txt \
${RHASSPY_APP}/profiles/en/kaldi/
-1
View File
@@ -1 +0,0 @@
ENV RHASSPY_APP /usr/share/rhasspy
@@ -1,207 +0,0 @@
ARG BUILD_FROM
FROM $BUILD_FROM
LABEL maintainer="Michael Hansen <hansen.mike@gmail.com>"
ARG BUILD_ARCH
ARG CPU_ARCH
ENV LANG C.UTF-8
ARG MAKE_THREADS=4
WORKDIR /
COPY etc/qemu-arm-static /usr/bin/
COPY etc/qemu-aarch64-static /usr/bin/
RUN apt-get update && \
apt-get install --no-install-recommends --yes \
bash jq unzip \
python3 python3-pip python3-dev \
build-essential portaudio19-dev swig \
libatlas-base-dev \
sox espeak flite alsa-utils \
git curl \
autoconf libtool automake bison \
sphinxbase-utils sphinxtrain
COPY download/openfst-1.6.2-1_${BUILD_ARCH}.deb /
RUN if [ "$BUILD_ARCH" != "aarch64" ]; then \
apt-get install --no-install-recommends --yes libfst-dev libfst-tools; \
else \
dpkg -i /openfst-1.6.2-1_${BUILD_ARCH}.deb; \
rm /openfst*.deb; \
fi
RHASSPY_TOOLS
# Install Python dependencies
RUN python3 -m pip install --no-cache-dir setuptools wheel
RUN apt-get install -y libfreetype6-dev libpng-dev pkg-config libffi-dev libssl-dev
COPY requirements.txt /requirements.txt
RUN if [ "$BUILD_ARCH" != "amd64" ]; then \
grep -v flair /requirements.txt > /requirements-noflair.txt; \
mv /requirements-noflair.txt /requirements.txt; \
fi
RUN python3 -m pip install --no-cache-dir -r /requirements.txt
# Install Pocketsphinx Python module with no sound
COPY download/pocketsphinx-python.tar.gz /
RUN python3 -m pip install --no-cache-dir /pocketsphinx-python.tar.gz && \
rm -rf /pocketsphinx-python*
# Install snowboy
COPY download/snowboy-1.3.0.tar.gz /
RUN if [ "$BUILD_ARCH" != "aarch64" ]; then pip3 install --no-cache-dir /snowboy-1.3.0.tar.gz; fi
RUN apt-get install --no-install-recommends --yes flite libttspico-utils
COPY download/kaldi_${BUILD_ARCH}.tar.gz /kaldi.tar.gz
RUN mkdir -p /opt && \
tar -C /opt -xzf /kaldi.tar.gz && \
rm /kaldi.tar.gz
RUN ldconfig
# Copy bw and mllr_solve to /usr/bin
RUN find / -name bw -exec cp '{}' /usr/bin/ \;
RUN find / -name mllr_solve -exec cp '{}' /usr/bin/ \;
# Install gstreamer and plugins
RUN apt-get install --no-install-recommends --yes gstreamer1.0-tools gstreamer1.0-plugins-good
ENV RHASSPY_APP /usr/share/rhasspy
# Copy script to run
COPY docker/run.sh /run.sh
RUN chmod +x /run.sh
COPY profiles/zh/profile.json \
profiles/zh/custom_words.txt \
profiles/zh/espeak_phonemes.txt \
profiles/zh/phoneme_examples.txt \
profiles/zh/frequent_words.txt \
profiles/zh/sentences.ini \
profiles/zh/stop_words.txt ${RHASSPY_APP}/profiles/zh/
COPY profiles/hi/ \
profiles/hi/profile.json \
profiles/hi/custom_words.txt \
profiles/hi/espeak_phonemes.txt \
profiles/hi/phoneme_examples.txt \
profiles/hi/frequent_words.txt \
profiles/hi/sentences.ini \
profiles/hi/stop_words.txt ${RHASSPY_APP}/profiles/hi/
COPY profiles/el/profile.json \
profiles/el/custom_words.txt \
profiles/el/espeak_phonemes.txt \
profiles/el/phoneme_examples.txt \
profiles/el/frequent_words.txt \
profiles/el/sentences.ini \
profiles/el/stop_words.txt ${RHASSPY_APP}/profiles/el/
COPY profiles/de/profile.json \
profiles/de/custom_words.txt \
profiles/de/espeak_phonemes.txt \
profiles/de/phoneme_examples.txt \
profiles/de/frequent_words.txt \
profiles/de/sentences.ini \
profiles/de/stop_words.txt ${RHASSPY_APP}/profiles/de/
COPY profiles/it/profile.json \
profiles/it/custom_words.txt \
profiles/it/espeak_phonemes.txt \
profiles/it/phoneme_examples.txt \
profiles/it/frequent_words.txt \
profiles/it/sentences.ini \
profiles/it/stop_words.txt ${RHASSPY_APP}/profiles/it/
COPY profiles/es/profile.json \
profiles/es/custom_words.txt \
profiles/es/espeak_phonemes.txt \
profiles/es/phoneme_examples.txt \
profiles/es/frequent_words.txt \
profiles/es/sentences.ini \
profiles/es/stop_words.txt ${RHASSPY_APP}/profiles/es/
COPY profiles/fr/profile.json \
profiles/fr/custom_words.txt \
profiles/fr/espeak_phonemes.txt \
profiles/fr/phoneme_examples.txt \
profiles/fr/frequent_words.txt \
profiles/fr/sentences.ini \
profiles/fr/stop_words.txt ${RHASSPY_APP}/profiles/fr/
COPY profiles/ru/profile.json \
profiles/ru/custom_words.txt \
profiles/ru/espeak_phonemes.txt \
profiles/ru/phoneme_examples.txt \
profiles/ru/frequent_words.txt \
profiles/ru/sentences.ini \
profiles/ru/stop_words.txt ${RHASSPY_APP}/profiles/ru/
COPY profiles/nl/profile.json \
profiles/nl/custom_words.txt \
profiles/nl/espeak_phonemes.txt \
profiles/nl/phoneme_examples.txt \
profiles/nl/frequent_words.txt \
profiles/nl/sentences.ini \
profiles/nl/stop_words.txt ${RHASSPY_APP}/profiles/nl/
COPY profiles/vi/profile.json \
profiles/vi/custom_words.txt \
profiles/vi/espeak_phonemes.txt \
profiles/vi/phoneme_examples.txt \
profiles/vi/frequent_words.txt \
profiles/vi/sentences.ini \
profiles/vi/stop_words.txt ${RHASSPY_APP}/profiles/vi/
COPY profiles/pt/profile.json \
profiles/pt/custom_words.txt \
profiles/pt/espeak_phonemes.txt \
profiles/pt/phoneme_examples.txt \
profiles/pt/frequent_words.txt \
profiles/pt/sentences.ini \
profiles/pt/stop_words.txt ${RHASSPY_APP}/profiles/pt/
COPY profiles/sv/profile.json \
profiles/sv/custom_words.txt \
profiles/sv/espeak_phonemes.txt \
profiles/sv/phoneme_examples.txt \
profiles/sv/frequent_words.txt \
profiles/sv/sentences.ini \
profiles/sv/stop_words.txt ${RHASSPY_APP}/profiles/sv/
COPY profiles/ca/profile.json \
profiles/ca/custom_words.txt \
profiles/ca/espeak_phonemes.txt \
profiles/ca/phoneme_examples.txt \
profiles/ca/frequent_words.txt \
profiles/ca/sentences.ini \
profiles/ca/stop_words.txt ${RHASSPY_APP}/profiles/ca/
COPY profiles/en/profile.json \
profiles/en/custom_words.txt \
profiles/en/espeak_phonemes.txt \
profiles/en/phoneme_examples.txt \
profiles/en/frequent_words.txt \
profiles/en/sentences.ini \
profiles/en/stop_words.txt ${RHASSPY_APP}/profiles/en/
COPY profiles/defaults.json ${RHASSPY_APP}/profiles/
COPY docker/rhasspy ${RHASSPY_APP}/bin/
COPY dist/ ${RHASSPY_APP}/dist/
COPY etc/wav/* ${RHASSPY_APP}/etc/wav/
COPY rhasspy/profile_schema.json ${RHASSPY_APP}/rhasspy/
COPY rhasspy/train/jsgf2fst/*.py ${RHASSPY_APP}/rhasspy/train/jsgf2fst/
COPY rhasspy/train/*.py ${RHASSPY_APP}/rhasspy/train/
COPY *.py ${RHASSPY_APP}/
COPY rhasspy/*.py ${RHASSPY_APP}/rhasspy/
ENV CONFIG_PATH /data/options.json
ENV KALDI_PREFIX /opt
ENTRYPOINT ["/run.sh"]
@@ -1,96 +0,0 @@
ARG BUILD_FROM
FROM $BUILD_FROM
LABEL maintainer="Michael Hansen <hansen.mike@gmail.com>"
ARG BUILD_ARCH
ARG CPU_ARCH
ENV LANG C.UTF-8
ARG MAKE_THREADS=4
WORKDIR /
COPY etc/qemu-arm-static /usr/bin/
COPY etc/qemu-aarch64-static /usr/bin/
RUN apt-get update && \
apt-get install --no-install-recommends --yes \
bash jq unzip \
python3 python3-pip python3-dev \
build-essential portaudio19-dev swig \
libatlas-base-dev \
sox espeak flite alsa-utils \
git curl \
autoconf libtool automake bison \
sphinxbase-utils sphinxtrain
COPY download/openfst-1.6.2-1_${BUILD_ARCH}.deb /
RUN if [ "$BUILD_ARCH" != "aarch64" ]; then \
apt-get install --no-install-recommends --yes libfst-dev libfst-tools; \
else \
dpkg -i /openfst-1.6.2-1_${BUILD_ARCH}.deb; \
rm /openfst*.deb; \
fi
RHASSPY_TOOLS
# Install Python dependencies
RUN python3 -m pip install --no-cache-dir setuptools wheel
RUN apt-get install -y libfreetype6-dev libpng-dev pkg-config libffi-dev libssl-dev
COPY requirements.txt /requirements.txt
RUN if [ "$BUILD_ARCH" != "amd64" ]; then \
grep -v flair /requirements.txt > /requirements-noflair.txt; \
mv /requirements-noflair.txt /requirements.txt; \
fi
RUN python3 -m pip install --no-cache-dir -r /requirements.txt
# Install Pocketsphinx Python module with no sound
COPY download/pocketsphinx-python.tar.gz /
RUN python3 -m pip install --no-cache-dir /pocketsphinx-python.tar.gz && \
rm -rf /pocketsphinx-python*
# Install snowboy
COPY download/snowboy-1.3.0.tar.gz /
RUN if [ "$BUILD_ARCH" != "aarch64" ]; then pip3 install --no-cache-dir /snowboy-1.3.0.tar.gz; fi
RUN apt-get install --no-install-recommends --yes flite libttspico-utils
COPY download/kaldi_${BUILD_ARCH}.tar.gz /kaldi.tar.gz
RUN mkdir -p /opt && \
tar -C /opt -xzf /kaldi.tar.gz && \
rm /kaldi.tar.gz
RUN ldconfig
# Copy bw and mllr_solve to /usr/bin
RUN find / -name bw -exec cp '{}' /usr/bin/ \;
RUN find / -name mllr_solve -exec cp '{}' /usr/bin/ \;
# Install gstreamer and plugins
RUN apt-get install --no-install-recommends --yes gstreamer1.0-tools gstreamer1.0-plugins-good
ENV RHASSPY_APP /usr/share/rhasspy
# Copy script to run
COPY docker/run.sh /run.sh
RUN chmod +x /run.sh
COPY profiles/en/ ${RHASSPY_APP}/profiles/en/
COPY profiles/defaults.json ${RHASSPY_APP}/profiles/
COPY docker/rhasspy ${RHASSPY_APP}/bin/
COPY dist/ ${RHASSPY_APP}/dist/
COPY etc/wav/* ${RHASSPY_APP}/etc/wav/
COPY rhasspy/profile_schema.json ${RHASSPY_APP}/rhasspy/
COPY rhasspy/train/jsgf2fst/*.py ${RHASSPY_APP}/rhasspy/train/jsgf2fst/
COPY rhasspy/train/*.py ${RHASSPY_APP}/rhasspy/train/
COPY *.py ${RHASSPY_APP}/
COPY rhasspy/*.py ${RHASSPY_APP}/rhasspy/
ENV CONFIG_PATH /data/options.json
ENV KALDI_PREFIX /opt
ENTRYPOINT ["/run.sh"]
@@ -1,113 +0,0 @@
ARG BUILD_FROM
FROM $BUILD_FROM
LABEL maintainer="Michael Hansen <hansen.mike@gmail.com>"
ARG BUILD_ARCH
ARG CPU_ARCH
ENV LANG C.UTF-8
ARG MAKE_THREADS=4
WORKDIR /
COPY etc/qemu-arm-static /usr/bin/
COPY etc/qemu-aarch64-static /usr/bin/
RUN apt-get update && \
apt-get install --no-install-recommends --yes \
bash jq unzip \
python3 python3-pip python3-dev \
build-essential portaudio19-dev swig \
libatlas-base-dev \
sox espeak flite alsa-utils \
git curl \
autoconf libtool automake bison \
sphinxbase-utils sphinxtrain
COPY download/openfst-1.6.2-1_${BUILD_ARCH}.deb /
RUN if [ "$BUILD_ARCH" != "aarch64" ]; then \
apt-get install --no-install-recommends --yes libfst-dev libfst-tools; \
else \
dpkg -i /openfst-1.6.2-1_${BUILD_ARCH}.deb; \
rm /openfst*.deb; \
fi
RHASSPY_TOOLS
# Install Python dependencies
RUN python3 -m pip install --no-cache-dir setuptools wheel
RUN apt-get install -y libfreetype6-dev libpng-dev pkg-config libffi-dev libssl-dev
COPY requirements.txt /requirements.txt
RUN if [ "$BUILD_ARCH" != "amd64" ]; then \
grep -v flair /requirements.txt > /requirements-noflair.txt; \
mv /requirements-noflair.txt /requirements.txt; \
fi
RUN python3 -m pip install --no-cache-dir -r /requirements.txt
# Install Pocketsphinx Python module with no sound
COPY download/pocketsphinx-python.tar.gz /
RUN python3 -m pip install --no-cache-dir /pocketsphinx-python.tar.gz && \
rm -rf /pocketsphinx-python*
# Install snowboy
COPY download/snowboy-1.3.0.tar.gz /
RUN if [ "$BUILD_ARCH" != "aarch64" ]; then pip3 install --no-cache-dir /snowboy-1.3.0.tar.gz; fi
RUN apt-get install --no-install-recommends --yes flite libttspico-utils
COPY download/kaldi_${BUILD_ARCH}.tar.gz /kaldi.tar.gz
RUN mkdir -p /opt && \
tar -C /opt -xzf /kaldi.tar.gz && \
rm /kaldi.tar.gz
RUN ldconfig
# Copy bw and mllr_solve to /usr/bin
RUN find / -name bw -exec cp '{}' /usr/bin/ \;
RUN find / -name mllr_solve -exec cp '{}' /usr/bin/ \;
# Install gstreamer and plugins
RUN apt-get install --no-install-recommends --yes gstreamer1.0-tools gstreamer1.0-plugins-good
RUN apt-get install -y pulseaudio
COPY etc/pulseaudio.client.conf /etc/pulse/client.conf
# Create new user
ENV UNAME=rhasspy
RUN export UNAME=$UNAME UID=1000 GID=1000 && \
mkdir -p "/home/${UNAME}" && \
echo "${UNAME}:x:${UID}:${GID}:${UNAME} User,,,:/home/${UNAME}:/bin/bash" >> /etc/passwd && \
echo "${UNAME}:x:${UID}:" >> /etc/group && \
mkdir -p /etc/sudoers.d && \
echo "${UNAME} ALL=(ALL) NOPASSWD: ALL" > /etc/sudoers.d/${UNAME} && \
chmod 0440 /etc/sudoers.d/${UNAME} && \
chown ${UID}:${GID} -R /home/${UNAME} && \
gpasswd -a ${UNAME} audio
ENV RHASSPY_APP /home/${UNAME}
# Copy script to run
COPY docker/run.sh /run.sh
RUN chmod +x /run.sh
# Switch to new user
USER $UNAME
ENV HOME /home/${UNAME}
COPY profiles/en/ ${RHASSPY_APP}/profiles/en/
COPY profiles/defaults.json ${RHASSPY_APP}/profiles/
COPY docker/rhasspy ${RHASSPY_APP}/bin/
COPY dist/ ${RHASSPY_APP}/dist/
COPY etc/wav/* ${RHASSPY_APP}/etc/wav/
COPY rhasspy/profile_schema.json ${RHASSPY_APP}/rhasspy/
COPY rhasspy/train/jsgf2fst/*.py ${RHASSPY_APP}/rhasspy/train/jsgf2fst/
COPY rhasspy/train/*.py ${RHASSPY_APP}/rhasspy/train/
COPY *.py ${RHASSPY_APP}/
COPY rhasspy/*.py ${RHASSPY_APP}/rhasspy/
ENV CONFIG_PATH /data/options.json
ENV KALDI_PREFIX /opt
ENTRYPOINT ["/run.sh"]
@@ -1,222 +0,0 @@
ARG BUILD_FROM
FROM $BUILD_FROM
LABEL maintainer="Michael Hansen <hansen.mike@gmail.com>"
ARG BUILD_ARCH
ARG CPU_ARCH
ENV LANG C.UTF-8
ARG MAKE_THREADS=4
WORKDIR /
COPY etc/qemu-arm-static /usr/bin/
COPY etc/qemu-aarch64-static /usr/bin/
RUN apt-get update && \
apt-get install --no-install-recommends --yes \
bash jq unzip \
python3 python3-pip python3-dev \
build-essential portaudio19-dev swig \
libatlas-base-dev \
sox espeak flite alsa-utils \
git curl \
autoconf libtool automake bison \
sphinxbase-utils sphinxtrain
COPY download/rhasspy-tools_${BUILD_ARCH}.tar.gz /
RUN tar -C /usr -xvf /rhasspy-tools_${BUILD_ARCH}.tar.gz && \
rm -f /rhasspy-tools_${BUILD_ARCH}.tar.gz
# Install Python dependencies
RUN python3 -m pip install --no-cache-dir setuptools wheel
RUN apt-get install -y libfreetype6-dev libpng-dev pkg-config libffi-dev libssl-dev
COPY requirements.txt /requirements.txt
RUN if [ "$BUILD_ARCH" != "amd64" ]; then \
grep -v flair /requirements.txt > /requirements-noflair.txt; \
mv /requirements-noflair.txt /requirements.txt; \
fi
RUN python3 -m pip install --no-cache-dir -r /requirements.txt
# Install Pocketsphinx Python module with no sound
COPY download/pocketsphinx-python.tar.gz /
RUN python3 -m pip install --no-cache-dir /pocketsphinx-python.tar.gz && \
rm -rf /pocketsphinx-python*
# Install snowboy
COPY download/snowboy-1.3.0.tar.gz /
RUN if [ "$BUILD_ARCH" != "aarch64" ]; then pip3 install --no-cache-dir /snowboy-1.3.0.tar.gz; fi
RUN apt-get install --no-install-recommends --yes flite libttspico-utils lame
COPY download/kaldi_${BUILD_ARCH}.tar.gz /kaldi.tar.gz
RUN mkdir -p /opt && \
tar -C /opt -xzf /kaldi.tar.gz && \
rm /kaldi.tar.gz
RUN ldconfig
# Copy bw and mllr_solve to /usr/bin
RUN find / -name bw -exec cp '{}' /usr/bin/ \;
RUN find / -name mllr_solve -exec cp '{}' /usr/bin/ \;
# Install gstreamer and plugins
RUN apt-get install --no-install-recommends --yes gstreamer1.0-tools gstreamer1.0-plugins-good
ENV RHASSPY_APP /usr/share/rhasspy
# Copy script to run
COPY docker/run.sh /run.sh
RUN chmod +x /run.sh
COPY profiles/zh/profile.json \
profiles/zh/custom_words.txt \
profiles/zh/espeak_phonemes.txt \
profiles/zh/phoneme_examples.txt \
profiles/zh/frequent_words.txt \
profiles/zh/sentences.ini \
profiles/zh/stop_words.txt ${RHASSPY_APP}/profiles/zh/
COPY profiles/hi/ \
profiles/hi/profile.json \
profiles/hi/custom_words.txt \
profiles/hi/espeak_phonemes.txt \
profiles/hi/phoneme_examples.txt \
profiles/hi/frequent_words.txt \
profiles/hi/sentences.ini \
profiles/hi/stop_words.txt ${RHASSPY_APP}/profiles/hi/
COPY profiles/el/profile.json \
profiles/el/custom_words.txt \
profiles/el/espeak_phonemes.txt \
profiles/el/phoneme_examples.txt \
profiles/el/frequent_words.txt \
profiles/el/sentences.ini \
profiles/el/stop_words.txt ${RHASSPY_APP}/profiles/el/
COPY profiles/de/profile.json \
profiles/de/custom_words.txt \
profiles/de/espeak_phonemes.txt \
profiles/de/phoneme_examples.txt \
profiles/de/frequent_words.txt \
profiles/de/sentences.ini \
profiles/de/stop_words.txt ${RHASSPY_APP}/profiles/de/
COPY profiles/de/kaldi/custom_words.txt \
profiles/de/kaldi/espeak_phonemes.txt \
profiles/de/kaldi/phoneme_examples.txt \
${RHASSPY_APP}/profiles/de/kaldi/
COPY profiles/it/profile.json \
profiles/it/custom_words.txt \
profiles/it/espeak_phonemes.txt \
profiles/it/phoneme_examples.txt \
profiles/it/frequent_words.txt \
profiles/it/sentences.ini \
profiles/it/stop_words.txt ${RHASSPY_APP}/profiles/it/
COPY profiles/es/profile.json \
profiles/es/custom_words.txt \
profiles/es/espeak_phonemes.txt \
profiles/es/phoneme_examples.txt \
profiles/es/frequent_words.txt \
profiles/es/sentences.ini \
profiles/es/stop_words.txt ${RHASSPY_APP}/profiles/es/
COPY profiles/fr/profile.json \
profiles/fr/custom_words.txt \
profiles/fr/espeak_phonemes.txt \
profiles/fr/phoneme_examples.txt \
profiles/fr/frequent_words.txt \
profiles/fr/sentences.ini \
profiles/fr/stop_words.txt ${RHASSPY_APP}/profiles/fr/
COPY profiles/fr/kaldi/custom_words.txt \
profiles/fr/kaldi/espeak_phonemes.txt \
profiles/fr/kaldi/phoneme_examples.txt \
${RHASSPY_APP}/profiles/fr/kaldi/
COPY profiles/ru/profile.json \
profiles/ru/custom_words.txt \
profiles/ru/espeak_phonemes.txt \
profiles/ru/phoneme_examples.txt \
profiles/ru/frequent_words.txt \
profiles/ru/sentences.ini \
profiles/ru/stop_words.txt ${RHASSPY_APP}/profiles/ru/
COPY profiles/nl/profile.json \
profiles/nl/custom_words.txt \
profiles/nl/espeak_phonemes.txt \
profiles/nl/phoneme_examples.txt \
profiles/nl/frequent_words.txt \
profiles/nl/sentences.ini \
profiles/nl/stop_words.txt ${RHASSPY_APP}/profiles/nl/
COPY profiles/nl/kaldi/custom_words.txt \
profiles/nl/kaldi/espeak_phonemes.txt \
profiles/nl/kaldi/phoneme_examples.txt \
${RHASSPY_APP}/profiles/nl/kaldi/
COPY profiles/vi/profile.json \
profiles/vi/custom_words.txt \
profiles/vi/espeak_phonemes.txt \
profiles/vi/phoneme_examples.txt \
profiles/vi/frequent_words.txt \
profiles/vi/sentences.ini \
profiles/vi/stop_words.txt ${RHASSPY_APP}/profiles/vi/
COPY profiles/pt/profile.json \
profiles/pt/custom_words.txt \
profiles/pt/espeak_phonemes.txt \
profiles/pt/phoneme_examples.txt \
profiles/pt/frequent_words.txt \
profiles/pt/sentences.ini \
profiles/pt/stop_words.txt ${RHASSPY_APP}/profiles/pt/
COPY profiles/sv/profile.json \
profiles/sv/custom_words.txt \
profiles/sv/espeak_phonemes.txt \
profiles/sv/phoneme_examples.txt \
profiles/sv/frequent_words.txt \
profiles/sv/sentences.ini \
profiles/sv/stop_words.txt ${RHASSPY_APP}/profiles/sv/
COPY profiles/ca/profile.json \
profiles/ca/custom_words.txt \
profiles/ca/espeak_phonemes.txt \
profiles/ca/phoneme_examples.txt \
profiles/ca/frequent_words.txt \
profiles/ca/sentences.ini \
profiles/ca/stop_words.txt ${RHASSPY_APP}/profiles/ca/
COPY profiles/en/profile.json \
profiles/en/custom_words.txt \
profiles/en/espeak_phonemes.txt \
profiles/en/phoneme_examples.txt \
profiles/en/frequent_words.txt \
profiles/en/sentences.ini \
profiles/en/stop_words.txt ${RHASSPY_APP}/profiles/en/
COPY profiles/en/kaldi/custom_words.txt \
profiles/en/kaldi/espeak_phonemes.txt \
profiles/en/kaldi/phoneme_examples.txt \
${RHASSPY_APP}/profiles/en/kaldi/
COPY profiles/defaults.json ${RHASSPY_APP}/profiles/
COPY docker/rhasspy ${RHASSPY_APP}/bin/
COPY dist/ ${RHASSPY_APP}/dist/
COPY etc/wav/* ${RHASSPY_APP}/etc/wav/
COPY rhasspy/profile_schema.json ${RHASSPY_APP}/rhasspy/
COPY rhasspy/train/jsgf2fst/*.py ${RHASSPY_APP}/rhasspy/train/jsgf2fst/
COPY rhasspy/train/*.py ${RHASSPY_APP}/rhasspy/train/
COPY *.py ${RHASSPY_APP}/
COPY rhasspy/*.py ${RHASSPY_APP}/rhasspy/
COPY VERSION ${RHASSPY_APP}/
ENV CONFIG_PATH /data/options.json
ENV KALDI_PREFIX /opt
ENTRYPOINT ["/run.sh"]
@@ -1,91 +0,0 @@
ARG BUILD_FROM
FROM $BUILD_FROM
LABEL maintainer="Michael Hansen <hansen.mike@gmail.com>"
ARG BUILD_ARCH
ARG CPU_ARCH
ENV LANG C.UTF-8
ARG MAKE_THREADS=4
WORKDIR /
COPY etc/qemu-arm-static /usr/bin/
COPY etc/qemu-aarch64-static /usr/bin/
RUN apt-get update && \
apt-get install --no-install-recommends --yes \
bash jq unzip \
python3 python3-pip python3-dev \
build-essential portaudio19-dev swig \
libfst-dev libfst-tools \
libatlas-base-dev \
sox espeak flite alsa-utils \
git curl \
autoconf libtool automake bison \
sphinxbase-utils sphinxtrain
COPY download/rhasspy-tools_${BUILD_ARCH}.tar.gz /
RUN tar -C /usr -xvf /rhasspy-tools_${BUILD_ARCH}.tar.gz && \
rm -f /rhasspy-tools_${BUILD_ARCH}.tar.gz
# Install Python dependencies
RUN python3 -m pip install --no-cache-dir setuptools wheel
RUN apt-get install -y libfreetype6-dev libpng-dev pkg-config libffi-dev libssl-dev
COPY requirements.txt /requirements.txt
RUN if [ "$BUILD_ARCH" != "amd64" ]; then \
grep -v flair /requirements.txt > /requirements-noflair.txt; \
mv /requirements-noflair.txt /requirements.txt; \
fi
RUN python3 -m pip install --no-cache-dir -r /requirements.txt
# Install Pocketsphinx Python module with no sound
COPY download/pocketsphinx-python.tar.gz /
RUN python3 -m pip install --no-cache-dir /pocketsphinx-python.tar.gz && \
rm -rf /pocketsphinx-python*
# Install snowboy
COPY download/snowboy-1.3.0.tar.gz /
RUN if [ "$BUILD_ARCH" != "aarch64" ]; then pip3 install --no-cache-dir /snowboy-1.3.0.tar.gz; fi
RUN apt-get install --no-install-recommends --yes flite libttspico-utils
COPY download/kaldi_${BUILD_ARCH}.tar.gz /kaldi.tar.gz
RUN mkdir -p /opt && \
tar -C /opt -xzf /kaldi.tar.gz && \
rm /kaldi.tar.gz
RUN ldconfig
# Copy bw and mllr_solve to /usr/bin
RUN find / -name bw -exec cp '{}' /usr/bin/ \;
RUN find / -name mllr_solve -exec cp '{}' /usr/bin/ \;
# Install gstreamer and plugins
RUN apt-get install --no-install-recommends --yes gstreamer1.0-tools gstreamer1.0-plugins-good
ENV RHASSPY_APP /usr/share/rhasspy
# Copy script to run
COPY docker/run.sh /run.sh
RUN chmod +x /run.sh
COPY profiles/en/ ${RHASSPY_APP}/profiles/en/
COPY profiles/defaults.json ${RHASSPY_APP}/profiles/
COPY docker/rhasspy ${RHASSPY_APP}/bin/
COPY dist/ ${RHASSPY_APP}/dist/
COPY etc/wav/* ${RHASSPY_APP}/etc/wav/
COPY rhasspy/profile_schema.json ${RHASSPY_APP}/rhasspy/
COPY rhasspy/train/jsgf2fst/*.py ${RHASSPY_APP}/rhasspy/train/jsgf2fst/
COPY rhasspy/train/*.py ${RHASSPY_APP}/rhasspy/train/
COPY *.py ${RHASSPY_APP}/
COPY rhasspy/*.py ${RHASSPY_APP}/rhasspy/
ENV CONFIG_PATH /data/options.json
ENV KALDI_PREFIX /opt
ENTRYPOINT ["/run.sh"]
@@ -1,108 +0,0 @@
ARG BUILD_FROM
FROM $BUILD_FROM
LABEL maintainer="Michael Hansen <hansen.mike@gmail.com>"
ARG BUILD_ARCH
ARG CPU_ARCH
ENV LANG C.UTF-8
ARG MAKE_THREADS=4
WORKDIR /
COPY etc/qemu-arm-static /usr/bin/
COPY etc/qemu-aarch64-static /usr/bin/
RUN apt-get update && \
apt-get install --no-install-recommends --yes \
bash jq unzip \
python3 python3-pip python3-dev \
build-essential portaudio19-dev swig \
libfst-dev libfst-tools \
libatlas-base-dev \
sox espeak flite alsa-utils \
git curl \
autoconf libtool automake bison \
sphinxbase-utils sphinxtrain
COPY download/rhasspy-tools_${BUILD_ARCH}.tar.gz /
RUN tar -C /usr -xvf /rhasspy-tools_${BUILD_ARCH}.tar.gz && \
rm -f /rhasspy-tools_${BUILD_ARCH}.tar.gz
# Install Python dependencies
RUN python3 -m pip install --no-cache-dir setuptools wheel
RUN apt-get install -y libfreetype6-dev libpng-dev pkg-config libffi-dev libssl-dev
COPY requirements.txt /requirements.txt
RUN if [ "$BUILD_ARCH" != "amd64" ]; then \
grep -v flair /requirements.txt > /requirements-noflair.txt; \
mv /requirements-noflair.txt /requirements.txt; \
fi
RUN python3 -m pip install --no-cache-dir -r /requirements.txt
# Install Pocketsphinx Python module with no sound
COPY download/pocketsphinx-python.tar.gz /
RUN python3 -m pip install --no-cache-dir /pocketsphinx-python.tar.gz && \
rm -rf /pocketsphinx-python*
# Install snowboy
COPY download/snowboy-1.3.0.tar.gz /
RUN if [ "$BUILD_ARCH" != "aarch64" ]; then pip3 install --no-cache-dir /snowboy-1.3.0.tar.gz; fi
RUN apt-get install --no-install-recommends --yes flite libttspico-utils
COPY download/kaldi_${BUILD_ARCH}.tar.gz /kaldi.tar.gz
RUN mkdir -p /opt && \
tar -C /opt -xzf /kaldi.tar.gz && \
rm /kaldi.tar.gz
RUN ldconfig
# Copy bw and mllr_solve to /usr/bin
RUN find / -name bw -exec cp '{}' /usr/bin/ \;
RUN find / -name mllr_solve -exec cp '{}' /usr/bin/ \;
# Install gstreamer and plugins
RUN apt-get install --no-install-recommends --yes gstreamer1.0-tools gstreamer1.0-plugins-good
RUN apt-get install -y pulseaudio
COPY etc/pulseaudio.client.conf /etc/pulse/client.conf
# Create new user
ENV UNAME=rhasspy
RUN export UNAME=$UNAME UID=1000 GID=1000 && \
mkdir -p "/home/${UNAME}" && \
echo "${UNAME}:x:${UID}:${GID}:${UNAME} User,,,:/home/${UNAME}:/bin/bash" >> /etc/passwd && \
echo "${UNAME}:x:${UID}:" >> /etc/group && \
mkdir -p /etc/sudoers.d && \
echo "${UNAME} ALL=(ALL) NOPASSWD: ALL" > /etc/sudoers.d/${UNAME} && \
chmod 0440 /etc/sudoers.d/${UNAME} && \
chown ${UID}:${GID} -R /home/${UNAME} && \
gpasswd -a ${UNAME} audio
ENV RHASSPY_APP /home/${UNAME}
# Copy script to run
COPY docker/run.sh /run.sh
RUN chmod +x /run.sh
# Switch to new user
USER $UNAME
ENV HOME /home/${UNAME}
COPY profiles/en/ ${RHASSPY_APP}/profiles/en/
COPY profiles/defaults.json ${RHASSPY_APP}/profiles/
COPY docker/rhasspy ${RHASSPY_APP}/bin/
COPY dist/ ${RHASSPY_APP}/dist/
COPY etc/wav/* ${RHASSPY_APP}/etc/wav/
COPY rhasspy/profile_schema.json ${RHASSPY_APP}/rhasspy/
COPY rhasspy/train/jsgf2fst/*.py ${RHASSPY_APP}/rhasspy/train/jsgf2fst/
COPY rhasspy/train/*.py ${RHASSPY_APP}/rhasspy/train/
COPY *.py ${RHASSPY_APP}/
COPY rhasspy/*.py ${RHASSPY_APP}/rhasspy/
ENV CONFIG_PATH /data/options.json
ENV KALDI_PREFIX /opt
ENTRYPOINT ["/run.sh"]
-1
View File
@@ -1 +0,0 @@
COPY profiles/en/ ${RHASSPY_APP}/profiles/en/
@@ -1,7 +0,0 @@
COPY download/phonetisaurus-2019.tar.gz /phonetisaurus.tar.gz
RUN cd / && tar -xf phonetisaurus.tar.gz
RUN cd /phonetisaurus && \
./configure && \
make -j $MAKE_THREADS && \
make install && \
rm -rf /phonetisaurus*
@@ -1,18 +0,0 @@
RUN apt-get update && \
apt-get install --no-install-recommends --yes \
bash jq unzip \
python3 python3-pip python3-dev \
build-essential portaudio19-dev swig \
libatlas-base-dev \
sox espeak flite alsa-utils \
git curl \
autoconf libtool automake bison \
sphinxbase-utils sphinxtrain
COPY download/openfst-1.6.2-1_${BUILD_ARCH}.deb /
RUN if [ "$BUILD_ARCH" != "aarch64" ]; then \
apt-get install --no-install-recommends --yes libfst-dev libfst-tools; \
else \
dpkg -i /openfst-1.6.2-1_${BUILD_ARCH}.deb; \
rm /openfst*.deb; \
fi
-67
View File
@@ -1,67 +0,0 @@
#!/usr/bin/env bash
# Directory of *this* script
DIR="$( cd "$( dirname "$0" )" && pwd )"
template="$DIR/Dockerfile.template"
out="$DIR/dockerfiles"
mkdir -p "$out"
# -----------------------------------------------------------------------------
# Uppercases an input string
function upper {
tr '[[:lower:]]' '[[:upper:]]'
}
# Creates m4 "define" statements from text files in one or more directories.
# The contents of dir/file.txt will be the value of variable FILE.
function set_variables {
echo "divert(-1)"
while [[ ! -z "$1" ]]; do
if [[ -d "$1" ]]; then
for var_file in $(find "$1" -type f -name "*.txt"); do
var_name=$(basename "$var_file" .txt | upper)
echo "define(\`$var_name', \`$(cat $var_file)')"
done
elif [[ -f "$1" ]]; then
var_file="$1"
var_name=$(basename "$var_file" .txt | upper)
echo "define(\`$var_name', \`$(cat $var_file)')"
fi
shift
done
echo "divert(0)dnl"
}
# -----------------------------------------------------------------------------
#------------
# From source
#------------
# set_variables "$DIR/shared/" "$DIR/from-source/" \
# "$DIR/alsa/" "$DIR/en_profile/" \
# | cat - "$template" | m4 > "$out/Dockerfile.from-source.alsa.en"
# set_variables "$DIR/shared/" "$DIR/from-source/" \
# "$DIR/pulseaudio/" "$DIR/en_profile/" \
# | cat - "$template" | m4 > "$out/Dockerfile.from-source.pulseaudio.en"
# set_variables "$DIR/shared/" "$DIR/from-source/" \
# "$DIR/alsa/" "$DIR/all_profiles/" \
# | cat - "$template" | m4 > "$out/Dockerfile.from-source.alsa.all"
#-----------
# Pre-built
#-----------
# set_variables "$DIR/shared/" "$DIR/prebuilt/" \
# "$DIR/alsa/" "$DIR/en_profile/" \
# | cat - "$template" | m4 > "$out/Dockerfile.prebuilt.alsa.en"
# set_variables "$DIR/shared/" "$DIR/prebuilt/" \
# "$DIR/pulseaudio/" "$DIR/en_profile/" \
# | cat - "$template" | m4 > "$out/Dockerfile.prebuilt.pulseaudio.en"
set_variables "$DIR/shared/" "$DIR/prebuilt/" \
"$DIR/alsa/" "$DIR/all_profiles/" \
| cat - "$template" | m4 > "$out/Dockerfile.prebuilt.alsa.all"
@@ -1,3 +0,0 @@
COPY download/phonetisaurus-2019_${BUILD_ARCH}.deb /phonetisaurus.deb
RUN dpkg -i /phonetisaurus.deb && \
rm /phonetisaurus.deb
@@ -1,3 +0,0 @@
COPY download/rhasspy-tools_${BUILD_ARCH}.tar.gz /
RUN tar -C /usr -xvf /rhasspy-tools_${BUILD_ARCH}.tar.gz && \
rm -f /rhasspy-tools_${BUILD_ARCH}.tar.gz
@@ -1,10 +0,0 @@
RUN apt-get update && \
apt-get install --no-install-recommends --yes \
bash jq unzip \
python3 python3-pip python3-dev \
build-essential portaudio19-dev swig \
libatlas-base-dev \
sox espeak flite alsa-utils \
git curl \
autoconf libtool automake bison \
sphinxbase-utils sphinxtrain
@@ -1,16 +0,0 @@
RUN apt-get install -y pulseaudio
COPY etc/pulseaudio.client.conf /etc/pulse/client.conf
# Create new user
ENV UNAME=rhasspy
RUN export UNAME=$UNAME UID=1000 GID=1000 && \
mkdir -p "/home/${UNAME}" && \
echo "${UNAME}:x:${UID}:${GID}:${UNAME} User,,,:/home/${UNAME}:/bin/bash" >> /etc/passwd && \
echo "${UNAME}:x:${UID}:" >> /etc/group && \
mkdir -p /etc/sudoers.d && \
echo "${UNAME} ALL=(ALL) NOPASSWD: ALL" > /etc/sudoers.d/${UNAME} && \
chmod 0440 /etc/sudoers.d/${UNAME} && \
chown ${UID}:${GID} -R /home/${UNAME} && \
gpasswd -a ${UNAME} audio
ENV RHASSPY_APP /home/${UNAME}
@@ -1,3 +0,0 @@
# Switch to new user
USER $UNAME
ENV HOME /home/${UNAME}
-2
View File
@@ -1,2 +0,0 @@
# Install gstreamer and plugins
RUN apt-get install --no-install-recommends --yes gstreamer1.0-tools gstreamer1.0-plugins-good
-4
View File
@@ -1,4 +0,0 @@
COPY download/kaldi_${BUILD_ARCH}.tar.gz /kaldi.tar.gz
RUN mkdir -p /opt && \
tar -C /opt -xzf /kaldi.tar.gz && \
rm /kaldi.tar.gz
-9
View File
@@ -1,9 +0,0 @@
# Install mitlm
RUN apt-get install --no-install-recommends --yes gfortran
COPY download/mitlm-0.4.2.tar.xz /
RUN cd / && tar -xf mitlm-0.4.2.tar.xz && cd mitlm-0.4.2/ && \
./configure && \
make -j $MAKE_THREADS && \
make install && \
rm -rf /mitlm-0.4.2*
@@ -1,7 +0,0 @@
# Install Mycroft Precise
COPY download/precise-engine_0.3.0_${CPU_ARCH}.tar.gz /precise-engine.tar.gz
RUN if [ "$BUILD_ARCH" != "aarch64" ]; then \
cd / && tar -xzf /precise-engine.tar.gz && \
ln -s /precise-engine/precise-engine /usr/bin/precise-engine && \
rm /precise-engine.tar.gz; \
fi
-9
View File
@@ -1,9 +0,0 @@
# Install Opengrm
COPY download/opengrm-ngram-1.3.3.tar.gz /
RUN cd / && tar -xf opengrm-ngram-1.3.3.tar.gz && \
cd opengrm-ngram-1.3.3 && \
./configure && \
make -j $MAKE_THREADS && \
make install && \
ldconfig && \
rm -rf /opengrm*
@@ -1,4 +0,0 @@
# Install Pocketsphinx Python module with no sound
COPY download/pocketsphinx-python.tar.gz /
RUN python3 -m pip install --no-cache-dir /pocketsphinx-python.tar.gz && \
rm -rf /pocketsphinx-python*
@@ -1,10 +0,0 @@
# Install Python dependencies
RUN python3 -m pip install --no-cache-dir setuptools wheel
RUN apt-get install -y libfreetype6-dev libpng-dev pkg-config libffi-dev libssl-dev
COPY requirements.txt /requirements.txt
RUN if [ "$BUILD_ARCH" != "amd64" ]; then \
grep -v flair /requirements.txt > /requirements-noflair.txt; \
mv /requirements-noflair.txt /requirements.txt; \
fi
RUN python3 -m pip install --no-cache-dir -r /requirements.txt
-2
View File
@@ -1,2 +0,0 @@
COPY etc/qemu-arm-static /usr/bin/
COPY etc/qemu-aarch64-static /usr/bin/
-10
View File
@@ -1,10 +0,0 @@
COPY profiles/defaults.json ${RHASSPY_APP}/profiles/
COPY docker/rhasspy ${RHASSPY_APP}/bin/
COPY dist/ ${RHASSPY_APP}/dist/
COPY etc/wav/* ${RHASSPY_APP}/etc/wav/
COPY rhasspy/profile_schema.json ${RHASSPY_APP}/rhasspy/
COPY rhasspy/train/jsgf2fst/*.py ${RHASSPY_APP}/rhasspy/train/jsgf2fst/
COPY rhasspy/train/*.py ${RHASSPY_APP}/rhasspy/train/
COPY *.py ${RHASSPY_APP}/
COPY rhasspy/*.py ${RHASSPY_APP}/rhasspy/
COPY VERSION ${RHASSPY_APP}/
-3
View File
@@ -1,3 +0,0 @@
# Install snowboy
COPY download/snowboy-1.3.0.tar.gz /
RUN if [ "$BUILD_ARCH" != "aarch64" ]; then pip3 install --no-cache-dir /snowboy-1.3.0.tar.gz; fi
-1
View File
@@ -1 +0,0 @@
RUN apt-get install --no-install-recommends --yes flite libttspico-utils lame
+9 -1
View File
@@ -69,7 +69,15 @@ Add to your [profile](profiles.md):
"username": "",
"port": 1883,
"password": "",
"site_id": "default"
"site_id": "default",
"tls": {
"enabled": false,
"ca_certs": "",
"cert_reqs": "CERT_REQUIRED",
"certfile": "",
"ciphers": "",
"keyfile": ""
}
}
```
+9 -1
View File
@@ -44,7 +44,15 @@ Add to your [profile](profiles.md):
"username": "",
"port": 1883,
"password": "",
"site_id": "default"
"site_id": "default",
"tls": {
"enabled": false,
"ca_certs": "",
"cert_reqs": "CERT_REQUIRED",
"certfile": "",
"ciphers": "",
"keyfile": ""
}
}
```
+10 -2
View File
@@ -36,7 +36,7 @@ Add to your [profile](profiles.md):
This system listens for up to `timeout_sec` for a voice command. The first few frames of audio data are discarded (`throwaway_buffers`) to avoid clicks from the microphone being engaged. When speech is detected for some number of successive frames (`speech_buffers`), the voice command is considered to have *started*. After `min_sec`, Rhasspy will start listening for silence. If at least `silence_sec` goes by without any speech detected, the command is considered *finished*, and the recorded WAV data is sent to the [speech recognition system](speech-to-text.md).
You may want to adjust `min_sec`, `silence_sec`, and `vad_mode` for your environment.
These control how short a voice command can be (`min_sec`), how much silence is required before Rhasspy stops listening (`silence_sec`), and how sensitive the voice activity detector is (`vad_mode`, higher is more sensitive).
These control how short a voice command can be (`min_sec`), how much silence is required before Rhasspy stops listening (`silence_sec`), and how aggressive the voice activity filter `vad_mode` is: this is an integer between 0 and 3. 0 is the least aggressive about filtering out non-speech, 3 is the most aggressive.
**NOTE**: you must set `chunk_size` such that (relative to sample rate) it produces 10, 20, or 30 millisecond buffers. This is required by `webrtcvad`.
@@ -86,7 +86,15 @@ Add to your [profile](profiles.md):
"username": "",
"port": 1883,
"password": "",
"site_id": "default"
"site_id": "default",
"tls": {
"enabled": false,
"ca_certs": "",
"cert_reqs": "CERT_REQUIRED",
"certfile": "",
"ciphers": "",
"keyfile": ""
}
}
```
+4 -1
View File
@@ -4,6 +4,9 @@ Rhasspy is designed to be run on different kinds of hardware, such as:
* Raspberry Pi 2-3 B/B+ (`armhf`/`aarch64`)
* Desktop/laptop/server (`amd64`)
* Raspberry Pi Zero (`armv6l`)
* You must use a [virtual environment](installation.md#virtual-environment)
* The [Kaldi speech recognizer](speech-to-text.md#kaldi) is **not** supported
The table below summarizes architecture compatibility with Rhasspy's components:
@@ -30,7 +33,7 @@ The table below summarizes architecture compatibility with Rhasspy's components:
To run Rhasspy on a Raspberry Pi, you'll need at least a 4 GB SD card and a good power supply. I highly recommend the [CanaKit Starter Kit](https://www.amazon.com/CanaKit-Raspberry-Starter-Premium-Black/dp/B07BCC8PK7), which includes a 32 GB SD card, a 2.5 A power supply, and a case.
Some components of Rhasspy will not work on the Raspberry Pi 3 B+ model (`aarch64`). As of the time of this writing, these are:
Some components of Rhasspy will not work on the Raspberry Pi 3 B+ model with a 64-bit operating system (`aarch64`). As of the time of this writing, these are:
* [snowboy](wake-word.md#snowboy) (wake word)
* [Mycroft Precise](wake-word.md#mycroft-precise) (wake word)
+56 -6
View File
@@ -46,10 +46,25 @@ If you're using [docker compose](https://docs.docker.com/compose/), add the foll
devices:
- "/dev/snd:/dev/snd"
command: --user-profiles /profiles --profile en
### Updating Docker Image
To update your Rhasspy Docker image, just run:
```bash
docker pull synesthesiam/rhasspy-server:latest
```
on your Rhasspy server and restart the Docker container. This may require running something like:
```bash
docker rm <container-name>
```
before doing a `docker run...`
## Hass.io
The second easiest was to install Rhasspy is as a [Hass.io add-on](https://www.home-assistant.io/addons/). Following the [installation instructions for Hass.io](https://www.home-assistant.io/hassio/installation/) before proceeding.
The second easiest way to install Rhasspy is as a [Hass.io add-on](https://www.home-assistant.io/addons/). Follow the [installation instructions for Hass.io](https://www.home-assistant.io/hassio/installation/) before proceeding.
To install the add-on, add my [Hass.IO Add-On Repository](https://github.com/synesthesiam/hassio-addons) in the Add-On Store, refresh, then install the "Rhasspy Assistant" under “Synesthesiam Hass.IO Add-Ons” (all the way at the bottom of the Add-On Store screen).
@@ -63,24 +78,55 @@ Before starting the add-on, make sure to give it access to your microphone and s
![Audio settings for Hass.io](img/hass-io-audio.png)
### Updating Hass.IO Add-On
You should receive notifications when a new version of Rhasspy is available for Hass.IO. Follow the instructions from Hass.IO on how to update the add-on.
## Virtual Environment
Rhasspy can be installed into a Python virtual environment, though there are a number of requirements. This may be desirable, however, if you have trouble getting Rhasspy to access your microphone from within a Docker container. To start, clone the repo somewhere:
git clone https://github.com/synesthesiam/rhasspy.git
```bash
git clone https://github.com/synesthesiam/rhasspy.git
```
Then run the `download-dependencies.sh` and `create-venv.sh` scripts (assumes a Debian distribution):
cd rhasspy/
./download-dependencies.sh
./create-venv.sh
```bash
cd rhasspy/
./download-dependencies.sh
./create-venv.sh
```
Once the installation finishes (5-10 minutes on a Raspberry Pi 3), you can use the `run-venv.sh` script to start Rhasspy:
./run-venv.sh --profile en
```bash
./run-venv.sh --profile en
```
If all is well, the web interface will be available at [http://localhost:12101](http://localhost:12101)
### Updating Virtual Environment
To update your Rhasspy virtual environment to the latest version, run:
```bash
git pull origin master
```
in your `rhasspy` directory, and then update your Python dependencies:
```bash
source .venv/bin/activate
pip3 install -r requirements.txt
```
You should also re-build the web interface:
1. Install [yarn](https://yarnpkg.com) on your system
2. Run `yarn install && yarn build` in the `rhasspy` directory
3. Restart any running instances of Rhasspy
### Running as a Service
Once installed, Rhasspy can be run as a [systemd service](https://systemd.io/). An [example unit file](https://github.com/synesthesiam/rhasspy/blob/master/etc/rhasspy.service) is available (thanks [UnderpantsGnome](https://github.com/UnderpantsGnome)):
@@ -151,4 +197,8 @@ On low memory devices like the Raspberry Pi, building the tools above can quickl
You can skip building Kaldi if you plan to just [use Pocketsphinx](speech-to-text.md#pocketsphinx) for speech recognition.
### Updating Source Install
Follow the same instructions as [updating a virtual environment](#updating-virtual-environment).
+36 -9
View File
@@ -65,6 +65,25 @@ In order to do something with the `rhasspy_ChangeLightColor` event, create an au
See the documentation on [actions](https://www.home-assistant.io/docs/automation/action/) for the different things you can do with Home Assistant.
### Intents
More recent versions of Home Assistant can accept intents directly. Add the following to your `configuration.yaml` file:
```yaml
intent:
```
This will enable intents over the HTTP API. Next, write [intent scripts](https://www.home-assistant.io/integrations/intent_script) to handle each Rhasspy intent:
```yaml
intent_script:
ChangeLightColor:
action:
...
```
The possible [actions](https://www.home-assistant.io/docs/automation/action/) are the same as in automations.
### MQTT
In addition to events, Rhasspy can also publish intents through MQTT ([Hermes protocol](https://docs.snips.ai/reference/dialogue#intent)).
@@ -74,14 +93,22 @@ Add to your [profile](profiles.md):
```json
"mqtt": {
"enabled": true,
"host": "localhost",
"username": "",
"password": "",
"port": 1883,
"reconnect_sec": 5,
"site_id": "default",
"publish_intents": true
"enabled": true,
"host": "localhost",
"username": "",
"password": "",
"port": 1883,
"reconnect_sec": 5,
"site_id": "default",
"publish_intents": true,
"tls": {
"enabled": false,
"ca_certs": "",
"cert_reqs": "CERT_REQUIRED",
"certfile": "",
"ciphers": "",
"keyfile": ""
}
}
```
@@ -207,7 +234,7 @@ The following environment variables are available to your program:
* `$RHASSPY_PROFILE` - name of the current profile (e.g., "en")
* `$RHASSPY_PROFILE_DIR` - directory of the current profile (where `profile.json` is)
See [handle.sh](https://github.com/synesthesiam/rhasspy/blob/master/bin/mock-commands/handle.sh) for an example program.
See [handle.sh](https://github.com/synesthesiam/rhasspy/blob/master/bin/mock-commands/handle.sh) or [handle.py](https://github.com/synesthesiam/rhasspy/blob/master/bin/mock-commands/handle.py) for example programs.
### Speech
+2 -2
View File
@@ -10,8 +10,8 @@ The following table summarizes the trade-offs of using each intent recognizer:
| [fsticuffs](intent-recognition.md#fsticuffs) | 1M+ | very fast | very fast | ignores unknown words |
| [fuzzywuzzy](intent-recognition.md#fuzzywuzzy) | 12-100 | fast | fast | fuzzy string matching |
| [adapt](intent-recognition.md#mycroft-adapt) | 100-1K | moderate | fast | ignores unknown words |
| [flair](intent-recognition.md#flair) | 1K-100K | very slow | moderate | handles unseen words |
| [rasaNLU](intent-recognition.md#rasanlu) | 1K-100K | very slow | moderate | handles unseen words |
| [flair](intent-recognition.md#flair) | 1K-100K | very slow | moderate | handles unseen words |
## Fsticuffs
@@ -55,7 +55,7 @@ See `rhasspy.intent.FuzzyWuzzyRecognizer` for details.
## Mycroft Adapt
Recognizes intents using [Mycroft Adapt](https://github.com/MycroftAI/adapt). Works best when you have a medium number of sentences (hundreds to thousands) and need to be able to recognize sentences not seen during training (no new words, though).
Recognizes intents using [Mycroft Adapt](https://github.com/MycroftAI/adapt). Works best when you have a medium number of sentences (hundreds to thousands) and need to be able to recognize sentences not seen during training (no new words, though). This recognizer does not support converters, i.e. numbers are not converted back to integers.
Add to your [profile](profiles.md):
+11 -3
View File
@@ -52,8 +52,9 @@ Application authors may want to use the [rhasspy-client](https://pypi.org/projec
* `?nohass=true` - stop Rhasspy from handling the intent
* `?timeout=<seconds>` - override default command timeout
* `?entity=<entity>&value=<value>` - set custom entity/value in recognized intent
* `/api/listen-for-wake-word`
* POST to wake Rhasspy up and return immediately
* `/api/listen-for-wake`
* POST "on" to have Rhasspy listen for a wake word
* POST "off" to disable wake word
* `/api/lookup`
* POST word as plain text to look up or guess pronunciation
* `?n=<number>` - return at most `n` guessed pronunciations
@@ -84,6 +85,10 @@ Application authors may want to use the [rhasspy-client](https://pypi.org/projec
* POST a WAV file and have Rhasspy process it as a voice command
* Returns intent JSON when command is finished
* `?nohass=true` - stop Rhasspy from handling the intent
* `/api/speech-to-text`
* POST a WAV file and have Rhasspy return the text transcription
* Set `Accept: application/json` to receive JSON with more details
* `?noheader=true` - send raw 16-bit 16Khz mono audio without a WAV header
* `/api/start-recording`
* POST to have Rhasspy start recording a voice command
* `/api/stop-recording`
@@ -425,12 +430,14 @@ All available profile sections and settings are listed below:
* `g2p_model` - finite-state transducer for phonetisaurus to guess word pronunciations
* `g2p_casing` - casing to force for g2p model (`upper`, `lower`, or blank)
* `dictionary_casing` - casing to force for dictionary words (`upper`, `lower`, or blank)
* `grammars_dir` - directory to write generated JSGF grammars from sentences ini file
* `slots_dir` - directory to look for [slots lists](training.md#slots-lists) (default: `slots`)
* `slot_programs` - directory to look for [slot programs](training.md#slot-programs) (default `slot_programs`)
* `fsts_dir` - directory to write generated finite state transducers from JSGF grammars
* `intent` - transforming text commands to intents
* `system` - intent recognition system (`fsticuffs`, `fuzzywuzzy`, `rasa`, `remote`, `adapt`, `command`, or `dummy`)
* `fsticuffs` - configuration for [OpenFST-based](https://www.openfst.org) intent recognizer
* `intent_fst` - path to generated finite state transducer with all intents combined
* `converters_dir` - directory to look for [converter](training.md#converters) programs (default: `converters`)
* `ignore_unknown_words` - true if words not in the FST symbol table should be ignored
* `fuzzy` - true if text is matching in a fuzzy manner, skipping words in `stop_words.txt`
* `fuzzywuzzy` - configuration for simplistic [Levenshtein distance](https://en.wikipedia.org/wiki/Levenshtein_distance) based intent recognizer
@@ -447,6 +454,7 @@ All available profile sections and settings are listed below:
* `command` - configuration for external speech-to-text program
* `program` - path to executable
* `arguments` - list of arguments to pass to program
* `replace_numbers` if true, automatically replace number ranges (`N..M`) or numbers (`N`) with words
* `text_to_speech` - pronouncing words
* `system` - text to speech system (`espeak`, `flite`, `picotts`, `marytts`, `command`, or `dummy`)
* `espeak` - configuration for [eSpeak](http://espeak.sourceforge.net)
+24
View File
@@ -8,6 +8,7 @@ The following table summarizes language support for the various speech to text s
| ------ | ------- | ------- | ------- | ------- | ------- | ------- | ------- | ------- | ------- | ------- | ------- | ------- | ------- |
| [pocketsphinx](speech-to-text.md#pocketsphinx) | &#x2713; | &#x2713; | &#x2713; | &#x2713; | &#x2713; | &#x2713; | &#x2713; | &#x2713; | &#x2713; | &#x2713; | | &#x2713; | &#x2713; |
| [kaldi](speech-to-text.md#kaldi) | &#x2713; | &#x2713; | | &#x2713; | | &#x2713; | | | | | &#x2713; | | |
| [google](speech-to-text.md#google-cloud) | &#x2713; | &#x2713; | &#x2713; | &#x2713; | &#x2713; | &#x2713; | &#x2713; | &#x2713; | &#x2713; | &#x2713; | &#x2713; | &#x2713; | &#x2713; |
## Pocketsphinx
@@ -77,6 +78,29 @@ Rhasspy expects a Kaldi-compatible profile to contain a `model` directory with a
If you just want to use Rhasspy for general speech to text, you can set `speech_to_text.kaldi.open_transcription` to `true` in your profile. This will use the included general language model (much slower) and ignore any custom voice commands you've specified.
## Google Cloud
Does speech recognition using [Google Cloud Speech-to-Text](https://cloud.google.com/speech-to-text) service.
You will need an active Google Cloud subscription and a JSON private key connected to a service account enabled to use
the speech-to-text API. The locale configured in your profile will be used for speech recognition.
```json
{
"locale": "en_US",
"speech_to_text": {
"system": "google",
"google": {
"credentials": "api-project-xxxxxxxx-abcdef.json",
"min_confidence": 0.7
}
}
}
```
Please note that this module sends the recorded audio after it's completed, so no streaming support.
See `rhasspy.stt.GoogleCloudDecoder` for details.
## Remote HTTP Server
Uses a remote HTTP server to transform speech (WAV) to text.
+16 -1
View File
@@ -29,6 +29,19 @@ Add to your [profile](profiles.md):
Remove the `voice` option to have `espeak` use your profile's language automatically.
You may also pass additional arguments to the `espeak` command. For example,
```json
"text_to_speech": {
"system": "espeak",
"espeak": {
"arguments": ["-s", "80"]
}
}
```
will speak the sentence more slowly.
See `rhasspy.tts.EspeakSentenceSpeaker` for more details.
## Flite
@@ -52,7 +65,9 @@ See `rhasspy.tts.FliteSentenceSpeaker` for details.
## PicoTTS
Uses SVOX's [picotts](https://en.wikipedia.org/wiki/SVOX) for text to speech. Sounds a bit better (to me) than `flite` or `espeak`, but only has a single English voice.
Uses SVOX's [picotts](https://en.wikipedia.org/wiki/SVOX) for text to speech. Sounds a bit better (to me) than `flite` or `espeak`.
Included languages are `en-US`, `en-GB`, `de-DE`, `es-ES`, `fr-FR` and `it-IT`.
Add to your [profile](profiles.md):
+96 -1
View File
@@ -5,7 +5,11 @@ Rhasspy is designed to recognize voice commands [in a template language](#senten
* Intent Recognition
* [Basic Syntax](#basic-syntax)
* [Named Entities](#tags)
* [Number Ranges](#number-ranges)
* [Slots](#slots-lists)
* [Slot Synonyms](#slot-synonyms)
* [Slot Programs](#slot-programs)
* [Converters](#converters)
* Speech Recognition
* [Custom Words](#custom-words)
* [Language Model Mixing](#language-model-mixing)
@@ -156,6 +160,24 @@ You can **share rules** across intents by referencing them as `<IntentName.rule_
The second intent (`GetLightColor`) references the `colors` rule from `SetLightColor`. Rule references without a dot must exist in the current intent.
### Number Ranges
Rhasspy supports using number literals (`75`) and number ranges (`1..10`) directly in your sentence templates. During training, the [num2words](https://pypi.org/project/num2words) package is used to generate words that the speech recognizer can handle ("seventy five"). For example:
```
[SetBrightness]
set brightness to (0..100){brightness}
```
The `brightness` property of the recognized `SetBrightness` intent will automatically be [converted](#converters) to an integer for you. You can optionally add a step to the integer range:
```
evens = 0..100,2
odds = 1..100,2
```
Under the hood, number ranges are actually references to the `rhasspy/number` [slot program](#slot-programs). You can override this behavior by creating your `slot_programs/rhasspy/number` program or disable it entirely by setting `intent.replace_numbers` to `false` in [your profile](profiles.md).
### Slots Lists
Large [alternatives](#alternatives) can become unwieldy quickly. For example, say you have a list of movie names:
@@ -185,7 +207,11 @@ play ($movies){movie_name}
When matched, the `PlayMovie` intent JSON will contain `movie_name` property with either "Primer", "Moon", etc.
Make sure to **re-train** Rhasspy whenever you update your slot values.
Make sure to **re-train** Rhasspy whenever you update your slot values!
#### Slot Directories
Slot files can be put in **sub-directories** under `slots`. A list in `slots/foo/bar` should be referenced in `sentences.ini` as `$foo/bar`.
#### Slot Synonyms
@@ -206,6 +232,75 @@ which is referenced by `$rooms` and will match:
This will always output just "den" because `[the:]` optionally matches "the" and then drops the word.
#### Slot Programs
Slot lists are great if your slot values always stay the same and are easily written out by hand. If you have slot values that you need to be generated *each time Rhasspy is trained*, you can use slot programs.
Create a directory named `slot_programs` in your profile (e.g., `$HOME/.config/rhasspy/profiles/en/slot_programs`):
```bash
slot_programs="${HOME}/.config/rhasspy/profiles/en/slot_programs"
mkdir -p "${slot_programs}"
```
Add a file in `slot_programs` with the name of your slot, e.g. `colors`. Write a program in this file, such as a bash script. Make sure to include the [shebang](https://en.wikipedia.org/wiki/Shebang_(Unix)) and mark the file as executable:
```bash
cat <<EOF > "${slot_programs}/colors"
#!/usr/bin/env bash
echo 'red'
echo 'green'
echo 'blue'
EOF
chmod +x "${slot_programs}/colors"
```
Now, when you reference `$colors` in your `sentences.ini`, Rhasspy will run the program you wrote and collect the slot values from each line. Note that you can output all the same things as regular [slots lists](#slots-lists), including optional words, alternatives, etc.
You can pass **arguments** to your program using the syntax `$name,arg1,arg2,...` in `sentences.ini` (no spaces). Arguments will be pass on the command-line, so `arg1` and `arg2` will be `$1` and `$2` in a bash script.
Like regular slots lists, slot programs can also be put in sub-directories under `slot_programs`. A program in `slot_programs/foo/bar` should be referenced in `sentences.ini` as `$foo/bar`.
#### Built-in Slots
Rhasspy includes a few built-in slots for each language:
* `$rhasspy/days` - day names of the week
* `$rhasspy/months` - month names of the year
### Converters
By default, all named entity values in a recognized intent's JSON are strings. If you need a different data type, such as an integer or float, or want to do some kind of complex *conversion*, use a converter:
```
[SetBrightness]
set brightness to (low:0 | medium:0.5 | high:1){brightness!float}
```
The `!name` syntax calls a converter by name. Rhasspy includes several built-in converters:
* int - convert to integer
* float - convert to real
* bool - convert to boolean
* lower - lower-case
* upper - upper-case
You can define your own converters by placing a file in the `converters` directory of your profile. Like [slot programs](#slot-programs), this file should contain a [shebang](https://en.wikipedia.org/wiki/Shebang_(Unix)) and be marked as executable (`chmod +x`). A file named `converters/foo/bar` should be referenced as `!foo/bar` in `sentences.ini`.
Your custom converter will receive the value to convert on standard in (`stdin`) encoded as JSON. You should print a converted JSON value to standard out `stdout`. The example below demonstrates converting a string value into an integer:
```python
#!/usr/bin/env python3
import sys
import json
value = json.load(sys.stdin)
print(int(value))
```
Converters can be *chained*, so `!foo!bar` will call the `foo` converter and then pass the result to `bar`.
### Special Cases
If one of your sentences happens to start with an optional word (e.g., `[the]`), this can lead to a problem:
+95 -5
View File
@@ -2,6 +2,11 @@
* [RGB Light Example](#rgb-light-example)
* [Client/Server Setup](#clientserver-setup)
* MATRIX Labs
* [Rhasspy Voice Assistant on MATRIX Voice and MATRIX Creator](https://www.hackster.io/matrix-labs/rhasspy-voice-assistant-on-matrix-voice-and-matrix-creator-97f92e)
* [Adding Intents for Rhasspy Offline Voice Assistant](https://www.hackster.io/matrix-labs/adding-intents-for-rhasspy-offline-voice-assistant-faa221)
* Rendered Obsolete
* [Home Assistant Voice Recognition with Rhasspy](https://rendered-obsolete.github.io/2020/01/02/rhasspy.html)
## RGB Light Example
@@ -63,7 +68,90 @@ You can now fill in the rest of the Home Assistant automation:
rgb_color: [255, 0, 0]
entity_id: light.bedroom
This will handle the specific case of setting the bedroom light to red, but not any other color. You can either add additional automations to handle these, or make use of [automation templating](https://www.home-assistant.io/docs/automation/templating/) to do it all at once.
This will handle the specific case of setting the bedroom light to red, but not any other color. You can either add additional automations to handle these, or make use of [automation templating](https://www.home-assistant.io/docs/automation/templating/) to do it all at once. [Home Assistant Template Example](Home-Assistant-Template-Example)
### Home Assistant Template Example
Using the following additions, you can get Home Assistant to respond to turning on / off *ANY* light in your setup.
#### Slots
Add the following JSON to the Slots tab in your Rhasspy web interface:
```json
{
"lights": [
"(living room wall):light.bulb_3",
"(living room desk):switch.m4",
"(living room floor):switch.sonoff",
"(bar lights):switch.maxcio1",
"(entry wall):light.bulb_4",
"(guest wall):light.bulb_6",
"(guest floor):switch.m5",
"(bedroom wall):light.bulb_5",
"(bedroom desk):light.bulb_1",
"(bedroom floor):light.bulb_2"
]
}
```
#### Sentences
A simple sentence to turn any of the lights in the slots file on or off.
Note the use of the `<state>` rule and the slot `$lights`
```
[ChangeLightState]
state = (on | off) {light_state}
turn [the] ($lights) {light_name} <state>
```
#### Home Assistant
In your Home Assistant `automations.yaml` file, use a `data_template` to get the Rhasspy event data with `trigger.event.data.<your property name>` and then pass those along to a script:
```yaml
- id: '1577164768008'
alias: Rhasspy Light States
description: Voice Control on/off states for all lights
trigger:
- event_data: {}
event_type: rhasspy_ChangeLightState
platform: event
condition: []
action:
- alias: ''
data_template:
light_name: "{{ trigger.event.data.light_name }}"
light_state: "{{ trigger.event.data.light_state }}"
service: script.rhasspy_light_state
```
In `scripts.yaml`, the `service_template` casts the `light_state` into a string and checks to see if you said 'on' or 'off'. The homeassistant-service can toggle both lights and switches, which is helpful if you have a combination of "light" types:
```yaml
rhasspy_light_state:
alias: change_light_state
fields:
light_name:
description: "Light Entity"
example: light.bulb_1
light_state:
description: "State to change the light to"
example: on
sequence:
- service_template: >
{% set this_state = light_state | string %}
{% if this_state == 'on' %}
homeassistant.turn_on
{%else %}
homeassistant.turn_off
{% endif %}
data_template:
entity_id: "{{ light_name }}"
```
## Client/Server Setup
@@ -96,9 +184,10 @@ Contributed by [jaburges](https://community.home-assistant.io/u/jaburges)
[Rhasspy]
Listen for wake word on Startup = UNchecked
[Home Assistant]
Do not use Home Assistant (note you obviously can instead of Node-Red)
[Intent Handling]
Do not handle intent on this device
#There is no harm in having the Server handle Intents, but the Client must handle Intents
[Wake Word]
No Wake word on this device
@@ -181,7 +270,8 @@ Contributed by [jaburges](https://community.home-assistant.io/u/jaburges)
Listen for wake word on Startup = checked
[Home Assistant]
Do not use Home Assistant (note you obviously can instead of Node-Red)
Enable Intent Handling on this device
#Do not use Home Assistant if using Node-Red
[Wake Word]
Use snowboy (this should trigger a download of more files)
+39 -1
View File
@@ -142,7 +142,18 @@ More example flows are available [on Github](https://github.com/synesthesiam/rha
### WebSocket Events
Whenever a voice command is recognized, Rhasspy emits JSON events over a websocket connection available at `ws://rhasspy:12101/api/events/intent` (replace `ws://` with `wss://` if you're using [secure hosting](usage.md#secure-hosting-with-https)).
Rhasspy supports multiple websocket event endpoints:
* `/api/events/intent`
* Intent recognized or not
* `/api/events/wake`
* Wake word detected
* `/api/events/text`
* Speech transcription
#### WebSocket Intents
Whenever a voice command is recognized, Rhasspy emits JSON events over a websocket connection available at `ws://YOUR_SERVER:12101/api/events/intent` (replace `ws://` with `wss://` if you're using [secure hosting](usage.md#secure-hosting-with-https)).
You can listen to these events in a [Node-RED](https://nodered.org) flow, and easily add offline, private voice commands to your home automation set up!
For the `ChangLightState` intent from the [RGB Light Example](index.md#rgb-light-example), Rhasspy will emit a JSON event like this over the websocket:
@@ -171,6 +182,33 @@ For the `ChangLightState` intent from the [RGB Light Example](index.md#rgb-light
}
```
#### WebSocket Wake
When the wake word is detected, or Rhasspy is woken up via the `/api/listen-for-command` HTTP endpoint, a JSON event is emitted at `ws://YOUR_SERVER:12101/api/events/wake` (`wss://` if using HTTPS) like:
```json
{
"wakewordId": "default",
"siteId": "default"
}
```
The `wakewordId` is set using the model or file name of your wakeword model (e.g., `porcupine` for `porcupine.ppn`). The `siteId` comes from your `mqtt.siteId` profile setting.
#### WebSocket Transcriptions
Each time a voice command is transcribed, Rhasspy emits a JSON event at `ws://YOUR_SERVER:12101/api/events/text` (`wss://` if using HTTPS) like:
```json
{
"text": "text from voice command",
"wakewordId": "default",
"siteId": "default"
}
```
The transcription is contained in the `text` property. `wakewordId` is the id of the wakeword that initiated the voice command (or `default`). The `siteId` comes from your `mqtt.siteId` profile setting.
## MQTT and Snips
Rhasspy is able to interoperate with Snips.AI services using the [Hermes protocol](https://docs.snips.ai/reference/hermes) over [MQTT](http://mqtt.org). The following components are Snips/Hermes compatible:
+10 -2
View File
@@ -37,7 +37,7 @@ Add to your [profile](profiles.md):
There are a lot of [keyword files](https://github.com/Picovoice/Porcupine/tree/master/resources/keyword_files) available for download. Use the `linux` platform if you're on desktop/laptop (`amd64`) and the `raspberrypi` platform if you're using a Raspberry Pi (`armhf`/`aarch64`). The `.ppn` files should go in the `porcupine` directory inside your profile (referenced by `keyword_path`).
If you want to create a custom wake word, you will need to run the [Porcupine Optimizer](https://github.com/Picovoice/Porcupine/tree/master/tools/optimizer). **NOTE**: the generated keyword file is only valid for 30 days, though you can always just re-run the optimizer.
If you want to create a custom wake word, you will need to use the [Picovoice Console](https://github.com/Picovoice/porcupine#picovoice-console). **NOTE**: the generated keyword file is only valid for 30 days, though you can always just re-run the optimizer.
See `rhasspy.wake.PorcupineWakeListener` for details.
@@ -181,7 +181,15 @@ Add to your [profile](profiles.md):
"username": "",
"port": 1883,
"password": "",
"site_id": "default"
"site_id": "default",
"tls": {
"enabled": false,
"ca_certs": "",
"cert_reqs": "CERT_REQUIRED",
"certfile": "",
"ciphers": "",
"keyfile": ""
}
}
```
+55 -6
View File
@@ -11,6 +11,7 @@ cpu_arch=$(uname --m)
DEFINE_string 'download-dir' "${this_dir}/download" 'Directory to cache downloaded files'
DEFINE_boolean 'precise' true 'Install Mycroft Precise'
DEFINE_boolean 'kaldi' true 'Install Kaldi'
DEFINE_boolean 'web' true "Install web UI"
DEFINE_boolean 'offline' false "Don't download anything"
DEFINE_boolean 'all-cpu' false 'Download dependencies for all CPU architectures'
DEFINE_string 'cpu-arch' "${cpu_arch}" 'CPU architecture (x86_64, armv7l, arm64v8, armv6l)'
@@ -44,6 +45,10 @@ if [[ "${FLAGS_kaldi}" -eq "${FLAGS_FALSE}" ]]; then
no_kaldi='true'
fi
if [[ "${FLAGS_web}" -eq "${FLAGS_FALSE}" ]]; then
no_web='true'
fi
# -----------------------------------------------------------------------------
function maybe_download {
@@ -65,8 +70,15 @@ declare -A CPU_TO_FRIENDLY
CPU_TO_FRIENDLY["x86_64"]="amd64"
CPU_TO_FRIENDLY["armv7l"]="armhf"
CPU_TO_FRIENDLY["arm64v8"]="aarch64"
CPU_TO_FRIENDLY["aarch64"]="aarch64"
CPU_TO_FRIENDLY["armv6l"]="armv6l"
declare -A FRIENDLY_TO_DOCKER
FRIENDLY_TO_DOCKER["amd64"]="amd64"
FRIENDLY_TO_DOCKER["armhf"]="armv7"
FRIENDLY_TO_DOCKER["aarch64"]="arm64"
FRIENDLY_TO_DOCKER["armv6l"]="armv6"
# CPU architecture
if [[ -n "${all_cpu}" ]]; then
CPU_ARCHS=("x86_64" "armv7l" "arm64v8")
@@ -81,12 +93,32 @@ fi
# -----------------------------------------------------------------------------
for FRIENDLY_ARCH in "${FRIENDLY_ARCHS[@]}"; do
rhasspy_files=("rhasspy-tools_${FRIENDLY_ARCH}.tar.gz" "rhasspy-web-dist.tar.gz")
rhasspy_files=()
if [[ -z "${no_tools}" ]]; then
# Install Rhasspy tools
rhasspy_files+=("rhasspy-tools_${FRIENDLY_ARCH}.tar.gz")
fi
if [[ -z "${no_web}" ]]; then
# Install web UI
rhasspy_files+=('rhasspy-web-dist.tar.gz')
fi
for rhasspy_file_name in "${rhasspy_files[@]}"; do
rhasspy_file="${download_dir}/${rhasspy_file_name}"
rhasspy_file_url="https://github.com/synesthesiam/rhasspy/releases/download/v2.0/${rhasspy_file_name}"
maybe_download "${rhasspy_file_url}" "${rhasspy_file}"
done
if [[ -z "${no_tools}" ]]; then
# Create link for docker buildx
DOCKER_ARCH="${FRIENDLY_TO_DOCKER[${FRIENDLY_ARCH}]}"
if [[ "${FRIENDLY_ARCH}" != "${DOCKER_ARCH}" ]]; then
ln -f "${download_dir}/rhasspy-tools_${FRIENDLY_ARCH}.tar.gz" \
"${download_dir}/rhasspy-tools_${DOCKER_ARCH}.tar.gz"
fi
fi
done
# -----------------------------------------------------------------------------
@@ -112,10 +144,18 @@ maybe_download "${snowboy_url}" "${snowboy_file}"
if [[ -z "${no_precise}" ]]; then
for CPU_ARCH in "${CPU_ARCHS[@]}"; do
case $CPU_ARCH in
x86_64|armv7l)
x86_64|armv7l|aarch64)
precise_file="${download_dir}/precise-engine_0.3.0_${CPU_ARCH}.tar.gz"
precise_url="https://github.com/MycroftAI/mycroft-precise/releases/download/v0.3.0/precise-engine_0.3.0_${CPU_ARCH}.tar.gz"
maybe_download "${precise_url}" "${precise_file}"
# Create link for docker buildx
FRIENDLY_ARCH="${CPU_TO_FRIENDLY[${CPU_ARCH}]}"
DOCKER_ARCH="${FRIENDLY_TO_DOCKER[${FRIENDLY_ARCH}]}"
if [[ "${CPU_ARCH}" != "${DOCKER_ARCH}" ]]; then
ln -f "${download_dir}/precise-engine_0.3.0_${CPU_ARCH}.tar.gz" \
"${download_dir}/precise-engine_0.3.0_${DOCKER_ARCH}.tar.gz"
fi
esac
done
fi
@@ -126,10 +166,19 @@ fi
if [[ -z "${no_kaldi}" ]]; then
for FRIENDLY_ARCH in "${FRIENDLY_ARCHS[@]}"; do
# Install pre-built package
kaldi_file="${download_dir}/kaldi_${FRIENDLY_ARCH}.tar.gz"
kaldi_url="https://github.com/synesthesiam/kaldi-docker/releases/download/v1.0/kaldi_${FRIENDLY_ARCH}.tar.gz"
maybe_download "${kaldi_url}" "${kaldi_file}"
if [[ "${FRIENDLY_ARCH}" != "armv6l" ]]; then
# Install pre-built package
kaldi_file="${download_dir}/kaldi_${FRIENDLY_ARCH}.tar.gz"
kaldi_url="https://github.com/synesthesiam/kaldi-docker/releases/download/v1.0/kaldi_${FRIENDLY_ARCH}.tar.gz"
maybe_download "${kaldi_url}" "${kaldi_file}"
# Create link for docker buildx
DOCKER_ARCH="${FRIENDLY_TO_DOCKER[${FRIENDLY_ARCH}]}"
if [[ "${FRIENDLY_ARCH}" != "${DOCKER_ARCH}" ]]; then
ln -f "${download_dir}/kaldi_${FRIENDLY_ARCH}.tar.gz" \
"${download_dir}/kaldi_${DOCKER_ARCH}.tar.gz"
fi
fi
done
fi
+3
View File
@@ -72,6 +72,9 @@ ignore_missing_imports = True
[mypy-json5.*]
ignore_missing_imports = True
[mypy-quart.*]
ignore_missing_imports = True
[mypy-quart_cors.*]
ignore_missing_imports = True
+61 -48
View File
@@ -1,17 +1,12 @@
#
# Copyright 2018 Picovoice Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
# You may not use this file except in compliance with the license. A copy of the license is located in the "LICENSE"
# file accompanying this source.
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
# an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
#
import os
@@ -20,7 +15,7 @@ from enum import Enum
class Porcupine(object):
"""Python binding for Picovoice's wake word detection (aka Porcupine) library."""
"""Python binding for Picovoice's wake word detection (Porcupine) engine."""
class PicovoiceStatuses(Enum):
"""Status codes corresponding to 'pv_status_t' defined in 'include/picovoice.h'"""
@@ -29,11 +24,17 @@ class Porcupine(object):
OUT_OF_MEMORY = 1
IO_ERROR = 2
INVALID_ARGUMENT = 3
STOP_ITERATION = 4
KEY_ERROR = 5
INVALID_STATE = 6
_PICOVOICE_STATUS_TO_EXCEPTION = {
PicovoiceStatuses.OUT_OF_MEMORY: MemoryError,
PicovoiceStatuses.IO_ERROR: IOError,
PicovoiceStatuses.INVALID_ARGUMENT: ValueError
PicovoiceStatuses.INVALID_ARGUMENT: ValueError,
PicovoiceStatuses.STOP_ITERATION: StopIteration,
PicovoiceStatuses.KEY_ERROR: KeyError,
PicovoiceStatuses.INVALID_STATE: ValueError,
}
class CPorcupine(Structure):
@@ -48,9 +49,9 @@ class Porcupine(object):
keyword_file_paths=None,
sensitivities=None):
"""
Loads Porcupine's shared library and creates an instance of wake word detection object.
Constructor.
:param library_path: Absolute path to Porcupine's shared library.
:param library_path: Absolute path to Porcupine's dynamic library.
:param model_file_path: Absolute path to file containing model parameters.
:param keyword_file_path: Absolute path to keyword file containing hyper-parameters. If not present then
'keyword_file_paths' will be used.
@@ -64,38 +65,38 @@ class Porcupine(object):
"""
if not os.path.exists(library_path):
raise IOError("Could not find Porcupine's library at '%s'" % library_path)
raise IOError("could'nt find Porcupine's library at '%s'" % library_path)
library = cdll.LoadLibrary(library_path)
if not os.path.exists(model_file_path):
raise IOError("Could not find model file at '%s'" % model_file_path)
raise IOError("could'nt find model file at '%s'" % model_file_path)
if sensitivity is not None and keyword_file_path is not None:
if not os.path.exists(keyword_file_path):
raise IOError("Could not find keyword file at '%s'" % keyword_file_path)
raise IOError("could'nt' find keyword file at '%s'" % keyword_file_path)
keyword_file_paths = [keyword_file_path]
if not (0 <= sensitivity <= 1):
raise ValueError('Sensitivity should be within [0, 1]')
raise ValueError('sensitivity should be within [0, 1]')
sensitivities = [sensitivity]
elif sensitivities is not None and keyword_file_paths is not None:
if len(keyword_file_paths) != len(sensitivities):
raise ValueError("Different number of sensitivity and keyword file path parameters are provided.")
raise ValueError("different number of sensitivity and keyword file path parameters are provided.")
for x in keyword_file_paths:
if not os.path.exists(os.path.expanduser(x)):
raise IOError("Could not find keyword file at '%s'" % x)
raise IOError("could not find keyword file at '%s'" % x)
for x in sensitivities:
if not (0 <= x <= 1):
raise ValueError('Sensitivity should be within [0, 1]')
raise ValueError('sensitivity should be within [0, 1]')
else:
raise ValueError("Sensitivity and/or keyword file path is missing")
raise ValueError("sensitivity and/or keyword file path is missing")
self._num_keywords = len(keyword_file_paths)
init_func = library.pv_porcupine_multiple_keywords_init
init_func = library.pv_porcupine_init
init_func.argtypes = [
c_char_p,
c_int,
@@ -107,44 +108,43 @@ class Porcupine(object):
self._handle = POINTER(self.CPorcupine)()
status = init_func(
model_file_path.encode(),
model_file_path.encode('utf-8'),
self._num_keywords,
(c_char_p * self._num_keywords)(*[os.path.expanduser(x).encode() for x in keyword_file_paths]),
(c_char_p * self._num_keywords)(*[os.path.expanduser(x).encode('utf-8') for x in keyword_file_paths]),
(c_float * self._num_keywords)(*sensitivities),
byref(self._handle))
if status is not self.PicovoiceStatuses.SUCCESS:
raise self._PICOVOICE_STATUS_TO_EXCEPTION[status]('Initialization failed')
self.process_func = library.pv_porcupine_multiple_keywords_process
self.process_func.argtypes = [POINTER(self.CPorcupine), POINTER(c_short), POINTER(c_int)]
self.process_func.restype = self.PicovoiceStatuses
raise self._PICOVOICE_STATUS_TO_EXCEPTION[status]('initialization failed')
self._delete_func = library.pv_porcupine_delete
self._delete_func.argtypes = [POINTER(self.CPorcupine)]
self._delete_func.restype = None
self._sample_rate = library.pv_sample_rate()
self.process_func = library.pv_porcupine_process
self.process_func.argtypes = [POINTER(self.CPorcupine), POINTER(c_short), POINTER(c_int)]
self.process_func.restype = self.PicovoiceStatuses
version_func = library.pv_porcupine_version
version_func.argtypes = []
version_func.restype = c_char_p
self._version = version_func().decode('utf-8')
self._frame_length = library.pv_porcupine_frame_length()
@property
def sample_rate(self):
"""Audio sample rate accepted by Porcupine library."""
self._sample_rate = library.pv_sample_rate()
return self._sample_rate
def delete(self):
"""Releases resources acquired by Porcupine's library."""
@property
def frame_length(self):
"""Number of audio samples per frame expected by C library."""
return self._frame_length
self._delete_func(self._handle)
def process(self, pcm):
"""
Monitors incoming audio stream for given wake word(s).
Processes a frame of the incoming audio stream and emits the detection result.
:param pcm: An array (or array-like) of consecutive audio samples. For more information regarding required audio
properties (i.e. sample rate, number of channels encoding, and number of samples per frame) please refer to
'include/pv_porcupine.h'.
:param pcm: A frame of audio samples. The number of samples per frame can be attained by calling
'.frame_length'. The incoming audio needs to have a sample rate equal to '.sample_rate' and be 16-bit
linearly-encoded. Porcupine operates on single-channel audio.
:return: For a single wake-word use cse True if wake word is detected. For multiple wake-word use case it
returns the index of detected wake-word. Indexing is 0-based and according to ordering of input keyword file
paths. It returns -1 when no keyword is detected.
@@ -153,7 +153,7 @@ class Porcupine(object):
result = c_int()
status = self.process_func(self._handle, (c_short * len(pcm))(*pcm), byref(result))
if status is not self.PicovoiceStatuses.SUCCESS:
raise self._PICOVOICE_STATUS_TO_EXCEPTION[status]('Processing failed')
raise self._PICOVOICE_STATUS_TO_EXCEPTION[status]()
keyword_index = result.value
@@ -162,7 +162,20 @@ class Porcupine(object):
else:
return keyword_index
def delete(self):
"""Releases resources acquired by Porcupine's library."""
@property
def version(self):
"""Getter for version"""
self._delete_func(self._handle)
return self._version
@property
def frame_length(self):
"""Getter for number of audio samples per frame."""
return self._frame_length
@property
def sample_rate(self):
"""Audio sample rate accepted by Picovoice."""
return self._sample_rate
+1
View File
@@ -1,6 +1,7 @@
{
"language": "ca",
"name": "ca",
"locale": "ca_ES",
"speech_to_text": {
"system": "pocketsphinx",
"dictionary_casing": "lower"
+29
View File
@@ -0,0 +1,29 @@
#!/usr/bin/env python3
import argparse
import sys
def main():
parser = argparse.ArgumentParser("number")
parser.add_argument("lower", type=int, help="Lower bound")
parser.add_argument("upper", type=int, help="Upper bound (inclusive)")
args, rest_args = parser.parse_known_args()
lower = args.lower
upper = args.upper
step = 1
if rest_args:
step = int(rest_args[0])
if upper < lower:
lower, upper = upper, lower
for n in range(lower, upper + 1, step):
print(n)
# -----------------------------------------------------------------------------
if __name__ == "__main__":
main()
+7
View File
@@ -0,0 +1,7 @@
dilluns
dimarts
dimecres
dijous
divendres
dissabte
diumenge
+12
View File
@@ -0,0 +1,12 @@
de gener
de febrer
de març
dabril
de maig
de juny
de juliol
dagost
de setembre
doctubre
de novembre
de desembre
+2 -1
View File
@@ -1,6 +1,7 @@
{
"language": "de",
"name": "de",
"locale": "de_DE",
"speech_to_text": {
"system": "pocketsphinx",
"dictionary_casing": "lower",
@@ -9,7 +10,7 @@
"base_language_model": "kaldi/base_language_model.txt",
"base_language_model_fst": "kaldi/base_language_model.fst",
"compatible": true,
"custom_words": "kaldi/custom_words.txt",
"custom_words": "kaldi_custom_words.txt",
"dictionary": "kaldi/dictionary.txt",
"graph": "graph",
"language_model": "kaldi/language_model.txt",
+29
View File
@@ -0,0 +1,29 @@
#!/usr/bin/env python3
import argparse
import sys
def main():
parser = argparse.ArgumentParser("number")
parser.add_argument("lower", type=int, help="Lower bound")
parser.add_argument("upper", type=int, help="Upper bound (inclusive)")
args, rest_args = parser.parse_known_args()
lower = args.lower
upper = args.upper
step = 1
if rest_args:
step = int(rest_args[0])
if upper < lower:
lower, upper = upper, lower
for n in range(lower, upper + 1, step):
print(n)
# -----------------------------------------------------------------------------
if __name__ == "__main__":
main()
+7
View File
@@ -0,0 +1,7 @@
Montag
Dienstag
Mittwoch
Donnerstag
Freitag
Samstag
Sonntag
+12
View File
@@ -0,0 +1,12 @@
Januar
Februar
März
April
Mai
Juni
Juli
August
September
Oktober
November
Dezember
+29 -13
View File
@@ -28,10 +28,10 @@
"program": ""
},
"forward_to_hass": false,
"system": "dummy",
"system": "dummy",
"remote": {
"url": "http://my-server:port/endpoint"
},
}
},
"home_assistant": {
"access_token": "",
@@ -52,14 +52,17 @@
"conversation": {
"handle_speech": true
},
"error_sound": true,
"fuzzywuzzy": {
"examples_json": "intent_examples.json",
"min_confidence": 0
},
"fsticuffs": {
"intent_fst": "intent.fst",
"intent_graph": "intent.json",
"ignore_unknown_words": true,
"fuzzy": true
"fuzzy": true,
"converters_dir": "converters"
},
"flair": {
"cache_dir": "flair/cache",
@@ -73,7 +76,8 @@
"rasa": {
"examples_markdown": "intent_examples.md",
"project_name": "rhasspy",
"url": "http://localhost:5005/"
"url": "http://localhost:5005/",
"model_dir": "/app/models"
},
"remote": {
"url": "http://my-server:12101/api/text-to-intent"
@@ -115,7 +119,15 @@
"publish_intents": true,
"reconnect_sec": 5,
"site_id": "default",
"username": ""
"username": "",
"tls": {
"enabled": false,
"ca_certs": "",
"cert_reqs": "CERT_REQUIRED",
"certfile": "",
"ciphers": "",
"keyfile": ""
}
},
"rhasspy": {
"listen_on_start": true,
@@ -125,7 +137,8 @@
"sounds": {
"recorded": "${RHASSPY_BASE_DIR}/etc/wav/beep_lo.wav",
"system": "aplay",
"wake": "${RHASSPY_BASE_DIR}/etc/wav/beep_hi.wav"
"wake": "${RHASSPY_BASE_DIR}/etc/wav/beep_hi.wav",
"error": "${RHASSPY_BASE_DIR}/etc/wav/beep_error.wav"
},
"speech_to_text": {
"command": {
@@ -186,6 +199,7 @@
"sentences_ini": "sentences.ini",
"sentences_dir": "intents",
"slots_dir": "slots",
"slot_programs_dir": "slot_programs",
"system": "dummy"
},
"text_to_speech": {
@@ -193,6 +207,7 @@
"arguments": [],
"program": ""
},
"disable_wake": false,
"espeak": {},
"flite": {
"voice": "kal16"
@@ -284,6 +299,7 @@
},
"system": "dummy"
},
"webhooks": {},
"download": {
"cache_dir": "download",
"conditions": {
@@ -316,31 +332,31 @@
"cache": false
},
"porcupine_params.pv": {
"url": "https://github.com/Picovoice/Porcupine/raw/master/lib/common/porcupine_params.pv",
"url": "https://github.com/Picovoice/porcupine/raw/v1.7/lib/common/porcupine_params.pv",
"cache": false
},
"porcupine.ppn": {
"cache": false,
"x86_64": {
"url": "https://github.com/Picovoice/Porcupine/raw/master/resources/keyword_files/linux/porcupine_linux.ppn"
"url": "https://github.com/Picovoice/Porcupine/raw/v1.7/resources/keyword_files/linux/porcupine_linux.ppn"
},
"armv7l": {
"url": "https://github.com/Picovoice/Porcupine/raw/master/resources/keyword_files/raspberrypi/porcupine_raspberrypi.ppn"
"url": "https://github.com/Picovoice/porcupine/raw/v1.7/resources/keyword_files/raspberry-pi/porcupine_raspberry-pi.ppn"
},
"aarch64": {
"url": "https://github.com/Picovoice/Porcupine/raw/master/resources/keyword_files/raspberrypi/porcupine_raspberrypi.ppn"
"url": "https://github.com/Picovoice/porcupine/raw/v1.7/resources/keyword_files/raspberry-pi/porcupine_raspberry-pi.ppn"
}
},
"libpv_porcupine.so": {
"cache": false,
"x86_64": {
"url": "https://github.com/Picovoice/Porcupine/raw/master/lib/linux/x86_64/libpv_porcupine.so"
"url": "https://github.com/Picovoice/porcupine/raw/v1.7/lib/linux/x86_64/libpv_porcupine.so"
},
"armv7l": {
"url": "https://github.com/Picovoice/Porcupine/raw/master/lib/raspberry-pi/cortex-a53/libpv_porcupine.so"
"url": "https://github.com/Picovoice/porcupine/raw/v1.7/lib/raspberry-pi/cortex-a53/libpv_porcupine.so"
},
"aarch64": {
"url": "https://github.com/Picovoice/Porcupine/raw/master/lib/raspberry-pi/cortex-a53/libpv_porcupine.so"
"url": "https://github.com/Picovoice/porcupine/raw/v1.7/lib/raspberry-pi/cortex-a53/libpv_porcupine.so"
}
}
}
+1
View File
@@ -1,6 +1,7 @@
{
"language": "el",
"name": "el",
"locale": "el_GR",
"speech_to_text": {
"g2p_casing": "lower",
"system": "pocketsphinx",
+29
View File
@@ -0,0 +1,29 @@
#!/usr/bin/env python3
import argparse
import sys
def main():
parser = argparse.ArgumentParser("number")
parser.add_argument("lower", type=int, help="Lower bound")
parser.add_argument("upper", type=int, help="Upper bound (inclusive)")
args, rest_args = parser.parse_known_args()
lower = args.lower
upper = args.upper
step = 1
if rest_args:
step = int(rest_args[0])
if upper < lower:
lower, upper = upper, lower
for n in range(lower, upper + 1, step):
print(n)
# -----------------------------------------------------------------------------
if __name__ == "__main__":
main()
+7
View File
@@ -0,0 +1,7 @@
Δευτέρα
Τρίτη
Τετάρτη
Πέμπτη
Παρασκευή
Σάββατο
Κυριακή
+12
View File
@@ -0,0 +1,12 @@
Ιανουαρίου
Φεβρουαρίου
Μαρτίου
Απριλίου
Μαΐου
Ιουνίου
Ιουλίου
Αυγούστου
Σεπτεμβρίου
Οκτωβρίου
Νοεμβρίου
Δεκεμβρίου
+2 -1
View File
@@ -1,6 +1,7 @@
{
"language": "en",
"name": "en",
"locale": "en_US",
"speech_to_text": {
"system": "pocketsphinx",
"dictionary_casing": "lower",
@@ -9,7 +10,7 @@
"base_language_model": "kaldi/base_language_model.txt",
"base_language_model_fst": "kaldi/base_language_model.fst",
"compatible": true,
"custom_words": "kaldi/custom_words.txt",
"custom_words": "kaldi_custom_words.txt",
"dictionary": "kaldi/dictionary.txt",
"graph": "graph",
"language_model": "kaldi/language_model.txt",
+29
View File
@@ -0,0 +1,29 @@
#!/usr/bin/env python3
import argparse
import sys
def main():
parser = argparse.ArgumentParser("number")
parser.add_argument("lower", type=int, help="Lower bound")
parser.add_argument("upper", type=int, help="Upper bound (inclusive)")
args, rest_args = parser.parse_known_args()
lower = args.lower
upper = args.upper
step = 1
if rest_args:
step = int(rest_args[0])
if upper < lower:
lower, upper = upper, lower
for n in range(lower, upper + 1, step):
print(n)
# -----------------------------------------------------------------------------
if __name__ == "__main__":
main()
+7
View File
@@ -0,0 +1,7 @@
Monday
Tuesday
Wednesday
Thursday
Friday
Saturday
Sunday
+12
View File
@@ -0,0 +1,12 @@
January
February
March
April
May
June
July
August
September
October
November
December
+1
View File
@@ -1,6 +1,7 @@
{
"language": "es",
"name": "es",
"locale": "es_ES",
"speech_to_text": {
"system": "pocketsphinx",
"dictionary_casing": "lower"
+29
View File
@@ -0,0 +1,29 @@
#!/usr/bin/env python3
import argparse
import sys
def main():
parser = argparse.ArgumentParser("number")
parser.add_argument("lower", type=int, help="Lower bound")
parser.add_argument("upper", type=int, help="Upper bound (inclusive)")
args, rest_args = parser.parse_known_args()
lower = args.lower
upper = args.upper
step = 1
if rest_args:
step = int(rest_args[0])
if upper < lower:
lower, upper = upper, lower
for n in range(lower, upper + 1, step):
print(n)
# -----------------------------------------------------------------------------
if __name__ == "__main__":
main()
+7
View File
@@ -0,0 +1,7 @@
lunes
martes
miércoles
jueves
viernes
sábado
domingo
+12
View File
@@ -0,0 +1,12 @@
enero
febrero
marzo
abril
mayo
junio
julio
agosto
septiembre
octubre
noviembre
diciembre
+2 -1
View File
@@ -1,6 +1,7 @@
{
"language": "fr",
"name": "fr",
"locale": "fr_FR",
"speech_to_text": {
"system": "pocketsphinx",
"dictionary_casing": "lower",
@@ -9,7 +10,7 @@
"base_language_model": "kaldi/base_language_model.txt",
"base_language_model_fst": "kaldi/base_language_model.fst",
"compatible": true,
"custom_words": "kaldi/custom_words.txt",
"custom_words": "kaldi_custom_words.txt",
"dictionary": "kaldi/dictionary.txt",
"graph": "graph",
"language_model": "kaldi/language_model.txt",
+29
View File
@@ -0,0 +1,29 @@
#!/usr/bin/env python3
import argparse
import sys
def main():
parser = argparse.ArgumentParser("number")
parser.add_argument("lower", type=int, help="Lower bound")
parser.add_argument("upper", type=int, help="Upper bound (inclusive)")
args, rest_args = parser.parse_known_args()
lower = args.lower
upper = args.upper
step = 1
if rest_args:
step = int(rest_args[0])
if upper < lower:
lower, upper = upper, lower
for n in range(lower, upper + 1, step):
print(n)
# -----------------------------------------------------------------------------
if __name__ == "__main__":
main()
+7
View File
@@ -0,0 +1,7 @@
lundi
mardi
mercredi
jeudi
vendredi
samedi
dimanche
+12
View File
@@ -0,0 +1,12 @@
janvier
février
mars
avril
mai
juin
juillet
août
septembre
octobre
novembre
décembre
+1
View File
@@ -1,6 +1,7 @@
{
"language": "hi",
"name": "hi",
"locale": "hi_IN",
"speech_to_text": {
"system": "pocketsphinx",
"dictionary_casing": "lower"
+29
View File
@@ -0,0 +1,29 @@
#!/usr/bin/env python3
import argparse
import sys
def main():
parser = argparse.ArgumentParser("number")
parser.add_argument("lower", type=int, help="Lower bound")
parser.add_argument("upper", type=int, help="Upper bound (inclusive)")
args, rest_args = parser.parse_known_args()
lower = args.lower
upper = args.upper
step = 1
if rest_args:
step = int(rest_args[0])
if upper < lower:
lower, upper = upper, lower
for n in range(lower, upper + 1, step):
print(n)
# -----------------------------------------------------------------------------
if __name__ == "__main__":
main()
+7
View File
@@ -0,0 +1,7 @@
सोमवार
मंगलवार
बुधवार
गुरुवार
शुक्रवार
शनिवार
रविवार
+12
View File
@@ -0,0 +1,12 @@
जनवरी
फ़रवरी
मार्च
अप्रैल
मई
जून
जुलाई
अगस्त
सितंबर
अक्तूबर
नवंबर
दिसंबर
+1
View File
@@ -1,6 +1,7 @@
{
"name": "it",
"language": "it",
"locale": "it_IT",
"speech_to_text": {
"system": "pocketsphinx",
"dictionary_casing": "lower"
+29
View File
@@ -0,0 +1,29 @@
#!/usr/bin/env python3
import argparse
import sys
def main():
parser = argparse.ArgumentParser("number")
parser.add_argument("lower", type=int, help="Lower bound")
parser.add_argument("upper", type=int, help="Upper bound (inclusive)")
args, rest_args = parser.parse_known_args()
lower = args.lower
upper = args.upper
step = 1
if rest_args:
step = int(rest_args[0])
if upper < lower:
lower, upper = upper, lower
for n in range(lower, upper + 1, step):
print(n)
# -----------------------------------------------------------------------------
if __name__ == "__main__":
main()
+7
View File
@@ -0,0 +1,7 @@
lunedì
martedì
mercoledì
giovedì
venerdì
sabato
domenica
+12
View File
@@ -0,0 +1,12 @@
gennaio
febbraio
marzo
aprile
maggio
giugno
luglio
agosto
settembre
ottobre
novembre
dicembre
+2 -1
View File
@@ -1,6 +1,7 @@
{
"language": "nl",
"name": "nl",
"locale": "nl_NL",
"speech_to_text": {
"system": "pocketsphinx",
"dictionary_casing": "lower",
@@ -8,7 +9,7 @@
"base_dictionary": "kaldi/base_dictionary.txt",
"base_language_model": "kaldi/base_language_model.txt",
"compatible": true,
"custom_words": "kaldi/custom_words.txt",
"custom_words": "kaldi_custom_words.txt",
"dictionary": "kaldi/dictionary.txt",
"graph": "graph",
"language_model": "kaldi/language_model.txt",

Some files were not shown because too many files have changed in this diff Show More