Compare commits
312 Commits
0.3.10
...
go/v0.3.45
| Author | SHA1 | Date | |
|---|---|---|---|
| cf2560c9f8 | |||
| 800d3d7089 | |||
| b471207f7a | |||
| 25c59b52e3 | |||
| 4c72097478 | |||
| 1053cfa0f8 | |||
| 21a42cb6cd | |||
| 16c4a0d985 | |||
| 298253401a | |||
| 32aa980069 | |||
| 7b4d396eb1 | |||
| 36968fbb30 | |||
| 7474888801 | |||
| d46b7a43eb | |||
| 2376b32a8a | |||
| b8a88cc30c | |||
| be0b117711 | |||
| 449c8ea5af | |||
| 900da76652 | |||
| df0ee24084 | |||
| d3d8f53156 | |||
| 6eee303d7e | |||
| 053d71f5aa | |||
| 4fbbf5882c | |||
| 7012103b3b | |||
| 967024d20a | |||
| b966a8078b | |||
| f63b015284 | |||
| 0a9672d910 | |||
| 4ccccd0cd2 | |||
| 354fb672a3 | |||
| 73abf0740a | |||
| 81c82935ac | |||
| ac3ec56584 | |||
| 2cbe12d4d0 | |||
| 1475b0e986 | |||
| 8ceab0b9b1 | |||
| 1496b597d3 | |||
| 983519e629 | |||
| 58fa98ccd7 | |||
| a7bc5a22d4 | |||
| 23bbff0b56 | |||
| a47b58e2f4 | |||
| 8cf64ee93e | |||
| 630edeb3d6 | |||
| b1b216d4c8 | |||
| 55dd29b0ff | |||
| ea0568a38d | |||
| 298c86d0d4 | |||
| 859420809b | |||
| 0fe3a89768 | |||
| 5b892fbfc5 | |||
| fb4ed21a7f | |||
| 4209f3a9fe | |||
| ff2c80d5f1 | |||
| 3a07a08121 | |||
| 592da81a8c | |||
| def8c93711 | |||
| f73088da58 | |||
| 06a761ecbd | |||
| 97d737a30a | |||
| c7bffbf603 | |||
| 02c40ea612 | |||
| ce5ffb980a | |||
| d5ca98a982 | |||
| b0146782d6 | |||
| 5aaea8fc90 | |||
| 5d7752b657 | |||
| 9d94746479 | |||
| a87f2e1e07 | |||
| 7b7d814484 | |||
| 2daf67f31c | |||
| 62dd631379 | |||
| 2511192ecb | |||
| 22cb90de4a | |||
| 3951834df2 | |||
| 1ea0de106c | |||
| a9bf929ebd | |||
| 3336cd704b | |||
| a57a84f90e | |||
| ad546a8f1a | |||
| 1f447a8dfc | |||
| f574d896e9 | |||
| a561c2d6d4 | |||
| 79b8395be0 | |||
| d2c11a611f | |||
| b0903413b1 | |||
| 2135223490 | |||
| 6f86944a06 | |||
| 9861be2787 | |||
| c6fab363e6 | |||
| c32099705f | |||
| a1eac015dc | |||
| 64dfc65d51 | |||
| 70d5cbd0e0 | |||
| 5428d36d16 | |||
| ed4c15b7aa | |||
| 525b722c44 | |||
| 72bf210164 | |||
| 93e81c3bc8 | |||
| cb0f8e6411 | |||
| 848b2dc753 | |||
| 60f0396fe0 | |||
| 344e137a61 | |||
| 6977be7fb7 | |||
| a4721de8aa | |||
| 378ba122c8 | |||
| 287160622f | |||
| a5d788a2e9 | |||
| 7f651e1e45 | |||
| ad5bec114d | |||
| f71c62ad0f | |||
| 44f7dd2d8b | |||
| 15a9508a78 | |||
| bdea9a53e8 | |||
| 81f58667ff | |||
| 680a2e4c31 | |||
| 13993db542 | |||
| 59d595a4f0 | |||
| 4c562e15a4 | |||
| 5bcdf454ec | |||
| 4ccdda44ac | |||
| 5e46825474 | |||
| fcab5a9581 | |||
| e7f5e0ac23 | |||
| 9a3906831b | |||
| 195db43b9e | |||
| 332553ec1e | |||
| 646af3f652 | |||
| f24ac65fcb | |||
| 14312c93f9 | |||
| 4346d4155d | |||
| 7790ac5040 | |||
| 7b3ea0b59d | |||
| e2af710369 | |||
| 6b1b620f39 | |||
| 966524da16 | |||
| 83c999f298 | |||
| abff8a4f56 | |||
| 188575f3a2 | |||
| 9f4d8ca187 | |||
| 72cc8f3a5e | |||
| 915dcab597 | |||
| d82052b168 | |||
| dbb65bc71c | |||
| 2498bc595b | |||
| fee63d712f | |||
| 5965ea30e6 | |||
| 5c75495c03 | |||
| 98ae3c66cb | |||
| 942e15484a | |||
| 2349e66a97 | |||
| 5750e3224f | |||
| 558b4dd69e | |||
| 02ef49f67e | |||
| 7cdf8f1d03 | |||
| 7f894f784a | |||
| a770251151 | |||
| 7ccf743bb6 | |||
| 75bedfe06d | |||
| 4ea9885d44 | |||
| 0930def9ec | |||
| 6aa5af7640 | |||
| e74fe5edf7 | |||
| cbb5d0fcdf | |||
| d489bc40e4 | |||
| 499b2f183a | |||
| f57b926f66 | |||
| 733dca5aea | |||
| 1ab7e8ca87 | |||
| 53426f794c | |||
| f8189685e5 | |||
| e6bd200c85 | |||
| 4a4c33fd9d | |||
| dc44922b92 | |||
| 67896cfba7 | |||
| ee8663e6d9 | |||
| 652c05f052 | |||
| 193d129d9e | |||
| e995df4bc3 | |||
| f6a30c3626 | |||
| ee95420f8f | |||
| 2221cd0209 | |||
| 8368d831aa | |||
| 91a128b3ed | |||
| 83d8b63351 | |||
| 0881aa856f | |||
| 77abb02ada | |||
| 119c6cd0dc | |||
| cc649aa78e | |||
| 0b0c9fce0e | |||
| d845e2107f | |||
| 69992a1b39 | |||
| f351280f49 | |||
| c15065b044 | |||
| db41f16051 | |||
| e276de61be | |||
| 43c786ab18 | |||
| 1199f39d55 | |||
| 70aab86257 | |||
| e4b0af7a8e | |||
| a120d06f6b | |||
| e01ca37e45 | |||
| f5c3d898e2 | |||
| a5a3697b7c | |||
| 4be9874ba2 | |||
| 5647a2dc6a | |||
| a067a5b5f3 | |||
| 475b0ecfc1 | |||
| 2a9a605144 | |||
| eea7ca571b | |||
| 11c16610ee | |||
| 2f4124eb66 | |||
| db3e31d7ce | |||
| d917af21ab | |||
| 948c8c7cfd | |||
| dec8e1acf3 | |||
| 3c020526ce | |||
| b0da19b07f | |||
| 70bcd9b018 | |||
| d2604b609d | |||
| e960e0cacf | |||
| 77b114d434 | |||
| 2860092cef | |||
| c6119c4835 | |||
| 415d1927e0 | |||
| 3d1e21d242 | |||
| 7a75e1c3f4 | |||
| c3430e448a | |||
| ae49ea60d2 | |||
| 02dc0ce0c8 | |||
| 15697a18e8 | |||
| 481881e59d | |||
| 11a25b26a7 | |||
| bf87358f6c | |||
| 04a8242230 | |||
| ceb96c301c | |||
| 307df8fdc0 | |||
| c44875a2dd | |||
| 31f11990ca | |||
| b79b85856d | |||
| 6e861c6a19 | |||
| cf62296a51 | |||
| 387b132814 | |||
| de83de8624 | |||
| fe91b5a717 | |||
| a052506a5d | |||
| 6f2d6d0d69 | |||
| a8ae6025bd | |||
| 1b8332a609 | |||
| fe16ec7e57 | |||
| c5ce5e46bd | |||
| 99d66670bc | |||
| 072b42cac6 | |||
| acadb5b4c2 | |||
| 2e1de6c3af | |||
| 67de30908b | |||
| 9d398eff0e | |||
| 02b7312a00 | |||
| 08c35e84f3 | |||
| b92a5c1fc7 | |||
| 8997a587c5 | |||
| dc3d03d742 | |||
| 84df40715c | |||
| b639fb501a | |||
| 746ff47757 | |||
| 2d62db8118 | |||
| 7a2adcd9ba | |||
| 9ccf3ef0e8 | |||
| 0edab6d558 | |||
| 4b344f0cd8 | |||
| 17ddc4d5ba | |||
| 3e33860c47 | |||
| 0269a10833 | |||
| 7af3e9a334 | |||
| d666876be1 | |||
| 564fab7ec1 | |||
| 155c6c2a2a | |||
| d43cbe9344 | |||
| 57cc474c9f | |||
| 586603f8e1 | |||
| d57887d22a | |||
| 6183bcfc5f | |||
| 65f6113b4d | |||
| 8d88b89db1 | |||
| 62885e8963 | |||
| 9fc094a5da | |||
| f97383c17f | |||
| 4b892ec5e7 | |||
| 41035485db | |||
| 9696f4c917 | |||
| 55abf5f5ac | |||
| a1b2e41710 | |||
| dff4ab26e4 | |||
| 83b6e1cdf7 | |||
| 38dbaa15ea | |||
| 0e531b6061 | |||
| de94ef5537 | |||
| 6ef9d13877 | |||
| 1c7b94757d | |||
| 83486e0bef | |||
| 9787e8a53f | |||
| f59d6685ad | |||
| 8a986ef384 | |||
| 78f9f55e14 | |||
| c2e006f664 | |||
| 4b8e9737a5 | |||
| 722b09eaa4 | |||
| f5b4f5a1f2 | |||
| 4407d8da55 | |||
| 73b73527cd | |||
| 4df0e3a741 |
+39
-28
@@ -1,19 +1,25 @@
|
||||
# Built application files
|
||||
*.apk
|
||||
*.ap_
|
||||
# Object files
|
||||
*.o
|
||||
|
||||
# Java class files
|
||||
*.class
|
||||
# Temp files
|
||||
nohup.out
|
||||
|
||||
# Gradle files
|
||||
.gradle/
|
||||
build/
|
||||
gradlew
|
||||
gradlew.bat
|
||||
gradle
|
||||
|
||||
# Local configuration file (sdk path, etc)
|
||||
local.properties
|
||||
gradle.properties
|
||||
|
||||
# Android
|
||||
android/build
|
||||
android/lib/build
|
||||
android/model-en/build
|
||||
android/model-en/src/main/assets/model-en-us
|
||||
android/repo
|
||||
*.apk
|
||||
*.ap_
|
||||
|
||||
# Cmake
|
||||
.cxx
|
||||
@@ -24,12 +30,8 @@ wheelhouse
|
||||
__pycache__
|
||||
*.egg-info
|
||||
python/dist
|
||||
python/vosk/*.cc
|
||||
python/vosk/*.c
|
||||
python/vosk/*.h
|
||||
python/vosk/*.i
|
||||
python/vosk/vosk.py
|
||||
python/vosk/vosk_wrap.cpp
|
||||
python/build
|
||||
python/vosk/*.so
|
||||
python/test/db
|
||||
python/test/hyp
|
||||
python/test/model
|
||||
@@ -38,23 +40,32 @@ python/test/result.txt
|
||||
python/test/wav.scp
|
||||
|
||||
# Java
|
||||
*.so
|
||||
java/org
|
||||
java/*.cc
|
||||
java/model-spk/
|
||||
java/model/
|
||||
*.class
|
||||
java/lib/model
|
||||
java/demo/model
|
||||
java/lib/build
|
||||
java/demo/build
|
||||
|
||||
# CSharp
|
||||
csharp/gen
|
||||
csharp/*.exe
|
||||
csharp/*.c
|
||||
csharp/model/
|
||||
csharp/test.wav
|
||||
*.dll
|
||||
*.so
|
||||
*.dylib
|
||||
*.nupkg
|
||||
csharp/demo/model
|
||||
csharp/demo/test.wav
|
||||
csharp/demo/bin
|
||||
csharp/demo/obj
|
||||
|
||||
# Node
|
||||
nodejs/vosk_wrap.cc
|
||||
nodejs/example/model
|
||||
nodejs/example/test.wav
|
||||
nodejs/demo/model
|
||||
nodejs/demo/model-spk
|
||||
nodejs/demo/test.wav
|
||||
nodejs/node_modules
|
||||
nodejs/package-lock.json
|
||||
nodejs/build
|
||||
|
||||
# C
|
||||
c/test_vosk
|
||||
c/test_vosk_speaker
|
||||
c/oprofile_data
|
||||
c/model
|
||||
c/test.wav
|
||||
|
||||
+1
-2
@@ -7,11 +7,10 @@ matrix:
|
||||
services:
|
||||
- docker
|
||||
env: DOCKER_IMAGE=alphacep/kaldi-manylinux:latest
|
||||
PLAT=manylinux2010_x86_64
|
||||
|
||||
install:
|
||||
- docker pull $DOCKER_IMAGE
|
||||
|
||||
script:
|
||||
- docker run --rm -e PLAT=$PLAT -v `pwd`:/io $DOCKER_IMAGE $PRE_CMD /io/travis/build-wheels.sh
|
||||
- docker run --rm -v `pwd`:/io $DOCKER_IMAGE $PRE_CMD /io/travis/build-wheels.sh
|
||||
- ls wheelhouse/
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
cmake_minimum_required(VERSION 3.13)
|
||||
project(vosk-api CXX)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_EXTENSIONS OFF)
|
||||
|
||||
add_library(vosk
|
||||
src/language_model.cc
|
||||
src/model.cc
|
||||
src/recognizer.cc
|
||||
src/spk_model.cc
|
||||
src/vosk_api.cc
|
||||
)
|
||||
|
||||
find_package(kaldi REQUIRED)
|
||||
target_link_libraries(vosk PUBLIC kaldi-base kaldi-online2 kaldi-rnnlm fstngram)
|
||||
|
||||
include(GNUInstallDirs)
|
||||
install(TARGETS vosk DESTINATION ${CMAKE_INSTALL_LIBDIR})
|
||||
install(FILES src/vosk_api.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
|
||||
@@ -1,26 +1,27 @@
|
||||
Vosk is an open source speech recognition toolkit which supports 9
|
||||
languages - English, German, French, Spanish, Portuguese, Chinese,
|
||||
Russian, Turkish, Vietnamese. Vosk works offline with small (50 Mb), but
|
||||
accurate model, zero-latency response with streaming API, reconfigurable
|
||||
# Vosk Speech Recognition Toolkit
|
||||
|
||||
Vosk is an offline open source speech recognition toolkit. It enables
|
||||
speech recognition for 20+ languages and dialects - English, Indian
|
||||
English, German, French, Spanish, Portuguese, Chinese, Russian, Turkish,
|
||||
Vietnamese, Italian, Dutch, Catalan, Arabic, Greek, Farsi, Filipino,
|
||||
Ukrainian, Kazakh, Swedish, Japanese, Esperanto, Hindi, Czech, Polish.
|
||||
More to come.
|
||||
|
||||
Vosk models are small (50 Mb) but provide continuous large vocabulary
|
||||
transcription, zero-latency response with streaming API, reconfigurable
|
||||
vocabulary and speaker identification.
|
||||
|
||||
### Installation and usage
|
||||
Speech recognition bindings implemented for various programming languages
|
||||
like Python, Java, Node.JS, C#, C++, Rust, Go and others.
|
||||
|
||||
For Vosk installation instructions, examples and turorial and documentation visit https://alphacephei.com/vosk
|
||||
Vosk supplies speech recognition for chatbots, smart home appliances,
|
||||
virtual assistants. It can also create subtitles for movies,
|
||||
transcription for lectures and interviews.
|
||||
|
||||
### Build
|
||||
Vosk scales from small devices like Raspberry Pi or Android smartphone to
|
||||
big clusters.
|
||||
|
||||
[](https://travis-ci.com/alphacep/vosk-api)
|
||||
# Documentation
|
||||
|
||||
### Models for different languages
|
||||
|
||||
For information about models see [the documentation on available models](https://alphacephei.com/vosk/models.html).
|
||||
|
||||
### Contact Us
|
||||
|
||||
If you have any questions, feel free to:
|
||||
|
||||
* Post an issue here on github
|
||||
* Send us an e-mail at [contact@alphacephei.com](mailto:contact@alphacephei.com)
|
||||
* Join our group dedicated to speech recognition on Telegram [@speech_recognition](https://t.me/speech_recognition)
|
||||
* We have a Wechat group which is pretty big, so it is invitation-only. Mail us to join the group and provide some information about yourself.
|
||||
For installation instructions, examples and documentation visit [Vosk
|
||||
Website](https://alphacephei.com/vosk).
|
||||
|
||||
@@ -1,67 +0,0 @@
|
||||
# Vosk CMake File
|
||||
|
||||
cmake_minimum_required(VERSION 3.4.1)
|
||||
|
||||
if ("x${ANDROID_ABI}" STREQUAL "xarmeabi-v7a")
|
||||
set(OPENBLAS_ARCH "armv7")
|
||||
set(KALDI_SUFFIX "arm_32")
|
||||
elseif ("x${ANDROID_ABI}" STREQUAL "xarm64-v8a")
|
||||
set(OPENBLAS_ARCH "armv8")
|
||||
set(KALDI_SUFFIX "arm_64")
|
||||
elseif ("x${ANDROID_ABI}" STREQUAL "xx86")
|
||||
set(OPENBLAS_ARCH "atom")
|
||||
set(KALDI_SUFFIX "x86")
|
||||
else ("x${ANDROID_ABI}" STREQUAL "xarmeabi-v7a")
|
||||
set(OPENBLAS_ARCH "atom")
|
||||
set(KALDI_SUFFIX "x86_64")
|
||||
endif ("x${ANDROID_ABI}" STREQUAL "xarmeabi-v7a")
|
||||
|
||||
set(KALDI_ROOT "${PROJECT_SOURCE_DIR}/build/kaldi_${KALDI_SUFFIX}/kaldi")
|
||||
set(LIB_ROOT "${PROJECT_SOURCE_DIR}/build/kaldi_${KALDI_SUFFIX}/local")
|
||||
|
||||
set(API_SOURCES
|
||||
"${PROJECT_SOURCE_DIR}/../src/kaldi_recognizer.cc"
|
||||
"${PROJECT_SOURCE_DIR}/../src/kaldi_recognizer.h"
|
||||
"${PROJECT_SOURCE_DIR}/../src/model.cc"
|
||||
"${PROJECT_SOURCE_DIR}/../src/model.h"
|
||||
"${PROJECT_SOURCE_DIR}/../src/spk_model.cc"
|
||||
"${PROJECT_SOURCE_DIR}/../src/spk_model.h"
|
||||
"${PROJECT_SOURCE_DIR}/../src/vosk_api.cc"
|
||||
"${PROJECT_SOURCE_DIR}/../src/vosk_api.h"
|
||||
)
|
||||
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O3 -DFST_NO_DYNAMIC_LINKING")
|
||||
|
||||
add_library( kaldi_jni SHARED
|
||||
build/generated-src/cpp/vosk_wrap.cc
|
||||
${API_SOURCES}
|
||||
)
|
||||
|
||||
include_directories("${PROJECT_SOURCE_DIR}/../src" "build/kaldi_${KALDI_SUFFIX}/kaldi/src" "build/kaldi_${KALDI_SUFFIX}/local/include")
|
||||
|
||||
target_link_libraries( kaldi_jni
|
||||
${KALDI_ROOT}/src/online2/kaldi-online2.a
|
||||
${KALDI_ROOT}/src/decoder/kaldi-decoder.a
|
||||
${KALDI_ROOT}/src/ivector/kaldi-ivector.a
|
||||
${KALDI_ROOT}/src/gmm/kaldi-gmm.a
|
||||
${KALDI_ROOT}/src/nnet3/kaldi-nnet3.a
|
||||
${KALDI_ROOT}/src/tree/kaldi-tree.a
|
||||
${KALDI_ROOT}/src/feat/kaldi-feat.a
|
||||
${KALDI_ROOT}/src/lat/kaldi-lat.a
|
||||
${KALDI_ROOT}/src/lm/kaldi-lm.a
|
||||
${KALDI_ROOT}/src/hmm/kaldi-hmm.a
|
||||
${KALDI_ROOT}/src/transform/kaldi-transform.a
|
||||
${KALDI_ROOT}/src/cudamatrix/kaldi-cudamatrix.a
|
||||
${KALDI_ROOT}/src/matrix/kaldi-matrix.a
|
||||
${KALDI_ROOT}/src/fstext/kaldi-fstext.a
|
||||
${KALDI_ROOT}/src/util/kaldi-util.a
|
||||
${KALDI_ROOT}/src/base/kaldi-base.a
|
||||
${LIB_ROOT}/lib/libfst.a
|
||||
${LIB_ROOT}/lib/libfstngram.a
|
||||
${LIB_ROOT}/lib/libopenblas_${OPENBLAS_ARCH}-r0.3.7.a
|
||||
${LIB_ROOT}/lib/libclapack.a
|
||||
${LIB_ROOT}/lib/liblapack.a
|
||||
${LIB_ROOT}/lib/libblas.a
|
||||
${LIB_ROOT}/lib/libf2c.a
|
||||
log
|
||||
)
|
||||
+2
-21
@@ -1,22 +1,3 @@
|
||||
This is still work in progress, more to come
|
||||
Vosk library for Android
|
||||
|
||||
## TODO
|
||||
|
||||
* Optimize graph construction, current one is below accuracy
|
||||
|
||||
* Load model from the AAR (mmap them in tflite style)
|
||||
|
||||
* Add decoding speed measurement
|
||||
|
||||
* Add wakeup word
|
||||
|
||||
* Add speakerid
|
||||
|
||||
* Integrate proper hardware optimized neural network library. Candidates are:
|
||||
|
||||
* https://github.com/XiaoMi/mace
|
||||
* https://github.com/Tencent/ncnn
|
||||
* https://developer.android.com/ndk/guides/neuralnetworks/ (since API level 27)
|
||||
* https://github.com/google/XNNPACK
|
||||
|
||||
* Quantization for the models
|
||||
See for details https://alphacephei.com/vosk/android
|
||||
|
||||
@@ -1,141 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Copyright 2019 Alpha Cephei 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
|
||||
#
|
||||
# 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.
|
||||
|
||||
if [ "x$ANDROID_SDK_HOME" == "x" ]; then
|
||||
echo "ANDROID_SDK_HOME environment variable is undefined, define it with local.properties or with export"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -d "$ANDROID_SDK_HOME" ]; then
|
||||
echo "ANDROID_SDK_HOME ($ANDROID_SDK_HOME) is missing. Make sure you have sdk installed"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -d "$ANDROID_SDK_HOME/ndk-bundle" ]; then
|
||||
echo "$ANDROID_SDK_HOME/ndk-bundle is missing. Make sure you have ndk installed within sdk"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
set -x
|
||||
|
||||
OS_NAME=`echo $(uname -s) | tr '[:upper:]' '[:lower:]'`
|
||||
ANDROID_NDK_HOME=$ANDROID_SDK_HOME/ndk-bundle
|
||||
ANDROID_TOOLCHAIN_PATH=$ANDROID_NDK_HOME/toolchains/llvm/prebuilt/${OS_NAME}-x86_64
|
||||
WORKDIR_X86=`pwd`/build/kaldi_x86
|
||||
WORKDIR_X86_64=`pwd`/build/kaldi_x86_64
|
||||
WORKDIR_ARM32=`pwd`/build/kaldi_arm_32
|
||||
WORKDIR_ARM64=`pwd`/build/kaldi_arm_64
|
||||
PATH=$PATH:$ANDROID_NDK_HOME/toolchains/llvm/prebuilt/${OS_NAME}-x86_64/bin
|
||||
OPENFST_VERSION=1.6.7
|
||||
|
||||
mkdir -p $WORKDIR_ARM64/local/lib $WORKDIR_ARM32/local/lib $WORKDIR_X86_64/local/lib $WORKDIR_X86/local/lib
|
||||
|
||||
# Build standalone CLAPACK since gfortran is missing
|
||||
cd build
|
||||
git clone https://github.com/simonlynen/android_libs
|
||||
cd android_libs/lapack
|
||||
sed -i.bak -e 's/APP_STL := gnustl_static/APP_STL := c++_static/g' jni/Application.mk && \
|
||||
sed -i.bak -e 's/android-10/android-21/g' project.properties && \
|
||||
sed -i.bak -e 's/APP_ABI := armeabi armeabi-v7a/APP_ABI := armeabi-v7a arm64-v8a x86_64 x86/g' jni/Application.mk && \
|
||||
sed -i.bak -e 's/LOCAL_MODULE:= testlapack/#LOCAL_MODULE:= testlapack/g' jni/Android.mk && \
|
||||
sed -i.bak -e 's/LOCAL_SRC_FILES:= testclapack.cpp/#LOCAL_SRC_FILES:= testclapack.cpp/g' jni/Android.mk && \
|
||||
sed -i.bak -e 's/LOCAL_STATIC_LIBRARIES := lapack/#LOCAL_STATIC_LIBRARIES := lapack/g' jni/Android.mk && \
|
||||
sed -i.bak -e 's/include $(BUILD_SHARED_LIBRARY)/#include $(BUILD_SHARED_LIBRARY)/g' jni/Android.mk && \
|
||||
${ANDROID_NDK_HOME}/ndk-build && \
|
||||
cp obj/local/armeabi-v7a/*.a ${WORKDIR_ARM32}/local/lib && \
|
||||
cp obj/local/arm64-v8a/*.a ${WORKDIR_ARM64}/local/lib
|
||||
cp obj/local/x86_64/*.a ${WORKDIR_X86_64}/local/lib
|
||||
cp obj/local/x86/*.a ${WORKDIR_X86}/local/lib
|
||||
|
||||
# Architecture-specific part
|
||||
|
||||
for arch in arm32 arm64 x86_64 x86; do
|
||||
#for arch in x86_64; do
|
||||
|
||||
case $arch in
|
||||
arm32)
|
||||
BLAS_ARCH=ARMV7
|
||||
WORKDIR=$WORKDIR_ARM32
|
||||
HOST=arm-linux-androideabi
|
||||
AR=arm-linux-androideabi-ar
|
||||
CC=armv7a-linux-androideabi21-clang
|
||||
CXX=armv7a-linux-androideabi21-clang++
|
||||
ARCHFLAGS="-mfloat-abi=softfp -mfpu=neon"
|
||||
;;
|
||||
arm64)
|
||||
BLAS_ARCH=ARMV8
|
||||
WORKDIR=$WORKDIR_ARM64
|
||||
HOST=aarch64-linux-android
|
||||
AR=aarch64-linux-android-ar
|
||||
CC=aarch64-linux-android21-clang
|
||||
CXX=aarch64-linux-android21-clang++
|
||||
ARCHFLAGS=""
|
||||
;;
|
||||
x86_64)
|
||||
BLAS_ARCH=ATOM
|
||||
WORKDIR=$WORKDIR_X86_64
|
||||
HOST=x86_64-linux-android
|
||||
AR=x86_64-linux-android-ar
|
||||
CC=x86_64-linux-android21-clang
|
||||
CXX=x86_64-linux-android21-clang++
|
||||
ARCHFLAGS=""
|
||||
;;
|
||||
x86)
|
||||
BLAS_ARCH=ATOM
|
||||
WORKDIR=$WORKDIR_X86
|
||||
HOST=i686-linux-android
|
||||
AR=i686-linux-android-ar
|
||||
CC=i686-linux-android21-clang
|
||||
CXX=i686-linux-android21-clang++
|
||||
ARCHFLAGS=""
|
||||
;;
|
||||
esac
|
||||
|
||||
# openblas first
|
||||
cd $WORKDIR
|
||||
git clone -b v0.3.7 --single-branch https://github.com/xianyi/OpenBLAS
|
||||
make -C OpenBLAS TARGET=$BLAS_ARCH ONLY_CBLAS=1 AR=$AR CC=$CC HOSTCC=gcc ARM_SOFTFP_ABI=1 USE_THREAD=0 NUM_THREADS=1 -j4
|
||||
make -C OpenBLAS install PREFIX=$WORKDIR/local
|
||||
|
||||
# tools directory --> we'll only compile OpenFST
|
||||
cd $WORKDIR
|
||||
git clone https://github.com/alphacep/openfst
|
||||
cd openfst
|
||||
autoreconf -i
|
||||
CXX=$CXX CXXFLAGS="$ARCHFLAGS -O3 -DFST_NO_DYNAMIC_LINKING" ./configure --prefix=${WORKDIR}/local \
|
||||
--enable-shared --enable-static --with-pic --disable-bin \
|
||||
--enable-lookahead-fsts --enable-ngram-fsts --host=$HOST --build=x86-linux-gnu
|
||||
make -j 8
|
||||
make install
|
||||
|
||||
# Kaldi itself
|
||||
cd $WORKDIR
|
||||
git clone -b android-mix --single-branch https://github.com/alphacep/kaldi
|
||||
cd $WORKDIR/kaldi/src
|
||||
if [ "`uname`" == "Darwin" ]; then
|
||||
sed -i.bak -e 's/libfst.dylib/libfst.a/' configure
|
||||
fi
|
||||
|
||||
CXX=$CXX CXXFLAGS="$ARCHFLAGS -O3 -DFST_NO_DYNAMIC_LINKING" ./configure --use-cuda=no \
|
||||
--mathlib=OPENBLAS --shared \
|
||||
--android-incdir=${ANDROID_TOOLCHAIN_PATH}/sysroot/usr/include \
|
||||
--host=$HOST --openblas-root=${WORKDIR}/local \
|
||||
--fst-root=${WORKDIR}/local --fst-version=${OPENFST_VERSION}
|
||||
|
||||
make -j 8 depend
|
||||
make -j 8 online2 lm
|
||||
|
||||
done
|
||||
+48
-51
@@ -1,68 +1,65 @@
|
||||
buildscript {
|
||||
repositories {
|
||||
google()
|
||||
jcenter()
|
||||
mavenCentral()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:3.5.3'
|
||||
classpath 'com.android.tools.build:gradle:4.2.0'
|
||||
classpath 'com.vanniktech:gradle-maven-publish-plugin:0.18.0'
|
||||
}
|
||||
}
|
||||
|
||||
allprojects {
|
||||
repositories {
|
||||
google()
|
||||
jcenter()
|
||||
}
|
||||
version = '0.3.45'
|
||||
}
|
||||
|
||||
apply plugin: 'com.android.library'
|
||||
subprojects {
|
||||
|
||||
android {
|
||||
compileSdkVersion 29
|
||||
defaultConfig {
|
||||
minSdkVersion 21
|
||||
targetSdkVersion 29
|
||||
versionCode 5
|
||||
versionName "5.2"
|
||||
setProperty("archivesBaseName", "kaldi-android-$versionName")
|
||||
externalNativeBuild {
|
||||
cmake {
|
||||
arguments "-DCMAKE_VERBOSE_MAKEFILE=ON", "-DANDROID_ARM_NEON=TRUE", "-DCMAKE_CXX_FLAGS_RELEASE=-O3"
|
||||
apply plugin: 'com.android.library'
|
||||
apply plugin: 'maven-publish'
|
||||
apply plugin: 'com.vanniktech.maven.publish'
|
||||
|
||||
plugins.withId('com.vanniktech.maven.publish') {
|
||||
mavenPublish {
|
||||
group = 'com.alphacephei'
|
||||
version = version
|
||||
sonatypeHost = 's01'
|
||||
androidVariantToPublish = 'release'
|
||||
}
|
||||
}
|
||||
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
publishing {
|
||||
publications {
|
||||
aar(MavenPublication) {
|
||||
groupId 'com.alphacephei'
|
||||
version version
|
||||
pom {
|
||||
url = 'http://www.alphacephei.com.com/vosk/'
|
||||
licenses {
|
||||
license {
|
||||
name = 'The Apache License, Version 2.0'
|
||||
url = 'http://www.apache.org/licenses/LICENSE-2.0.txt'
|
||||
}
|
||||
}
|
||||
developers {
|
||||
developer {
|
||||
id = 'com.alphacephei'
|
||||
name = 'Alpha Cephei Inc'
|
||||
email = 'contact@alphacephei.com'
|
||||
}
|
||||
}
|
||||
scm {
|
||||
connection = 'scm:git:git://github.com/alphacep/vosk-api.git'
|
||||
url = 'https://github.com/alphacep/vosk-api/'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ndk {
|
||||
abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86_64', 'x86'
|
||||
}
|
||||
}
|
||||
sourceSets {
|
||||
main {
|
||||
java.srcDirs = ['src/main/java', 'build/generated-src']
|
||||
}
|
||||
}
|
||||
externalNativeBuild {
|
||||
cmake {
|
||||
path "CMakeLists.txt"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
task swig {
|
||||
doLast {
|
||||
mkdir 'build/generated-src/java'
|
||||
mkdir 'build/generated-src/cpp'
|
||||
exec {
|
||||
commandLine 'swig',
|
||||
"-c++",
|
||||
"-java", "-package", "org.kaldi",
|
||||
"-outdir", "build/generated-src/java", "-o", "build/generated-src/cpp/vosk_wrap.cc",
|
||||
"../src/vosk.i"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
task kaldi(type: Exec) {
|
||||
commandLine './build-kaldi.sh'
|
||||
environment ANDROID_SDK_HOME: android.getSdkDirectory()
|
||||
}
|
||||
|
||||
preBuild.dependsOn kaldi, swig
|
||||
|
||||
Executable
+135
@@ -0,0 +1,135 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Copyright 2019-2021 Alpha Cephei 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
|
||||
#
|
||||
# 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.
|
||||
|
||||
if [ "x$ANDROID_NDK_HOME" == "x" ]; then
|
||||
echo "ANDROID_NDK_HOME environment variable is undefined, define it with local.properties or with export"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -d "$ANDROID_NDK_HOME" ]; then
|
||||
echo "ANDROID_NDK_HOME ($ANDROID_NDK_HOME) is missing. Make sure you have ndk installed"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
set -x
|
||||
|
||||
OS_NAME=`echo $(uname -s) | tr '[:upper:]' '[:lower:]'`
|
||||
ANDROID_TOOLCHAIN_PATH=$ANDROID_NDK_HOME/toolchains/llvm/prebuilt/${OS_NAME}-x86_64
|
||||
WORKDIR_BASE=`pwd`/build
|
||||
PATH=$ANDROID_NDK_HOME/toolchains/llvm/prebuilt/${OS_NAME}-x86_64/bin:$PATH
|
||||
OPENFST_VERSION=1.8.0
|
||||
|
||||
for arch in armeabi-v7a arm64-v8a x86_64 x86; do
|
||||
|
||||
WORKDIR=${WORKDIR_BASE}/kaldi_${arch}
|
||||
|
||||
case $arch in
|
||||
armeabi-v7a)
|
||||
BLAS_ARCH=ARMV7
|
||||
HOST=arm-linux-androideabi
|
||||
AR=llvm-ar
|
||||
RANLIB=llvm-ranlib
|
||||
CC=armv7a-linux-androideabi21-clang
|
||||
CXX=armv7a-linux-androideabi21-clang++
|
||||
ARCHFLAGS="-mfloat-abi=softfp -mfpu=neon"
|
||||
;;
|
||||
arm64-v8a)
|
||||
BLAS_ARCH=ARMV8
|
||||
HOST=aarch64-linux-android
|
||||
AR=llvm-ar
|
||||
RANLIB=llvm-ranlib
|
||||
CC=aarch64-linux-android21-clang
|
||||
CXX=aarch64-linux-android21-clang++
|
||||
ARCHFLAGS=""
|
||||
;;
|
||||
x86_64)
|
||||
BLAS_ARCH=ATOM
|
||||
HOST=x86_64-linux-android
|
||||
AR=llvm-ar
|
||||
RANLIB=llvm-ranlib
|
||||
CC=x86_64-linux-android21-clang
|
||||
CXX=x86_64-linux-android21-clang++
|
||||
ARCHFLAGS=""
|
||||
;;
|
||||
x86)
|
||||
BLAS_ARCH=ATOM
|
||||
HOST=i686-linux-android
|
||||
AR=llvm-ar
|
||||
RANLIB=llvm-ranlib
|
||||
CC=i686-linux-android21-clang
|
||||
CXX=i686-linux-android21-clang++
|
||||
ARCHFLAGS=""
|
||||
;;
|
||||
esac
|
||||
|
||||
mkdir -p $WORKDIR/local/lib
|
||||
|
||||
# openblas first
|
||||
cd $WORKDIR
|
||||
git clone -b v0.3.13 --single-branch https://github.com/xianyi/OpenBLAS
|
||||
make -C OpenBLAS TARGET=$BLAS_ARCH ONLY_CBLAS=1 AR=$AR CC=$CC HOSTCC=gcc ARM_SOFTFP_ABI=1 USE_THREAD=0 NUM_THREADS=1 -j4
|
||||
make -C OpenBLAS install PREFIX=$WORKDIR/local
|
||||
|
||||
# CLAPACK
|
||||
cd $WORKDIR
|
||||
git clone -b v3.2.1 --single-branch https://github.com/alphacep/clapack
|
||||
mkdir -p clapack/BUILD && cd clapack/BUILD
|
||||
cmake -DCMAKE_C_FLAGS=$ARCHFLAGS -DCMAKE_C_COMPILER_TARGET=$HOST \
|
||||
-DCMAKE_C_COMPILER=$CC -DCMAKE_SYSTEM_NAME=Generic -DCMAKE_AR=$ANDROID_NDK_HOME/toolchains/llvm/prebuilt/${OS_NAME}-x86_64/bin/$AR \
|
||||
-DCMAKE_TRY_COMPILE_TARGET_TYPE=STATIC_LIBRARY \
|
||||
-DCMAKE_CROSSCOMPILING=True ..
|
||||
make -j 8 -C F2CLIBS/libf2c
|
||||
make -j 8 -C BLAS/SRC
|
||||
make -j 8 -C SRC
|
||||
find . -name "*.a" | xargs cp -t $WORKDIR/local/lib
|
||||
|
||||
# tools directory --> we'll only compile OpenFST
|
||||
cd $WORKDIR
|
||||
git clone https://github.com/alphacep/openfst
|
||||
cd openfst
|
||||
autoreconf -i
|
||||
CXX=$CXX CXXFLAGS="$ARCHFLAGS -O3 -DFST_NO_DYNAMIC_LINKING" ./configure --prefix=${WORKDIR}/local \
|
||||
--enable-shared --enable-static --with-pic --disable-bin \
|
||||
--enable-lookahead-fsts --enable-ngram-fsts --host=$HOST --build=x86-linux-gnu
|
||||
make -j 8
|
||||
make install
|
||||
|
||||
# Kaldi itself
|
||||
cd $WORKDIR
|
||||
git clone -b vosk-android --single-branch https://github.com/alphacep/kaldi
|
||||
cd $WORKDIR/kaldi/src
|
||||
CXX=$CXX AR=$AR RANLIB=$RANLIB CXXFLAGS="$ARCHFLAGS -O3 -DFST_NO_DYNAMIC_LINKING" ./configure --use-cuda=no \
|
||||
--mathlib=OPENBLAS_CLAPACK --shared \
|
||||
--android-incdir=${ANDROID_TOOLCHAIN_PATH}/sysroot/usr/include \
|
||||
--host=$HOST --openblas-root=${WORKDIR}/local \
|
||||
--fst-root=${WORKDIR}/local --fst-version=${OPENFST_VERSION}
|
||||
make -j 8 depend
|
||||
cd $WORKDIR/kaldi/src
|
||||
make -j 8 online2 lm rnnlm
|
||||
|
||||
# Vosk-api
|
||||
cd $WORKDIR
|
||||
mkdir -p $WORKDIR/vosk
|
||||
make -j 8 -C ${WORKDIR_BASE}/../../../src \
|
||||
OUTDIR=$WORKDIR/vosk \
|
||||
KALDI_ROOT=${WORKDIR}/kaldi \
|
||||
OPENFST_ROOT=${WORKDIR}/local \
|
||||
OPENBLAS_ROOT=${WORKDIR}/local \
|
||||
CXX=$CXX \
|
||||
EXTRA_LDFLAGS="-llog -static-libstdc++ -Wl,-soname,libvosk.so"
|
||||
cp $WORKDIR/vosk/libvosk.so $WORKDIR/../../src/main/jniLibs/$arch/libvosk.so
|
||||
|
||||
done
|
||||
@@ -0,0 +1,55 @@
|
||||
def archiveName = "vosk-android"
|
||||
def pomName = "Vosk Android"
|
||||
def pomDescription = "Vosk speech recognition library for Android"
|
||||
|
||||
android {
|
||||
compileSdkVersion 29
|
||||
defaultConfig {
|
||||
minSdkVersion 21
|
||||
targetSdkVersion 29
|
||||
versionCode 6
|
||||
versionName = version
|
||||
archivesBaseName = archiveName
|
||||
ndkVersion = "22.1.7171670"
|
||||
}
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
}
|
||||
}
|
||||
|
||||
task buildVosk(type: Exec) {
|
||||
commandLine './build-vosk.sh'
|
||||
environment ANDROID_NDK_HOME: android.getNdkDirectory()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
api 'net.java.dev.jna:jna:4.4.0@aar'
|
||||
}
|
||||
|
||||
//preBuild.dependsOn buildVosk
|
||||
|
||||
publishing {
|
||||
publications {
|
||||
aar(MavenPublication) {
|
||||
artifactId = archiveName
|
||||
artifact("$buildDir/outputs/aar/$archiveName-release.aar")
|
||||
pom {
|
||||
name = pomName
|
||||
description = pomDescription
|
||||
}
|
||||
//generate pom nodes for dependencies
|
||||
pom.withXml {
|
||||
def dependenciesNode = asNode().appendNode('dependencies')
|
||||
configurations.implementation.allDependencies.each { dependency ->
|
||||
if (dependency.name != 'unspecified') {
|
||||
def dependencyNode = dependenciesNode.appendNode('dependency')
|
||||
dependencyNode.appendNode('groupId', dependency.group)
|
||||
dependencyNode.appendNode('artifactId', dependency.name)
|
||||
dependencyNode.appendNode('version', dependency.version)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,3 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="org.kaldi">
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="org.vosk">
|
||||
</manifest>
|
||||
@@ -0,0 +1,62 @@
|
||||
package org.vosk;
|
||||
|
||||
import com.sun.jna.Native;
|
||||
import com.sun.jna.Library;
|
||||
import com.sun.jna.Platform;
|
||||
import com.sun.jna.Pointer;
|
||||
import java.io.File;
|
||||
import java.io.InputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
|
||||
public class LibVosk {
|
||||
|
||||
static {
|
||||
Native.register(LibVosk.class, "vosk");
|
||||
}
|
||||
|
||||
public static native void vosk_set_log_level(int level);
|
||||
|
||||
public static native Pointer vosk_model_new(String path);
|
||||
|
||||
public static native void vosk_model_free(Pointer model);
|
||||
|
||||
public static native Pointer vosk_spk_model_new(String path);
|
||||
|
||||
public static native void vosk_spk_model_free(Pointer model);
|
||||
|
||||
public static native Pointer vosk_recognizer_new(Model model, float sample_rate);
|
||||
|
||||
public static native Pointer vosk_recognizer_new_spk(Pointer model, float sample_rate, Pointer spk_model);
|
||||
|
||||
public static native Pointer vosk_recognizer_new_grm(Pointer model, float sample_rate, String grammar);
|
||||
|
||||
public static native void vosk_recognizer_set_max_alternatives(Pointer recognizer, int max_alternatives);
|
||||
|
||||
public static native void vosk_recognizer_set_words(Pointer recognizer, boolean words);
|
||||
|
||||
public static native void vosk_recognizer_set_partial_words(Pointer recognizer, boolean partial_words);
|
||||
|
||||
public static native void vosk_recognizer_set_spk_model(Pointer recognizer, Pointer spk_model);
|
||||
|
||||
public static native boolean vosk_recognizer_accept_waveform(Pointer recognizer, byte[] data, int len);
|
||||
|
||||
public static native boolean vosk_recognizer_accept_waveform_s(Pointer recognizer, short[] data, int len);
|
||||
|
||||
public static native boolean vosk_recognizer_accept_waveform_f(Pointer recognizer, float[] data, int len);
|
||||
|
||||
public static native String vosk_recognizer_result(Pointer recognizer);
|
||||
|
||||
public static native String vosk_recognizer_final_result(Pointer recognizer);
|
||||
|
||||
public static native String vosk_recognizer_partial_result(Pointer recognizer);
|
||||
|
||||
public static native void vosk_recognizer_reset(Pointer recognizer);
|
||||
|
||||
public static native void vosk_recognizer_free(Pointer recognizer);
|
||||
|
||||
public static void setLogLevel(LogLevel loglevel) {
|
||||
vosk_set_log_level(loglevel.getValue());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package org.vosk;
|
||||
|
||||
public enum LogLevel {
|
||||
WARNINGS(-1), // Print warning and errors
|
||||
INFO(0), // Print info, along with warning and error messages, but no debug
|
||||
DEBUG(1); // Print debug info
|
||||
|
||||
private final int value;
|
||||
|
||||
LogLevel(int value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public int getValue() {
|
||||
return this.value;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package org.vosk;
|
||||
|
||||
import com.sun.jna.PointerType;
|
||||
|
||||
public class Model extends PointerType implements AutoCloseable {
|
||||
public Model() {
|
||||
}
|
||||
|
||||
public Model(String path) {
|
||||
super(LibVosk.vosk_model_new(path));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
LibVosk.vosk_model_free(this.getPointer());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
package org.vosk;
|
||||
|
||||
import com.sun.jna.PointerType;
|
||||
|
||||
public class Recognizer extends PointerType implements AutoCloseable {
|
||||
public Recognizer(Model model, float sampleRate) {
|
||||
super(LibVosk.vosk_recognizer_new(model, sampleRate));
|
||||
}
|
||||
|
||||
public Recognizer(Model model, float sampleRate, SpeakerModel spkModel) {
|
||||
super(LibVosk.vosk_recognizer_new_spk(model.getPointer(), sampleRate, spkModel.getPointer()));
|
||||
}
|
||||
|
||||
public Recognizer(Model model, float sampleRate, String grammar) {
|
||||
super(LibVosk.vosk_recognizer_new_grm(model.getPointer(), sampleRate, grammar));
|
||||
}
|
||||
|
||||
public void setMaxAlternatives(int maxAlternatives) {
|
||||
LibVosk.vosk_recognizer_set_max_alternatives(this.getPointer(), maxAlternatives);
|
||||
}
|
||||
|
||||
public void setWords(boolean words) {
|
||||
LibVosk.vosk_recognizer_set_words(this.getPointer(), words);
|
||||
}
|
||||
|
||||
public void setPartialWords(boolean partial_words) {
|
||||
LibVosk.vosk_recognizer_set_partial_words(this.getPointer(), partial_words);
|
||||
}
|
||||
|
||||
public void setSpeakerModel(SpeakerModel spkModel) {
|
||||
LibVosk.vosk_recognizer_set_spk_model(this.getPointer(), spkModel.getPointer());
|
||||
}
|
||||
|
||||
public boolean acceptWaveForm(byte[] data, int len) {
|
||||
return LibVosk.vosk_recognizer_accept_waveform(this.getPointer(), data, len);
|
||||
}
|
||||
|
||||
public boolean acceptWaveForm(short[] data, int len) {
|
||||
return LibVosk.vosk_recognizer_accept_waveform_s(this.getPointer(), data, len);
|
||||
}
|
||||
|
||||
public boolean acceptWaveForm(float[] data, int len) {
|
||||
return LibVosk.vosk_recognizer_accept_waveform_f(this.getPointer(), data, len);
|
||||
}
|
||||
|
||||
public String getResult() {
|
||||
return LibVosk.vosk_recognizer_result(this.getPointer());
|
||||
}
|
||||
|
||||
public String getPartialResult() {
|
||||
return LibVosk.vosk_recognizer_partial_result(this.getPointer());
|
||||
}
|
||||
|
||||
public String getFinalResult() {
|
||||
return LibVosk.vosk_recognizer_final_result(this.getPointer());
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
LibVosk.vosk_recognizer_reset(this.getPointer());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
LibVosk.vosk_recognizer_free(this.getPointer());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package org.vosk;
|
||||
|
||||
import com.sun.jna.PointerType;
|
||||
|
||||
public class SpeakerModel extends PointerType implements AutoCloseable {
|
||||
public SpeakerModel() {
|
||||
}
|
||||
|
||||
public SpeakerModel(String path) {
|
||||
super(LibVosk.vosk_spk_model_new(path));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
LibVosk.vosk_spk_model_free(this.getPointer());
|
||||
}
|
||||
}
|
||||
+14
-7
@@ -12,28 +12,35 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package org.kaldi;
|
||||
package org.vosk.android;
|
||||
|
||||
/** Interface to receive recognition results */
|
||||
/**
|
||||
* Interface to receive recognition results
|
||||
*/
|
||||
public interface RecognitionListener {
|
||||
|
||||
/**
|
||||
* Called when partial recognition result is available.
|
||||
*/
|
||||
public void onPartialResult(String hypothesis);
|
||||
void onPartialResult(String hypothesis);
|
||||
|
||||
/**
|
||||
* Called after the recognition is ended.
|
||||
* Called after silence occured.
|
||||
*/
|
||||
public void onResult(String hypothesis);
|
||||
void onResult(String hypothesis);
|
||||
|
||||
/**
|
||||
* Called after stream end.
|
||||
*/
|
||||
void onFinalResult(String hypothesis);
|
||||
|
||||
/**
|
||||
* Called when an error occurs.
|
||||
*/
|
||||
public void onError(Exception exception);
|
||||
void onError(Exception exception);
|
||||
|
||||
/**
|
||||
* Called after timeout expired
|
||||
*/
|
||||
public void onTimeout();
|
||||
void onTimeout();
|
||||
}
|
||||
@@ -0,0 +1,257 @@
|
||||
// Copyright 2019 Alpha Cephei 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
|
||||
//
|
||||
// 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.
|
||||
|
||||
package org.vosk.android;
|
||||
|
||||
import android.media.AudioFormat;
|
||||
import android.media.AudioRecord;
|
||||
import android.media.MediaRecorder.AudioSource;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
|
||||
import org.vosk.Recognizer;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Service that records audio in a thread, passes it to a recognizer and emits
|
||||
* recognition results. Recognition events are passed to a client using
|
||||
* {@link RecognitionListener}
|
||||
*/
|
||||
public class SpeechService {
|
||||
|
||||
private final Recognizer recognizer;
|
||||
|
||||
private final int sampleRate;
|
||||
private final static float BUFFER_SIZE_SECONDS = 0.2f;
|
||||
private final int bufferSize;
|
||||
private final AudioRecord recorder;
|
||||
|
||||
private RecognizerThread recognizerThread;
|
||||
|
||||
private final Handler mainHandler = new Handler(Looper.getMainLooper());
|
||||
|
||||
/**
|
||||
* Creates speech service. Service holds the AudioRecord object, so you
|
||||
* need to call {@link #shutdown()} in order to properly finalize it.
|
||||
*
|
||||
* @throws IOException thrown if audio recorder can not be created for some reason.
|
||||
*/
|
||||
public SpeechService(Recognizer recognizer, float sampleRate) throws IOException {
|
||||
this.recognizer = recognizer;
|
||||
this.sampleRate = (int) sampleRate;
|
||||
|
||||
bufferSize = Math.round(this.sampleRate * BUFFER_SIZE_SECONDS);
|
||||
recorder = new AudioRecord(
|
||||
AudioSource.VOICE_RECOGNITION, this.sampleRate,
|
||||
AudioFormat.CHANNEL_IN_MONO,
|
||||
AudioFormat.ENCODING_PCM_16BIT, bufferSize * 2);
|
||||
|
||||
if (recorder.getState() == AudioRecord.STATE_UNINITIALIZED) {
|
||||
recorder.release();
|
||||
throw new IOException(
|
||||
"Failed to initialize recorder. Microphone might be already in use.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Starts recognition. Does nothing if recognition is active.
|
||||
*
|
||||
* @return true if recognition was actually started
|
||||
*/
|
||||
public boolean startListening(RecognitionListener listener) {
|
||||
if (null != recognizerThread)
|
||||
return false;
|
||||
|
||||
recognizerThread = new RecognizerThread(listener);
|
||||
recognizerThread.start();
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts recognition. After specified timeout listening stops and the
|
||||
* endOfSpeech signals about that. Does nothing if recognition is active.
|
||||
* <p>
|
||||
* timeout - timeout in milliseconds to listen.
|
||||
*
|
||||
* @return true if recognition was actually started
|
||||
*/
|
||||
public boolean startListening(RecognitionListener listener, int timeout) {
|
||||
if (null != recognizerThread)
|
||||
return false;
|
||||
|
||||
recognizerThread = new RecognizerThread(listener, timeout);
|
||||
recognizerThread.start();
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean stopRecognizerThread() {
|
||||
if (null == recognizerThread)
|
||||
return false;
|
||||
|
||||
try {
|
||||
recognizerThread.interrupt();
|
||||
recognizerThread.join();
|
||||
} catch (InterruptedException e) {
|
||||
// Restore the interrupted status.
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
|
||||
recognizerThread = null;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops recognition. Listener should receive final result if there is
|
||||
* any. Does nothing if recognition is not active.
|
||||
*
|
||||
* @return true if recognition was actually stopped
|
||||
*/
|
||||
public boolean stop() {
|
||||
return stopRecognizerThread();
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancel recognition. Do not post any new events, simply cancel processing.
|
||||
* Does nothing if recognition is not active.
|
||||
*
|
||||
* @return true if recognition was actually stopped
|
||||
*/
|
||||
public boolean cancel() {
|
||||
if (recognizerThread != null) {
|
||||
recognizerThread.setPause(true);
|
||||
}
|
||||
return stopRecognizerThread();
|
||||
}
|
||||
|
||||
/**
|
||||
* Shutdown the recognizer and release the recorder
|
||||
*/
|
||||
public void shutdown() {
|
||||
recorder.release();
|
||||
}
|
||||
|
||||
public void setPause(boolean paused) {
|
||||
if (recognizerThread != null) {
|
||||
recognizerThread.setPause(paused);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets recognizer in a thread, starts recognition over again
|
||||
*/
|
||||
public void reset() {
|
||||
if (recognizerThread != null) {
|
||||
recognizerThread.reset();
|
||||
}
|
||||
}
|
||||
|
||||
private final class RecognizerThread extends Thread {
|
||||
|
||||
private int remainingSamples;
|
||||
private final int timeoutSamples;
|
||||
private final static int NO_TIMEOUT = -1;
|
||||
private volatile boolean paused = false;
|
||||
private volatile boolean reset = false;
|
||||
|
||||
RecognitionListener listener;
|
||||
|
||||
public RecognizerThread(RecognitionListener listener, int timeout) {
|
||||
this.listener = listener;
|
||||
if (timeout != NO_TIMEOUT)
|
||||
this.timeoutSamples = timeout * sampleRate / 1000;
|
||||
else
|
||||
this.timeoutSamples = NO_TIMEOUT;
|
||||
this.remainingSamples = this.timeoutSamples;
|
||||
}
|
||||
|
||||
public RecognizerThread(RecognitionListener listener) {
|
||||
this(listener, NO_TIMEOUT);
|
||||
}
|
||||
|
||||
/**
|
||||
* When we are paused, don't process audio by the recognizer and don't emit
|
||||
* any listener results
|
||||
*
|
||||
* @param paused the status of pause
|
||||
*/
|
||||
public void setPause(boolean paused) {
|
||||
this.paused = paused;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set reset state to signal reset of the recognizer and start over
|
||||
*/
|
||||
public void reset() {
|
||||
this.reset = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
|
||||
recorder.startRecording();
|
||||
if (recorder.getRecordingState() == AudioRecord.RECORDSTATE_STOPPED) {
|
||||
recorder.stop();
|
||||
IOException ioe = new IOException(
|
||||
"Failed to start recording. Microphone might be already in use.");
|
||||
mainHandler.post(() -> listener.onError(ioe));
|
||||
}
|
||||
|
||||
short[] buffer = new short[bufferSize];
|
||||
|
||||
while (!interrupted()
|
||||
&& ((timeoutSamples == NO_TIMEOUT) || (remainingSamples > 0))) {
|
||||
int nread = recorder.read(buffer, 0, buffer.length);
|
||||
|
||||
if (paused) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (reset) {
|
||||
recognizer.reset();
|
||||
reset = false;
|
||||
}
|
||||
|
||||
if (nread < 0)
|
||||
throw new RuntimeException("error reading audio buffer");
|
||||
|
||||
if (recognizer.acceptWaveForm(buffer, nread)) {
|
||||
final String result = recognizer.getResult();
|
||||
mainHandler.post(() -> listener.onResult(result));
|
||||
} else {
|
||||
final String partialResult = recognizer.getPartialResult();
|
||||
mainHandler.post(() -> listener.onPartialResult(partialResult));
|
||||
}
|
||||
|
||||
if (timeoutSamples != NO_TIMEOUT) {
|
||||
remainingSamples = remainingSamples - nread;
|
||||
}
|
||||
}
|
||||
|
||||
recorder.stop();
|
||||
|
||||
if (!paused) {
|
||||
// If we met timeout signal that speech ended
|
||||
if (timeoutSamples != NO_TIMEOUT && remainingSamples <= 0) {
|
||||
mainHandler.post(() -> listener.onTimeout());
|
||||
} else {
|
||||
final String finalResult = recognizer.getFinalResult();
|
||||
mainHandler.post(() -> listener.onFinalResult(finalResult));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,165 @@
|
||||
// Copyright 2019 Alpha Cephei 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
|
||||
//
|
||||
// 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.
|
||||
|
||||
package org.vosk.android;
|
||||
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
|
||||
import org.vosk.Recognizer;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
/**
|
||||
* Service that recognizes stream audio in a thread, passes it to a recognizer and emits
|
||||
* recognition results. Recognition events are passed to a client using
|
||||
* {@link RecognitionListener}
|
||||
*/
|
||||
public class SpeechStreamService {
|
||||
|
||||
private final Recognizer recognizer;
|
||||
private final InputStream inputStream;
|
||||
private final int sampleRate;
|
||||
private final static float BUFFER_SIZE_SECONDS = 0.2f;
|
||||
private final int bufferSize;
|
||||
|
||||
private Thread recognizerThread;
|
||||
|
||||
private final Handler mainHandler = new Handler(Looper.getMainLooper());
|
||||
|
||||
/**
|
||||
* Creates speech service.
|
||||
**/
|
||||
public SpeechStreamService(Recognizer recognizer, InputStream inputStream, float sampleRate) {
|
||||
this.recognizer = recognizer;
|
||||
this.sampleRate = (int) sampleRate;
|
||||
this.inputStream = inputStream;
|
||||
bufferSize = Math.round(this.sampleRate * BUFFER_SIZE_SECONDS * 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts recognition. Does nothing if recognition is active.
|
||||
*
|
||||
* @return true if recognition was actually started
|
||||
*/
|
||||
public boolean start(RecognitionListener listener) {
|
||||
if (null != recognizerThread)
|
||||
return false;
|
||||
|
||||
recognizerThread = new RecognizerThread(listener);
|
||||
recognizerThread.start();
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts recognition. After specified timeout listening stops and the
|
||||
* endOfSpeech signals about that. Does nothing if recognition is active.
|
||||
* <p>
|
||||
* timeout - timeout in milliseconds to listen.
|
||||
*
|
||||
* @return true if recognition was actually started
|
||||
*/
|
||||
public boolean start(RecognitionListener listener, int timeout) {
|
||||
if (null != recognizerThread)
|
||||
return false;
|
||||
|
||||
recognizerThread = new RecognizerThread(listener, timeout);
|
||||
recognizerThread.start();
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops recognition. All listeners should receive final result if there is
|
||||
* any. Does nothing if recognition is not active.
|
||||
*
|
||||
* @return true if recognition was actually stopped
|
||||
*/
|
||||
public boolean stop() {
|
||||
if (null == recognizerThread)
|
||||
return false;
|
||||
|
||||
try {
|
||||
recognizerThread.interrupt();
|
||||
recognizerThread.join();
|
||||
} catch (InterruptedException e) {
|
||||
// Restore the interrupted status.
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
|
||||
recognizerThread = null;
|
||||
return true;
|
||||
}
|
||||
|
||||
private final class RecognizerThread extends Thread {
|
||||
|
||||
private int remainingSamples;
|
||||
private final int timeoutSamples;
|
||||
private final static int NO_TIMEOUT = -1;
|
||||
RecognitionListener listener;
|
||||
|
||||
public RecognizerThread(RecognitionListener listener, int timeout) {
|
||||
this.listener = listener;
|
||||
if (timeout != NO_TIMEOUT)
|
||||
this.timeoutSamples = timeout * sampleRate / 1000;
|
||||
else
|
||||
this.timeoutSamples = NO_TIMEOUT;
|
||||
this.remainingSamples = this.timeoutSamples;
|
||||
}
|
||||
|
||||
public RecognizerThread(RecognitionListener listener) {
|
||||
this(listener, NO_TIMEOUT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
|
||||
byte[] buffer = new byte[bufferSize];
|
||||
|
||||
while (!interrupted()
|
||||
&& ((timeoutSamples == NO_TIMEOUT) || (remainingSamples > 0))) {
|
||||
try {
|
||||
int nread = inputStream.read(buffer, 0, buffer.length);
|
||||
if (nread < 0) {
|
||||
break;
|
||||
} else {
|
||||
boolean isSilence = recognizer.acceptWaveForm(buffer, nread);
|
||||
if (isSilence) {
|
||||
final String result = recognizer.getResult();
|
||||
mainHandler.post(() -> listener.onResult(result));
|
||||
} else {
|
||||
final String partialResult = recognizer.getPartialResult();
|
||||
mainHandler.post(() -> listener.onPartialResult(partialResult));
|
||||
}
|
||||
}
|
||||
|
||||
if (timeoutSamples != NO_TIMEOUT) {
|
||||
remainingSamples = remainingSamples - nread;
|
||||
}
|
||||
|
||||
} catch (IOException e) {
|
||||
mainHandler.post(() -> listener.onError(e));
|
||||
}
|
||||
}
|
||||
|
||||
// If we met timeout signal that speech ended
|
||||
if (timeoutSamples != NO_TIMEOUT && remainingSamples <= 0) {
|
||||
mainHandler.post(() -> listener.onTimeout());
|
||||
} else {
|
||||
final String finalResult = recognizer.getFinalResult();
|
||||
mainHandler.post(() -> listener.onFinalResult(finalResult));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,150 @@
|
||||
// Copyright 2019 Alpha Cephei 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
|
||||
//
|
||||
// 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.
|
||||
|
||||
package org.vosk.android;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.AssetManager;
|
||||
import android.os.Environment;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.util.Log;
|
||||
|
||||
import org.vosk.Model;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStream;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
/**
|
||||
* Provides utility methods to sync model files to external storage to allow
|
||||
* C++ code access them. Relies on file named "uuid" to track updates.
|
||||
*/
|
||||
public class StorageService {
|
||||
|
||||
protected static final String TAG = StorageService.class.getSimpleName();
|
||||
|
||||
public interface Callback<R> {
|
||||
void onComplete(R result);
|
||||
}
|
||||
|
||||
public static void unpack(Context context, String sourcePath, final String targetPath, final Callback<Model> completeCallback, final Callback<IOException> errorCallback) {
|
||||
Executor executor = Executors.newSingleThreadExecutor(); // change according to your requirements
|
||||
Handler handler = new Handler(Looper.getMainLooper());
|
||||
executor.execute(() -> {
|
||||
try {
|
||||
final String outputPath = sync(context, sourcePath, targetPath);
|
||||
Model model = new Model(outputPath);
|
||||
handler.post(() -> completeCallback.onComplete(model));
|
||||
} catch (final IOException e) {
|
||||
handler.post(() -> errorCallback.onComplete(e));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static String sync(Context context, String sourcePath, String targetPath) throws IOException {
|
||||
|
||||
AssetManager assetManager = context.getAssets();
|
||||
|
||||
File externalFilesDir = context.getExternalFilesDir(null);
|
||||
if (externalFilesDir == null) {
|
||||
throw new IOException("cannot get external files dir, "
|
||||
+ "external storage state is " + Environment.getExternalStorageState());
|
||||
}
|
||||
|
||||
File targetDir = new File(externalFilesDir, targetPath);
|
||||
String resultPath = new File(targetDir, sourcePath).getAbsolutePath();
|
||||
String sourceUUID = readLine(assetManager.open(sourcePath + "/uuid"));
|
||||
try {
|
||||
String targetUUID = readLine(new FileInputStream(new File(targetDir, sourcePath + "/uuid")));
|
||||
if (targetUUID.equals(sourceUUID)) return resultPath;
|
||||
} catch (FileNotFoundException e) {
|
||||
// ignore
|
||||
}
|
||||
deleteContents(targetDir);
|
||||
|
||||
copyAssets(assetManager, sourcePath, targetDir);
|
||||
|
||||
// Copy uuid
|
||||
copyFile(assetManager, sourcePath + "/uuid", targetDir);
|
||||
|
||||
return resultPath;
|
||||
}
|
||||
|
||||
private static String readLine(InputStream is) throws IOException {
|
||||
return new BufferedReader(new InputStreamReader(is)).readLine();
|
||||
}
|
||||
|
||||
private static boolean deleteContents(File dir) {
|
||||
File[] files = dir.listFiles();
|
||||
boolean success = true;
|
||||
if (files != null) {
|
||||
for (File file : files) {
|
||||
if (file.isDirectory()) {
|
||||
success &= deleteContents(file);
|
||||
}
|
||||
if (!file.delete()) {
|
||||
success = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
private static void copyAssets(AssetManager assetManager, String path, File outPath) throws IOException {
|
||||
String[] assets = assetManager.list(path);
|
||||
if (assets == null) {
|
||||
return;
|
||||
}
|
||||
if (assets.length == 0) {
|
||||
if (!path.endsWith("uuid"))
|
||||
copyFile(assetManager, path, outPath);
|
||||
} else {
|
||||
File dir = new File(outPath, path);
|
||||
if (!dir.exists()) {
|
||||
Log.v(TAG, "Making directory " + dir.getAbsolutePath());
|
||||
if (!dir.mkdirs()) {
|
||||
Log.v(TAG, "Failed to create directory " + dir.getAbsolutePath());
|
||||
}
|
||||
}
|
||||
for (String asset : assets) {
|
||||
copyAssets(assetManager, path + "/" + asset, outPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void copyFile(AssetManager assetManager, String fileName, File outPath) throws IOException {
|
||||
InputStream in;
|
||||
|
||||
Log.v(TAG, "Copy " + fileName + " to " + outPath);
|
||||
in = assetManager.open(fileName);
|
||||
OutputStream out = new FileOutputStream(outPath + "/" + fileName);
|
||||
|
||||
byte[] buffer = new byte[4000];
|
||||
int read;
|
||||
while ((read = in.read(buffer)) != -1) {
|
||||
out.write(buffer, 0, read);
|
||||
}
|
||||
in.close();
|
||||
out.close();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
def archiveName = "vosk-model-en"
|
||||
def pomName = "Vosk English Model"
|
||||
def pomDescription = "Small English model for Android"
|
||||
|
||||
android {
|
||||
compileSdkVersion 29
|
||||
defaultConfig {
|
||||
minSdkVersion 21
|
||||
targetSdkVersion 29
|
||||
versionCode 6
|
||||
versionName = version
|
||||
archivesBaseName = archiveName
|
||||
}
|
||||
buildFeatures {
|
||||
buildConfig = false
|
||||
}
|
||||
sourceSets {
|
||||
main {
|
||||
assets.srcDirs += "$buildDir/generated/assets"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tasks.register('genUUID') {
|
||||
def uuid = UUID.randomUUID().toString()
|
||||
def odir = file("$buildDir/generated/assets/model-en-us")
|
||||
def ofile = file("$odir/uuid")
|
||||
doLast {
|
||||
mkdir odir
|
||||
ofile.text = uuid
|
||||
}
|
||||
}
|
||||
|
||||
preBuild.dependsOn(genUUID)
|
||||
|
||||
publishing {
|
||||
publications {
|
||||
aar(MavenPublication) {
|
||||
artifactId = archiveName
|
||||
artifact("$buildDir/outputs/aar/$archiveName-release.aar")
|
||||
pom {
|
||||
name = pomName
|
||||
description = pomDescription
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="org.vosk.model.en">
|
||||
</manifest>
|
||||
@@ -0,0 +1,7 @@
|
||||
US English model for mobile Vosk applications
|
||||
|
||||
Copyright 2020 Alpha Cephei Inc
|
||||
|
||||
Accuracy: 10.38 (tedlium test) 9.85 (librispeech test-clean)
|
||||
Speed: 0.11xRT (desktop)
|
||||
Latency: 0.15s (right context)
|
||||
@@ -0,0 +1 @@
|
||||
include ':lib', ':model-en'
|
||||
@@ -1,262 +0,0 @@
|
||||
// Copyright 2019 Alpha Cephei 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
|
||||
//
|
||||
// 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.
|
||||
|
||||
package org.kaldi;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStream;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.Reader;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Queue;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.AssetManager;
|
||||
import android.os.Environment;
|
||||
import android.util.Log;
|
||||
|
||||
/**
|
||||
* Provides utility methods to keep asset files to external storage to allow
|
||||
* further JNI code access assets from a filesystem.
|
||||
*
|
||||
* There must be special file {@value #ASSET_LIST_NAME} among the application
|
||||
* assets containing relative paths of assets to synchronize. If the
|
||||
* corresponding path does not exist on the external storage it is copied. If
|
||||
* the path exists checksums are compared and the asset is copied only if there
|
||||
* is a mismatch. Checksum is stored in a separate asset with the name that
|
||||
* consists of the original name and a suffix that depends on the checksum
|
||||
* algorithm (e.g. MD5). Checksum files are copied along with the corresponding
|
||||
* asset files.
|
||||
*
|
||||
* @author Alexander Solovets
|
||||
*/
|
||||
public class Assets {
|
||||
|
||||
protected static final String TAG = Assets.class.getSimpleName();
|
||||
|
||||
public static final String ASSET_LIST_NAME = "assets.lst";
|
||||
public static final String SYNC_DIR = "sync";
|
||||
public static final String HASH_EXT = ".md5";
|
||||
|
||||
private final AssetManager assetManager;
|
||||
private final File externalDir;
|
||||
|
||||
/**
|
||||
* Creates new instance for asset synchronization
|
||||
*
|
||||
* @param context
|
||||
* application context
|
||||
*
|
||||
* @throws IOException
|
||||
* if the directory does not exist
|
||||
*
|
||||
* @see android.content.Context#getExternalFilesDir
|
||||
* @see android.os.Environment#getExternalStorageState
|
||||
*/
|
||||
public Assets(Context context) throws IOException {
|
||||
File appDir = context.getExternalFilesDir(null);
|
||||
if (null == appDir)
|
||||
throw new IOException("cannot get external files dir, "
|
||||
+ "external storage state is " + Environment.getExternalStorageState());
|
||||
externalDir = new File(appDir, SYNC_DIR);
|
||||
assetManager = context.getAssets();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new instance with specified destination for assets
|
||||
*
|
||||
* @param context
|
||||
* application context to retrieve the assets
|
||||
* @param path
|
||||
* path to sync the files
|
||||
*/
|
||||
public Assets(Context context, String dest) {
|
||||
externalDir = new File(dest);
|
||||
assetManager = context.getAssets();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns destination path on external storage where assets are copied.
|
||||
*
|
||||
* @return path to application directory or null if it does not exists
|
||||
*/
|
||||
public File getExternalDir() {
|
||||
return externalDir;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the map of asset paths to the files checksums.
|
||||
*
|
||||
* @return path to the root of resources directory on external storage
|
||||
* @throws IOException
|
||||
* if an I/O error occurs or "assets.lst" is missing
|
||||
*/
|
||||
public Map<String, String> getItems() throws IOException {
|
||||
Map<String, String> items = new HashMap<String, String>();
|
||||
for (String path : readLines(openAsset(ASSET_LIST_NAME))) {
|
||||
Reader reader = new InputStreamReader(openAsset(path + HASH_EXT));
|
||||
items.put(path, new BufferedReader(reader).readLine());
|
||||
}
|
||||
return items;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns path to hash mappings for the previously copied files. This
|
||||
* method can be used to find out assets which must be updated.
|
||||
*/
|
||||
public Map<String, String> getExternalItems() {
|
||||
try {
|
||||
Map<String, String> items = new HashMap<String, String>();
|
||||
File assetFile = new File(externalDir, ASSET_LIST_NAME);
|
||||
for (String line : readLines(new FileInputStream(assetFile))) {
|
||||
String[] fields = line.split(" ");
|
||||
items.put(fields[0], fields[1]);
|
||||
}
|
||||
return items;
|
||||
} catch (IOException e) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* In case you want to create more smart sync implementation, this method
|
||||
* returns the list of items which must be synchronized.
|
||||
*/
|
||||
public Collection<String> getItemsToCopy(String path) throws IOException {
|
||||
Collection<String> items = new ArrayList<String>();
|
||||
Queue<String> queue = new ArrayDeque<String>();
|
||||
queue.offer(path);
|
||||
|
||||
while (!queue.isEmpty()) {
|
||||
path = queue.poll();
|
||||
String[] list = assetManager.list(path);
|
||||
for (String nested : list)
|
||||
queue.offer(nested);
|
||||
|
||||
if (list.length == 0)
|
||||
items.add(path);
|
||||
}
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
private List<String> readLines(InputStream source) throws IOException {
|
||||
List<String> lines = new ArrayList<String>();
|
||||
BufferedReader br = new BufferedReader(new InputStreamReader(source));
|
||||
String line;
|
||||
while (null != (line = br.readLine()))
|
||||
lines.add(line);
|
||||
return lines;
|
||||
}
|
||||
|
||||
private InputStream openAsset(String asset) throws IOException {
|
||||
return assetManager.open(new File(SYNC_DIR, asset).getPath());
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the list of synchronized items. The list is stored as a two-column
|
||||
* space-separated list of items in a text file. The file is located at the
|
||||
* root of synchronization directory in the external storage.
|
||||
*
|
||||
* @param items
|
||||
* the items
|
||||
* @throws IOException
|
||||
* if an I/O error occurs
|
||||
*/
|
||||
public void updateItemList(Map<String, String> items) throws IOException {
|
||||
File assetListFile = new File(externalDir, ASSET_LIST_NAME);
|
||||
PrintWriter pw = new PrintWriter(new FileOutputStream(assetListFile));
|
||||
for (Map.Entry<String, String> entry : items.entrySet())
|
||||
pw.format("%s %s\n", entry.getKey(), entry.getValue());
|
||||
pw.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies raw asset resource to external storage of the device.
|
||||
*
|
||||
* @param path
|
||||
* path of the asset to copy
|
||||
* @throws IOException
|
||||
* if an I/O error occurs
|
||||
*/
|
||||
public File copy(String asset) throws IOException {
|
||||
InputStream source = openAsset(asset);
|
||||
File destinationFile = new File(externalDir, asset);
|
||||
destinationFile.getParentFile().mkdirs();
|
||||
OutputStream destination = new FileOutputStream(destinationFile);
|
||||
byte[] buffer = new byte[1024];
|
||||
int nread;
|
||||
|
||||
while ((nread = source.read(buffer)) != -1) {
|
||||
if (nread == 0) {
|
||||
nread = source.read();
|
||||
if (nread < 0)
|
||||
break;
|
||||
destination.write(nread);
|
||||
continue;
|
||||
}
|
||||
destination.write(buffer, 0, nread);
|
||||
}
|
||||
destination.close();
|
||||
return destinationFile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs the sync of assets in the application and on the external
|
||||
* storage
|
||||
*
|
||||
* @return The folder on external storage with data
|
||||
* @throws IOException
|
||||
*/
|
||||
public File syncAssets() throws IOException {
|
||||
Collection<String> newItems = new ArrayList<String>();
|
||||
Collection<String> unusedItems = new ArrayList<String>();
|
||||
Map<String, String> items = getItems();
|
||||
Map<String, String> externalItems = getExternalItems();
|
||||
|
||||
for (String path : items.keySet()) {
|
||||
if (!items.get(path).equals(externalItems.get(path))
|
||||
|| !(new File(externalDir, path).exists()))
|
||||
newItems.add(path);
|
||||
}
|
||||
|
||||
unusedItems.addAll(externalItems.keySet());
|
||||
unusedItems.removeAll(items.keySet());
|
||||
|
||||
for (String path : newItems) {
|
||||
File file = copy(path);
|
||||
}
|
||||
|
||||
for (String path : unusedItems) {
|
||||
File file = new File(externalDir, path);
|
||||
file.delete();
|
||||
}
|
||||
|
||||
updateItemList(items);
|
||||
return externalDir;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,302 +0,0 @@
|
||||
// Copyright 2019 Alpha Cephei 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
|
||||
//
|
||||
// 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.
|
||||
|
||||
package org.kaldi;
|
||||
|
||||
import static java.lang.String.format;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
|
||||
import android.media.AudioFormat;
|
||||
import android.media.AudioRecord;
|
||||
import android.media.MediaRecorder.AudioSource;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.util.Log;
|
||||
|
||||
/**
|
||||
* Main class to access recognizer functions. After configuration this class
|
||||
* starts a listener thread which records the data and recognizes it using
|
||||
* VOSK engine. Recognition events are passed to a client using
|
||||
* {@link RecognitionListener}
|
||||
*
|
||||
*/
|
||||
public class SpeechRecognizer {
|
||||
|
||||
protected static final String TAG = SpeechRecognizer.class.getSimpleName();
|
||||
|
||||
private final KaldiRecognizer recognizer;
|
||||
|
||||
private final int sampleRate;
|
||||
private final static float BUFFER_SIZE_SECONDS = 0.4f;
|
||||
private int bufferSize;
|
||||
private final AudioRecord recorder;
|
||||
|
||||
private Thread recognizerThread;
|
||||
|
||||
private final Handler mainHandler = new Handler(Looper.getMainLooper());
|
||||
|
||||
private final Collection<RecognitionListener> listeners = new HashSet<RecognitionListener>();
|
||||
|
||||
/**
|
||||
* Creates speech recognizer. Recognizer holds the AudioRecord object, so you
|
||||
* need to call {@link release} in order to properly finalize it.
|
||||
*
|
||||
* @throws IOException thrown if audio recorder can not be created for some reason.
|
||||
*/
|
||||
public SpeechRecognizer(Model model) throws IOException {
|
||||
recognizer = new KaldiRecognizer(model, 16000.0f);
|
||||
sampleRate = 16000;
|
||||
bufferSize = Math.round(sampleRate * BUFFER_SIZE_SECONDS);
|
||||
recorder = new AudioRecord(
|
||||
AudioSource.VOICE_RECOGNITION, sampleRate,
|
||||
AudioFormat.CHANNEL_IN_MONO,
|
||||
AudioFormat.ENCODING_PCM_16BIT, bufferSize * 2);
|
||||
|
||||
if (recorder.getState() == AudioRecord.STATE_UNINITIALIZED) {
|
||||
recorder.release();
|
||||
throw new IOException(
|
||||
"Failed to initialize recorder. Microphone might be already in use.");
|
||||
}
|
||||
}
|
||||
|
||||
public SpeechRecognizer(Model model, SpkModel spkModel) throws IOException {
|
||||
recognizer = new KaldiRecognizer(model, spkModel, 16000.0f);
|
||||
sampleRate = 16000;
|
||||
bufferSize = Math.round(sampleRate * BUFFER_SIZE_SECONDS);
|
||||
recorder = new AudioRecord(
|
||||
AudioSource.VOICE_RECOGNITION, sampleRate,
|
||||
AudioFormat.CHANNEL_IN_MONO,
|
||||
AudioFormat.ENCODING_PCM_16BIT, bufferSize * 2);
|
||||
|
||||
if (recorder.getState() == AudioRecord.STATE_UNINITIALIZED) {
|
||||
recorder.release();
|
||||
throw new IOException(
|
||||
"Failed to initialize recorder. Microphone might be already in use.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds listener.
|
||||
*/
|
||||
public void addListener(RecognitionListener listener) {
|
||||
synchronized (listeners) {
|
||||
listeners.add(listener);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes listener.
|
||||
*/
|
||||
public void removeListener(RecognitionListener listener) {
|
||||
synchronized (listeners) {
|
||||
listeners.remove(listener);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts recognition. Does nothing if recognition is active.
|
||||
*
|
||||
* @return true if recognition was actually started
|
||||
*/
|
||||
public boolean startListening() {
|
||||
if (null != recognizerThread)
|
||||
return false;
|
||||
|
||||
recognizerThread = new RecognizerThread();
|
||||
recognizerThread.start();
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts recognition. After specified timeout listening stops and the
|
||||
* endOfSpeech signals about that. Does nothing if recognition is active.
|
||||
*
|
||||
* @timeout - timeout in milliseconds to listen.
|
||||
*
|
||||
* @return true if recognition was actually started
|
||||
*/
|
||||
public boolean startListening(int timeout) {
|
||||
if (null != recognizerThread)
|
||||
return false;
|
||||
|
||||
recognizerThread = new RecognizerThread(timeout);
|
||||
recognizerThread.start();
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean stopRecognizerThread() {
|
||||
if (null == recognizerThread)
|
||||
return false;
|
||||
|
||||
try {
|
||||
recognizerThread.interrupt();
|
||||
recognizerThread.join();
|
||||
} catch (InterruptedException e) {
|
||||
// Restore the interrupted status.
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
|
||||
recognizerThread = null;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops recognition. All listeners should receive final result if there is
|
||||
* any. Does nothing if recognition is not active.
|
||||
*
|
||||
* @return true if recognition was actually stopped
|
||||
*/
|
||||
public boolean stop() {
|
||||
boolean result = stopRecognizerThread();
|
||||
if (result) {
|
||||
mainHandler.post(new ResultEvent(recognizer.Result(), true));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancels recognition. Listeners do not receive final result. Does nothing
|
||||
* if recognition is not active.
|
||||
*
|
||||
* @return true if recognition was actually canceled
|
||||
*/
|
||||
public boolean cancel() {
|
||||
boolean result = stopRecognizerThread();
|
||||
recognizer.Result(); // Reset recognizer state
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shutdown the recognizer and release the recorder
|
||||
*/
|
||||
public void shutdown() {
|
||||
recorder.release();
|
||||
}
|
||||
|
||||
private final class RecognizerThread extends Thread {
|
||||
|
||||
private int remainingSamples;
|
||||
private int timeoutSamples;
|
||||
private final static int NO_TIMEOUT = -1;
|
||||
|
||||
public RecognizerThread(int timeout) {
|
||||
if (timeout != NO_TIMEOUT)
|
||||
this.timeoutSamples = timeout * sampleRate / 1000;
|
||||
else
|
||||
this.timeoutSamples = NO_TIMEOUT;
|
||||
this.remainingSamples = this.timeoutSamples;
|
||||
}
|
||||
|
||||
public RecognizerThread() {
|
||||
this(NO_TIMEOUT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
|
||||
recorder.startRecording();
|
||||
if (recorder.getRecordingState() == AudioRecord.RECORDSTATE_STOPPED) {
|
||||
recorder.stop();
|
||||
IOException ioe = new IOException(
|
||||
"Failed to start recording. Microphone might be already in use.");
|
||||
mainHandler.post(new OnErrorEvent(ioe));
|
||||
return;
|
||||
}
|
||||
|
||||
short[] buffer = new short[bufferSize];
|
||||
|
||||
while (!interrupted()
|
||||
&& ((timeoutSamples == NO_TIMEOUT) || (remainingSamples > 0))) {
|
||||
int nread = recorder.read(buffer, 0, buffer.length);
|
||||
|
||||
if (nread < 0) {
|
||||
throw new RuntimeException("error reading audio buffer");
|
||||
} else {
|
||||
boolean isFinal = recognizer.AcceptWaveform(buffer, nread);
|
||||
if (isFinal) {
|
||||
mainHandler.post(new ResultEvent(recognizer.Result(), true));
|
||||
} else {
|
||||
mainHandler.post(new ResultEvent(recognizer.PartialResult(), false));
|
||||
}
|
||||
}
|
||||
|
||||
if (timeoutSamples != NO_TIMEOUT) {
|
||||
remainingSamples = remainingSamples - nread;
|
||||
}
|
||||
}
|
||||
|
||||
recorder.stop();
|
||||
|
||||
// Remove all pending notifications.
|
||||
mainHandler.removeCallbacksAndMessages(null);
|
||||
|
||||
// If we met timeout signal that speech ended
|
||||
if (timeoutSamples != NO_TIMEOUT && remainingSamples <= 0) {
|
||||
mainHandler.post(new TimeoutEvent());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private abstract class RecognitionEvent implements Runnable {
|
||||
public void run() {
|
||||
RecognitionListener[] emptyArray = new RecognitionListener[0];
|
||||
for (RecognitionListener listener : listeners.toArray(emptyArray))
|
||||
execute(listener);
|
||||
}
|
||||
|
||||
protected abstract void execute(RecognitionListener listener);
|
||||
}
|
||||
|
||||
private class ResultEvent extends RecognitionEvent {
|
||||
protected final String hypothesis;
|
||||
private final boolean finalResult;
|
||||
|
||||
ResultEvent(String hypothesis, boolean finalResult) {
|
||||
this.hypothesis = hypothesis;
|
||||
this.finalResult = finalResult;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void execute(RecognitionListener listener) {
|
||||
if (finalResult)
|
||||
listener.onResult(hypothesis);
|
||||
else
|
||||
listener.onPartialResult(hypothesis);
|
||||
}
|
||||
}
|
||||
|
||||
private class OnErrorEvent extends RecognitionEvent {
|
||||
private final Exception exception;
|
||||
|
||||
OnErrorEvent(Exception exception) {
|
||||
this.exception = exception;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void execute(RecognitionListener listener) {
|
||||
listener.onError(exception);
|
||||
}
|
||||
}
|
||||
|
||||
private class TimeoutEvent extends RecognitionEvent {
|
||||
@Override
|
||||
protected void execute(RecognitionListener listener) {
|
||||
listener.onTimeout();
|
||||
}
|
||||
}
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
CFLAGS=-I../src
|
||||
LDFLAGS=-L../src -lvosk -ldl -lpthread -Wl,-rpath,../src
|
||||
|
||||
all: test_vosk test_vosk_speaker
|
||||
|
||||
test_vosk: test_vosk.o
|
||||
gcc $^ -o $@ $(LDFLAGS)
|
||||
|
||||
test_vosk_speaker: test_vosk_speaker.o
|
||||
gcc $^ -o $@ $(LDFLAGS)
|
||||
|
||||
%.o: %.c
|
||||
gcc $(CFLAGS) -c -o $@ $<
|
||||
|
||||
clean:
|
||||
rm -f *.o *.a test_vosk test_vosk_speaker
|
||||
@@ -0,0 +1,29 @@
|
||||
#include <vosk_api.h>
|
||||
#include <stdio.h>
|
||||
|
||||
int main() {
|
||||
FILE *wavin;
|
||||
char buf[3200];
|
||||
int nread, final;
|
||||
|
||||
VoskModel *model = vosk_model_new("model");
|
||||
VoskRecognizer *recognizer = vosk_recognizer_new(model, 16000.0);
|
||||
|
||||
wavin = fopen("test.wav", "rb");
|
||||
fseek(wavin, 44, SEEK_SET);
|
||||
while (!feof(wavin)) {
|
||||
nread = fread(buf, 1, sizeof(buf), wavin);
|
||||
final = vosk_recognizer_accept_waveform(recognizer, buf, nread);
|
||||
if (final) {
|
||||
printf("%s\n", vosk_recognizer_result(recognizer));
|
||||
} else {
|
||||
printf("%s\n", vosk_recognizer_partial_result(recognizer));
|
||||
}
|
||||
}
|
||||
printf("%s\n", vosk_recognizer_final_result(recognizer));
|
||||
|
||||
vosk_recognizer_free(recognizer);
|
||||
vosk_model_free(model);
|
||||
fclose(wavin);
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
#include <vosk_api.h>
|
||||
#include <stdio.h>
|
||||
|
||||
int main() {
|
||||
FILE *wavin;
|
||||
char buf[3200];
|
||||
int nread, final;
|
||||
|
||||
VoskModel *model = vosk_model_new("model");
|
||||
VoskSpkModel *spk_model = vosk_spk_model_new("spk-model");
|
||||
VoskRecognizer *recognizer = vosk_recognizer_new_spk(model, 16000.0, spk_model);
|
||||
|
||||
wavin = fopen("test.wav", "rb");
|
||||
fseek(wavin, 44, SEEK_SET);
|
||||
while (!feof(wavin)) {
|
||||
nread = fread(buf, 1, sizeof(buf), wavin);
|
||||
final = vosk_recognizer_accept_waveform(recognizer, buf, nread);
|
||||
if (final) {
|
||||
printf("%s\n", vosk_recognizer_result(recognizer));
|
||||
} else {
|
||||
printf("%s\n", vosk_recognizer_partial_result(recognizer));
|
||||
}
|
||||
}
|
||||
printf("%s\n", vosk_recognizer_final_result(recognizer));
|
||||
|
||||
vosk_recognizer_free(recognizer);
|
||||
vosk_spk_model_free(spk_model);
|
||||
vosk_model_free(model);
|
||||
return 0;
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
KALDI_ROOT ?= $(HOME)/kaldi
|
||||
CFLAGS := -std=c++11 -g -O2 -DPIC -fPIC -Wno-unused-function
|
||||
CPPFLAGS := -I$(KALDI_ROOT)/src -I$(KALDI_ROOT)/tools/openfst/include -I../src -DFST_NO_DYNAMIC_LINKING
|
||||
|
||||
KALDI_LIBS = \
|
||||
${KALDI_ROOT}/src/online2/kaldi-online2.a \
|
||||
${KALDI_ROOT}/src/decoder/kaldi-decoder.a \
|
||||
${KALDI_ROOT}/src/ivector/kaldi-ivector.a \
|
||||
${KALDI_ROOT}/src/gmm/kaldi-gmm.a \
|
||||
${KALDI_ROOT}/src/nnet3/kaldi-nnet3.a \
|
||||
${KALDI_ROOT}/src/tree/kaldi-tree.a \
|
||||
${KALDI_ROOT}/src/feat/kaldi-feat.a \
|
||||
${KALDI_ROOT}/src/lat/kaldi-lat.a \
|
||||
${KALDI_ROOT}/src/lm/kaldi-lm.a \
|
||||
${KALDI_ROOT}/src/hmm/kaldi-hmm.a \
|
||||
${KALDI_ROOT}/src/transform/kaldi-transform.a \
|
||||
${KALDI_ROOT}/src/cudamatrix/kaldi-cudamatrix.a \
|
||||
${KALDI_ROOT}/src/matrix/kaldi-matrix.a \
|
||||
${KALDI_ROOT}/src/fstext/kaldi-fstext.a \
|
||||
${KALDI_ROOT}/src/util/kaldi-util.a \
|
||||
${KALDI_ROOT}/src/base/kaldi-base.a \
|
||||
${KALDI_ROOT}/tools/openfst/lib/libfst.a \
|
||||
${KALDI_ROOT}/tools/openfst/lib/libfstngram.a \
|
||||
${KALDI_ROOT}/tools/OpenBLAS/libopenblas.a \
|
||||
-lgfortran -lstdc++
|
||||
|
||||
all: test.exe
|
||||
|
||||
test.exe: libkaldiwrap.so test.cs
|
||||
mcs test.cs gen/*.cs
|
||||
|
||||
VOSK_SOURCES = \
|
||||
vosk_wrap.c \
|
||||
../src/kaldi_recognizer.cc \
|
||||
../src/kaldi_recognizer.h \
|
||||
../src/model.cc \
|
||||
../src/model.h \
|
||||
../src/spk_model.cc \
|
||||
../src/spk_model.h \
|
||||
../src/vosk_api.cc \
|
||||
../src/vosk_api.h
|
||||
|
||||
libkaldiwrap.so: $(VOSK_SOURCES)
|
||||
$(CXX) -fpermissive $(CFLAGS) $(CPPFLAGS) -shared -o $@ $(VOSK_SOURCES) $(KALDI_LIBS)
|
||||
|
||||
vosk_wrap.c: ../src/vosk.i
|
||||
mkdir -p gen
|
||||
swig -csharp -DSWIG_CSHARP_NO_EXCEPTION_HELPER -dllimport "libkaldiwrap" \
|
||||
-namespace "Kaldi" -outdir gen -o vosk_wrap.c ../src/vosk.i
|
||||
|
||||
run: test.exe
|
||||
mono test.exe
|
||||
|
||||
clean:
|
||||
$(RM) *.so vosk_wrap.c *.o gen/*.cs test.exe
|
||||
@@ -0,0 +1,12 @@
|
||||
This is a nuget-based wrapper for libvosk library
|
||||
|
||||
See demo folder for example how to use the library. You can simply run
|
||||
"dotnet run" to run the demo. Make sure you unpacked the model and the
|
||||
test file.
|
||||
|
||||
See the nuget folder for the sources of the wrapper. Run build.sh to
|
||||
build nuget package.
|
||||
|
||||
Note we only support win64 and linux64 for now. No support for win32
|
||||
since it is a little bit painful to load the libraries depending on
|
||||
architecture. In theory we can add OSX some time or even Android.
|
||||
@@ -0,0 +1,80 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using Vosk;
|
||||
|
||||
public class VoskDemo
|
||||
{
|
||||
public static void DemoBytes(Model model)
|
||||
{
|
||||
// Demo byte buffer
|
||||
VoskRecognizer rec = new VoskRecognizer(model, 16000.0f);
|
||||
rec.SetMaxAlternatives(0);
|
||||
rec.SetWords(true);
|
||||
using(Stream source = File.OpenRead("test.wav")) {
|
||||
byte[] buffer = new byte[4096];
|
||||
int bytesRead;
|
||||
while((bytesRead = source.Read(buffer, 0, buffer.Length)) > 0) {
|
||||
if (rec.AcceptWaveform(buffer, bytesRead)) {
|
||||
Console.WriteLine(rec.Result());
|
||||
} else {
|
||||
Console.WriteLine(rec.PartialResult());
|
||||
}
|
||||
}
|
||||
}
|
||||
Console.WriteLine(rec.FinalResult());
|
||||
}
|
||||
|
||||
public static void DemoFloats(Model model)
|
||||
{
|
||||
// Demo float array
|
||||
VoskRecognizer rec = new VoskRecognizer(model, 16000.0f);
|
||||
using(Stream source = File.OpenRead("test.wav")) {
|
||||
byte[] buffer = new byte[4096];
|
||||
int bytesRead;
|
||||
while((bytesRead = source.Read(buffer, 0, buffer.Length)) > 0) {
|
||||
float[] fbuffer = new float[bytesRead / 2];
|
||||
for (int i = 0, n = 0; i < fbuffer.Length; i++, n+=2) {
|
||||
fbuffer[i] = BitConverter.ToInt16(buffer, n);
|
||||
}
|
||||
if (rec.AcceptWaveform(fbuffer, fbuffer.Length)) {
|
||||
Console.WriteLine(rec.Result());
|
||||
} else {
|
||||
Console.WriteLine(rec.PartialResult());
|
||||
}
|
||||
}
|
||||
}
|
||||
Console.WriteLine(rec.FinalResult());
|
||||
}
|
||||
|
||||
public static void DemoSpeaker(Model model)
|
||||
{
|
||||
// Output speakers
|
||||
SpkModel spkModel = new SpkModel("model-spk");
|
||||
VoskRecognizer rec = new VoskRecognizer(model, 16000.0f);
|
||||
rec.SetSpkModel(spkModel);
|
||||
|
||||
using(Stream source = File.OpenRead("test.wav")) {
|
||||
byte[] buffer = new byte[4096];
|
||||
int bytesRead;
|
||||
while((bytesRead = source.Read(buffer, 0, buffer.Length)) > 0) {
|
||||
if (rec.AcceptWaveform(buffer, bytesRead)) {
|
||||
Console.WriteLine(rec.Result());
|
||||
} else {
|
||||
Console.WriteLine(rec.PartialResult());
|
||||
}
|
||||
}
|
||||
}
|
||||
Console.WriteLine(rec.FinalResult());
|
||||
}
|
||||
|
||||
public static void Main()
|
||||
{
|
||||
// You can set to -1 to disable logging messages
|
||||
Vosk.Vosk.SetLogLevel(0);
|
||||
|
||||
Model model = new Model("model");
|
||||
DemoBytes(model);
|
||||
DemoFloats(model);
|
||||
DemoSpeaker(model);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net5.0</TargetFramework>
|
||||
<RootNamespace>VoskDemo</RootNamespace>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<RestoreSources>$(RestoreSources);../nuget</RestoreSources>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Vosk" Version="0.3.45" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,32 @@
|
||||
<?xml version="1.0"?>
|
||||
<package>
|
||||
<metadata>
|
||||
<id>Vosk</id>
|
||||
<version>0.3.45</version>
|
||||
<authors>Alpha Cephei Inc</authors>
|
||||
<owners>Alpha Cephei Inc</owners>
|
||||
<license type="expression">Apache-2.0</license>
|
||||
<projectUrl>https://alphacephei.com/vosk/</projectUrl>
|
||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||
<title>Vosk Speech Recognition Toolkit</title>
|
||||
<description>Vosk is an offline open source speech recognition toolkit. It enables speech recognition models for 20+ languages and dialects - English, Indian English, German, French, Spanish, Portuguese, Chinese, Russian, Turkish, Vietnamese, Italian, Dutch, Catalan, Arabic, Greek, Farsi, Filipino, Ukrainian, Kazakh, Swedish, Japanese, Esperanto, Hindi, Czech. More to come.
|
||||
|
||||
Vosk models are small (50 Mb) but provide continuous large vocabulary transcription, zero-latency response with streaming API, reconfigurable vocabulary and speaker identification.
|
||||
|
||||
Speech recognition bindings implemented for various programming languages like Python, Java, Node.JS, C#, C++ and others.
|
||||
|
||||
Vosk supplies speech recognition for chatbots, smart home appliances, virtual assistants. It can also create subtitles for movies, transcription for lectures and interviews.
|
||||
|
||||
Vosk scales from small devices like Raspberry Pi or Android smartphone to big clusters.</description>
|
||||
<releaseNotes>See for details https://github.com/alphacep/vosk-api/releases</releaseNotes>
|
||||
<repository type="git" url="https://github.com/alphacep/vosk-api.git" branch="master"/>
|
||||
<copyright>Copyright 2020-2050 Alpha Cephei Inc</copyright>
|
||||
<tags>speech recognition voice stt asr speech-to-text ai offline privacy</tags>
|
||||
<dependencies>
|
||||
<group targetFramework=".NETStandard2.0"/>
|
||||
</dependencies>
|
||||
</metadata>
|
||||
<files>
|
||||
<file src="**" exclude="src/*.cs;build.sh;**/.keep-me;*.nupkg" />
|
||||
</files>
|
||||
</package>
|
||||
Executable
+2
@@ -0,0 +1,2 @@
|
||||
mcs -out:lib/netstandard2.0/Vosk.dll -target:library src/*.cs
|
||||
nuget pack
|
||||
@@ -0,0 +1,11 @@
|
||||
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<NativeLibs Include="$(MSBuildThisFileDirectory)\lib\linux-x64\*.so" Condition="'$([MSBuild]::IsOsPlatform(Linux))'" />
|
||||
<NativeLibs Include="$(MSBuildThisFileDirectory)\lib\win-x64\*.dll" Condition="'$([MSBuild]::IsOsPlatform(Windows))'" />
|
||||
<NativeLibs Include="$(MSBuildThisFileDirectory)\lib\osx-universal\*.dylib" Condition="'$([MSBuild]::IsOsPlatform(OSX))'" />
|
||||
<None Include="@(NativeLibs)">
|
||||
<Link>%(FileName)%(Extension)</Link>
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,41 @@
|
||||
namespace Vosk {
|
||||
|
||||
public class Model : global::System.IDisposable {
|
||||
private global::System.Runtime.InteropServices.HandleRef handle;
|
||||
|
||||
internal Model(global::System.IntPtr cPtr) {
|
||||
handle = new global::System.Runtime.InteropServices.HandleRef(this, cPtr);
|
||||
}
|
||||
|
||||
internal static global::System.Runtime.InteropServices.HandleRef getCPtr(Model obj) {
|
||||
return (obj == null) ? new global::System.Runtime.InteropServices.HandleRef(null, global::System.IntPtr.Zero) : obj.handle;
|
||||
}
|
||||
|
||||
~Model() {
|
||||
Dispose(false);
|
||||
}
|
||||
|
||||
public void Dispose() {
|
||||
Dispose(true);
|
||||
global::System.GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing) {
|
||||
lock(this) {
|
||||
if (handle.Handle != global::System.IntPtr.Zero) {
|
||||
VoskPINVOKE.delete_Model(handle);
|
||||
handle = new global::System.Runtime.InteropServices.HandleRef(null, global::System.IntPtr.Zero);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Model(string model_path) : this(VoskPINVOKE.new_Model(model_path)) {
|
||||
}
|
||||
|
||||
public int FindWord(string word) {
|
||||
return VoskPINVOKE.Model_vosk_model_find_word(handle, word);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
namespace Vosk {
|
||||
|
||||
public class SpkModel : global::System.IDisposable {
|
||||
private global::System.Runtime.InteropServices.HandleRef handle;
|
||||
|
||||
internal SpkModel(global::System.IntPtr cPtr) {
|
||||
handle = new global::System.Runtime.InteropServices.HandleRef(this, cPtr);
|
||||
}
|
||||
|
||||
internal static global::System.Runtime.InteropServices.HandleRef getCPtr(SpkModel obj) {
|
||||
return (obj == null) ? new global::System.Runtime.InteropServices.HandleRef(null, global::System.IntPtr.Zero) : obj.handle;
|
||||
}
|
||||
|
||||
~SpkModel() {
|
||||
Dispose(false);
|
||||
}
|
||||
|
||||
public void Dispose() {
|
||||
Dispose(true);
|
||||
global::System.GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing) {
|
||||
lock(this) {
|
||||
if (handle.Handle != global::System.IntPtr.Zero) {
|
||||
VoskPINVOKE.delete_SpkModel(handle);
|
||||
handle = new global::System.Runtime.InteropServices.HandleRef(null, global::System.IntPtr.Zero);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public SpkModel(string model_path) : this(VoskPINVOKE.new_SpkModel(model_path)) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
namespace Vosk {
|
||||
|
||||
public class Vosk {
|
||||
public static void SetLogLevel(int level) {
|
||||
VoskPINVOKE.SetLogLevel(level);
|
||||
}
|
||||
|
||||
public static void GpuInit() {
|
||||
VoskPINVOKE.GpuInit();
|
||||
}
|
||||
|
||||
public static void GpuThreadInit() {
|
||||
VoskPINVOKE.GpuThreadInit();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
namespace Vosk {
|
||||
|
||||
class VoskPINVOKE {
|
||||
|
||||
static VoskPINVOKE() {
|
||||
}
|
||||
|
||||
[global::System.Runtime.InteropServices.DllImport("libvosk", EntryPoint="vosk_model_new")]
|
||||
public static extern global::System.IntPtr new_Model(string jarg1);
|
||||
|
||||
[global::System.Runtime.InteropServices.DllImport("libvosk", EntryPoint="vosk_model_free")]
|
||||
public static extern void delete_Model(global::System.Runtime.InteropServices.HandleRef jarg1);
|
||||
|
||||
[global::System.Runtime.InteropServices.DllImport("libvosk", EntryPoint="vosk_model_find_word")]
|
||||
public static extern int Model_vosk_model_find_word(global::System.Runtime.InteropServices.HandleRef jarg1, string jarg2);
|
||||
|
||||
[global::System.Runtime.InteropServices.DllImport("libvosk", EntryPoint="vosk_spk_model_new")]
|
||||
public static extern global::System.IntPtr new_SpkModel(string jarg1);
|
||||
|
||||
[global::System.Runtime.InteropServices.DllImport("libvosk", EntryPoint="vosk_spk_model_free")]
|
||||
public static extern void delete_SpkModel(global::System.Runtime.InteropServices.HandleRef jarg1);
|
||||
|
||||
[global::System.Runtime.InteropServices.DllImport("libvosk", EntryPoint="vosk_recognizer_new")]
|
||||
public static extern global::System.IntPtr new_VoskRecognizer(global::System.Runtime.InteropServices.HandleRef jarg1, float jarg2);
|
||||
|
||||
[global::System.Runtime.InteropServices.DllImport("libvosk", EntryPoint="vosk_recognizer_new_spk")]
|
||||
public static extern global::System.IntPtr new_VoskRecognizerSpk(global::System.Runtime.InteropServices.HandleRef jarg1, float jarg2, global::System.Runtime.InteropServices.HandleRef jarg3);
|
||||
|
||||
[global::System.Runtime.InteropServices.DllImport("libvosk", EntryPoint="vosk_recognizer_new_grm")]
|
||||
public static extern global::System.IntPtr new_VoskRecognizerGrm(global::System.Runtime.InteropServices.HandleRef jarg1, float jarg2, string jarg3);
|
||||
|
||||
[global::System.Runtime.InteropServices.DllImport("libvosk", EntryPoint="vosk_recognizer_free")]
|
||||
public static extern void delete_VoskRecognizer(global::System.Runtime.InteropServices.HandleRef jarg1);
|
||||
|
||||
[global::System.Runtime.InteropServices.DllImport("libvosk", EntryPoint="vosk_recognizer_set_max_alternatives")]
|
||||
public static extern void VoskRecognizer_SetMaxAlternatives(global::System.Runtime.InteropServices.HandleRef jarg1, int jarg2);
|
||||
|
||||
[global::System.Runtime.InteropServices.DllImport("libvosk", EntryPoint="vosk_recognizer_set_words")]
|
||||
public static extern void VoskRecognizer_SetWords(global::System.Runtime.InteropServices.HandleRef jarg1, int jarg2);
|
||||
|
||||
[global::System.Runtime.InteropServices.DllImport("libvosk", EntryPoint="vosk_recognizer_set_partial_words")]
|
||||
public static extern void VoskRecognizer_SetPartialWords(global::System.Runtime.InteropServices.HandleRef jarg1, int jarg2);
|
||||
|
||||
[global::System.Runtime.InteropServices.DllImport("libvosk", EntryPoint="vosk_recognizer_set_spk_model")]
|
||||
public static extern void VoskRecognizer_SetSpkModel(global::System.Runtime.InteropServices.HandleRef jarg1, global::System.Runtime.InteropServices.HandleRef jarg2);
|
||||
|
||||
[global::System.Runtime.InteropServices.DllImport("libvosk", EntryPoint="vosk_recognizer_accept_waveform")]
|
||||
public static extern bool VoskRecognizer_AcceptWaveform(global::System.Runtime.InteropServices.HandleRef jarg1, [global::System.Runtime.InteropServices.In, global::System.Runtime.InteropServices.MarshalAs(global::System.Runtime.InteropServices.UnmanagedType.LPArray)]byte[] jarg2, int jarg3);
|
||||
|
||||
[global::System.Runtime.InteropServices.DllImport("libvosk", EntryPoint="vosk_recognizer_accept_waveform_s")]
|
||||
public static extern bool VoskRecognizer_AcceptWaveformShort(global::System.Runtime.InteropServices.HandleRef jarg1, [global::System.Runtime.InteropServices.In, global::System.Runtime.InteropServices.MarshalAs(global::System.Runtime.InteropServices.UnmanagedType.LPArray)]short[] jarg2, int jarg3);
|
||||
|
||||
[global::System.Runtime.InteropServices.DllImport("libvosk", EntryPoint="vosk_recognizer_accept_waveform_f")]
|
||||
public static extern bool VoskRecognizer_AcceptWaveformFloat(global::System.Runtime.InteropServices.HandleRef jarg1, [global::System.Runtime.InteropServices.In, global::System.Runtime.InteropServices.MarshalAs(global::System.Runtime.InteropServices.UnmanagedType.LPArray)]float[] jarg2, int jarg3);
|
||||
|
||||
[global::System.Runtime.InteropServices.DllImport("libvosk", EntryPoint="vosk_recognizer_result")]
|
||||
public static extern global::System.IntPtr VoskRecognizer_Result(global::System.Runtime.InteropServices.HandleRef jarg1);
|
||||
|
||||
[global::System.Runtime.InteropServices.DllImport("libvosk", EntryPoint="vosk_recognizer_partial_result")]
|
||||
public static extern global::System.IntPtr VoskRecognizer_PartialResult(global::System.Runtime.InteropServices.HandleRef jarg1);
|
||||
|
||||
[global::System.Runtime.InteropServices.DllImport("libvosk", EntryPoint="vosk_recognizer_final_result")]
|
||||
public static extern global::System.IntPtr VoskRecognizer_FinalResult(global::System.Runtime.InteropServices.HandleRef jarg1);
|
||||
|
||||
[global::System.Runtime.InteropServices.DllImport("libvosk", EntryPoint="vosk_recognizer_reset")]
|
||||
public static extern void VoskRecognizer_Reset(global::System.Runtime.InteropServices.HandleRef jarg1);
|
||||
|
||||
[global::System.Runtime.InteropServices.DllImport("libvosk", EntryPoint="vosk_set_log_level")]
|
||||
public static extern void SetLogLevel(int jarg1);
|
||||
|
||||
[global::System.Runtime.InteropServices.DllImport("libvosk", EntryPoint="vosk_gpu_init")]
|
||||
public static extern void GpuInit();
|
||||
|
||||
[global::System.Runtime.InteropServices.DllImport("libvosk", EntryPoint="vosk_gpu_thread_init")]
|
||||
public static extern void GpuThreadInit();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
namespace Vosk {
|
||||
|
||||
public class VoskRecognizer : System.IDisposable {
|
||||
private System.Runtime.InteropServices.HandleRef handle;
|
||||
|
||||
internal VoskRecognizer(System.IntPtr cPtr) {
|
||||
handle = new System.Runtime.InteropServices.HandleRef(this, cPtr);
|
||||
}
|
||||
|
||||
internal static System.Runtime.InteropServices.HandleRef getCPtr(VoskRecognizer obj) {
|
||||
return (obj == null) ? new System.Runtime.InteropServices.HandleRef(null, System.IntPtr.Zero) : obj.handle;
|
||||
}
|
||||
|
||||
~VoskRecognizer() {
|
||||
Dispose(false);
|
||||
}
|
||||
|
||||
public void Dispose() {
|
||||
Dispose(true);
|
||||
System.GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing) {
|
||||
lock(this) {
|
||||
if (handle.Handle != System.IntPtr.Zero) {
|
||||
VoskPINVOKE.delete_VoskRecognizer(handle);
|
||||
handle = new System.Runtime.InteropServices.HandleRef(null, System.IntPtr.Zero);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public VoskRecognizer(Model model, float sample_rate) : this(VoskPINVOKE.new_VoskRecognizer(Model.getCPtr(model), sample_rate)) {
|
||||
}
|
||||
|
||||
public VoskRecognizer(Model model, float sample_rate, SpkModel spk_model) : this(VoskPINVOKE.new_VoskRecognizerSpk(Model.getCPtr(model), sample_rate, SpkModel.getCPtr(spk_model))) {
|
||||
}
|
||||
|
||||
public VoskRecognizer(Model model, float sample_rate, string grammar) : this(VoskPINVOKE.new_VoskRecognizerGrm(Model.getCPtr(model), sample_rate, grammar)) {
|
||||
}
|
||||
|
||||
public void SetMaxAlternatives(int max_alternatives) {
|
||||
VoskPINVOKE.VoskRecognizer_SetMaxAlternatives(handle, max_alternatives);
|
||||
}
|
||||
|
||||
public void SetWords(bool words) {
|
||||
VoskPINVOKE.VoskRecognizer_SetWords(handle, words ? 1 : 0);
|
||||
}
|
||||
|
||||
public void SetPartialWords(bool partial_words) {
|
||||
VoskPINVOKE.VoskRecognizer_SetPartialWords(handle, partial_words ? 1 : 0);
|
||||
}
|
||||
|
||||
public void SetSpkModel(SpkModel spk_model) {
|
||||
VoskPINVOKE.VoskRecognizer_SetSpkModel(handle, SpkModel.getCPtr(spk_model));
|
||||
}
|
||||
|
||||
public bool AcceptWaveform(byte[] data, int len) {
|
||||
return VoskPINVOKE.VoskRecognizer_AcceptWaveform(handle, data, len);
|
||||
}
|
||||
|
||||
public bool AcceptWaveform(short[] sdata, int len) {
|
||||
return VoskPINVOKE.VoskRecognizer_AcceptWaveformShort(handle, sdata, len);
|
||||
}
|
||||
|
||||
public bool AcceptWaveform(float[] fdata, int len) {
|
||||
return VoskPINVOKE.VoskRecognizer_AcceptWaveformFloat(handle, fdata, len);
|
||||
}
|
||||
|
||||
private static string PtrToStringUTF8(System.IntPtr ptr) {
|
||||
int len = 0;
|
||||
while (System.Runtime.InteropServices.Marshal.ReadByte(ptr, len) != 0)
|
||||
len++;
|
||||
byte[] array = new byte[len];
|
||||
System.Runtime.InteropServices.Marshal.Copy(ptr, array, 0, len);
|
||||
return System.Text.Encoding.UTF8.GetString(array);
|
||||
}
|
||||
|
||||
public string Result() {
|
||||
return PtrToStringUTF8(VoskPINVOKE.VoskRecognizer_Result(handle));
|
||||
}
|
||||
|
||||
public string PartialResult() {
|
||||
return PtrToStringUTF8(VoskPINVOKE.VoskRecognizer_PartialResult(handle));
|
||||
}
|
||||
|
||||
public string FinalResult() {
|
||||
return PtrToStringUTF8(VoskPINVOKE.VoskRecognizer_FinalResult(handle));
|
||||
}
|
||||
|
||||
public void Reset() {
|
||||
VoskPINVOKE.VoskRecognizer_Reset(handle);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using Kaldi;
|
||||
|
||||
public class Test
|
||||
{
|
||||
public static void Main()
|
||||
{
|
||||
|
||||
Vosk.SetLogLevel(0);
|
||||
Model model = new Model("model");
|
||||
KaldiRecognizer rec = new KaldiRecognizer(model, 16000.0f);
|
||||
|
||||
using(Stream source = File.OpenRead("test.wav")) {
|
||||
byte[] buffer = new byte[4096];
|
||||
int bytesRead;
|
||||
while((bytesRead = source.Read(buffer, 0, buffer.Length)) > 0) {
|
||||
if (rec.AcceptWaveform(buffer, bytesRead)) {
|
||||
Console.WriteLine(rec.Result());
|
||||
} else {
|
||||
Console.WriteLine(rec.PartialResult());
|
||||
}
|
||||
}
|
||||
}
|
||||
Console.WriteLine(rec.FinalResult());
|
||||
|
||||
rec = new KaldiRecognizer(model, 16000.0f);
|
||||
|
||||
using(Stream source = File.OpenRead("test.wav")) {
|
||||
byte[] buffer = new byte[4096];
|
||||
int bytesRead;
|
||||
while((bytesRead = source.Read(buffer, 0, buffer.Length)) > 0) {
|
||||
float[] fbuffer = new float[bytesRead / 2];
|
||||
for (int i = 0, n = 0; i < fbuffer.Length; i++, n+=2) {
|
||||
fbuffer[i] = (short)(buffer[n] | buffer[n+1] << 8);
|
||||
}
|
||||
if (rec.AcceptWaveform(fbuffer, fbuffer.Length)) {
|
||||
Console.WriteLine(rec.Result());
|
||||
GC.Collect();
|
||||
} else {
|
||||
Console.WriteLine(rec.PartialResult());
|
||||
}
|
||||
}
|
||||
}
|
||||
Console.WriteLine(rec.FinalResult());
|
||||
}
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
See https://alphacephei.com/vosk/accuracy.html
|
||||
@@ -1 +0,0 @@
|
||||
See https://alphacephei.com/vosk/adaptation.html
|
||||
@@ -1 +0,0 @@
|
||||
See https://alphacephei.com/vosk/models.html
|
||||
+176
@@ -0,0 +1,176 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. this License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
@@ -0,0 +1 @@
|
||||
See example subfolder for instructions how to use the module
|
||||
@@ -0,0 +1,7 @@
|
||||
// Go bindings for Vosk speech recognition toolkit. Vosk is an offline
|
||||
// open source speech to text API for Android, iOS, Raspberry Pi and
|
||||
// servers. It enables speech recognition models for 18 languages and
|
||||
// dialects - English, Indian English, German, French, Spanish, Portuguese,
|
||||
// Chinese, Russian, Turkish, Vietnamese, Italian, Dutch, Catalan, Arabic,
|
||||
// Greek, Farsi, Filipino, Ukrainian.
|
||||
package vosk
|
||||
@@ -0,0 +1,31 @@
|
||||
To try this package do the following steps:
|
||||
|
||||
On Linux (we download library and set LD_LIBRARY_PATH)
|
||||
|
||||
```
|
||||
git clone https://github.com/alphacep/vosk-api
|
||||
cd vosk-api/go/example
|
||||
wget https://github.com/alphacep/vosk-api/releases/download/v0.3.45/vosk-linux-x86_64-0.3.45.zip
|
||||
unzip vosk-linux-x86_64-0.3.45.zip
|
||||
wget https://alphacephei.com/vosk/models/vosk-model-small-en-us-0.15.zip
|
||||
unzip vosk-model-small-en-us-0.15.zip
|
||||
mv vosk-model-small-en-us-0.15 model
|
||||
cp ../../python/example/test.wav .
|
||||
VOSK_PATH=`pwd`/vosk-linux-x86_64-0.3.45 LD_LIBRARY_PATH=$VOSK_PATH CGO_CPPFLAGS="-I $VOSK_PATH" CGO_LDFLAGS="-L $VOSK_PATH" go run . -f test.wav
|
||||
```
|
||||
|
||||
for Windows (we place DLLs in current folder where linker finds them):
|
||||
|
||||
```
|
||||
git clone https://github.com/alphacep/vosk-api
|
||||
cd vosk-api/go/example
|
||||
wget https://github.com/alphacep/vosk-api/releases/download/v0.3.45/vosk-linux-x86_64-0.3.45.zip
|
||||
unzip vosk-linux-x86_64-0.3.45.zip
|
||||
cp vosk-linux-x86_64-0.3.45/*.dll .
|
||||
cp vosk-linux-x86_64-0.3.45/*.h .
|
||||
wget https://alphacephei.com/vosk/models/vosk-model-small-en-us-0.15.zip
|
||||
unzip vosk-model-small-en-us-0.15.zip
|
||||
mv vosk-model-small-en-us-0.15 model
|
||||
cp ../../python/example/test.wav .
|
||||
VOSK_PATH=`pwd` LD_LIBRARY_PATH=$VOSK_PATH CGO_CPPFLAGS="-I $VOSK_PATH" CGO_LDFLAGS="-L $VOSK_PATH -lvosk -lpthread -dl" go run . -f test.wav
|
||||
```
|
||||
@@ -0,0 +1,2 @@
|
||||
// Example package for Vosk Go bindings.
|
||||
package main
|
||||
@@ -0,0 +1,63 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"encoding/json"
|
||||
|
||||
vosk "github.com/alphacep/vosk-api/go"
|
||||
)
|
||||
|
||||
func main() {
|
||||
var filename string
|
||||
flag.StringVar(&filename, "f", "", "file to transcribe")
|
||||
flag.Parse()
|
||||
|
||||
model, err := vosk.NewModel("model")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// we can check if word is in the vocabulary
|
||||
// fmt.Println(model.FindWord("air"))
|
||||
|
||||
sampleRate := 16000.0
|
||||
rec, err := vosk.NewRecognizer(model, sampleRate)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
rec.SetWords(1)
|
||||
|
||||
file, err := os.Open(filename)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
reader := bufio.NewReader(file)
|
||||
buf := make([]byte, 4096)
|
||||
|
||||
for {
|
||||
_, err := reader.Read(buf)
|
||||
if err != nil {
|
||||
if err != io.EOF {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
|
||||
if rec.AcceptWaveform(buf) != 0 {
|
||||
fmt.Println(rec.Result())
|
||||
}
|
||||
}
|
||||
|
||||
// Unmarshal example for final result
|
||||
var jres map[string]interface{}
|
||||
json.Unmarshal([]byte(rec.FinalResult()), &jres)
|
||||
fmt.Println(jres["text"])
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
module github.com/alphacep/vosk-api/go
|
||||
|
||||
go 1.16
|
||||
|
||||
replace (
|
||||
github.com/alphacep/vosk-api/go => ./
|
||||
)
|
||||
+162
@@ -0,0 +1,162 @@
|
||||
package vosk
|
||||
|
||||
// #cgo CPPFLAGS: -I ${SRCDIR}/../src
|
||||
// #cgo LDFLAGS: -L ${SRCDIR}/../src -lvosk -ldl -lpthread
|
||||
// #include <stdlib.h>
|
||||
// #include <vosk_api.h>
|
||||
import "C"
|
||||
import "unsafe"
|
||||
|
||||
// VoskModel contains a reference to the C VoskModel
|
||||
type VoskModel struct {
|
||||
model *C.struct_VoskModel
|
||||
}
|
||||
|
||||
// NewModel creates a new VoskModel instance
|
||||
func NewModel(modelPath string) (*VoskModel, error) {
|
||||
cmodelPath := C.CString(modelPath)
|
||||
defer C.free(unsafe.Pointer(cmodelPath))
|
||||
internal := C.vosk_model_new(cmodelPath)
|
||||
model := &VoskModel{model: internal}
|
||||
return model, nil
|
||||
}
|
||||
|
||||
func (m *VoskModel) Free() {
|
||||
C.vosk_model_free(m.model)
|
||||
}
|
||||
|
||||
func freeModel(model *VoskModel) {
|
||||
C.vosk_model_free(model.model)
|
||||
}
|
||||
|
||||
// FindWord checks if a word can be recognized by the model.
|
||||
// Returns the word symbol if the word exists inside the model or
|
||||
// -1 otherwise.
|
||||
func (m *VoskModel) FindWord(word string) int {
|
||||
cstr := C.CString(word)
|
||||
defer C.free(unsafe.Pointer(cstr))
|
||||
i := C.vosk_model_find_word(m.model, cstr)
|
||||
return int(i)
|
||||
}
|
||||
|
||||
// VoskSpkModel contains a reference to the C VoskSpkModel
|
||||
type VoskSpkModel struct {
|
||||
spkModel *C.struct_VoskSpkModel
|
||||
}
|
||||
|
||||
// NewSpkModel creates a new VoskSpkModel instance
|
||||
func NewSpkModel(spkModelPath string) (*VoskSpkModel, error) {
|
||||
cspkModelPath := C.CString(spkModelPath)
|
||||
defer C.free(unsafe.Pointer(cspkModelPath))
|
||||
internal := C.vosk_spk_model_new(cspkModelPath)
|
||||
spkModel := &VoskSpkModel{spkModel: internal}
|
||||
return spkModel, nil
|
||||
}
|
||||
|
||||
func freeSpkModel(model *VoskSpkModel) {
|
||||
C.vosk_spk_model_free(model.spkModel)
|
||||
}
|
||||
|
||||
func(s *VoskSpkModel) Free() {
|
||||
C.vosk_spk_model_free(s.spkModel)
|
||||
}
|
||||
|
||||
// VoskRecognizer contains a reference to the C VoskRecognizer
|
||||
type VoskRecognizer struct {
|
||||
rec *C.struct_VoskRecognizer
|
||||
}
|
||||
|
||||
func freeRecognizer(recognizer *VoskRecognizer) {
|
||||
C.vosk_recognizer_free(recognizer.rec)
|
||||
}
|
||||
|
||||
func (r *VoskRecognizer) Free() {
|
||||
C.vosk_recognizer_free(r.rec)
|
||||
}
|
||||
|
||||
// NewRecognizer creates a new VoskRecognizer instance
|
||||
func NewRecognizer(model *VoskModel, sampleRate float64) (*VoskRecognizer, error) {
|
||||
internal := C.vosk_recognizer_new(model.model, C.float(sampleRate))
|
||||
rec := &VoskRecognizer{rec: internal}
|
||||
return rec, nil
|
||||
}
|
||||
|
||||
// NewRecognizerSpk creates a new VoskRecognizer instance with a speaker model.
|
||||
func NewRecognizerSpk(model *VoskModel, sampleRate float64, spkModel *VoskSpkModel) (*VoskRecognizer, error) {
|
||||
internal := C.vosk_recognizer_new_spk(model.model, C.float(sampleRate), spkModel.spkModel)
|
||||
rec := &VoskRecognizer{rec: internal}
|
||||
return rec, nil
|
||||
}
|
||||
|
||||
// NewRecognizerGrm creates a new VoskRecognizer instance with the phrase list.
|
||||
func NewRecognizerGrm(model *VoskModel, sampleRate float64, grammar string) (*VoskRecognizer, error) {
|
||||
cgrammar := C.CString(grammar)
|
||||
defer C.free(unsafe.Pointer(cgrammar))
|
||||
internal := C.vosk_recognizer_new_grm(model.model, C.float(sampleRate), cgrammar)
|
||||
rec := &VoskRecognizer{rec: internal}
|
||||
return rec, nil
|
||||
}
|
||||
|
||||
// SetSpkModel adds a speaker model to an already initialized recognizer.
|
||||
func (r *VoskRecognizer) SetSpkModel(spkModel *VoskSpkModel) {
|
||||
C.vosk_recognizer_set_spk_model(r.rec, spkModel.spkModel)
|
||||
}
|
||||
|
||||
// SetMaxAlternatives configures the recognizer to output n-best results.
|
||||
func (r *VoskRecognizer) SetMaxAlternatives(maxAlternatives int) {
|
||||
C.vosk_recognizer_set_max_alternatives(r.rec, C.int(maxAlternatives))
|
||||
}
|
||||
|
||||
// SetWords enables words with times in the ouput.
|
||||
func (r *VoskRecognizer) SetWords(words int) {
|
||||
C.vosk_recognizer_set_words(r.rec, C.int(words))
|
||||
}
|
||||
|
||||
// SetPartialWords enables words with times in the partial ouput.
|
||||
func (r *VoskRecognizer) SetPartialWords(words int) {
|
||||
C.vosk_recognizer_set_partial_words(r.rec, C.int(words))
|
||||
}
|
||||
|
||||
// AcceptWaveform accepts and processes a new chunk of the voice data.
|
||||
func (r *VoskRecognizer) AcceptWaveform(buffer []byte) int {
|
||||
cbuf := C.CBytes(buffer)
|
||||
defer C.free(cbuf)
|
||||
i := C.vosk_recognizer_accept_waveform(r.rec, (*C.char)(cbuf), C.int(len(buffer)))
|
||||
return int(i)
|
||||
}
|
||||
|
||||
// Result returns a speech recognition result.
|
||||
func (r *VoskRecognizer) Result() string {
|
||||
return C.GoString(C.vosk_recognizer_result(r.rec))
|
||||
}
|
||||
|
||||
// PartialResult returns a partial speech recognition result.
|
||||
func (r *VoskRecognizer) PartialResult() string {
|
||||
return C.GoString(C.vosk_recognizer_partial_result(r.rec))
|
||||
}
|
||||
|
||||
// FinalResult returns a speech recognition result. Same as result, but doesn't wait
|
||||
// for silence.
|
||||
func (r *VoskRecognizer) FinalResult() string {
|
||||
return C.GoString(C.vosk_recognizer_final_result(r.rec))
|
||||
}
|
||||
|
||||
// Reset resets the recognizer.
|
||||
func (r *VoskRecognizer) Reset() {
|
||||
C.vosk_recognizer_reset(r.rec)
|
||||
}
|
||||
|
||||
// SetLogLevel sets the log level for Kaldi messages.
|
||||
func SetLogLevel(logLevel int) {
|
||||
C.vosk_set_log_level(C.int(logLevel))
|
||||
}
|
||||
|
||||
// GPUInit automatically selects a CUDA device and allows multithreading.
|
||||
func GPUInit() {
|
||||
C.vosk_gpu_init()
|
||||
}
|
||||
|
||||
// GPUThreadInit inits CUDA device in a multi-threaded environment.
|
||||
func GPUThreadInit() {
|
||||
C.vosk_gpu_thread_init()
|
||||
}
|
||||
@@ -13,59 +13,15 @@
|
||||
92375229240C550B00DD6076 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 92375228240C550B00DD6076 /* Assets.xcassets */; };
|
||||
9237522C240C550B00DD6076 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 9237522A240C550B00DD6076 /* LaunchScreen.storyboard */; };
|
||||
92375234240C558900DD6076 /* Vosk.swift in Sources */ = {isa = PBXBuildFile; fileRef = 92375233240C558900DD6076 /* Vosk.swift */; };
|
||||
9237523C240C642000DD6076 /* libkaldiwrap.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 9237523A240C642000DD6076 /* libkaldiwrap.a */; };
|
||||
92375244240C6DAF00DD6076 /* Accelerate.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 92375243240C6DAF00DD6076 /* Accelerate.framework */; };
|
||||
92375246240C6DC900DD6076 /* libstdc++.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 92375245240C6DC900DD6076 /* libstdc++.tbd */; };
|
||||
92375266240C6EFE00DD6076 /* disambig_tid.int in CopyFiles */ = {isa = PBXBuildFile; fileRef = 92375248240C6E3D00DD6076 /* disambig_tid.int */; };
|
||||
92375267240C6EFE00DD6076 /* final.mdl in CopyFiles */ = {isa = PBXBuildFile; fileRef = 92375249240C6E3D00DD6076 /* final.mdl */; };
|
||||
92375268240C6EFE00DD6076 /* Gr.fst in CopyFiles */ = {isa = PBXBuildFile; fileRef = 9237524A240C6E3D00DD6076 /* Gr.fst */; };
|
||||
92375269240C6EFE00DD6076 /* HCLr.fst in CopyFiles */ = {isa = PBXBuildFile; fileRef = 9237524B240C6E3D00DD6076 /* HCLr.fst */; };
|
||||
9237526A240C6EFE00DD6076 /* mfcc.conf in CopyFiles */ = {isa = PBXBuildFile; fileRef = 92375253240C6E3D00DD6076 /* mfcc.conf */; };
|
||||
9237526B240C6EFE00DD6076 /* word_boundary.int in CopyFiles */ = {isa = PBXBuildFile; fileRef = 92375254240C6E3D00DD6076 /* word_boundary.int */; };
|
||||
9237526C240C6EFE00DD6076 /* words.txt in CopyFiles */ = {isa = PBXBuildFile; fileRef = 92375255240C6E3D00DD6076 /* words.txt */; };
|
||||
9237526E240C6F1500DD6076 /* final.dubm in CopyFiles */ = {isa = PBXBuildFile; fileRef = 9237524D240C6E3D00DD6076 /* final.dubm */; };
|
||||
9237526F240C6F1500DD6076 /* final.ie in CopyFiles */ = {isa = PBXBuildFile; fileRef = 9237524E240C6E3D00DD6076 /* final.ie */; };
|
||||
92375270240C6F1500DD6076 /* final.mat in CopyFiles */ = {isa = PBXBuildFile; fileRef = 9237524F240C6E3D00DD6076 /* final.mat */; };
|
||||
92375271240C6F1500DD6076 /* global_cmvn.stats in CopyFiles */ = {isa = PBXBuildFile; fileRef = 92375250240C6E3D00DD6076 /* global_cmvn.stats */; };
|
||||
92375272240C6F1500DD6076 /* online_cmvn.conf in CopyFiles */ = {isa = PBXBuildFile; fileRef = 92375251240C6E3D00DD6076 /* online_cmvn.conf */; };
|
||||
92375273240C6F1500DD6076 /* splice.conf in CopyFiles */ = {isa = PBXBuildFile; fileRef = 92375252240C6E3D00DD6076 /* splice.conf */; };
|
||||
92375274240C6F1E00DD6076 /* 10001-90210-01803.wav in Resources */ = {isa = PBXBuildFile; fileRef = 92375256240C6E3D00DD6076 /* 10001-90210-01803.wav */; };
|
||||
925527A9273C492C00FFD9CC /* libvosk.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 925527A8273C492C00FFD9CC /* libvosk.a */; };
|
||||
92833003273C466E00058B52 /* libc++.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 92833002273C466E00058B52 /* libc++.tbd */; };
|
||||
92BACED125BE125A00B5CC93 /* vosk-model-small-en-us-0.15 in Resources */ = {isa = PBXBuildFile; fileRef = 928CC50C25BE124400490481 /* vosk-model-small-en-us-0.15 */; };
|
||||
92D6B8D325BDFEAC007FF08D /* VoskModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 92D6B8D225BDFEAC007FF08D /* VoskModel.swift */; };
|
||||
92D86BD6253F823F0040D53F /* vosk-model-spk-0.4 in Resources */ = {isa = PBXBuildFile; fileRef = 92D86BD4253F823F0040D53F /* vosk-model-spk-0.4 */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXCopyFilesBuildPhase section */
|
||||
92375265240C6ECF00DD6076 /* CopyFiles */ = {
|
||||
isa = PBXCopyFilesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
dstPath = "model-en";
|
||||
dstSubfolderSpec = 7;
|
||||
files = (
|
||||
92375266240C6EFE00DD6076 /* disambig_tid.int in CopyFiles */,
|
||||
92375267240C6EFE00DD6076 /* final.mdl in CopyFiles */,
|
||||
92375268240C6EFE00DD6076 /* Gr.fst in CopyFiles */,
|
||||
92375269240C6EFE00DD6076 /* HCLr.fst in CopyFiles */,
|
||||
9237526A240C6EFE00DD6076 /* mfcc.conf in CopyFiles */,
|
||||
9237526B240C6EFE00DD6076 /* word_boundary.int in CopyFiles */,
|
||||
9237526C240C6EFE00DD6076 /* words.txt in CopyFiles */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
9237526D240C6F0400DD6076 /* CopyFiles */ = {
|
||||
isa = PBXCopyFilesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
dstPath = "model-en/ivector";
|
||||
dstSubfolderSpec = 7;
|
||||
files = (
|
||||
9237526E240C6F1500DD6076 /* final.dubm in CopyFiles */,
|
||||
9237526F240C6F1500DD6076 /* final.ie in CopyFiles */,
|
||||
92375270240C6F1500DD6076 /* final.mat in CopyFiles */,
|
||||
92375271240C6F1500DD6076 /* global_cmvn.stats in CopyFiles */,
|
||||
92375272240C6F1500DD6076 /* online_cmvn.conf in CopyFiles */,
|
||||
92375273240C6F1500DD6076 /* splice.conf in CopyFiles */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXCopyFilesBuildPhase section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
9237521E240C550B00DD6076 /* VoskApiTest.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = VoskApiTest.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
92375221240C550B00DD6076 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
||||
@@ -75,25 +31,15 @@
|
||||
9237522B240C550B00DD6076 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
|
||||
9237522D240C550B00DD6076 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
92375233240C558900DD6076 /* Vosk.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Vosk.swift; sourceTree = "<group>"; };
|
||||
9237523A240C642000DD6076 /* libkaldiwrap.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libkaldiwrap.a; sourceTree = "<group>"; };
|
||||
92375243240C6DAF00DD6076 /* Accelerate.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Accelerate.framework; path = System/Library/Frameworks/Accelerate.framework; sourceTree = SDKROOT; };
|
||||
92375245240C6DC900DD6076 /* libstdc++.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = "libstdc++.tbd"; path = "usr/lib/libstdc++.tbd"; sourceTree = SDKROOT; };
|
||||
92375248240C6E3D00DD6076 /* disambig_tid.int */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = disambig_tid.int; sourceTree = "<group>"; };
|
||||
92375249240C6E3D00DD6076 /* final.mdl */ = {isa = PBXFileReference; lastKnownFileType = file; path = final.mdl; sourceTree = "<group>"; };
|
||||
9237524A240C6E3D00DD6076 /* Gr.fst */ = {isa = PBXFileReference; lastKnownFileType = file; path = Gr.fst; sourceTree = "<group>"; };
|
||||
9237524B240C6E3D00DD6076 /* HCLr.fst */ = {isa = PBXFileReference; lastKnownFileType = file; path = HCLr.fst; sourceTree = "<group>"; };
|
||||
9237524D240C6E3D00DD6076 /* final.dubm */ = {isa = PBXFileReference; lastKnownFileType = file; path = final.dubm; sourceTree = "<group>"; };
|
||||
9237524E240C6E3D00DD6076 /* final.ie */ = {isa = PBXFileReference; lastKnownFileType = file; path = final.ie; sourceTree = "<group>"; };
|
||||
9237524F240C6E3D00DD6076 /* final.mat */ = {isa = PBXFileReference; lastKnownFileType = file; path = final.mat; sourceTree = "<group>"; };
|
||||
92375250240C6E3D00DD6076 /* global_cmvn.stats */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = global_cmvn.stats; sourceTree = "<group>"; };
|
||||
92375251240C6E3D00DD6076 /* online_cmvn.conf */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = online_cmvn.conf; sourceTree = "<group>"; };
|
||||
92375252240C6E3D00DD6076 /* splice.conf */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = splice.conf; sourceTree = "<group>"; };
|
||||
92375253240C6E3D00DD6076 /* mfcc.conf */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = mfcc.conf; sourceTree = "<group>"; };
|
||||
92375254240C6E3D00DD6076 /* word_boundary.int */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = word_boundary.int; sourceTree = "<group>"; };
|
||||
92375255240C6E3D00DD6076 /* words.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = words.txt; sourceTree = "<group>"; };
|
||||
92375256240C6E3D00DD6076 /* 10001-90210-01803.wav */ = {isa = PBXFileReference; lastKnownFileType = audio.wav; path = "10001-90210-01803.wav"; sourceTree = "<group>"; };
|
||||
925527A8273C492C00FFD9CC /* libvosk.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libvosk.a; sourceTree = "<group>"; };
|
||||
92833002273C466E00058B52 /* libc++.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = "libc++.tbd"; path = "usr/lib/libc++.tbd"; sourceTree = SDKROOT; };
|
||||
928CC50C25BE124400490481 /* vosk-model-small-en-us-0.15 */ = {isa = PBXFileReference; lastKnownFileType = folder; name = "vosk-model-small-en-us-0.15"; path = "/Users/shmyrev/Documents/IOS/VoskApiTest/VoskApiTest/Vosk/vosk-model-small-en-us-0.15"; sourceTree = "<absolute>"; };
|
||||
92AA22AD244CDD1200DA464B /* vosk_api.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = vosk_api.h; sourceTree = "<group>"; };
|
||||
92AA22AE244CDD5200DA464B /* bridging.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = bridging.h; sourceTree = "<group>"; };
|
||||
92D6B8D225BDFEAC007FF08D /* VoskModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VoskModel.swift; sourceTree = "<group>"; };
|
||||
92D86BD4253F823F0040D53F /* vosk-model-spk-0.4 */ = {isa = PBXFileReference; lastKnownFileType = folder; path = "vosk-model-spk-0.4"; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
@@ -101,9 +47,9 @@
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
92375246240C6DC900DD6076 /* libstdc++.tbd in Frameworks */,
|
||||
92833003273C466E00058B52 /* libc++.tbd in Frameworks */,
|
||||
92375244240C6DAF00DD6076 /* Accelerate.framework in Frameworks */,
|
||||
9237523C240C642000DD6076 /* libkaldiwrap.a in Frameworks */,
|
||||
925527A9273C492C00FFD9CC /* libvosk.a in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@@ -139,6 +85,7 @@
|
||||
9237522A240C550B00DD6076 /* LaunchScreen.storyboard */,
|
||||
9237522D240C550B00DD6076 /* Info.plist */,
|
||||
92375233240C558900DD6076 /* Vosk.swift */,
|
||||
92D6B8D225BDFEAC007FF08D /* VoskModel.swift */,
|
||||
);
|
||||
path = VoskApiTest;
|
||||
sourceTree = "<group>";
|
||||
@@ -146,10 +93,11 @@
|
||||
92375239240C642000DD6076 /* Vosk */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
92375247240C6E3D00DD6076 /* model-android */,
|
||||
92375256240C6E3D00DD6076 /* 10001-90210-01803.wav */,
|
||||
92D86BD4253F823F0040D53F /* vosk-model-spk-0.4 */,
|
||||
928CC50C25BE124400490481 /* vosk-model-small-en-us-0.15 */,
|
||||
925527A8273C492C00FFD9CC /* libvosk.a */,
|
||||
92AA22AD244CDD1200DA464B /* vosk_api.h */,
|
||||
9237523A240C642000DD6076 /* libkaldiwrap.a */,
|
||||
92375256240C6E3D00DD6076 /* 10001-90210-01803.wav */,
|
||||
);
|
||||
name = Vosk;
|
||||
path = VoskApiTest/Vosk;
|
||||
@@ -158,40 +106,12 @@
|
||||
92375242240C6DAF00DD6076 /* Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
92375245240C6DC900DD6076 /* libstdc++.tbd */,
|
||||
92833002273C466E00058B52 /* libc++.tbd */,
|
||||
92375243240C6DAF00DD6076 /* Accelerate.framework */,
|
||||
);
|
||||
name = Frameworks;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
92375247240C6E3D00DD6076 /* model-android */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
92375248240C6E3D00DD6076 /* disambig_tid.int */,
|
||||
92375249240C6E3D00DD6076 /* final.mdl */,
|
||||
9237524A240C6E3D00DD6076 /* Gr.fst */,
|
||||
9237524B240C6E3D00DD6076 /* HCLr.fst */,
|
||||
9237524C240C6E3D00DD6076 /* ivector */,
|
||||
92375253240C6E3D00DD6076 /* mfcc.conf */,
|
||||
92375254240C6E3D00DD6076 /* word_boundary.int */,
|
||||
92375255240C6E3D00DD6076 /* words.txt */,
|
||||
);
|
||||
path = "model-android";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
9237524C240C6E3D00DD6076 /* ivector */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
9237524D240C6E3D00DD6076 /* final.dubm */,
|
||||
9237524E240C6E3D00DD6076 /* final.ie */,
|
||||
9237524F240C6E3D00DD6076 /* final.mat */,
|
||||
92375250240C6E3D00DD6076 /* global_cmvn.stats */,
|
||||
92375251240C6E3D00DD6076 /* online_cmvn.conf */,
|
||||
92375252240C6E3D00DD6076 /* splice.conf */,
|
||||
);
|
||||
path = ivector;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
@@ -202,8 +122,6 @@
|
||||
9237521A240C550B00DD6076 /* Sources */,
|
||||
9237521B240C550B00DD6076 /* Frameworks */,
|
||||
9237521C240C550B00DD6076 /* Resources */,
|
||||
92375265240C6ECF00DD6076 /* CopyFiles */,
|
||||
9237526D240C6F0400DD6076 /* CopyFiles */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
@@ -227,7 +145,7 @@
|
||||
9237521D240C550B00DD6076 = {
|
||||
CreatedOnToolsVersion = 8.3.2;
|
||||
LastSwiftMigration = 0920;
|
||||
ProvisioningStyle = Automatic;
|
||||
ProvisioningStyle = Manual;
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -254,9 +172,11 @@
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
92BACED125BE125A00B5CC93 /* vosk-model-small-en-us-0.15 in Resources */,
|
||||
92375274240C6F1E00DD6076 /* 10001-90210-01803.wav in Resources */,
|
||||
9237522C240C550B00DD6076 /* LaunchScreen.storyboard in Resources */,
|
||||
92375229240C550B00DD6076 /* Assets.xcassets in Resources */,
|
||||
92D86BD6253F823F0040D53F /* vosk-model-spk-0.4 in Resources */,
|
||||
92375227240C550B00DD6076 /* Main.storyboard in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
@@ -270,6 +190,7 @@
|
||||
files = (
|
||||
92375224240C550B00DD6076 /* ViewController.swift in Sources */,
|
||||
92375222240C550B00DD6076 /* AppDelegate.swift in Sources */,
|
||||
92D6B8D325BDFEAC007FF08D /* VoskModel.swift in Sources */,
|
||||
92375234240C558900DD6076 /* Vosk.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
@@ -302,7 +223,6 @@
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
@@ -324,7 +244,7 @@
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
@@ -361,7 +281,6 @@
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
@@ -383,7 +302,7 @@
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
ENABLE_NS_ASSERTIONS = NO;
|
||||
@@ -398,6 +317,7 @@
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 10.3;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
SDKROOT = iphoneos;
|
||||
SWIFT_OBJC_BRIDGING_HEADER = bridging.h;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
|
||||
@@ -412,6 +332,9 @@
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
DEVELOPMENT_TEAM = "";
|
||||
ENABLE_BITCODE = NO;
|
||||
INFOPLIST_FILE = VoskApiTest/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||
@@ -421,11 +344,13 @@
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.alphacephei.VoskApiTest;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
SWIFT_INSTALL_OBJC_HEADER = YES;
|
||||
SWIFT_OBJC_BRIDGING_HEADER = VoskApiTest/bridging.h;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
SWIFT_SWIFT3_OBJC_INFERENCE = Default;
|
||||
SWIFT_VERSION = 4.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
@@ -434,6 +359,9 @@
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
DEVELOPMENT_TEAM = "";
|
||||
ENABLE_BITCODE = NO;
|
||||
INFOPLIST_FILE = VoskApiTest/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||
@@ -443,10 +371,12 @@
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.alphacephei.VoskApiTest;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
SWIFT_INSTALL_OBJC_HEADER = YES;
|
||||
SWIFT_OBJC_BRIDGING_HEADER = VoskApiTest/bridging.h;
|
||||
SWIFT_SWIFT3_OBJC_INFERENCE = Default;
|
||||
SWIFT_VERSION = 4.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
|
||||
@@ -2,6 +2,6 @@
|
||||
<Workspace
|
||||
version = "1.0">
|
||||
<FileRef
|
||||
location = "self:VoskApiTest.xcodeproj">
|
||||
location = "self:">
|
||||
</FileRef>
|
||||
</Workspace>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13771" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="BYZ-38-t0r">
|
||||
<device id="retina4_7" orientation="portrait">
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13771" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="bdW-KL-Y8Z">
|
||||
<device id="retina5_5" orientation="portrait">
|
||||
<adaptation id="fullscreen"/>
|
||||
</device>
|
||||
<dependencies>
|
||||
@@ -10,23 +10,52 @@
|
||||
</dependencies>
|
||||
<scenes>
|
||||
<!--View Controller-->
|
||||
<scene sceneID="tne-QT-ifu">
|
||||
<scene sceneID="nEc-89-Iqu">
|
||||
<objects>
|
||||
<viewController id="BYZ-38-t0r" customClass="ViewController" customModule="VoskApiTest" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<textView key="view" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" editable="NO" textAlignment="natural" id="CtX-mx-X98">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<viewController id="bdW-KL-Y8Z" customClass="ViewController" customModule="VoskApiTest" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<layoutGuides>
|
||||
<viewControllerLayoutGuide type="top" id="Hyr-Dz-4mU"/>
|
||||
<viewControllerLayoutGuide type="bottom" id="w4A-5X-uBu"/>
|
||||
</layoutGuides>
|
||||
<view key="view" contentMode="scaleToFill" id="m5v-US-bvR">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="736"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<button opaque="NO" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="IaT-no-U3i" userLabel="Microphone">
|
||||
<rect key="frame" x="124" y="34" width="157" height="30"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<state key="normal" title="Recognize Microphone"/>
|
||||
<connections>
|
||||
<action selector="runRecognizeMicrohpone:" destination="bdW-KL-Y8Z" eventType="touchUpInside" id="hGB-lz-N2B"/>
|
||||
</connections>
|
||||
</button>
|
||||
<button opaque="NO" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="GC5-nT-FQR" userLabel="File">
|
||||
<rect key="frame" x="90" y="84" width="221" height="41"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<state key="normal" title="Recognize File"/>
|
||||
<connections>
|
||||
<action selector="runRecognizeFile:" destination="bdW-KL-Y8Z" eventType="touchUpInside" id="xp5-Yi-rnN"/>
|
||||
</connections>
|
||||
</button>
|
||||
<textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" fixedFrame="YES" text="Results here" textAlignment="natural" translatesAutoresizingMaskIntoConstraints="NO" id="w4X-cu-USq">
|
||||
<rect key="frame" x="11" y="112" width="383" height="569"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="14"/>
|
||||
<textInputTraits key="textInputTraits" autocapitalizationType="sentences"/>
|
||||
</textView>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="14"/>
|
||||
<textInputTraits key="textInputTraits" autocapitalizationType="sentences"/>
|
||||
</textView>
|
||||
</view>
|
||||
<connections>
|
||||
<outlet property="mainText" destination="CtX-mx-X98" id="oJy-5J-NKp"/>
|
||||
<outlet property="mainText" destination="w4X-cu-USq" id="rZS-nz-Wql"/>
|
||||
<outlet property="recognizeFile" destination="GC5-nT-FQR" id="dRe-tc-IA0"/>
|
||||
<outlet property="recognizeMicrophone" destination="IaT-no-U3i" id="IuM-aa-pAP"/>
|
||||
</connections>
|
||||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="nWA-4D-pA6" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="32.799999999999997" y="32.833583208395808"/>
|
||||
<point key="canvasLocation" x="-17.39130434782609" y="-285.32608695652175"/>
|
||||
</scene>
|
||||
</scenes>
|
||||
</document>
|
||||
|
||||
@@ -3,30 +3,113 @@
|
||||
// VoskApiTest
|
||||
//
|
||||
// Created by Niсkolay Shmyrev on 01.03.20.
|
||||
// Copyright © 2020 Alpha Cephei. All rights reserved.
|
||||
// Copyright © 2020-2021 Alpha Cephei. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import AVFoundation
|
||||
|
||||
enum WorkMode {
|
||||
case stopped
|
||||
case microphone
|
||||
case file
|
||||
}
|
||||
|
||||
class ViewController: UIViewController {
|
||||
|
||||
@IBOutlet var mainText: UITextView!
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
DispatchQueue.global(qos: .userInitiated).async {
|
||||
DispatchQueue.main.async {
|
||||
self.mainText.text = "Processing file..."
|
||||
var mode: WorkMode!
|
||||
|
||||
@IBOutlet weak var recognizeFile: UIButton!
|
||||
@IBOutlet weak var mainText: UITextView!
|
||||
@IBOutlet weak var recognizeMicrophone: UIButton!
|
||||
|
||||
var audioEngine : AVAudioEngine!
|
||||
var processingQueue: DispatchQueue!
|
||||
var model : VoskModel!
|
||||
|
||||
func setMode(mode: WorkMode) {
|
||||
switch mode {
|
||||
case .stopped:
|
||||
self.recognizeFile.isEnabled = true
|
||||
self.recognizeMicrophone.isEnabled = true
|
||||
self.recognizeMicrophone.setTitle("Recognize Microphone",for: .normal)
|
||||
case .microphone:
|
||||
self.recognizeFile.isEnabled = false
|
||||
self.recognizeMicrophone.isEnabled = true
|
||||
self.recognizeMicrophone.setTitle("Stop Microphone",for: .normal)
|
||||
self.mainText.text = ""
|
||||
case .file:
|
||||
self.recognizeFile.isEnabled = false
|
||||
self.recognizeMicrophone.isEnabled = false
|
||||
self.mainText.text = "Processing file..."
|
||||
}
|
||||
self.mode = mode
|
||||
}
|
||||
|
||||
func startAudioEngine() {
|
||||
do {
|
||||
|
||||
// Create a new audio engine.
|
||||
audioEngine = AVAudioEngine()
|
||||
|
||||
let inputNode = audioEngine.inputNode
|
||||
let formatInput = inputNode.inputFormat(forBus: 0)
|
||||
let formatPcm = AVAudioFormat.init(commonFormat: AVAudioCommonFormat.pcmFormatInt16, sampleRate: formatInput.sampleRate, channels: 1, interleaved: true)
|
||||
|
||||
let recognizer = Vosk(model: model, sampleRate: Float(formatInput.sampleRate))
|
||||
|
||||
inputNode.installTap(onBus: 0,
|
||||
bufferSize: UInt32(formatInput.sampleRate / 10),
|
||||
format: formatPcm) { buffer, time in
|
||||
self.processingQueue.async {
|
||||
let res = recognizer.recognizeData(buffer: buffer)
|
||||
DispatchQueue.main.async {
|
||||
self.mainText.text = res + "\n" + self.mainText.text
|
||||
}
|
||||
}
|
||||
}
|
||||
let vosk = Vosk()
|
||||
let res = vosk.recognizeFile()
|
||||
|
||||
// Start the stream of audio data.
|
||||
audioEngine.prepare()
|
||||
try audioEngine.start()
|
||||
} catch {
|
||||
print("Unable to start AVAudioEngine: \(error.localizedDescription)")
|
||||
}
|
||||
}
|
||||
|
||||
func stopAudioEngine() {
|
||||
audioEngine.stop()
|
||||
}
|
||||
|
||||
@IBAction func runRecognizeMicrohpone(_ sender: Any) {
|
||||
if (mode == .stopped) {
|
||||
setMode(mode: .microphone)
|
||||
startAudioEngine()
|
||||
} else {
|
||||
stopAudioEngine()
|
||||
setMode(mode: .stopped)
|
||||
}
|
||||
}
|
||||
|
||||
@IBAction func runRecognizeFile(_ sender: Any) {
|
||||
setMode(mode: .file)
|
||||
processingQueue.async {
|
||||
let recognizer = Vosk(model: self.model, sampleRate: 16000.0)
|
||||
let res = recognizer.recognizeFile()
|
||||
DispatchQueue.main.async {
|
||||
self.mainText.text = res
|
||||
self.setMode(mode: .stopped)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
setMode(mode: .stopped)
|
||||
processingQueue = DispatchQueue(label: "recognizerQueue")
|
||||
model = VoskModel()
|
||||
}
|
||||
|
||||
override func didReceiveMemoryWarning() {
|
||||
super.didReceiveMemoryWarning()
|
||||
}
|
||||
|
||||
+33
-16
@@ -3,35 +3,52 @@
|
||||
// VoskApiTest
|
||||
//
|
||||
// Created by Niсkolay Shmyrev on 01.03.20.
|
||||
// Copyright © 2020 Alpha Cephei. All rights reserved.
|
||||
// Copyright © 2020-2021 Alpha Cephei. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import AVFoundation
|
||||
|
||||
public final class Vosk {
|
||||
|
||||
var recognizer : OpaquePointer!
|
||||
|
||||
init(model: VoskModel, sampleRate: Float) {
|
||||
recognizer = vosk_recognizer_new_spk(model.model, sampleRate, model.spkModel)
|
||||
}
|
||||
|
||||
deinit {
|
||||
vosk_recognizer_free(recognizer);
|
||||
}
|
||||
|
||||
func recognizeFile() -> String {
|
||||
var sres = ""
|
||||
|
||||
if let resourcePath = Bundle.main.resourcePath {
|
||||
|
||||
let modelPath = resourcePath + "/model-en"
|
||||
|
||||
let model = vosk_model_new(modelPath);
|
||||
let recognizer = vosk_recognizer_new(model, 16000.0)
|
||||
|
||||
|
||||
let audioFile = URL(fileURLWithPath: resourcePath + "/10001-90210-01803.wav")
|
||||
|
||||
if let data = try? Data(contentsOf: audioFile) {
|
||||
let _ = data.withUnsafeBytes {
|
||||
vosk_recognizer_accept_waveform(recognizer, $0, Int32(data.count))
|
||||
}
|
||||
let res = vosk_recognizer_final_result(recognizer);
|
||||
sres = String(validatingUTF8: res!)!;
|
||||
print(sres);
|
||||
let _ = data.withUnsafeBytes {
|
||||
vosk_recognizer_accept_waveform(recognizer, $0, Int32(data.count))
|
||||
}
|
||||
let res = vosk_recognizer_final_result(recognizer);
|
||||
sres = String(validatingUTF8: res!)!;
|
||||
print(sres);
|
||||
}
|
||||
|
||||
vosk_recognizer_free(recognizer)
|
||||
vosk_model_free(model)
|
||||
}
|
||||
|
||||
return sres
|
||||
}
|
||||
|
||||
|
||||
func recognizeData(buffer : AVAudioPCMBuffer) -> String {
|
||||
let dataLen = Int(buffer.frameLength * 2)
|
||||
let channels = UnsafeBufferPointer(start: buffer.int16ChannelData, count: 1)
|
||||
let endOfSpeech = channels[0].withMemoryRebound(to: Int8.self, capacity: dataLen) {
|
||||
vosk_recognizer_accept_waveform(recognizer, $0, Int32(dataLen))
|
||||
}
|
||||
let res = endOfSpeech == 1 ?vosk_recognizer_result(recognizer) :vosk_recognizer_partial_result(recognizer)
|
||||
return String(validatingUTF8: res!)!;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2020 Alpha Cephei Inc.
|
||||
// Copyright 2020-2021 Alpha Cephei Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
@@ -12,37 +12,281 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
/* This header contains the C API for Vosk speech recognition system */
|
||||
|
||||
#ifndef _VOSK_API_H_
|
||||
#define _VOSK_API_H_
|
||||
#ifndef VOSK_API_H
|
||||
#define VOSK_API_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** Model stores all the data required for recognition
|
||||
* it contains static data and can be shared across processing
|
||||
* threads. */
|
||||
typedef struct VoskModel VoskModel;
|
||||
|
||||
|
||||
/** Speaker model is the same as model but contains the data
|
||||
* for speaker identification. */
|
||||
typedef struct VoskSpkModel VoskSpkModel;
|
||||
|
||||
|
||||
/** Recognizer object is the main object which processes data.
|
||||
* Each recognizer usually runs in own thread and takes audio as input.
|
||||
* Once audio is processed recognizer returns JSON object as a string
|
||||
* which represent decoded information - words, confidences, times, n-best lists,
|
||||
* speaker information and so on */
|
||||
typedef struct VoskRecognizer VoskRecognizer;
|
||||
|
||||
|
||||
/** Loads model data from the file and returns the model object
|
||||
*
|
||||
* @param model_path: the path of the model on the filesystem
|
||||
* @returns model object or NULL if problem occured */
|
||||
VoskModel *vosk_model_new(const char *model_path);
|
||||
|
||||
|
||||
/** Releases the model memory
|
||||
*
|
||||
* The model object is reference-counted so if some recognizer
|
||||
* depends on this model, model might still stay alive. When
|
||||
* last recognizer is released, model will be released too. */
|
||||
void vosk_model_free(VoskModel *model);
|
||||
|
||||
|
||||
/** Check if a word can be recognized by the model
|
||||
* @param word: the word
|
||||
* @returns the word symbol if @param word exists inside the model
|
||||
* or -1 otherwise.
|
||||
* Reminding that word symbol 0 is for <epsilon> */
|
||||
int vosk_model_find_word(VoskModel *model, const char *word);
|
||||
|
||||
|
||||
/** Loads speaker model data from the file and returns the model object
|
||||
*
|
||||
* @param model_path: the path of the model on the filesystem
|
||||
* @returns model object or NULL if problem occured */
|
||||
VoskSpkModel *vosk_spk_model_new(const char *model_path);
|
||||
|
||||
|
||||
/** Releases the model memory
|
||||
*
|
||||
* The model object is reference-counted so if some recognizer
|
||||
* depends on this model, model might still stay alive. When
|
||||
* last recognizer is released, model will be released too. */
|
||||
void vosk_spk_model_free(VoskSpkModel *model);
|
||||
|
||||
/** Creates the recognizer object
|
||||
*
|
||||
* The recognizers process the speech and return text using shared model data
|
||||
* @param model VoskModel containing static data for recognizer. Model can be
|
||||
* shared across recognizers, even running in different threads.
|
||||
* @param sample_rate The sample rate of the audio you going to feed into the recognizer.
|
||||
* Make sure this rate matches the audio content, it is a common
|
||||
* issue causing accuracy problems.
|
||||
* @returns recognizer object or NULL if problem occured */
|
||||
VoskRecognizer *vosk_recognizer_new(VoskModel *model, float sample_rate);
|
||||
VoskRecognizer *vosk_recognizer_new_spk(VoskModel *model, VoskSpkModel *spk_model, float sample_rate);
|
||||
|
||||
|
||||
/** Creates the recognizer object with speaker recognition
|
||||
*
|
||||
* With the speaker recognition mode the recognizer not just recognize
|
||||
* text but also return speaker vectors one can use for speaker identification
|
||||
*
|
||||
* @param model VoskModel containing static data for recognizer. Model can be
|
||||
* shared across recognizers, even running in different threads.
|
||||
* @param sample_rate The sample rate of the audio you going to feed into the recognizer.
|
||||
* Make sure this rate matches the audio content, it is a common
|
||||
* issue causing accuracy problems.
|
||||
* @param spk_model speaker model for speaker identification
|
||||
* @returns recognizer object or NULL if problem occured */
|
||||
VoskRecognizer *vosk_recognizer_new_spk(VoskModel *model, float sample_rate, VoskSpkModel *spk_model);
|
||||
|
||||
|
||||
/** Creates the recognizer object with the phrase list
|
||||
*
|
||||
* Sometimes when you want to improve recognition accuracy and when you don't need
|
||||
* to recognize large vocabulary you can specify a list of phrases to recognize. This
|
||||
* will improve recognizer speed and accuracy but might return [unk] if user said
|
||||
* something different.
|
||||
*
|
||||
* Only recognizers with lookahead models support this type of quick configuration.
|
||||
* Precompiled HCLG graph models are not supported.
|
||||
*
|
||||
* @param model VoskModel containing static data for recognizer. Model can be
|
||||
* shared across recognizers, even running in different threads.
|
||||
* @param sample_rate The sample rate of the audio you going to feed into the recognizer.
|
||||
* Make sure this rate matches the audio content, it is a common
|
||||
* issue causing accuracy problems.
|
||||
* @param grammar The string with the list of phrases to recognize as JSON array of strings,
|
||||
* for example "["one two three four five", "[unk]"]".
|
||||
*
|
||||
* @returns recognizer object or NULL if problem occured */
|
||||
VoskRecognizer *vosk_recognizer_new_grm(VoskModel *model, float sample_rate, const char *grammar);
|
||||
|
||||
|
||||
/** Adds speaker model to already initialized recognizer
|
||||
*
|
||||
* Can add speaker recognition model to already created recognizer. Helps to initialize
|
||||
* speaker recognition for grammar-based recognizer.
|
||||
*
|
||||
* @param spk_model Speaker recognition model */
|
||||
void vosk_recognizer_set_spk_model(VoskRecognizer *recognizer, VoskSpkModel *spk_model);
|
||||
|
||||
|
||||
/** Configures recognizer to output n-best results
|
||||
*
|
||||
* <pre>
|
||||
* {
|
||||
* "alternatives": [
|
||||
* { "text": "one two three four five", "confidence": 0.97 },
|
||||
* { "text": "one two three for five", "confidence": 0.03 },
|
||||
* ]
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* @param max_alternatives - maximum alternatives to return from recognition results
|
||||
*/
|
||||
void vosk_recognizer_set_max_alternatives(VoskRecognizer *recognizer, int max_alternatives);
|
||||
|
||||
|
||||
/** Enables words with times in the output
|
||||
*
|
||||
* <pre>
|
||||
* "result" : [{
|
||||
* "conf" : 1.000000,
|
||||
* "end" : 1.110000,
|
||||
* "start" : 0.870000,
|
||||
* "word" : "what"
|
||||
* }, {
|
||||
* "conf" : 1.000000,
|
||||
* "end" : 1.530000,
|
||||
* "start" : 1.110000,
|
||||
* "word" : "zero"
|
||||
* }, {
|
||||
* "conf" : 1.000000,
|
||||
* "end" : 1.950000,
|
||||
* "start" : 1.530000,
|
||||
* "word" : "zero"
|
||||
* }, {
|
||||
* "conf" : 1.000000,
|
||||
* "end" : 2.340000,
|
||||
* "start" : 1.950000,
|
||||
* "word" : "zero"
|
||||
* }, {
|
||||
* "conf" : 1.000000,
|
||||
* "end" : 2.610000,
|
||||
* "start" : 2.340000,
|
||||
* "word" : "one"
|
||||
* }],
|
||||
* </pre>
|
||||
*
|
||||
* @param words - boolean value
|
||||
*/
|
||||
void vosk_recognizer_set_words(VoskRecognizer *recognizer, int words);
|
||||
|
||||
|
||||
/** Accept voice data
|
||||
*
|
||||
* accept and process new chunk of voice data
|
||||
*
|
||||
* @param data - audio data in PCM 16-bit mono format
|
||||
* @param length - length of the audio data
|
||||
* @returns 1 if silence is occured and you can retrieve a new utterance with result method
|
||||
* 0 if decoding continues
|
||||
* -1 if exception occured */
|
||||
int vosk_recognizer_accept_waveform(VoskRecognizer *recognizer, const char *data, int length);
|
||||
|
||||
|
||||
/** Same as above but the version with the short data for language bindings where you have
|
||||
* audio as array of shorts */
|
||||
int vosk_recognizer_accept_waveform_s(VoskRecognizer *recognizer, const short *data, int length);
|
||||
|
||||
|
||||
/** Same as above but the version with the float data for language bindings where you have
|
||||
* audio as array of floats */
|
||||
int vosk_recognizer_accept_waveform_f(VoskRecognizer *recognizer, const float *data, int length);
|
||||
|
||||
|
||||
/** Returns speech recognition result
|
||||
*
|
||||
* @returns the result in JSON format which contains decoded line, decoded
|
||||
* words, times in seconds and confidences. You can parse this result
|
||||
* with any json parser
|
||||
*
|
||||
* <pre>
|
||||
* {
|
||||
* "text" : "what zero zero zero one"
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* If alternatives enabled it returns result with alternatives, see also vosk_recognizer_set_alternatives().
|
||||
*
|
||||
* If word times enabled returns word time, see also vosk_recognizer_set_word_times().
|
||||
*/
|
||||
const char *vosk_recognizer_result(VoskRecognizer *recognizer);
|
||||
|
||||
|
||||
/** Returns partial speech recognition
|
||||
*
|
||||
* @returns partial speech recognition text which is not yet finalized.
|
||||
* result may change as recognizer process more data.
|
||||
*
|
||||
* <pre>
|
||||
* {
|
||||
* "partial" : "cyril one eight zero"
|
||||
* }
|
||||
* </pre>
|
||||
*/
|
||||
const char *vosk_recognizer_partial_result(VoskRecognizer *recognizer);
|
||||
|
||||
|
||||
/** Returns speech recognition result. Same as result, but doesn't wait for silence
|
||||
* You usually call it in the end of the stream to get final bits of audio. It
|
||||
* flushes the feature pipeline, so all remaining audio chunks got processed.
|
||||
*
|
||||
* @returns speech result in JSON format.
|
||||
*/
|
||||
const char *vosk_recognizer_final_result(VoskRecognizer *recognizer);
|
||||
|
||||
|
||||
/** Resets the recognizer
|
||||
*
|
||||
* Resets current results so the recognition can continue from scratch */
|
||||
void vosk_recognizer_reset(VoskRecognizer *recognizer);
|
||||
|
||||
|
||||
/** Releases recognizer object
|
||||
*
|
||||
* Underlying model is also unreferenced and if needed released */
|
||||
void vosk_recognizer_free(VoskRecognizer *recognizer);
|
||||
|
||||
/** Set log level for Kaldi messages
|
||||
*
|
||||
* @param log_level the level
|
||||
* 0 - default value to print info and error messages but no debug
|
||||
* less than 0 - don't print info messages
|
||||
* greather than 0 - more verbose mode
|
||||
*/
|
||||
void vosk_set_log_level(int log_level);
|
||||
|
||||
/**
|
||||
* Init, automatically select a CUDA device and allow multithreading.
|
||||
* Must be called once from the main thread.
|
||||
* Has no effect if HAVE_CUDA flag is not set.
|
||||
*/
|
||||
void vosk_gpu_init();
|
||||
|
||||
/**
|
||||
* Init CUDA device in a multi-threaded environment.
|
||||
* Must be called for each thread.
|
||||
* Has no effect if HAVE_CUDA flag is not set.
|
||||
*/
|
||||
void vosk_gpu_thread_init();
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _VOSK_API_H_ */
|
||||
#endif /* VOSK_API_H */
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
//
|
||||
// Vosk.swift
|
||||
// VoskApiTest
|
||||
//
|
||||
// Created by Niсkolay Shmyrev on 01.03.20.
|
||||
// Copyright © 2020-2021 Alpha Cephei. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public final class VoskModel {
|
||||
|
||||
var model : OpaquePointer!
|
||||
var spkModel : OpaquePointer!
|
||||
|
||||
init() {
|
||||
|
||||
// Set to -1 to disable logs
|
||||
vosk_set_log_level(0);
|
||||
|
||||
if let resourcePath = Bundle.main.resourcePath {
|
||||
let modelPath = resourcePath + "/vosk-model-small-en-us-0.15"
|
||||
let spkModelPath = resourcePath + "/vosk-model-spk-0.4"
|
||||
|
||||
model = vosk_model_new(modelPath)
|
||||
spkModel = vosk_spk_model_new(spkModelPath)
|
||||
}
|
||||
}
|
||||
|
||||
deinit {
|
||||
vosk_model_free(model)
|
||||
vosk_spk_model_free(spkModel)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,65 +0,0 @@
|
||||
KALDI_ROOT ?= $(HOME)/kaldi
|
||||
CFLAGS := -g -O2 -DPIC -fPIC -Wno-unused-function
|
||||
CPPFLAGS := -I$(JAVA_HOME)/include -I$(JAVA_HOME)/include/linux -I$(KALDI_ROOT)/src -I$(KALDI_ROOT)/tools/openfst/include -I../src
|
||||
|
||||
KALDI_LIBS = \
|
||||
${KALDI_ROOT}/src/online2/kaldi-online2.a \
|
||||
${KALDI_ROOT}/src/decoder/kaldi-decoder.a \
|
||||
${KALDI_ROOT}/src/ivector/kaldi-ivector.a \
|
||||
${KALDI_ROOT}/src/gmm/kaldi-gmm.a \
|
||||
${KALDI_ROOT}/src/nnet3/kaldi-nnet3.a \
|
||||
${KALDI_ROOT}/src/tree/kaldi-tree.a \
|
||||
${KALDI_ROOT}/src/feat/kaldi-feat.a \
|
||||
${KALDI_ROOT}/src/lat/kaldi-lat.a \
|
||||
${KALDI_ROOT}/src/lm/kaldi-lm.a \
|
||||
${KALDI_ROOT}/src/hmm/kaldi-hmm.a \
|
||||
${KALDI_ROOT}/src/transform/kaldi-transform.a \
|
||||
${KALDI_ROOT}/src/cudamatrix/kaldi-cudamatrix.a \
|
||||
${KALDI_ROOT}/src/matrix/kaldi-matrix.a \
|
||||
${KALDI_ROOT}/src/fstext/kaldi-fstext.a \
|
||||
${KALDI_ROOT}/src/util/kaldi-util.a \
|
||||
${KALDI_ROOT}/src/base/kaldi-base.a \
|
||||
${KALDI_ROOT}/tools/openfst/lib/libfst.a \
|
||||
${KALDI_ROOT}/tools/openfst/lib/libfstngram.a \
|
||||
${KALDI_ROOT}/tools/OpenBLAS/libopenblas.a \
|
||||
-lgfortran
|
||||
|
||||
all: libvosk_jni.so
|
||||
|
||||
VOSK_SOURCES = \
|
||||
vosk_wrap.cc \
|
||||
../src/kaldi_recognizer.cc \
|
||||
../src/kaldi_recognizer.h \
|
||||
../src/model.cc \
|
||||
../src/model.h \
|
||||
../src/spk_model.cc \
|
||||
../src/spk_model.h \
|
||||
../src/vosk_api.cc \
|
||||
../src/vosk_api.h
|
||||
|
||||
libvosk_jni.so: $(VOSK_SOURCES)
|
||||
$(CXX) -shared -o $@ $(CPPFLAGS) $(CFLAGS) $(VOSK_SOURCES) $(KALDI_LIBS)
|
||||
|
||||
vosk_wrap.cc: ../src/vosk.i
|
||||
mkdir -p org/kaldi
|
||||
swig -c++ -I../src \
|
||||
-java -package org.kaldi \
|
||||
-outdir org/kaldi -o $@ $<
|
||||
|
||||
clean:
|
||||
$(RM) *.so *_wrap.cc *_wrap.o test/*.class
|
||||
$(RM) -r org model-en
|
||||
|
||||
model:
|
||||
wget https://alphacephei.com/kaldi/models/vosk-model-small-en-us-0.3.zip
|
||||
unzip vosk-model-small-en-us-0.3.zip && rm vosk-model-small-en-us-0.3.zip
|
||||
mv vosk-model-small-en-us-0.3 model
|
||||
|
||||
model-spk:
|
||||
wget https://alphacephei.com/kaldi/models/vosk-model-spk-0.3.zip
|
||||
unzip vosk-model-spk-0.3.zip && rm vosk-model-spk-0.3.zip
|
||||
mv vosk-model-spk-0.3 model-spk
|
||||
|
||||
run: model model-spk
|
||||
javac test/*.java org/kaldi/*.java
|
||||
java -Djava.library.path=. -cp . test.DecoderTest
|
||||
+4
-16
@@ -1,19 +1,7 @@
|
||||
Java API sample
|
||||
Java bindings for Vosk API using jnr-ffi
|
||||
|
||||
Doesn't work on Windows or Mac yet, help to prepare the packaged jars is welcome.
|
||||
See demo project for details, build it with Gradle.
|
||||
|
||||
For now to try it:
|
||||
Download model and unpack as "model" folder in the demo project.
|
||||
|
||||
On Linux you can do
|
||||
|
||||
1. Build recent kaldi
|
||||
1. `git clone https://github.com/alphacep/vosk-api`
|
||||
1. `cd vosk-api/java`
|
||||
1. `export KALDI_ROOT=<KALDI_ROOT>`
|
||||
1. `export JAVA_HOME=<JAVA_HOME>`
|
||||
1. `make`
|
||||
1. `make run`
|
||||
|
||||
For details of the code you can check:
|
||||
|
||||
https://github.com/alphacep/vosk-api/blob/master/java/test/DecoderTest.java
|
||||
Make sure you are using recent JDK and Gradle.
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
plugins {
|
||||
id 'application'
|
||||
}
|
||||
|
||||
application {
|
||||
mainClass = 'org.vosk.demo.DecoderDemo'
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation group: 'net.java.dev.jna', name: 'jna', version: '5.7.0'
|
||||
implementation group: 'com.alphacephei', name: 'vosk', version: '0.3.40+'
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package org.vosk.demo;
|
||||
|
||||
import java.io.FileInputStream;
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import javax.sound.sampled.AudioSystem;
|
||||
import javax.sound.sampled.UnsupportedAudioFileException;
|
||||
|
||||
import org.vosk.LogLevel;
|
||||
import org.vosk.Recognizer;
|
||||
import org.vosk.LibVosk;
|
||||
import org.vosk.Model;
|
||||
|
||||
public class DecoderDemo {
|
||||
|
||||
public static void main(String[] argv) throws IOException, UnsupportedAudioFileException {
|
||||
LibVosk.setLogLevel(LogLevel.DEBUG);
|
||||
|
||||
try (Model model = new Model("model");
|
||||
InputStream ais = AudioSystem.getAudioInputStream(new BufferedInputStream(new FileInputStream("../../python/example/test.wav")));
|
||||
Recognizer recognizer = new Recognizer(model, 16000)) {
|
||||
|
||||
int nbytes;
|
||||
byte[] b = new byte[4096];
|
||||
while ((nbytes = ais.read(b)) >= 0) {
|
||||
if (recognizer.acceptWaveForm(b, nbytes)) {
|
||||
System.out.println(recognizer.getResult());
|
||||
} else {
|
||||
System.out.println(recognizer.getPartialResult());
|
||||
}
|
||||
}
|
||||
|
||||
System.out.println(recognizer.getFinalResult());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
buildscript {
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
}
|
||||
|
||||
plugins {
|
||||
id 'java-library'
|
||||
id 'maven-publish'
|
||||
id 'com.vanniktech.maven.publish' version '0.18.0'
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
archivesBaseName = 'vosk'
|
||||
group = 'com.alphacephei'
|
||||
version = '0.3.45'
|
||||
|
||||
mavenPublish {
|
||||
group = 'com.alphacephei'
|
||||
version = version
|
||||
sonatypeHost = 's01'
|
||||
}
|
||||
|
||||
dependencies {
|
||||
api group: 'net.java.dev.jna', name: 'jna', version: '5.7.0'
|
||||
testImplementation 'junit:junit:4.13'
|
||||
}
|
||||
|
||||
publishing {
|
||||
publications {
|
||||
mavenJava(MavenPublication) {
|
||||
artifactId = 'vosk'
|
||||
from components.java
|
||||
pom {
|
||||
name = 'Vosk'
|
||||
description = 'Speech recognition library'
|
||||
url = 'http://www.alphacephei.com.com/vosk/'
|
||||
licenses {
|
||||
license {
|
||||
name = 'The Apache License, Version 2.0'
|
||||
url = 'http://www.apache.org/licenses/LICENSE-2.0.txt'
|
||||
}
|
||||
}
|
||||
developers {
|
||||
developer {
|
||||
id = 'alphacephei'
|
||||
name = 'Alpha Cephei Inc'
|
||||
email = 'contact@alphacephei.com'
|
||||
}
|
||||
}
|
||||
scm {
|
||||
connection = 'scm:git:git://github.com/alphacep/vosk-api.git'
|
||||
url = 'https://github.com/alphacep/vosk-api/'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
test {
|
||||
dependsOn cleanTest
|
||||
testLogging.showStandardStreams = true
|
||||
}
|
||||
|
||||
java {
|
||||
withSourcesJar()
|
||||
withJavadocJar()
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
package org.vosk;
|
||||
|
||||
import com.sun.jna.Native;
|
||||
import com.sun.jna.Library;
|
||||
import com.sun.jna.Platform;
|
||||
import com.sun.jna.Pointer;
|
||||
import java.io.File;
|
||||
import java.io.InputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
|
||||
public class LibVosk {
|
||||
|
||||
private static void unpackDll(File targetDir, String lib) throws IOException {
|
||||
InputStream source = LibVosk.class.getResourceAsStream("/win32-x86-64/" + lib + ".dll");
|
||||
Files.copy(source, new File(targetDir, lib + ".dll").toPath(), StandardCopyOption.REPLACE_EXISTING);
|
||||
}
|
||||
|
||||
static {
|
||||
|
||||
if (Platform.isWindows()) {
|
||||
// We have to unpack dependencies
|
||||
try {
|
||||
// To get a tmp folder we unpack small library and mark it for deletion
|
||||
File tmpFile = Native.extractFromResourcePath("/win32-x86-64/empty");
|
||||
File tmpDir = tmpFile.getParentFile();
|
||||
new File(tmpDir, tmpFile.getName() + ".x").createNewFile();
|
||||
|
||||
// Now unpack dependencies
|
||||
unpackDll(tmpDir, "libwinpthread-1");
|
||||
unpackDll(tmpDir, "libgcc_s_seh-1");
|
||||
unpackDll(tmpDir, "libstdc++-6");
|
||||
|
||||
} catch (IOException e) {
|
||||
// Nothing for now, it will fail on next step
|
||||
} finally {
|
||||
Native.register(LibVosk.class, "libvosk");
|
||||
}
|
||||
} else {
|
||||
Native.register(LibVosk.class, "vosk");
|
||||
}
|
||||
}
|
||||
|
||||
public static native void vosk_set_log_level(int level);
|
||||
|
||||
public static native Pointer vosk_model_new(String path);
|
||||
|
||||
public static native void vosk_model_free(Pointer model);
|
||||
|
||||
public static native Pointer vosk_spk_model_new(String path);
|
||||
|
||||
public static native void vosk_spk_model_free(Pointer model);
|
||||
|
||||
public static native Pointer vosk_recognizer_new(Model model, float sample_rate);
|
||||
|
||||
public static native Pointer vosk_recognizer_new_spk(Pointer model, float sample_rate, Pointer spk_model);
|
||||
|
||||
public static native Pointer vosk_recognizer_new_grm(Pointer model, float sample_rate, String grammar);
|
||||
|
||||
public static native void vosk_recognizer_set_max_alternatives(Pointer recognizer, int max_alternatives);
|
||||
|
||||
public static native void vosk_recognizer_set_words(Pointer recognizer, boolean words);
|
||||
|
||||
public static native void vosk_recognizer_set_partial_words(Pointer recognizer, boolean partial_words);
|
||||
|
||||
public static native void vosk_recognizer_set_spk_model(Pointer recognizer, Pointer spk_model);
|
||||
|
||||
public static native boolean vosk_recognizer_accept_waveform(Pointer recognizer, byte[] data, int len);
|
||||
|
||||
public static native boolean vosk_recognizer_accept_waveform_s(Pointer recognizer, short[] data, int len);
|
||||
|
||||
public static native boolean vosk_recognizer_accept_waveform_f(Pointer recognizer, float[] data, int len);
|
||||
|
||||
public static native String vosk_recognizer_result(Pointer recognizer);
|
||||
|
||||
public static native String vosk_recognizer_final_result(Pointer recognizer);
|
||||
|
||||
public static native String vosk_recognizer_partial_result(Pointer recognizer);
|
||||
|
||||
public static native void vosk_recognizer_reset(Pointer recognizer);
|
||||
|
||||
public static native void vosk_recognizer_free(Pointer recognizer);
|
||||
|
||||
public static void setLogLevel(LogLevel loglevel) {
|
||||
vosk_set_log_level(loglevel.getValue());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package org.vosk;
|
||||
|
||||
public enum LogLevel {
|
||||
WARNINGS(-1), // Print warning and errors
|
||||
INFO(0), // Print info, along with warning and error messages, but no debug
|
||||
DEBUG(1); // Print debug info
|
||||
|
||||
private final int value;
|
||||
|
||||
LogLevel(int value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public int getValue() {
|
||||
return this.value;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package org.vosk;
|
||||
|
||||
import java.io.IOException;
|
||||
import com.sun.jna.PointerType;
|
||||
|
||||
public class Model extends PointerType implements AutoCloseable {
|
||||
public Model() {
|
||||
}
|
||||
|
||||
public Model(String path) throws IOException {
|
||||
super(LibVosk.vosk_model_new(path));
|
||||
|
||||
if (getPointer() == null) {
|
||||
throw new IOException("Failed to create a model");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
LibVosk.vosk_model_free(this.getPointer());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
package org.vosk;
|
||||
|
||||
import com.sun.jna.PointerType;
|
||||
import java.io.IOException;
|
||||
|
||||
public class Recognizer extends PointerType implements AutoCloseable {
|
||||
public Recognizer(Model model, float sampleRate) throws IOException {
|
||||
super(LibVosk.vosk_recognizer_new(model, sampleRate));
|
||||
|
||||
if (getPointer() == null) {
|
||||
throw new IOException("Failed to create a recognizer");
|
||||
}
|
||||
}
|
||||
|
||||
public Recognizer(Model model, float sampleRate, SpeakerModel spkModel) {
|
||||
super(LibVosk.vosk_recognizer_new_spk(model.getPointer(), sampleRate, spkModel.getPointer()));
|
||||
}
|
||||
|
||||
public Recognizer(Model model, float sampleRate, String grammar) {
|
||||
super(LibVosk.vosk_recognizer_new_grm(model.getPointer(), sampleRate, grammar));
|
||||
}
|
||||
|
||||
public void setMaxAlternatives(int maxAlternatives) {
|
||||
LibVosk.vosk_recognizer_set_max_alternatives(this.getPointer(), maxAlternatives);
|
||||
}
|
||||
|
||||
public void setWords(boolean words) {
|
||||
LibVosk.vosk_recognizer_set_words(this.getPointer(), words);
|
||||
}
|
||||
|
||||
public void setPartialWords(boolean partial_words) {
|
||||
LibVosk.vosk_recognizer_set_partial_words(this.getPointer(), partial_words);
|
||||
}
|
||||
|
||||
public void setSpeakerModel(SpeakerModel spkModel) {
|
||||
LibVosk.vosk_recognizer_set_spk_model(this.getPointer(), spkModel.getPointer());
|
||||
}
|
||||
|
||||
public boolean acceptWaveForm(byte[] data, int len) {
|
||||
return LibVosk.vosk_recognizer_accept_waveform(this.getPointer(), data, len);
|
||||
}
|
||||
|
||||
public boolean acceptWaveForm(short[] data, int len) {
|
||||
return LibVosk.vosk_recognizer_accept_waveform_s(this.getPointer(), data, len);
|
||||
}
|
||||
|
||||
public boolean acceptWaveForm(float[] data, int len) {
|
||||
return LibVosk.vosk_recognizer_accept_waveform_f(this.getPointer(), data, len);
|
||||
}
|
||||
|
||||
public String getResult() {
|
||||
return LibVosk.vosk_recognizer_result(this.getPointer());
|
||||
}
|
||||
|
||||
public String getPartialResult() {
|
||||
return LibVosk.vosk_recognizer_partial_result(this.getPointer());
|
||||
}
|
||||
|
||||
public String getFinalResult() {
|
||||
return LibVosk.vosk_recognizer_final_result(this.getPointer());
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
LibVosk.vosk_recognizer_reset(this.getPointer());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
LibVosk.vosk_recognizer_free(this.getPointer());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package org.vosk;
|
||||
|
||||
import com.sun.jna.PointerType;
|
||||
import java.io.IOException;
|
||||
|
||||
public class SpeakerModel extends PointerType implements AutoCloseable {
|
||||
public SpeakerModel() {
|
||||
}
|
||||
|
||||
public SpeakerModel(String path) throws IOException {
|
||||
super(LibVosk.vosk_spk_model_new(path));
|
||||
|
||||
if (getPointer() == null) {
|
||||
throw new IOException("Failed to create a speaker model");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
LibVosk.vosk_spk_model_free(this.getPointer());
|
||||
}
|
||||
}
|
||||
+1
@@ -0,0 +1 @@
|
||||
Mainly for work around JNA API
|
||||
@@ -0,0 +1,103 @@
|
||||
package org.vosk.test;
|
||||
|
||||
import java.io.FileInputStream;
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.Assert;
|
||||
|
||||
import javax.sound.sampled.AudioSystem;
|
||||
import javax.sound.sampled.UnsupportedAudioFileException;
|
||||
|
||||
import org.vosk.LogLevel;
|
||||
import org.vosk.Recognizer;
|
||||
import org.vosk.LibVosk;
|
||||
import org.vosk.Model;
|
||||
|
||||
public class DecoderTest {
|
||||
|
||||
@Test
|
||||
public void decoderTest() throws IOException, UnsupportedAudioFileException {
|
||||
LibVosk.setLogLevel(LogLevel.DEBUG);
|
||||
|
||||
try (Model model = new Model("model");
|
||||
InputStream ais = AudioSystem.getAudioInputStream(new BufferedInputStream(new FileInputStream("../../python/example/test.wav")));
|
||||
Recognizer recognizer = new Recognizer(model, 16000)) {
|
||||
|
||||
recognizer.setMaxAlternatives(10);
|
||||
recognizer.setWords(true);
|
||||
recognizer.setPartialWords(true);
|
||||
|
||||
int nbytes;
|
||||
byte[] b = new byte[4096];
|
||||
while ((nbytes = ais.read(b)) >= 0) {
|
||||
if (recognizer.acceptWaveForm(b, nbytes)) {
|
||||
System.out.println(recognizer.getResult());
|
||||
} else {
|
||||
System.out.println(recognizer.getPartialResult());
|
||||
}
|
||||
}
|
||||
|
||||
System.out.println(recognizer.getFinalResult());
|
||||
}
|
||||
Assert.assertTrue(true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void decoderTestShort() throws IOException, UnsupportedAudioFileException {
|
||||
LibVosk.setLogLevel(LogLevel.DEBUG);
|
||||
|
||||
try (Model model = new Model("model");
|
||||
InputStream ais = AudioSystem.getAudioInputStream(new BufferedInputStream(new FileInputStream("../../python/example/test.wav")));
|
||||
Recognizer recognizer = new Recognizer(model, 16000)) {
|
||||
|
||||
int nbytes;
|
||||
byte[] b = new byte[4096];
|
||||
short[] s = new short[2048];
|
||||
while ((nbytes = ais.read(b)) >= 0) {
|
||||
ByteBuffer.wrap(b).order(ByteOrder.LITTLE_ENDIAN).asShortBuffer().get(s);
|
||||
if (recognizer.acceptWaveForm(s, nbytes / 2)) {
|
||||
System.out.println(recognizer.getResult());
|
||||
} else {
|
||||
System.out.println(recognizer.getPartialResult());
|
||||
}
|
||||
}
|
||||
|
||||
System.out.println(recognizer.getFinalResult());
|
||||
}
|
||||
Assert.assertTrue(true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void decoderTestGrammar() throws IOException, UnsupportedAudioFileException {
|
||||
LibVosk.setLogLevel(LogLevel.DEBUG);
|
||||
|
||||
try (Model model = new Model("model");
|
||||
InputStream ais = AudioSystem.getAudioInputStream(new BufferedInputStream(new FileInputStream("../../python/example/test.wav")));
|
||||
Recognizer recognizer = new Recognizer(model, 16000, "[\"one two three four five six seven eight nine zero oh\"]")) {
|
||||
|
||||
int nbytes;
|
||||
byte[] b = new byte[4096];
|
||||
while ((nbytes = ais.read(b)) >= 0) {
|
||||
if (recognizer.acceptWaveForm(b, nbytes)) {
|
||||
System.out.println(recognizer.getResult());
|
||||
} else {
|
||||
System.out.println(recognizer.getPartialResult());
|
||||
}
|
||||
}
|
||||
|
||||
System.out.println(recognizer.getFinalResult());
|
||||
}
|
||||
Assert.assertTrue(true);
|
||||
}
|
||||
|
||||
|
||||
@Test(expected = IOException.class)
|
||||
public void decoderTestException() throws IOException {
|
||||
Model model = new Model("model_missing");
|
||||
}
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
package test;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.nio.*;
|
||||
|
||||
import org.kaldi.KaldiRecognizer;
|
||||
import org.kaldi.Model;
|
||||
import org.kaldi.SpkModel;
|
||||
import org.kaldi.Vosk;
|
||||
|
||||
public class DecoderTest {
|
||||
static {
|
||||
System.loadLibrary("vosk_jni");
|
||||
}
|
||||
|
||||
public static void main(String args[]) throws IOException {
|
||||
Vosk.SetLogLevel(-10);
|
||||
|
||||
FileInputStream ais = new FileInputStream(new File("../python/example/test.wav"));
|
||||
Model model = new Model("model");
|
||||
SpkModel spkModel = new SpkModel("model-spk");
|
||||
KaldiRecognizer rec = new KaldiRecognizer(model, spkModel, 16000.0f);
|
||||
|
||||
int nbytes;
|
||||
byte[] b = new byte[4096];
|
||||
while ((nbytes = ais.read(b)) >= 0) {
|
||||
if (rec.AcceptWaveform(b)) {
|
||||
System.out.println(rec.Result());
|
||||
} else {
|
||||
System.out.println(rec.PartialResult());
|
||||
}
|
||||
}
|
||||
System.out.println(rec.FinalResult());
|
||||
}
|
||||
}
|
||||
+3
-3
@@ -1,3 +1,3 @@
|
||||
build
|
||||
node_modules
|
||||
vosk_wrap.cc
|
||||
demo/model
|
||||
demo/model-spk
|
||||
demo/test.wav
|
||||
|
||||
+28
-14
@@ -1,20 +1,34 @@
|
||||
Installation requires vosk-api checkout, it doesn't yet work with `npm
|
||||
install vosk`. We have to figure out how to properly distribute native
|
||||
modules for Vosk.
|
||||
This is an FFI-NAPI wrapper for the Vosk library.
|
||||
|
||||
The build tested with node-0.10.15, node-0.12 is not yet supported by swig.
|
||||
## Usage
|
||||
|
||||
Still, you need swig of newest version 4.0.1
|
||||
Bindings mostly follow Vosk interface, some methods are not yet fully implemented.
|
||||
|
||||
Build like this
|
||||
See [demo folder](https://github.com/alphacep/vosk-api/tree/master/nodejs/demo) for
|
||||
details.
|
||||
|
||||
```
|
||||
npm install --kaldi_root=/home/suser/kaldi
|
||||
```
|
||||
## About
|
||||
|
||||
Then test with
|
||||
Vosk is an offline open source speech recognition toolkit. It enables
|
||||
speech recognition for 20+ languages and dialects - English, Indian
|
||||
English, German, French, Spanish, Portuguese, Chinese, Russian, Turkish,
|
||||
Vietnamese, Italian, Dutch, Catalan, Arabic, Greek, Farsi, Filipino,
|
||||
Ukrainian, Kazakh, Swedish, Japanese, Esperanto, Hindi, Czech, Polish.
|
||||
More to come.
|
||||
|
||||
```
|
||||
cd example
|
||||
node test.js
|
||||
```
|
||||
Vosk models are small (50 Mb) but provide continuous large vocabulary
|
||||
transcription, zero-latency response with streaming API, reconfigurable
|
||||
vocabulary and speaker identification.
|
||||
|
||||
Vosk supplies speech recognition for chatbots, smart home appliances,
|
||||
virtual assistants. It can also create subtitles for movies,
|
||||
transcription for lectures and interviews.
|
||||
|
||||
Vosk scales from small devices like Raspberry Pi or Android smartphone to
|
||||
big clusters.
|
||||
|
||||
# Documentation
|
||||
|
||||
For installation instructions, examples and documentation visit [Vosk
|
||||
Website](https://alphacephei.com/vosk). See also our project on
|
||||
[Github](https://github.com/alphacep/vosk-api).
|
||||
|
||||
@@ -1,70 +0,0 @@
|
||||
{
|
||||
'targets': [
|
||||
{
|
||||
'target_name': 'vosk',
|
||||
'sources': [
|
||||
'../src/kaldi_recognizer.cc',
|
||||
'../src/model.cc',
|
||||
'../src/spk_model.cc',
|
||||
'../src/vosk_api.cc',
|
||||
'vosk_wrap.cc',
|
||||
],
|
||||
'cflags': [
|
||||
'-std=c++11',
|
||||
'-DFST_NO_DYNAMIC_LINKING',
|
||||
'-Wno-deprecated-declarations',
|
||||
'-Wno-sign-compare',
|
||||
'-Wno-unused-local-typedefs',
|
||||
'-Wno-ignored-quaifiers',
|
||||
'-Wno-extra',
|
||||
],
|
||||
'cflags_cc!' : [
|
||||
'-fno-rtti',
|
||||
'-fno-exceptions',
|
||||
],
|
||||
'actions': [
|
||||
{
|
||||
'action_name': 'swig',
|
||||
'inputs': [
|
||||
'../src/vosk.i',
|
||||
],
|
||||
'outputs': [
|
||||
'vosk_wrap.cc',
|
||||
],
|
||||
'action': ['swig', '-c++', '-javascript', '-o', 'vosk_wrap.cc', '-v8', '-DV8_MAJOR_VERSION=10', '../src/vosk.i']
|
||||
},
|
||||
],
|
||||
'include_dirs': [
|
||||
'<@(kaldi_root)/src',
|
||||
'<@(kaldi_root)/tools/openfst/include',
|
||||
'../src',
|
||||
],
|
||||
'link_settings': {
|
||||
'libraries': [
|
||||
'<@(kaldi_root)/src/online2/kaldi-online2.a',
|
||||
'<@(kaldi_root)/src/decoder/kaldi-decoder.a',
|
||||
'<@(kaldi_root)/src/ivector/kaldi-ivector.a',
|
||||
'<@(kaldi_root)/src/gmm/kaldi-gmm.a',
|
||||
'<@(kaldi_root)/src/nnet3/kaldi-nnet3.a',
|
||||
'<@(kaldi_root)/src/tree/kaldi-tree.a',
|
||||
'<@(kaldi_root)/src/feat/kaldi-feat.a',
|
||||
'<@(kaldi_root)/src/lat/kaldi-lat.a',
|
||||
'<@(kaldi_root)/src/lm/kaldi-lm.a',
|
||||
'<@(kaldi_root)/src/hmm/kaldi-hmm.a',
|
||||
'<@(kaldi_root)/src/transform/kaldi-transform.a',
|
||||
'<@(kaldi_root)/src/cudamatrix/kaldi-cudamatrix.a',
|
||||
'<@(kaldi_root)/src/matrix/kaldi-matrix.a',
|
||||
'<@(kaldi_root)/src/fstext/kaldi-fstext.a',
|
||||
'<@(kaldi_root)/src/util/kaldi-util.a',
|
||||
'<@(kaldi_root)/src/base/kaldi-base.a',
|
||||
'<@(kaldi_root)/tools/openfst/lib/libfst.a',
|
||||
'<@(kaldi_root)/tools/openfst/lib/libfstngram.a',
|
||||
'<@(kaldi_root)/tools/OpenBLAS/libopenblas.a',
|
||||
],
|
||||
'library_dirs': [
|
||||
'/usr/lib',
|
||||
],
|
||||
},
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
var vosk = require('..')
|
||||
|
||||
const fs = require("fs");
|
||||
const { spawn } = require("child_process");
|
||||
|
||||
MODEL_PATH = "model"
|
||||
FILE_NAME = "test.wav"
|
||||
SAMPLE_RATE = 16000
|
||||
BUFFER_SIZE = 4000
|
||||
|
||||
if (!fs.existsSync(MODEL_PATH)) {
|
||||
console.log("Please download the model from https://alphacephei.com/vosk/models and unpack as " + MODEL_PATH + " in the current folder.")
|
||||
process.exit()
|
||||
}
|
||||
|
||||
if (process.argv.length > 2)
|
||||
FILE_NAME = process.argv[2]
|
||||
|
||||
vosk.setLogLevel(0);
|
||||
const model = new vosk.Model(MODEL_PATH);
|
||||
const rec = new vosk.Recognizer({model: model, sampleRate: SAMPLE_RATE});
|
||||
|
||||
const ffmpeg_run = spawn('ffmpeg', ['-loglevel', 'quiet', '-i', FILE_NAME,
|
||||
'-ar', String(SAMPLE_RATE) , '-ac', '1',
|
||||
'-f', 's16le', '-bufsize', String(BUFFER_SIZE) , '-']);
|
||||
|
||||
ffmpeg_run.stdout.on('data', (stdout) => {
|
||||
if (rec.acceptWaveform(stdout))
|
||||
console.log(rec.result());
|
||||
else
|
||||
console.log(rec.partialResult());
|
||||
console.log(rec.finalResult());
|
||||
});
|
||||
@@ -0,0 +1,46 @@
|
||||
var vosk = require('..')
|
||||
|
||||
const fs = require("fs");
|
||||
var mic = require("mic");
|
||||
|
||||
MODEL_PATH = "model"
|
||||
SAMPLE_RATE = 16000
|
||||
|
||||
if (!fs.existsSync(MODEL_PATH)) {
|
||||
console.log("Please download the model from https://alphacephei.com/vosk/models and unpack as " + MODEL_PATH + " in the current folder.")
|
||||
process.exit()
|
||||
}
|
||||
|
||||
vosk.setLogLevel(0);
|
||||
const model = new vosk.Model(MODEL_PATH);
|
||||
const rec = new vosk.Recognizer({model: model, sampleRate: SAMPLE_RATE});
|
||||
|
||||
var micInstance = mic({
|
||||
rate: String(SAMPLE_RATE),
|
||||
channels: '1',
|
||||
debug: false,
|
||||
device: 'default',
|
||||
});
|
||||
|
||||
var micInputStream = micInstance.getAudioStream();
|
||||
|
||||
micInputStream.on('data', data => {
|
||||
if (rec.acceptWaveform(data))
|
||||
console.log(rec.result());
|
||||
else
|
||||
console.log(rec.partialResult());
|
||||
});
|
||||
|
||||
micInputStream.on('audioProcessExitComplete', function() {
|
||||
console.log("Cleaning up");
|
||||
console.log(rec.finalResult());
|
||||
rec.free();
|
||||
model.free();
|
||||
});
|
||||
|
||||
process.on('SIGINT', function() {
|
||||
console.log("\nStopping");
|
||||
micInstance.stop();
|
||||
});
|
||||
|
||||
micInstance.start();
|
||||
@@ -0,0 +1,48 @@
|
||||
var vosk = require('..')
|
||||
|
||||
const fs = require("fs");
|
||||
const { Readable } = require("stream");
|
||||
const wav = require("wav");
|
||||
|
||||
MODEL_PATH = "model"
|
||||
FILE_NAME = "test.wav"
|
||||
|
||||
if (!fs.existsSync(MODEL_PATH)) {
|
||||
console.log("Please download the model from https://alphacephei.com/vosk/models and unpack as " + MODEL_PATH + " in the current folder.")
|
||||
process.exit()
|
||||
}
|
||||
|
||||
if (process.argv.length > 2)
|
||||
FILE_NAME = process.argv[2]
|
||||
|
||||
vosk.setLogLevel(0);
|
||||
const model = new vosk.Model(MODEL_PATH);
|
||||
|
||||
const wfReader = new wav.Reader();
|
||||
const wfReadable = new Readable().wrap(wfReader);
|
||||
|
||||
wfReader.on('format', async ({ audioFormat, sampleRate, channels }) => {
|
||||
if (audioFormat != 1 || channels != 1) {
|
||||
console.error("Audio file must be WAV format mono PCM.");
|
||||
process.exit(1);
|
||||
}
|
||||
const rec = new vosk.Recognizer({model: model, sampleRate: sampleRate});
|
||||
rec.setMaxAlternatives(10);
|
||||
rec.setWords(true);
|
||||
rec.setPartialWords(true);
|
||||
for await (const data of wfReadable) {
|
||||
const end_of_speech = rec.acceptWaveform(data);
|
||||
if (end_of_speech) {
|
||||
console.log(JSON.stringify(rec.result(), null, 4));
|
||||
} else {
|
||||
console.log(JSON.stringify(rec.partialResult(), null, 4));
|
||||
}
|
||||
}
|
||||
console.log(JSON.stringify(rec.finalResult(rec), null, 4));
|
||||
rec.free();
|
||||
});
|
||||
|
||||
fs.createReadStream(FILE_NAME, {'highWaterMark': 4096}).pipe(wfReader).on('finish',
|
||||
function (err) {
|
||||
model.free();
|
||||
});
|
||||
@@ -0,0 +1,46 @@
|
||||
var vosk = require('..')
|
||||
|
||||
const async = require("async");
|
||||
const fs = require("fs");
|
||||
const { Readable } = require("stream");
|
||||
const wav = require("wav");
|
||||
|
||||
MODEL_PATH = "model"
|
||||
|
||||
if (!fs.existsSync(MODEL_PATH)) {
|
||||
console.log("Please download the model from https://alphacephei.com/vosk/models and unpack as " + MODEL_PATH + " in the current folder.")
|
||||
process.exit()
|
||||
}
|
||||
|
||||
// Process file 4 times in parallel with a single model
|
||||
files = Array(10).fill("test.wav")
|
||||
const model = new vosk.Model(MODEL_PATH)
|
||||
|
||||
async.filter(files, function(filePath, callback) {
|
||||
const wfReader = new wav.Reader();
|
||||
const wfReadable = new Readable().wrap(wfReader);
|
||||
|
||||
wfReader.on('format', async ({ audioFormat, sampleRate, channels }) => {
|
||||
const rec = new vosk.Recognizer({model: model, sampleRate: sampleRate});
|
||||
if (audioFormat != 1 || channels != 1) {
|
||||
console.error("Audio file must be WAV format mono PCM.");
|
||||
process.exit(1);
|
||||
}
|
||||
for await (const data of wfReadable) {
|
||||
const end_of_speech = await rec.acceptWaveformAsync(data);
|
||||
if (end_of_speech) {
|
||||
console.log(rec.result());
|
||||
}
|
||||
}
|
||||
console.log(rec.finalResult(rec));
|
||||
rec.free();
|
||||
// Signal we are done without errors
|
||||
callback(null, true);
|
||||
});
|
||||
|
||||
fs.createReadStream(filePath, {'highWaterMark': 4096}).pipe(wfReader);
|
||||
|
||||
}, function(err, results) {
|
||||
model.free();
|
||||
console.log("Done!!!!!");
|
||||
});
|
||||
@@ -0,0 +1,53 @@
|
||||
const vosk = require('..');
|
||||
|
||||
const fs = require("fs");
|
||||
const { Readable } = require("stream");
|
||||
const wav = require("wav");
|
||||
|
||||
MODEL_PATH = "model"
|
||||
SPEAKER_MODEL_PATH = "model-spk"
|
||||
FILE_NAME = "test.wav"
|
||||
|
||||
if (!fs.existsSync(MODEL_PATH)) {
|
||||
console.log("Please download the model from https://alphacephei.com/vosk/models and unpack as " + MODEL_PATH + " in the current folder.")
|
||||
process.exit()
|
||||
}
|
||||
|
||||
if (!fs.existsSync(SPEAKER_MODEL_PATH)) {
|
||||
console.log("Please download the speaker model from https://alphacephei.com/vosk/models and unpack as " + SPEAKER_MODEL_PATH + " in the current folder.")
|
||||
process.exit()
|
||||
}
|
||||
|
||||
if (process.argv.length > 2)
|
||||
FILE_NAME = process.argv[2]
|
||||
|
||||
const model = new vosk.Model(MODEL_PATH);
|
||||
const speakerModel = new vosk.SpeakerModel(SPEAKER_MODEL_PATH);
|
||||
|
||||
const wfReader = new wav.Reader();
|
||||
const wfReadable = new Readable().wrap(wfReader);
|
||||
|
||||
wfReader.on('format', async ({ audioFormat, sampleRate, channels }) => {
|
||||
if (audioFormat != 1 || channels != 1) {
|
||||
console.error('Audio file must be WAV format mono PCM.');
|
||||
process.exit(1);
|
||||
}
|
||||
// const rec = new vosk.Recognizer({ model: model,
|
||||
// speakerModel: speakerModel,
|
||||
// sampleRate: sampleRate });
|
||||
const rec = new vosk.Recognizer({model: model, sampleRate: sampleRate});
|
||||
rec.setSpkModel(speakerModel);
|
||||
for await (const data of wfReadable) {
|
||||
const end_of_speech = rec.acceptWaveform(data);
|
||||
if (end_of_speech) {
|
||||
console.log(rec.finalResult());
|
||||
}
|
||||
}
|
||||
console.log(rec.finalResult());
|
||||
rec.free();
|
||||
});
|
||||
|
||||
fs.createReadStream(FILE_NAME, { highWaterMark: 4096 }).pipe(wfReader).on('finish', function (err) {
|
||||
model.free();
|
||||
speakerModel.free();
|
||||
});
|
||||
@@ -0,0 +1,84 @@
|
||||
var vosk = require('..')
|
||||
|
||||
const fs = require("fs");
|
||||
const { spawn } = require("child_process");
|
||||
const { stringifySync } = require('subtitle')
|
||||
|
||||
MODEL_PATH = "model"
|
||||
FILE_NAME = "test.wav"
|
||||
SAMPLE_RATE = 16000
|
||||
BUFFER_SIZE = 4000
|
||||
|
||||
if (!fs.existsSync(MODEL_PATH)) {
|
||||
console.log("Please download the model from https://alphacephei.com/vosk/models and unpack as " + MODEL_PATH + " in the current folder.")
|
||||
process.exit()
|
||||
}
|
||||
|
||||
if (process.argv.length > 2)
|
||||
FILE_NAME = process.argv[2]
|
||||
|
||||
vosk.setLogLevel(-1);
|
||||
const model = new vosk.Model(MODEL_PATH);
|
||||
const rec = new vosk.Recognizer({model: model, sampleRate: SAMPLE_RATE});
|
||||
rec.setWords(true);
|
||||
|
||||
const ffmpeg_run = spawn('ffmpeg', ['-loglevel', 'quiet', '-i', FILE_NAME,
|
||||
'-ar', String(SAMPLE_RATE) , '-ac', '1',
|
||||
'-f', 's16le', '-bufsize', String(BUFFER_SIZE), '-']);
|
||||
|
||||
WORDS_PER_LINE = 7
|
||||
const subs = []
|
||||
const results = []
|
||||
ffmpeg_run.stdout.on('data', (stdout) => {
|
||||
if (rec.acceptWaveform(stdout))
|
||||
results.push(rec.result());
|
||||
results.push(rec.finalResult());
|
||||
});
|
||||
|
||||
ffmpeg_run.on('exit', code => {
|
||||
rec.free();
|
||||
model.free();
|
||||
results.forEach(element =>{
|
||||
if (!element.hasOwnProperty('result'))
|
||||
return;
|
||||
const words = element.result;
|
||||
if (words.length == 1) {
|
||||
subs.push({
|
||||
type: 'cue',
|
||||
data: {
|
||||
start: words[0].start * 1000,
|
||||
end: words[0].end * 1000,
|
||||
text: words[0].word
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
var start_index = 0;
|
||||
var text = words[0].word + " ";
|
||||
for (let i = 1; i < words.length; i++) {
|
||||
text += words[i].word + " ";
|
||||
if (i % WORDS_PER_LINE == 0) {
|
||||
subs.push({
|
||||
type: 'cue',
|
||||
data: {
|
||||
start: words[start_index].start * 1000,
|
||||
end: words[i].end * 1000,
|
||||
text: text.slice(0, text.length-1)
|
||||
}
|
||||
});
|
||||
start_index = i;
|
||||
text = "";
|
||||
}
|
||||
}
|
||||
if (start_index != words.length - 1)
|
||||
subs.push({
|
||||
type: 'cue',
|
||||
data: {
|
||||
start: words[start_index].start * 1000,
|
||||
end: words[words.length-1].end * 1000,
|
||||
text: text
|
||||
}
|
||||
});
|
||||
});
|
||||
console.log(stringifySync(subs, {format: "SRT"}));
|
||||
});
|
||||
@@ -1,200 +0,0 @@
|
||||
const fs = require('fs')
|
||||
const { Readable } = require('stream')
|
||||
const wav = require('wav')
|
||||
|
||||
const { Model, KaldiRecognizer, SpkModel } = require('..')
|
||||
|
||||
try {
|
||||
fs.accessSync('model', fs.constants.R_OK)
|
||||
} catch (err) {
|
||||
console.error(
|
||||
"Please download the model from https://github.com/alphacep/kaldi-android-demo/releases and unpack as 'model' in the current folder."
|
||||
)
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
try {
|
||||
fs.accessSync('model-spk', fs.constants.R_OK)
|
||||
} catch (err) {
|
||||
console.error(
|
||||
"Please download the speaker model from https://github.com/alphacep/kaldi-android-demo/releases and unpack as 'model-spk' in the current folder."
|
||||
)
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
const wfStream = fs.createReadStream('test.wav', { highWaterMark: 4096 })
|
||||
const wfReader = new wav.Reader()
|
||||
|
||||
const model = new Model('model')
|
||||
const spkModel = new SpkModel('model-spk')
|
||||
const spk_sig = [
|
||||
4.658117,
|
||||
1.277387,
|
||||
3.346158,
|
||||
-1.473036,
|
||||
-2.15727,
|
||||
2.461757,
|
||||
3.76756,
|
||||
-1.241252,
|
||||
2.333765,
|
||||
0.642588,
|
||||
-2.848165,
|
||||
1.229534,
|
||||
3.907015,
|
||||
1.726496,
|
||||
-1.188692,
|
||||
1.16322,
|
||||
-0.668811,
|
||||
-0.623309,
|
||||
4.628018,
|
||||
0.407197,
|
||||
0.089955,
|
||||
0.920438,
|
||||
1.47237,
|
||||
-0.311365,
|
||||
-0.437051,
|
||||
-0.531738,
|
||||
-1.591781,
|
||||
3.095415,
|
||||
0.439524,
|
||||
-0.274787,
|
||||
4.03165,
|
||||
2.665864,
|
||||
4.815553,
|
||||
1.581063,
|
||||
1.078242,
|
||||
5.017717,
|
||||
-0.089395,
|
||||
-3.123428,
|
||||
5.34038,
|
||||
0.456982,
|
||||
2.465727,
|
||||
2.131833,
|
||||
4.056272,
|
||||
1.178392,
|
||||
-2.075712,
|
||||
-1.568503,
|
||||
0.847139,
|
||||
0.409214,
|
||||
1.84727,
|
||||
0.986758,
|
||||
4.222116,
|
||||
2.235512,
|
||||
1.369377,
|
||||
4.283126,
|
||||
2.278125,
|
||||
-1.467577,
|
||||
-0.999971,
|
||||
3.070041,
|
||||
1.462214,
|
||||
0.423204,
|
||||
2.143578,
|
||||
0.567174,
|
||||
-2.294655,
|
||||
1.864723,
|
||||
4.307356,
|
||||
2.610872,
|
||||
-1.238721,
|
||||
0.551861,
|
||||
2.861954,
|
||||
0.59613,
|
||||
-0.715396,
|
||||
-1.395357,
|
||||
2.706177,
|
||||
-2.004444,
|
||||
2.055255,
|
||||
0.458283,
|
||||
1.231968,
|
||||
3.48234,
|
||||
2.993858,
|
||||
0.402819,
|
||||
0.940885,
|
||||
0.360162,
|
||||
-2.173674,
|
||||
-2.504609,
|
||||
0.329541,
|
||||
3.653913,
|
||||
3.638025,
|
||||
-1.406409,
|
||||
2.14059,
|
||||
1.662765,
|
||||
-0.991323,
|
||||
0.770921,
|
||||
0.010094,
|
||||
3.775469,
|
||||
1.847511,
|
||||
2.074432,
|
||||
-1.928593,
|
||||
0.807414,
|
||||
2.964505,
|
||||
0.128597,
|
||||
1.297962,
|
||||
2.645227,
|
||||
0.136405,
|
||||
-2.543087,
|
||||
0.932246,
|
||||
2.405783,
|
||||
-2.122267,
|
||||
3.044013,
|
||||
0.486728,
|
||||
4.395338,
|
||||
0.474267,
|
||||
0.781297,
|
||||
1.694144,
|
||||
-0.831078,
|
||||
-0.462362,
|
||||
-0.964715,
|
||||
3.187863,
|
||||
6.008708,
|
||||
1.725954,
|
||||
3.667886,
|
||||
-1.467623,
|
||||
3.370667,
|
||||
2.72555,
|
||||
-0.796541,
|
||||
2.416543,
|
||||
0.675401,
|
||||
-0.737634,
|
||||
-1.709676,
|
||||
]
|
||||
|
||||
function dotp(x, y) {
|
||||
function dotp_sum(a, b) {
|
||||
return a + b
|
||||
}
|
||||
function dotp_times(a, i) {
|
||||
return x[i] * y[i]
|
||||
}
|
||||
return x.map(dotp_times).reduce(dotp_sum, 0)
|
||||
}
|
||||
|
||||
function cosineSimilarity(A, B) {
|
||||
var similarity =
|
||||
dotp(A, B) / (Math.sqrt(dotp(A, A)) * Math.sqrt(dotp(B, B)))
|
||||
return similarity
|
||||
}
|
||||
|
||||
function cosine_dist(x, y) {
|
||||
return 1 - cosineSimilarity(x, y)
|
||||
}
|
||||
|
||||
wfReader.on('format', async ({ audioFormat, sampleRate, channels }) => {
|
||||
if (audioFormat != 1 || channels != 1) {
|
||||
console.error('Audio file must be WAV format mono PCM.')
|
||||
process.exit(1)
|
||||
}
|
||||
const rec = new KaldiRecognizer(model, spkModel, sampleRate)
|
||||
for await (const data of new Readable().wrap(wfReader)) {
|
||||
const result = await rec.AcceptWaveform(data)
|
||||
if (result != 0) {
|
||||
console.log(await rec.Result())
|
||||
} else {
|
||||
console.log(await rec.PartialResult())
|
||||
}
|
||||
}
|
||||
console.log('X-vector:', res['spk'])
|
||||
console.log('Speaker distance:', cosine_dist(spk_sig, res['spk']))
|
||||
console.log(await rec.FinalResult())
|
||||
})
|
||||
|
||||
wfStream.pipe(wfReader)
|
||||
@@ -1,38 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
const fs = require("fs");
|
||||
const { Readable } = require("stream");
|
||||
const wav = require("wav");
|
||||
|
||||
const { Model, KaldiRecognizer } = require("..");
|
||||
|
||||
try {
|
||||
fs.accessSync("model", fs.constants.R_OK);
|
||||
} catch(err) {
|
||||
console.error("Please download the model from https://github.com/alphacep/kaldi-android-demo/releases and unpack as 'model' in the current folder.");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const wfStream = fs.createReadStream("test.wav", {'highWaterMark': 4096});
|
||||
const wfReader = new wav.Reader();
|
||||
|
||||
const model = new Model("model");
|
||||
|
||||
wfReader.on('format', async ({ audioFormat, sampleRate, channels }) => {
|
||||
if (audioFormat != 1 || channels != 1) {
|
||||
console.error("Audio file must be WAV format mono PCM.");
|
||||
process.exit(1);
|
||||
}
|
||||
const rec = new KaldiRecognizer(model, sampleRate);
|
||||
for await (const data of new Readable().wrap(wfReader)) {
|
||||
const result = await rec.AcceptWaveform(data);
|
||||
if (result != 0) {
|
||||
console.log(await rec.Result());
|
||||
} else {
|
||||
console.log(await rec.PartialResult());
|
||||
}
|
||||
}
|
||||
console.log(await rec.FinalResult());
|
||||
});
|
||||
|
||||
wfStream.pipe(wfReader);
|
||||
+432
-2
@@ -1,3 +1,433 @@
|
||||
const voskNativeModule = require('./build/Release/vosk.node');
|
||||
// @ts-check
|
||||
'use strict'
|
||||
|
||||
module.exports = voskNativeModule;
|
||||
/**
|
||||
* @module vosk
|
||||
*/
|
||||
|
||||
const os = require('os');
|
||||
const path = require('path');
|
||||
/** @type {import('ffi-napi')} */
|
||||
const ffi = require('ffi-napi');
|
||||
/** @type {import('ref-napi')} */
|
||||
const ref = require('ref-napi');
|
||||
|
||||
const vosk_model = ref.types.void;
|
||||
const vosk_model_ptr = ref.refType(vosk_model);
|
||||
const vosk_spk_model = ref.types.void;
|
||||
const vosk_spk_model_ptr = ref.refType(vosk_spk_model);
|
||||
const vosk_recognizer = ref.types.void;
|
||||
const vosk_recognizer_ptr = ref.refType(vosk_recognizer);
|
||||
|
||||
/**
|
||||
* @typedef {Object} WordResult
|
||||
* @property {number} conf The confidence rate in the detection. 0 For unlikely, and 1 for totally accurate.
|
||||
* @property {number} start The start of the timeframe when the word is pronounced in seconds
|
||||
* @property {number} end The end of the timeframe when the word is pronounced in seconds
|
||||
* @property {string} word The word detected
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Object} RecognitionResults
|
||||
* @property {WordResult[]} result Details about the words that have been detected
|
||||
* @property {string} text The complete sentence that have been detected
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Object} SpeakerResults
|
||||
* @property {number[]} spk A floating vector representing speaker identity. It is usually about 128 numbers which uniquely represent speaker voice.
|
||||
* @property {number} spk_frames The number of frames used to extract speaker vector. The more frames you have the more reliable is speaker vector.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Object} BaseRecognizerParam
|
||||
* @property {Model} model The language model to be used
|
||||
* @property {number} sampleRate The sample rate. Most models are trained at 16kHz
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Object} GrammarRecognizerParam
|
||||
* @property {string[]} grammar The list of sentences to be recognized.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Object} SpeakerRecognizerParam
|
||||
* @property {SpeakerModel} speakerModel The SpeakerModel that will enable speaker identification
|
||||
*/
|
||||
|
||||
/**
|
||||
* @template {SpeakerRecognizerParam | GrammarRecognizerParam} T
|
||||
* @typedef {T extends SpeakerRecognizerParam ? SpeakerResults & RecognitionResults : RecognitionResults} Result
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Object} PartialResults
|
||||
* @property {string} partial The partial sentence that have been detected until now
|
||||
*/
|
||||
|
||||
/** @typedef {string[]} Grammar The list of strings to be recognized */
|
||||
|
||||
let soname;
|
||||
if (os.platform() == 'win32') {
|
||||
// Update path to load dependent dlls
|
||||
let currentPath = process.env.Path;
|
||||
let dllDirectory = path.resolve(path.join(__dirname, "lib", "win-x86_64"));
|
||||
process.env.Path = dllDirectory + path.delimiter + currentPath;
|
||||
|
||||
soname = path.join(__dirname, "lib", "win-x86_64", "libvosk.dll")
|
||||
} else if (os.platform() == 'darwin') {
|
||||
soname = path.join(__dirname, "lib", "osx-universal", "libvosk.dylib")
|
||||
} else {
|
||||
soname = path.join(__dirname, "lib", "linux-x86_64", "libvosk.so")
|
||||
}
|
||||
|
||||
const libvosk = ffi.Library(soname, {
|
||||
'vosk_set_log_level': ['void', ['int']],
|
||||
'vosk_model_new': [vosk_model_ptr, ['string']],
|
||||
'vosk_model_free': ['void', [vosk_model_ptr]],
|
||||
'vosk_spk_model_new': [vosk_spk_model_ptr, ['string']],
|
||||
'vosk_spk_model_free': ['void', [vosk_spk_model_ptr]],
|
||||
'vosk_recognizer_new': [vosk_recognizer_ptr, [vosk_model_ptr, 'float']],
|
||||
'vosk_recognizer_new_spk': [vosk_recognizer_ptr, [vosk_model_ptr, 'float', vosk_spk_model_ptr]],
|
||||
'vosk_recognizer_new_grm': [vosk_recognizer_ptr, [vosk_model_ptr, 'float', 'string']],
|
||||
'vosk_recognizer_free': ['void', [vosk_recognizer_ptr]],
|
||||
'vosk_recognizer_set_max_alternatives': ['void', [vosk_recognizer_ptr, 'int']],
|
||||
'vosk_recognizer_set_words': ['void', [vosk_recognizer_ptr, 'bool']],
|
||||
'vosk_recognizer_set_partial_words': ['void', [vosk_recognizer_ptr, 'bool']],
|
||||
'vosk_recognizer_set_spk_model': ['void', [vosk_recognizer_ptr, vosk_spk_model_ptr]],
|
||||
'vosk_recognizer_accept_waveform': ['bool', [vosk_recognizer_ptr, 'pointer', 'int']],
|
||||
'vosk_recognizer_result': ['string', [vosk_recognizer_ptr]],
|
||||
'vosk_recognizer_final_result': ['string', [vosk_recognizer_ptr]],
|
||||
'vosk_recognizer_partial_result': ['string', [vosk_recognizer_ptr]],
|
||||
'vosk_recognizer_reset': ['void', [vosk_recognizer_ptr]],
|
||||
});
|
||||
|
||||
/**
|
||||
* Set log level for Kaldi messages
|
||||
* @param {number} level The higher, the more verbose. 0 for infos and errors. Less than 0 for silence.
|
||||
*/
|
||||
function setLogLevel(level) {
|
||||
libvosk.vosk_set_log_level(level);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a Model from a model file.
|
||||
* @see models [models](https://alphacephei.com/vosk/models)
|
||||
*/
|
||||
class Model {
|
||||
/**
|
||||
* Build a Model to be used with the voice recognition. Each language should have it's own Model
|
||||
* for the speech recognition to work.
|
||||
* @param {string} modelPath The abstract pathname to the model
|
||||
* @see models [models](https://alphacephei.com/vosk/models)
|
||||
*/
|
||||
constructor(modelPath) {
|
||||
/**
|
||||
* Store the handle.
|
||||
* For internal use only
|
||||
* @type {unknown}
|
||||
*/
|
||||
this.handle = libvosk.vosk_model_new(modelPath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Releases the model memory
|
||||
*
|
||||
* The model object is reference-counted so if some recognizer
|
||||
* depends on this model, model might still stay alive. When
|
||||
* last recognizer is released, model will be released too.
|
||||
*/
|
||||
free() {
|
||||
libvosk.vosk_model_free(this.handle);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a Speaker Model from a speaker model file.
|
||||
* The Speaker Model enables speaker identification.
|
||||
* @see models [models](https://alphacephei.com/vosk/models)
|
||||
*/
|
||||
class SpeakerModel {
|
||||
/**
|
||||
* Loads speaker model data from the file and returns the model object
|
||||
*
|
||||
* @param {string} modelPath the path of the model on the filesystem
|
||||
* @see models [models](https://alphacephei.com/vosk/models)
|
||||
*/
|
||||
constructor(modelPath) {
|
||||
/**
|
||||
* Store the handle.
|
||||
* For internal use only
|
||||
* @type {unknown}
|
||||
*/
|
||||
this.handle = libvosk.vosk_spk_model_new(modelPath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Releases the model memory
|
||||
*
|
||||
* The model object is reference-counted so if some recognizer
|
||||
* depends on this model, model might still stay alive. When
|
||||
* last recognizer is released, model will be released too.
|
||||
*/
|
||||
free() {
|
||||
libvosk.vosk_spk_model_free(this.handle);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to narrow down type while using `hasOwnProperty`.
|
||||
* @see hasOwnProperty [typescript issue](https://fettblog.eu/typescript-hasownproperty/)
|
||||
* @template {Object} Obj
|
||||
* @template {PropertyKey} Key
|
||||
* @param {Obj} obj
|
||||
* @param {Key} prop
|
||||
* @returns {obj is Obj & Record<Key, unknown>}
|
||||
*/
|
||||
function hasOwnProperty(obj, prop) {
|
||||
return obj.hasOwnProperty(prop)
|
||||
}
|
||||
|
||||
/**
|
||||
* @template T
|
||||
* @template U
|
||||
* @typedef {{ [P in Exclude<keyof T, keyof U>]?: never }} Without
|
||||
*/
|
||||
|
||||
/**
|
||||
* @template T
|
||||
* @template U
|
||||
* @typedef {(T | U) extends object ? (Without<T, U> & U) | (Without<U, T> & T) : T | U} XOR
|
||||
*/
|
||||
|
||||
/**
|
||||
* Create a Recognizer that will be able to transform audio streams into text using a Model.
|
||||
* @template {XOR<SpeakerRecognizerParam, Partial<GrammarRecognizerParam>>} T extra parameter
|
||||
* @see Model
|
||||
*/
|
||||
class Recognizer {
|
||||
/**
|
||||
* Create a Recognizer that will handle speech to text recognition.
|
||||
* @constructor
|
||||
* @param {T & BaseRecognizerParam} param The Recognizer parameters
|
||||
*
|
||||
* Sometimes when you want to improve recognition accuracy and when you don't need
|
||||
* to recognize large vocabulary you can specify a list of phrases to recognize. This
|
||||
* will improve recognizer speed and accuracy but might return [unk] if user said
|
||||
* something different.
|
||||
*
|
||||
* Only recognizers with lookahead models support this type of quick configuration.
|
||||
* Precompiled HCLG graph models are not supported.
|
||||
*/
|
||||
constructor(param) {
|
||||
const { model, sampleRate } = param
|
||||
// Prevent the user to receive unpredictable results
|
||||
if (hasOwnProperty(param, 'speakerModel') && hasOwnProperty(param, 'grammar')) {
|
||||
throw new Error('grammar and speakerModel cannot be used together for now.')
|
||||
}
|
||||
/**
|
||||
* Store the handle.
|
||||
* For internal use only
|
||||
* @type {unknown}
|
||||
*/
|
||||
this.handle = hasOwnProperty(param, 'speakerModel')
|
||||
? libvosk.vosk_recognizer_new_spk(model.handle, sampleRate, param.speakerModel.handle)
|
||||
: hasOwnProperty(param, 'grammar')
|
||||
? libvosk.vosk_recognizer_new_grm(model.handle, sampleRate, JSON.stringify(param.grammar))
|
||||
: libvosk.vosk_recognizer_new(model.handle, sampleRate);
|
||||
}
|
||||
|
||||
/**
|
||||
* Releases the model memory
|
||||
*
|
||||
* The model object is reference-counted so if some recognizer
|
||||
* depends on this model, model might still stay alive. When
|
||||
* last recognizer is released, model will be released too.
|
||||
*/
|
||||
free() {
|
||||
libvosk.vosk_recognizer_free(this.handle);
|
||||
}
|
||||
|
||||
/** Configures recognizer to output n-best results
|
||||
*
|
||||
* <pre>
|
||||
* {
|
||||
* "alternatives": [
|
||||
* { "text": "one two three four five", "confidence": 0.97 },
|
||||
* { "text": "one two three for five", "confidence": 0.03 },
|
||||
* ]
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* @param max_alternatives - maximum alternatives to return from recognition results
|
||||
*/
|
||||
setMaxAlternatives(max_alternatives) {
|
||||
libvosk.vosk_recognizer_set_max_alternatives(this.handle, max_alternatives);
|
||||
}
|
||||
|
||||
/** Configures recognizer to output words with times
|
||||
*
|
||||
* <pre>
|
||||
* "result" : [{
|
||||
* "conf" : 1.000000,
|
||||
* "end" : 1.110000,
|
||||
* "start" : 0.870000,
|
||||
* "word" : "what"
|
||||
* }, {
|
||||
* "conf" : 1.000000,
|
||||
* "end" : 1.530000,
|
||||
* "start" : 1.110000,
|
||||
* "word" : "zero"
|
||||
* }, {
|
||||
* "conf" : 1.000000,
|
||||
* "end" : 1.950000,
|
||||
* "start" : 1.530000,
|
||||
* "word" : "zero"
|
||||
* }, {
|
||||
* "conf" : 1.000000,
|
||||
* "end" : 2.340000,
|
||||
* "start" : 1.950000,
|
||||
* "word" : "zero"
|
||||
* }, {
|
||||
* "conf" : 1.000000,
|
||||
* "end" : 2.610000,
|
||||
* "start" : 2.340000,
|
||||
* "word" : "one"
|
||||
* }],
|
||||
* </pre>
|
||||
*
|
||||
* @param words - boolean value
|
||||
*/
|
||||
setWords(words) {
|
||||
libvosk.vosk_recognizer_set_words(this.handle, words);
|
||||
}
|
||||
|
||||
/** Same as above, but for partial results*/
|
||||
setPartialWords(partial_words) {
|
||||
libvosk.vosk_recognizer_set_partial_words(this.handle, partial_words);
|
||||
}
|
||||
|
||||
/** Adds speaker recognition model to already created recognizer. Helps to initialize
|
||||
* speaker recognition for grammar-based recognizer.
|
||||
*
|
||||
* @param spk_model Speaker recognition model
|
||||
*/
|
||||
setSpkModel(spk_model) {
|
||||
libvosk.vosk_recognizer_set_spk_model(this.handle, spk_model.handle);
|
||||
}
|
||||
|
||||
/**
|
||||
* Accept voice data
|
||||
*
|
||||
* accept and process new chunk of voice data
|
||||
*
|
||||
* @param {Buffer} data audio data in PCM 16-bit mono format
|
||||
* @returns true if silence is occured and you can retrieve a new utterance with result method
|
||||
*/
|
||||
acceptWaveform(data) {
|
||||
return libvosk.vosk_recognizer_accept_waveform(this.handle, data, data.length);
|
||||
};
|
||||
|
||||
/**
|
||||
* Accept voice data
|
||||
*
|
||||
* accept and process new chunk of voice data
|
||||
*
|
||||
* @param {Buffer} data audio data in PCM 16-bit mono format
|
||||
* @returns true if silence is occured and you can retrieve a new utterance with result method
|
||||
*/
|
||||
acceptWaveformAsync(data) {
|
||||
return new Promise((resolve, reject) => {
|
||||
libvosk.vosk_recognizer_accept_waveform.async(this.handle, data, data.length, function(err, result) {
|
||||
if (err) {
|
||||
reject(err);
|
||||
} else {
|
||||
resolve(result);
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
/** Returns speech recognition result in a string
|
||||
*
|
||||
* @returns the result in JSON format which contains decoded line, decoded
|
||||
* words, times in seconds and confidences. You can parse this result
|
||||
* with any json parser
|
||||
* <pre>
|
||||
* {
|
||||
* "result" : [{
|
||||
* "conf" : 1.000000,
|
||||
* "end" : 1.110000,
|
||||
* "start" : 0.870000,
|
||||
* "word" : "what"
|
||||
* }, {
|
||||
* "conf" : 1.000000,
|
||||
* "end" : 1.530000,
|
||||
* "start" : 1.110000,
|
||||
* "word" : "zero"
|
||||
* }, {
|
||||
* "conf" : 1.000000,
|
||||
* "end" : 1.950000,
|
||||
* "start" : 1.530000,
|
||||
* "word" : "zero"
|
||||
* }, {
|
||||
* "conf" : 1.000000,
|
||||
* "end" : 2.340000,
|
||||
* "start" : 1.950000,
|
||||
* "word" : "zero"
|
||||
* }, {
|
||||
* "conf" : 1.000000,
|
||||
* "end" : 2.610000,
|
||||
* "start" : 2.340000,
|
||||
* "word" : "one"
|
||||
* }],
|
||||
* "text" : "what zero zero zero one"
|
||||
* }
|
||||
* </pre>
|
||||
*/
|
||||
resultString() {
|
||||
return libvosk.vosk_recognizer_result(this.handle);
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns speech recognition results
|
||||
* @returns {Result<T>} The results
|
||||
*/
|
||||
result() {
|
||||
return JSON.parse(libvosk.vosk_recognizer_result(this.handle));
|
||||
};
|
||||
|
||||
/**
|
||||
* speech recognition text which is not yet finalized.
|
||||
* result may change as recognizer process more data.
|
||||
*
|
||||
* @returns {PartialResults} The partial results
|
||||
*/
|
||||
partialResult() {
|
||||
return JSON.parse(libvosk.vosk_recognizer_partial_result(this.handle));
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns speech recognition result. Same as result, but doesn't wait for silence
|
||||
* You usually call it in the end of the stream to get final bits of audio. It
|
||||
* flushes the feature pipeline, so all remaining audio chunks got processed.
|
||||
*
|
||||
* @returns {Result<T>} speech result.
|
||||
*/
|
||||
finalResult() {
|
||||
return JSON.parse(libvosk.vosk_recognizer_final_result(this.handle));
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* Resets current results so the recognition can continue from scratch
|
||||
*/
|
||||
reset() {
|
||||
libvosk.vosk_recognizer_reset(this.handle);
|
||||
}
|
||||
}
|
||||
|
||||
exports.setLogLevel = setLogLevel
|
||||
exports.Model = Model
|
||||
exports.SpeakerModel = SpeakerModel
|
||||
exports.Recognizer = Recognizer
|
||||
|
||||
+7
-4
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "vosk",
|
||||
"version": "0.3.8",
|
||||
"description": "Node binding for continuous voice recoginition through pocketsphinx.",
|
||||
"version": "0.3.45",
|
||||
"description": "Node binding for continuous offline voice recoginition with Vosk library.",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git://github.com/alphacep/vosk-api.git"
|
||||
@@ -15,10 +15,13 @@
|
||||
"author": "Alpha Cephei Inc.",
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
"node": ">= 10.x.x"
|
||||
"node": ">= 12.x.x"
|
||||
},
|
||||
"dependencies": {
|
||||
"node-gyp": "^5.1.1",
|
||||
"async": "^3.2.0",
|
||||
"ffi-napi": "^4.0.3",
|
||||
"mic": "^2.1.2",
|
||||
"ref-napi": ">=2.0.0",
|
||||
"wav": "^1.0.2"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,639 @@
|
||||
[MAIN]
|
||||
|
||||
# Analyse import fallback blocks. This can be used to support both Python 2 and
|
||||
# 3 compatible code, which means that the block might have code that exists
|
||||
# only in one or another interpreter, leading to false positives when analysed.
|
||||
analyse-fallback-blocks=no
|
||||
|
||||
# Load and enable all available extensions. Use --list-extensions to see a list
|
||||
# all available extensions.
|
||||
#enable-all-extensions=
|
||||
|
||||
# In error mode, messages with a category besides ERROR or FATAL are
|
||||
# suppressed, and no reports are done by default. Error mode is compatible with
|
||||
# disabling specific errors.
|
||||
#errors-only=
|
||||
|
||||
# Always return a 0 (non-error) status code, even if lint errors are found.
|
||||
# This is primarily useful in continuous integration scripts.
|
||||
#exit-zero=
|
||||
|
||||
# A comma-separated list of package or module names from where C extensions may
|
||||
# be loaded. Extensions are loading into the active Python interpreter and may
|
||||
# run arbitrary code.
|
||||
extension-pkg-allow-list=
|
||||
|
||||
# A comma-separated list of package or module names from where C extensions may
|
||||
# be loaded. Extensions are loading into the active Python interpreter and may
|
||||
# run arbitrary code. (This is an alternative name to extension-pkg-allow-list
|
||||
# for backward compatibility.)
|
||||
extension-pkg-whitelist=
|
||||
|
||||
# Return non-zero exit code if any of these messages/categories are detected,
|
||||
# even if score is above --fail-under value. Syntax same as enable. Messages
|
||||
# specified are enabled, while categories only check already-enabled messages.
|
||||
fail-on=
|
||||
|
||||
# Specify a score threshold under which the program will exit with error.
|
||||
fail-under=10
|
||||
|
||||
# Interpret the stdin as a python script, whose filename needs to be passed as
|
||||
# the module_or_package argument.
|
||||
#from-stdin=
|
||||
|
||||
# Files or directories to be skipped. They should be base names, not paths.
|
||||
ignore=CVS
|
||||
|
||||
# Add files or directories matching the regular expressions patterns to the
|
||||
# ignore-list. The regex matches against paths and can be in Posix or Windows
|
||||
# format. Because '\' represents the directory delimiter on Windows systems, it
|
||||
# can't be used as an escape character.
|
||||
ignore-paths=
|
||||
|
||||
# Files or directories matching the regular expression patterns are skipped.
|
||||
# The regex matches against base names, not paths. The default value ignores
|
||||
# Emacs file locks
|
||||
ignore-patterns=^\.#
|
||||
|
||||
# List of module names for which member attributes should not be checked
|
||||
# (useful for modules/projects where namespaces are manipulated during runtime
|
||||
# and thus existing member attributes cannot be deduced by static analysis). It
|
||||
# supports qualified module names, as well as Unix pattern matching.
|
||||
ignored-modules=
|
||||
|
||||
# Python code to execute, usually for sys.path manipulation such as
|
||||
# pygtk.require().
|
||||
#init-hook=
|
||||
|
||||
# Use multiple processes to speed up Pylint. Specifying 0 will auto-detect the
|
||||
# number of processors available to use, and will cap the count on Windows to
|
||||
# avoid hangs.
|
||||
jobs=1
|
||||
|
||||
# Control the amount of potential inferred values when inferring a single
|
||||
# object. This can help the performance when dealing with large functions or
|
||||
# complex, nested conditions.
|
||||
limit-inference-results=100
|
||||
|
||||
# List of plugins (as comma separated values of python module names) to load,
|
||||
# usually to register additional checkers.
|
||||
load-plugins=
|
||||
|
||||
# Pickle collected data for later comparisons.
|
||||
persistent=yes
|
||||
|
||||
# Minimum Python version to use for version dependent checks. Will default to
|
||||
# the version used to run pylint.
|
||||
py-version=3.7
|
||||
|
||||
# Discover python modules and packages in the file system subtree.
|
||||
recursive=no
|
||||
|
||||
# When enabled, pylint would attempt to guess common misconfiguration and emit
|
||||
# user-friendly hints instead of false-positive error messages.
|
||||
suggestion-mode=yes
|
||||
|
||||
# Allow loading of arbitrary C extensions. Extensions are imported into the
|
||||
# active Python interpreter and may run arbitrary code.
|
||||
unsafe-load-any-extension=no
|
||||
|
||||
# In verbose mode, extra non-checker-related info will be displayed.
|
||||
#verbose=
|
||||
|
||||
|
||||
[REPORTS]
|
||||
|
||||
# Python expression which should return a score less than or equal to 10. You
|
||||
# have access to the variables 'fatal', 'error', 'warning', 'refactor',
|
||||
# 'convention', and 'info' which contain the number of messages in each
|
||||
# category, as well as 'statement' which is the total number of statements
|
||||
# analyzed. This score is used by the global evaluation report (RP0004).
|
||||
evaluation=max(0, 0 if fatal else 10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10))
|
||||
|
||||
# Template used to display messages. This is a python new-style format string
|
||||
# used to format the message information. See doc for all details.
|
||||
msg-template=
|
||||
|
||||
# Set the output format. Available formats are text, parseable, colorized, json
|
||||
# and msvs (visual studio). You can also give a reporter class, e.g.
|
||||
# mypackage.mymodule.MyReporterClass.
|
||||
#output-format=
|
||||
|
||||
# Tells whether to display a full report or only the messages.
|
||||
reports=no
|
||||
|
||||
# Activate the evaluation score.
|
||||
score=yes
|
||||
|
||||
|
||||
[MESSAGES CONTROL]
|
||||
|
||||
# Only show warnings with the listed confidence levels. Leave empty to show
|
||||
# all. Valid levels: HIGH, CONTROL_FLOW, INFERENCE, INFERENCE_FAILURE,
|
||||
# UNDEFINED.
|
||||
confidence=HIGH,
|
||||
CONTROL_FLOW,
|
||||
INFERENCE,
|
||||
INFERENCE_FAILURE,
|
||||
UNDEFINED
|
||||
|
||||
# Disable the message, report, category or checker with the given id(s). You
|
||||
# can either give multiple identifiers separated by comma (,) or put this
|
||||
# option multiple times (only on the command line, not in the configuration
|
||||
# file where it should appear only once). You can also use "--disable=all" to
|
||||
# disable everything first and then re-enable specific checks. For example, if
|
||||
# you want to run only the similarities checker, you can use "--disable=all
|
||||
# --enable=similarities". If you want to run only the classes checker, but have
|
||||
# no Warning level messages displayed, use "--disable=all --enable=classes
|
||||
# --disable=W".
|
||||
disable=raw-checker-failed,
|
||||
bad-inline-option,
|
||||
locally-disabled,
|
||||
file-ignored,
|
||||
suppressed-message,
|
||||
useless-suppression,
|
||||
deprecated-pragma,
|
||||
use-symbolic-message-instead,
|
||||
missing-module-docstring, # added
|
||||
missing-class-docstring, # added
|
||||
missing-function-docstring, # added
|
||||
invalid-name, # added
|
||||
broad-except, # added
|
||||
wrong-import-order, # added
|
||||
consider-using-f-string, # added
|
||||
logging-format-interpolation, # added
|
||||
no-member, # added
|
||||
consider-using-with, # added
|
||||
unused-argument,
|
||||
no-else-return, # added
|
||||
too-few-public-methods, # added
|
||||
import-error, # added
|
||||
protected-access, # added
|
||||
unused-variable, # added
|
||||
R0801 # added (similarity of two scripts)
|
||||
|
||||
# Enable the message, report, category or checker with the given id(s). You can
|
||||
# either give multiple identifier separated by comma (,) or put this option
|
||||
# multiple time (only on the command line, not in the configuration file where
|
||||
# it should appear only once). See also the "--disable" option for examples.
|
||||
enable=c-extension-no-member
|
||||
|
||||
|
||||
[BASIC]
|
||||
|
||||
# Naming style matching correct argument names.
|
||||
argument-naming-style=snake_case
|
||||
|
||||
# Regular expression matching correct argument names. Overrides argument-
|
||||
# naming-style. If left empty, argument names will be checked with the set
|
||||
# naming style.
|
||||
#argument-rgx=
|
||||
|
||||
# Naming style matching correct attribute names.
|
||||
attr-naming-style=snake_case
|
||||
|
||||
# Regular expression matching correct attribute names. Overrides attr-naming-
|
||||
# style. If left empty, attribute names will be checked with the set naming
|
||||
# style.
|
||||
#attr-rgx=
|
||||
|
||||
# Bad variable names which should always be refused, separated by a comma.
|
||||
bad-names=foo,
|
||||
bar,
|
||||
baz,
|
||||
toto,
|
||||
tutu,
|
||||
tata
|
||||
|
||||
# Bad variable names regexes, separated by a comma. If names match any regex,
|
||||
# they will always be refused
|
||||
bad-names-rgxs=
|
||||
|
||||
# Naming style matching correct class attribute names.
|
||||
class-attribute-naming-style=any
|
||||
|
||||
# Regular expression matching correct class attribute names. Overrides class-
|
||||
# attribute-naming-style. If left empty, class attribute names will be checked
|
||||
# with the set naming style.
|
||||
#class-attribute-rgx=
|
||||
|
||||
# Naming style matching correct class constant names.
|
||||
class-const-naming-style=UPPER_CASE
|
||||
|
||||
# Regular expression matching correct class constant names. Overrides class-
|
||||
# const-naming-style. If left empty, class constant names will be checked with
|
||||
# the set naming style.
|
||||
#class-const-rgx=
|
||||
|
||||
# Naming style matching correct class names.
|
||||
class-naming-style=PascalCase
|
||||
|
||||
# Regular expression matching correct class names. Overrides class-naming-
|
||||
# style. If left empty, class names will be checked with the set naming style.
|
||||
#class-rgx=
|
||||
|
||||
# Naming style matching correct constant names.
|
||||
const-naming-style=UPPER_CASE
|
||||
|
||||
# Regular expression matching correct constant names. Overrides const-naming-
|
||||
# style. If left empty, constant names will be checked with the set naming
|
||||
# style.
|
||||
#const-rgx=
|
||||
|
||||
# Minimum line length for functions/classes that require docstrings, shorter
|
||||
# ones are exempt.
|
||||
docstring-min-length=-1
|
||||
|
||||
# Naming style matching correct function names.
|
||||
function-naming-style=snake_case
|
||||
|
||||
# Regular expression matching correct function names. Overrides function-
|
||||
# naming-style. If left empty, function names will be checked with the set
|
||||
# naming style.
|
||||
#function-rgx=
|
||||
|
||||
# Good variable names which should always be accepted, separated by a comma.
|
||||
good-names=i,
|
||||
j,
|
||||
k,
|
||||
ex,
|
||||
Run,
|
||||
_,
|
||||
x,
|
||||
y,
|
||||
nx,
|
||||
ny
|
||||
|
||||
# Good variable names regexes, separated by a comma. If names match any regex,
|
||||
# they will always be accepted
|
||||
good-names-rgxs=
|
||||
|
||||
# Include a hint for the correct naming format with invalid-name.
|
||||
include-naming-hint=no
|
||||
|
||||
# Naming style matching correct inline iteration names.
|
||||
inlinevar-naming-style=any
|
||||
|
||||
# Regular expression matching correct inline iteration names. Overrides
|
||||
# inlinevar-naming-style. If left empty, inline iteration names will be checked
|
||||
# with the set naming style.
|
||||
#inlinevar-rgx=
|
||||
|
||||
# Naming style matching correct method names.
|
||||
method-naming-style=snake_case
|
||||
|
||||
# Regular expression matching correct method names. Overrides method-naming-
|
||||
# style. If left empty, method names will be checked with the set naming style.
|
||||
#method-rgx=
|
||||
|
||||
# Naming style matching correct module names.
|
||||
module-naming-style=snake_case
|
||||
|
||||
# Regular expression matching correct module names. Overrides module-naming-
|
||||
# style. If left empty, module names will be checked with the set naming style.
|
||||
#module-rgx=
|
||||
|
||||
# Colon-delimited sets of names that determine each other's naming style when
|
||||
# the name regexes allow several styles.
|
||||
name-group=
|
||||
|
||||
# Regular expression which should only match function or class names that do
|
||||
# not require a docstring.
|
||||
no-docstring-rgx=^_
|
||||
|
||||
# List of decorators that produce properties, such as abc.abstractproperty. Add
|
||||
# to this list to register other decorators that produce valid properties.
|
||||
# These decorators are taken in consideration only for invalid-name.
|
||||
property-classes=abc.abstractproperty
|
||||
|
||||
# Regular expression matching correct type variable names. If left empty, type
|
||||
# variable names will be checked with the set naming style.
|
||||
#typevar-rgx=
|
||||
|
||||
# Naming style matching correct variable names.
|
||||
variable-naming-style=snake_case
|
||||
|
||||
# Regular expression matching correct variable names. Overrides variable-
|
||||
# naming-style. If left empty, variable names will be checked with the set
|
||||
# naming style.
|
||||
#variable-rgx=
|
||||
|
||||
|
||||
[VARIABLES]
|
||||
|
||||
# List of additional names supposed to be defined in builtins. Remember that
|
||||
# you should avoid defining new builtins when possible.
|
||||
additional-builtins=
|
||||
|
||||
# Tells whether unused global variables should be treated as a violation.
|
||||
allow-global-unused-variables=yes
|
||||
|
||||
# List of names allowed to shadow builtins
|
||||
allowed-redefined-builtins=
|
||||
|
||||
# List of strings which can identify a callback function by name. A callback
|
||||
# name must start or end with one of those strings.
|
||||
callbacks=cb_,
|
||||
_cb
|
||||
|
||||
# A regular expression matching the name of dummy variables (i.e. expected to
|
||||
# not be used).
|
||||
dummy-variables-rgx=_+$|(_[a-zA-Z0-9_]*[a-zA-Z0-9]+?$)|dummy|^ignored_|^unused_
|
||||
|
||||
# Argument names that match this expression will be ignored.
|
||||
ignored-argument-names=_.*|^ignored_|^unused_
|
||||
|
||||
# Tells whether we should check for unused import in __init__ files.
|
||||
init-import=no
|
||||
|
||||
# List of qualified module names which can have objects that can redefine
|
||||
# builtins.
|
||||
redefining-builtins-modules=six.moves,past.builtins,future.builtins,builtins,io
|
||||
|
||||
|
||||
[SIMILARITIES]
|
||||
|
||||
# Comments are removed from the similarity computation
|
||||
ignore-comments=yes
|
||||
|
||||
# Docstrings are removed from the similarity computation
|
||||
ignore-docstrings=yes
|
||||
|
||||
# Imports are removed from the similarity computation
|
||||
ignore-imports=yes
|
||||
|
||||
# Signatures are removed from the similarity computation
|
||||
ignore-signatures=yes
|
||||
|
||||
# Minimum lines number of a similarity.
|
||||
min-similarity-lines=4
|
||||
|
||||
|
||||
[MISCELLANEOUS]
|
||||
|
||||
# List of note tags to take in consideration, separated by a comma.
|
||||
notes=FIXME,
|
||||
XXX,
|
||||
TODO
|
||||
|
||||
# Regular expression of note tags to take in consideration.
|
||||
notes-rgx=
|
||||
|
||||
|
||||
[STRING]
|
||||
|
||||
# This flag controls whether inconsistent-quotes generates a warning when the
|
||||
# character used as a quote delimiter is used inconsistently within a module.
|
||||
check-quote-consistency=no
|
||||
|
||||
# This flag controls whether the implicit-str-concat should generate a warning
|
||||
# on implicit string concatenation in sequences defined over several lines.
|
||||
check-str-concat-over-line-jumps=no
|
||||
|
||||
|
||||
[IMPORTS]
|
||||
|
||||
# List of modules that can be imported at any level, not just the top level
|
||||
# one.
|
||||
allow-any-import-level=
|
||||
|
||||
# Allow wildcard imports from modules that define __all__.
|
||||
allow-wildcard-with-all=no
|
||||
|
||||
# Deprecated modules which should not be used, separated by a comma.
|
||||
deprecated-modules=
|
||||
|
||||
# Output a graph (.gv or any supported image format) of external dependencies
|
||||
# to the given file (report RP0402 must not be disabled).
|
||||
ext-import-graph=
|
||||
|
||||
# Output a graph (.gv or any supported image format) of all (i.e. internal and
|
||||
# external) dependencies to the given file (report RP0402 must not be
|
||||
# disabled).
|
||||
import-graph=
|
||||
|
||||
# Output a graph (.gv or any supported image format) of internal dependencies
|
||||
# to the given file (report RP0402 must not be disabled).
|
||||
int-import-graph=
|
||||
|
||||
# Force import order to recognize a module as part of the standard
|
||||
# compatibility libraries.
|
||||
known-standard-library=
|
||||
|
||||
# Force import order to recognize a module as part of a third party library.
|
||||
known-third-party=enchant
|
||||
|
||||
# Couples of modules and preferred modules, separated by a comma.
|
||||
preferred-modules=
|
||||
|
||||
|
||||
[EXCEPTIONS]
|
||||
|
||||
# Exceptions that will emit a warning when caught.
|
||||
overgeneral-exceptions=BaseException,
|
||||
Exception
|
||||
|
||||
|
||||
[DESIGN]
|
||||
|
||||
# List of regular expressions of class ancestor names to ignore when counting
|
||||
# public methods (see R0903)
|
||||
exclude-too-few-public-methods=
|
||||
|
||||
# List of qualified class names to ignore when counting class parents (see
|
||||
# R0901)
|
||||
ignored-parents=
|
||||
|
||||
# Maximum number of arguments for function / method.
|
||||
max-args=5
|
||||
|
||||
# Maximum number of attributes for a class (see R0902).
|
||||
max-attributes=7
|
||||
|
||||
# Maximum number of boolean expressions in an if statement (see R0916).
|
||||
max-bool-expr=5
|
||||
|
||||
# Maximum number of branch for function / method body.
|
||||
max-branches=12
|
||||
|
||||
# Maximum number of locals for function / method body.
|
||||
max-locals=15
|
||||
|
||||
# Maximum number of parents for a class (see R0901).
|
||||
max-parents=7
|
||||
|
||||
# Maximum number of public methods for a class (see R0904).
|
||||
max-public-methods=20
|
||||
|
||||
# Maximum number of return / yield for function / method body.
|
||||
max-returns=6
|
||||
|
||||
# Maximum number of statements in function / method body.
|
||||
max-statements=50
|
||||
|
||||
# Minimum number of public methods for a class (see R0903).
|
||||
min-public-methods=2
|
||||
|
||||
|
||||
[METHOD_ARGS]
|
||||
|
||||
# List of qualified names (i.e., library.method) which require a timeout
|
||||
# parameter e.g. 'requests.api.get,requests.api.post'
|
||||
timeout-methods=requests.api.delete,requests.api.get,requests.api.head,requests.api.options,requests.api.patch,requests.api.post,requests.api.put,requests.api.request
|
||||
|
||||
|
||||
[LOGGING]
|
||||
|
||||
# The type of string formatting that logging methods do. `old` means using %
|
||||
# formatting, `new` is for `{}` formatting.
|
||||
logging-format-style=old
|
||||
|
||||
# Logging modules to check that the string format arguments are in logging
|
||||
# function parameter format.
|
||||
logging-modules=logging
|
||||
|
||||
|
||||
[FORMAT]
|
||||
|
||||
# Expected format of line ending, e.g. empty (any line ending), LF or CRLF.
|
||||
expected-line-ending-format=
|
||||
|
||||
# Regexp for a line that is allowed to be longer than the limit.
|
||||
ignore-long-lines=^\s*(# )?<?https?://\S+>?$
|
||||
|
||||
# Number of spaces of indent required inside a hanging or continued line.
|
||||
indent-after-paren=4
|
||||
|
||||
# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1
|
||||
# tab).
|
||||
indent-string=' '
|
||||
|
||||
# Maximum number of characters on a single line.
|
||||
max-line-length=100
|
||||
|
||||
# Maximum number of lines in a module.
|
||||
max-module-lines=1000
|
||||
|
||||
# Allow the body of a class to be on the same line as the declaration if body
|
||||
# contains single statement.
|
||||
single-line-class-stmt=no
|
||||
|
||||
# Allow the body of an if to be on the same line as the test if there is no
|
||||
# else.
|
||||
single-line-if-stmt=no
|
||||
|
||||
|
||||
[REFACTORING]
|
||||
|
||||
# Maximum number of nested blocks for function / method body
|
||||
max-nested-blocks=5
|
||||
|
||||
# Complete name of functions that never returns. When checking for
|
||||
# inconsistent-return-statements if a never returning function is called then
|
||||
# it will be considered as an explicit return statement and no message will be
|
||||
# printed.
|
||||
never-returning-functions=sys.exit,argparse.parse_error
|
||||
|
||||
|
||||
[TYPECHECK]
|
||||
|
||||
# List of decorators that produce context managers, such as
|
||||
# contextlib.contextmanager. Add to this list to register other decorators that
|
||||
# produce valid context managers.
|
||||
contextmanager-decorators=contextlib.contextmanager
|
||||
|
||||
# List of members which are set dynamically and missed by pylint inference
|
||||
# system, and so shouldn't trigger E1101 when accessed. Python regular
|
||||
# expressions are accepted.
|
||||
generated-members=
|
||||
|
||||
# Tells whether to warn about missing members when the owner of the attribute
|
||||
# is inferred to be None.
|
||||
ignore-none=yes
|
||||
|
||||
# This flag controls whether pylint should warn about no-member and similar
|
||||
# checks whenever an opaque object is returned when inferring. The inference
|
||||
# can return multiple potential results while evaluating a Python object, but
|
||||
# some branches might not be evaluated, which results in partial inference. In
|
||||
# that case, it might be useful to still emit no-member and other checks for
|
||||
# the rest of the inferred objects.
|
||||
ignore-on-opaque-inference=yes
|
||||
|
||||
# List of symbolic message names to ignore for Mixin members.
|
||||
ignored-checks-for-mixins=no-member,
|
||||
not-async-context-manager,
|
||||
not-context-manager,
|
||||
attribute-defined-outside-init
|
||||
|
||||
# List of class names for which member attributes should not be checked (useful
|
||||
# for classes with dynamically set attributes). This supports the use of
|
||||
# qualified names.
|
||||
ignored-classes=optparse.Values,thread._local,_thread._local,argparse.Namespace
|
||||
|
||||
# Show a hint with possible names when a member name was not found. The aspect
|
||||
# of finding the hint is based on edit distance.
|
||||
missing-member-hint=yes
|
||||
|
||||
# The minimum edit distance a name should have in order to be considered a
|
||||
# similar match for a missing member name.
|
||||
missing-member-hint-distance=1
|
||||
|
||||
# The total number of similar names that should be taken in consideration when
|
||||
# showing a hint for a missing member.
|
||||
missing-member-max-choices=1
|
||||
|
||||
# Regex pattern to define which classes are considered mixins.
|
||||
mixin-class-rgx=.*[Mm]ixin
|
||||
|
||||
# List of decorators that change the signature of a decorated function.
|
||||
signature-mutators=
|
||||
|
||||
|
||||
[CLASSES]
|
||||
|
||||
# Warn about protected attribute access inside special methods
|
||||
check-protected-access-in-special-methods=no
|
||||
|
||||
# List of method names used to declare (i.e. assign) instance attributes.
|
||||
defining-attr-methods=__init__,
|
||||
__new__,
|
||||
setUp,
|
||||
__post_init__
|
||||
|
||||
# List of member names, which should be excluded from the protected access
|
||||
# warning.
|
||||
exclude-protected=_asdict,
|
||||
_fields,
|
||||
_replace,
|
||||
_source,
|
||||
_make
|
||||
|
||||
# List of valid names for the first argument in a class method.
|
||||
valid-classmethod-first-arg=cls
|
||||
|
||||
# List of valid names for the first argument in a metaclass class method.
|
||||
valid-metaclass-classmethod-first-arg=cls
|
||||
|
||||
|
||||
[SPELLING]
|
||||
|
||||
# Limits count of emitted suggestions for spelling mistakes.
|
||||
max-spelling-suggestions=4
|
||||
|
||||
# Spelling dictionary name. Available dictionaries: none. To make it work,
|
||||
# install the 'python-enchant' package.
|
||||
spelling-dict=
|
||||
|
||||
# List of comma separated words that should be considered directives if they
|
||||
# appear at the beginning of a comment and should not be checked.
|
||||
spelling-ignore-comment-directives=fmt: on,fmt: off,noqa:,noqa,nosec,isort:skip,mypy:
|
||||
|
||||
# List of comma separated words that should not be checked.
|
||||
spelling-ignore-words=
|
||||
|
||||
# A path to a file that contains the private dictionary; one word per line.
|
||||
spelling-private-dict-file=
|
||||
|
||||
# Tells whether to store unknown words to the private dictionary (see the
|
||||
# --spelling-private-dict-file option) instead of raising a message.
|
||||
spelling-store-unknown-words=no
|
||||
+24
-2
@@ -1,3 +1,25 @@
|
||||
Python module for vosk-api
|
||||
This is a Python module for Vosk.
|
||||
|
||||
See for details https://github.com/alphacep/vosk-api
|
||||
Vosk is an offline open source speech recognition toolkit. It enables
|
||||
speech recognition for 20+ languages and dialects - English, Indian
|
||||
English, German, French, Spanish, Portuguese, Chinese, Russian, Turkish,
|
||||
Vietnamese, Italian, Dutch, Catalan, Arabic, Greek, Farsi, Filipino,
|
||||
Ukrainian, Kazakh, Swedish, Japanese, Esperanto, Hindi, Czech, Polish.
|
||||
More to come.
|
||||
|
||||
Vosk models are small (50 Mb) but provide continuous large vocabulary
|
||||
transcription, zero-latency response with streaming API, reconfigurable
|
||||
vocabulary and speaker identification.
|
||||
|
||||
Vosk supplies speech recognition for chatbots, smart home appliances,
|
||||
virtual assistants. It can also create subtitles for movies,
|
||||
transcription for lectures and interviews.
|
||||
|
||||
Vosk scales from small devices like Raspberry Pi or Android smartphone to
|
||||
big clusters.
|
||||
|
||||
# Documentation
|
||||
|
||||
For installation instructions, examples and documentation visit [Vosk
|
||||
Website](https://alphacephei.com/vosk). See also our project on
|
||||
[Github](https://github.com/alphacep/vosk-api).
|
||||
|
||||
File diff suppressed because one or more lines are too long
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user