Compare commits
227 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 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 | |||
| 1a771e0172 | |||
| 4333c3c242 | |||
| b831c9ad57 | |||
| e5c08f7710 | |||
| 51c2968595 | |||
| 5993376322 | |||
| c4281622b9 | |||
| 9d26014de2 | |||
| f6c115d215 | |||
| 6bd102d778 | |||
| d3d6af5712 | |||
| 876093446f | |||
| 336f219f09 | |||
| 584251cbdc | |||
| a34995a788 | |||
| 998e5da227 | |||
| e04c15e367 | |||
| b81f69d407 | |||
| 0ac2064281 | |||
| 1d00bd244e | |||
| 8f5efc58c9 | |||
| a0c5ae1b5e | |||
| 99f48f9de1 | |||
| 1948b23f32 | |||
| c9eb572fc5 | |||
| 7d9895ff81 | |||
| d507210ef8 | |||
| db0a3d23d5 | |||
| f4f920f3cd | |||
| 75993ea276 | |||
| ee9bacb092 | |||
| b1e775c67b | |||
| d631e567aa | |||
| 9edf45be42 | |||
| 8b4b3c646a | |||
| 31bb0557d9 | |||
| 4593183cf9 | |||
| fdc45f1187 | |||
| b3d3c6d12c | |||
| f5f0794def | |||
| 55664fcca0 | |||
| afbf330f16 | |||
| 25aadf61bc | |||
| be47056467 | |||
| 8f623e0aea | |||
| 78025435e4 | |||
| 910455802e | |||
| b12914955c | |||
| dfe11eaf83 | |||
| 37fbe1a52b | |||
| af11bb2361 | |||
| 8da8697c1e | |||
| 944dc87531 | |||
| 5c4dd4644e | |||
| 9bbd172cfd | |||
| dbf9de77c3 | |||
| 8b790cd162 | |||
| 80219066e9 | |||
| 26fa5f098f | |||
| d75bb36131 | |||
| 30c5e8ca79 | |||
| c00e36fab6 | |||
| 80e60b9118 | |||
| e3a95a44bc | |||
| 86caf526f5 | |||
| e09f32b4b4 | |||
| 3803ab345d | |||
| 889b43136f | |||
| b517cf46af | |||
| ffd810fe00 | |||
| a1a0ed70a1 | |||
| 1554d9ede7 | |||
| 6f3190d83d | |||
| 3caaa32ec0 | |||
| 3facf3ccf5 | |||
| fad954e6e3 | |||
| c4809cb618 | |||
| a241423baf | |||
| 1e9421dd38 | |||
| b09ffda760 | |||
| aeff663a7c | |||
| fcd17fcd4c | |||
| feffb2711d | |||
| f76e5b592f | |||
| 71bdc900e3 | |||
| 96bbf5abc2 | |||
| df9424a228 | |||
| 3310acaf54 | |||
| e8722d462d | |||
| 14b2c13ed6 | |||
| 19af324096 | |||
| 0ca7b94e08 | |||
| 04ed310229 | |||
| 7ac33d521c | |||
| 08ada63da4 | |||
| cef3fd72fb | |||
| bca0b86e37 | |||
| bf973ff434 | |||
| a172d60b20 | |||
| 060e4395c2 | |||
| 03f1417454 | |||
| 6b787a4d1e | |||
| 095fac1de4 | |||
| 5ba9e353c7 | |||
| be117bfbb8 | |||
| c4f9bfe735 | |||
| cc96ef700f | |||
| cb41d8c94b | |||
| 8913b93703 | |||
| 174bf7b0ff | |||
| 7e44844cd5 | |||
| c0aa87af1b | |||
| f66f7a9290 | |||
| 378fa3499a | |||
| 01eeec498d | |||
| d422dfbddb | |||
| bd4a48d5a8 | |||
| aa91ccf68b | |||
| bda43565e1 | |||
| 8c9bae3ad1 | |||
| 87fc3bc073 | |||
| 9c101ffa86 | |||
| 5649d9e3c7 | |||
| 5ee96b6d04 | |||
| 6a7854919e | |||
| a6f6c0409b | |||
| 41446c980c | |||
| 7bed44a96e | |||
| 3f7bd2f586 | |||
| 2ba8466e0a | |||
| f80dcce5ed | |||
| f4aed3e9a4 | |||
| 5c4ef66a08 | |||
| 29d2564efd | |||
| 380fa9c7c8 | |||
| e55c6e09f8 | |||
| 355b58447a | |||
| fe35675eaa | |||
| ae9db8652a | |||
| ac3e52596c | |||
| 17bffb6739 | |||
| 8839771794 | |||
| 95ee23521b | |||
| 38672ad1ca | |||
| 79cee1cab6 | |||
| 3aece57b6e | |||
| 78e66149f8 | |||
| 6805077934 | |||
| 688178ba5f | |||
| cc13917903 | |||
| 65ebfb0dac | |||
| 3954d89329 | |||
| 41ebb41bd8 | |||
| 545800454b | |||
| edad7eba9e | |||
| bb8188afe6 | |||
| 305961e8dc | |||
| 06972ec3ad | |||
| 345dfde6fb | |||
| 1326d3820f | |||
| 9437f88ce2 | |||
| 67b5814108 | |||
| 5a49c26d9d |
+31
-38
@@ -1,25 +1,22 @@
|
||||
# Built application files
|
||||
*.apk
|
||||
*.ap_
|
||||
|
||||
# Java class files
|
||||
*.class
|
||||
|
||||
# Object files
|
||||
*.o
|
||||
|
||||
# Temp files
|
||||
nohup.out
|
||||
|
||||
# Gradle files
|
||||
.gradle/
|
||||
build/
|
||||
gradlew
|
||||
gradlew.bat
|
||||
gradle
|
||||
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_
|
||||
# Local configuration file (sdk path, etc)
|
||||
local.properties
|
||||
|
||||
# Cmake
|
||||
.cxx
|
||||
@@ -30,8 +27,12 @@ wheelhouse
|
||||
__pycache__
|
||||
*.egg-info
|
||||
python/dist
|
||||
python/build
|
||||
python/vosk/*.so
|
||||
python/vosk/*.cc
|
||||
python/vosk/*.c
|
||||
python/vosk/*.h
|
||||
python/vosk/*.i
|
||||
python/vosk/vosk.py
|
||||
python/vosk/vosk_wrap.cpp
|
||||
python/test/db
|
||||
python/test/hyp
|
||||
python/test/model
|
||||
@@ -40,32 +41,24 @@ python/test/result.txt
|
||||
python/test/wav.scp
|
||||
|
||||
# Java
|
||||
*.class
|
||||
java/lib/model
|
||||
java/demo/model
|
||||
java/lib/build
|
||||
java/demo/build
|
||||
*.so
|
||||
java/org
|
||||
java/*.cc
|
||||
java/model-spk/
|
||||
java/model/
|
||||
|
||||
# CSharp
|
||||
*.dll
|
||||
*.so
|
||||
*.dylib
|
||||
*.nupkg
|
||||
csharp/demo/model
|
||||
csharp/demo/test.wav
|
||||
csharp/demo/bin
|
||||
csharp/demo/obj
|
||||
csharp/gen
|
||||
csharp/*.exe
|
||||
csharp/*.c
|
||||
csharp/model/
|
||||
csharp/test.wav
|
||||
Vosk.dll
|
||||
|
||||
# Node
|
||||
nodejs/demo/model
|
||||
nodejs/demo/model-spk
|
||||
nodejs/demo/test.wav
|
||||
nodejs/vosk_wrap.cc
|
||||
nodejs/example/model
|
||||
nodejs/example/test.wav
|
||||
nodejs/node_modules
|
||||
nodejs/package-lock.json
|
||||
|
||||
# C
|
||||
c/test_vosk
|
||||
c/test_vosk_speaker
|
||||
c/oprofile_data
|
||||
c/model
|
||||
c/test.wav
|
||||
nodejs/build
|
||||
|
||||
+2
-1
@@ -7,10 +7,11 @@ matrix:
|
||||
services:
|
||||
- docker
|
||||
env: DOCKER_IMAGE=alphacep/kaldi-manylinux:latest
|
||||
PLAT=manylinux2010_x86_64
|
||||
|
||||
install:
|
||||
- docker pull $DOCKER_IMAGE
|
||||
|
||||
script:
|
||||
- docker run --rm -v `pwd`:/io $DOCKER_IMAGE $PRE_CMD /io/travis/build-wheels.sh
|
||||
- docker run --rm -e PLAT=$PLAT -v `pwd`:/io $DOCKER_IMAGE $PRE_CMD /io/travis/build-wheels.sh
|
||||
- ls wheelhouse/
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
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
|
||||
src/postprocessor.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,27 +0,0 @@
|
||||
# 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.
|
||||
|
||||
Speech recognition bindings implemented for various programming languages
|
||||
like Python, Java, Node.JS, C#, C++, Rust, Go 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.
|
||||
|
||||
# Documentation
|
||||
|
||||
For installation instructions, examples and documentation visit [Vosk
|
||||
Website](https://alphacephei.com/vosk).
|
||||
@@ -1,21 +1,25 @@
|
||||
<!-- WEHUB_ZH_README -->
|
||||
> [!NOTE]
|
||||
> 本文档由 WeHub 基于上游 README 翻译整理,属于社区翻译,非官方中文文档。
|
||||
> [English](./README.en.md) · [原始项目](https://github.com/alphacep/vosk-api) · [上游 README](https://github.com/alphacep/vosk-api/blob/HEAD/README.md)
|
||||
> 原作者、版权与许可证归属以原始项目及本仓库 LICENSE 文件为准。
|
||||
# About
|
||||
|
||||
# Vosk Speech Recognition Toolkit
|
||||
Vosk is an offline open source speech recognition toolkit. It enables
|
||||
speech recognition models for 17 languages and dialects - English, Indian
|
||||
English, German, French, Spanish, Portuguese, Chinese, Russian, Turkish,
|
||||
Vietnamese, Italian, Dutch, Catalan, Arabic, Greek, Farsi, Filipino.
|
||||
|
||||
Vosk 是一款离线开源语音识别工具包。它支持 20 多种语言及方言的语音识别——英语、印度英语、德语、法语、西班牙语、葡萄牙语、中文、俄语、土耳其语、越南语、意大利语、荷兰语、加泰罗尼亚语、阿拉伯语、希腊语、波斯语、菲律宾语、乌克兰语、哈萨克语、瑞典语、日语、世界语、印地语、捷克语、波兰语。更多语言即将推出。
|
||||
Vosk models are small (50 Mb) but provide continuous large vocabulary
|
||||
transcription, zero-latency response with streaming API, reconfigurable
|
||||
vocabulary and speaker identification.
|
||||
|
||||
Vosk 模型体积小(50 Mb),但可提供连续大词汇量转写、通过流式 API(streaming API)实现零延迟响应、可重新配置的词汇表以及说话人识别。
|
||||
Speech recognition bindings implemented for various programming languages
|
||||
like Python, Java, Node.JS, C#, C++ and others.
|
||||
|
||||
已为 Python、Java、Node.JS、C#、C++、Rust、Go 等多种编程语言实现语音识别绑定。
|
||||
Vosk supplies speech recognition for chatbots, smart home appliances,
|
||||
virtual assistants. It can also create subtitles for movies,
|
||||
transcription for lectures and interviews.
|
||||
|
||||
Vosk 为聊天机器人、智能家居设备、虚拟助手提供语音识别,还可为电影生成字幕,为讲座和访谈提供转写。
|
||||
Vosk scales from small devices like Raspberry Pi or Android smartphone to
|
||||
big clusters.
|
||||
|
||||
Vosk 的部署规模可从小型设备(如 Raspberry Pi 或 Android 智能手机)扩展到大型集群。
|
||||
# Documentation
|
||||
|
||||
# 文档
|
||||
|
||||
有关安装说明、示例和文档,请访问 [Vosk 网站](https://alphacephei.com/vosk).
|
||||
For installation instructions, examples and documentation visit [Vosk
|
||||
Website](https://alphacephei.com/vosk).
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
# WeHub 来源说明
|
||||
|
||||
- 原始项目:`alphacep/vosk-api`
|
||||
- 原始仓库:https://github.com/alphacep/vosk-api
|
||||
- 导入方式:上游默认分支的最新快照
|
||||
- 原作者、版权和许可证信息以原始仓库及本仓库 LICENSE 为准
|
||||
- 本文件仅用于记录来源,不代表 WeHub 是原项目作者
|
||||
@@ -0,0 +1,69 @@
|
||||
# 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/language_model.cc"
|
||||
"${PROJECT_SOURCE_DIR}/../src/language_model.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} -std=c++17 -O3 -DFST_NO_DYNAMIC_LINKING")
|
||||
|
||||
add_library( vosk_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( vosk_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.a
|
||||
${LIB_ROOT}/lib/libclapack.a
|
||||
${LIB_ROOT}/lib/liblapack.a
|
||||
${LIB_ROOT}/lib/libblas.a
|
||||
${LIB_ROOT}/lib/libf2c.a
|
||||
log
|
||||
)
|
||||
Executable
+141
@@ -0,0 +1,141 @@
|
||||
#!/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.8.0
|
||||
|
||||
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.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
|
||||
|
||||
# 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_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
|
||||
make -j 8 online2 lm
|
||||
|
||||
done
|
||||
+92
-35
@@ -1,53 +1,110 @@
|
||||
buildscript {
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
jcenter()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:8.13.0'
|
||||
classpath 'com.vanniktech:gradle-maven-publish-plugin:0.34.0'
|
||||
classpath 'com.android.tools.build:gradle:3.5.3'
|
||||
classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.8.5'
|
||||
}
|
||||
}
|
||||
|
||||
plugins {
|
||||
id "com.jfrog.bintray" version "1.8.5"
|
||||
}
|
||||
|
||||
def archiveName = "vosk-android"
|
||||
def libVersion = "0.3.17"
|
||||
|
||||
allprojects {
|
||||
version = '0.3.75'
|
||||
repositories {
|
||||
google()
|
||||
jcenter()
|
||||
}
|
||||
}
|
||||
|
||||
subprojects {
|
||||
apply plugin: 'com.android.library'
|
||||
apply plugin: 'maven-publish'
|
||||
|
||||
apply plugin: 'com.android.library'
|
||||
apply plugin: 'com.vanniktech.maven.publish'
|
||||
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
android {
|
||||
compileSdkVersion 29
|
||||
defaultConfig {
|
||||
minSdkVersion 21
|
||||
targetSdkVersion 29
|
||||
versionCode 6
|
||||
versionName = libVersion
|
||||
archivesBaseName = archiveName
|
||||
externalNativeBuild {
|
||||
cmake {
|
||||
arguments "-DCMAKE_VERBOSE_MAKEFILE=ON", "-DANDROID_ARM_NEON=TRUE", "-DCMAKE_CXX_FLAGS_RELEASE=-O3"
|
||||
}
|
||||
}
|
||||
ndk {
|
||||
abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86_64', 'x86'
|
||||
}
|
||||
}
|
||||
|
||||
mavenPublishing {
|
||||
publishToMavenCentral()
|
||||
signAllPublications()
|
||||
sourceSets {
|
||||
main {
|
||||
java.srcDirs = ['src/main/java', 'build/generated-src']
|
||||
}
|
||||
}
|
||||
externalNativeBuild {
|
||||
cmake {
|
||||
path "CMakeLists.txt"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mavenPublishing {
|
||||
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/'
|
||||
}
|
||||
Properties properties = new Properties()
|
||||
properties.load(project.rootProject.file('local.properties').newDataInputStream())
|
||||
|
||||
bintray {
|
||||
user = properties.getProperty("bintray.user")
|
||||
key = properties.getProperty("bintray.apikey")
|
||||
pkg {
|
||||
repo = 'vosk'
|
||||
name = 'vosk-android'
|
||||
userOrg = "alphacep"
|
||||
licenses = ['Apache2.0']
|
||||
websiteUrl = 'https://github.com/alphacep/vosk-api'
|
||||
issueTrackerUrl = 'https://github.com/alphacep/vosk-api/issues'
|
||||
vcsUrl = 'https://github.com/alphacep/vosk-api'
|
||||
version {
|
||||
name = libVersion
|
||||
vcsTag = libVersion
|
||||
}
|
||||
}
|
||||
publications = ['aar']
|
||||
}
|
||||
|
||||
publishing {
|
||||
publications {
|
||||
aar(MavenPublication) {
|
||||
groupId 'com.alphacep'
|
||||
artifactId archiveName
|
||||
version libVersion
|
||||
artifact("$buildDir/outputs/aar/$archiveName-release.aar")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
@@ -1,140 +0,0 @@
|
||||
#!/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_TOOLCHAIN_PATH/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"
|
||||
PAGESIZE_LDFLAGS=""
|
||||
;;
|
||||
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=""
|
||||
# Ensure compatibility with 16KiB page size devices
|
||||
PAGESIZE_LDFLAGS="-Wl,-z,common-page-size=4096 -Wl,-z,max-page-size=16384"
|
||||
;;
|
||||
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=""
|
||||
PAGESIZE_LDFLAGS=""
|
||||
;;
|
||||
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=""
|
||||
PAGESIZE_LDFLAGS=""
|
||||
;;
|
||||
esac
|
||||
|
||||
mkdir -p $WORKDIR/local/lib
|
||||
|
||||
# openblas first
|
||||
cd $WORKDIR
|
||||
git clone -b v0.3.20 --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 -j 8
|
||||
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_TOOLCHAIN_PATH/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 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 ${PAGESIZE_LDFLAGS}"
|
||||
cp $WORKDIR/vosk/libvosk.so $WORKDIR/../../src/main/jniLibs/$arch/libvosk.so
|
||||
|
||||
done
|
||||
@@ -1,39 +0,0 @@
|
||||
def archiveName = "vosk-android"
|
||||
def pomName = "Vosk Android"
|
||||
def pomDescription = "Vosk speech recognition library for Android"
|
||||
|
||||
android {
|
||||
namespace 'org.vosk'
|
||||
compileSdkVersion 36
|
||||
defaultConfig {
|
||||
minSdkVersion 21
|
||||
targetSdkVersion 36
|
||||
versionCode 10
|
||||
versionName = version
|
||||
archivesBaseName = archiveName
|
||||
ndkVersion = "28.2.13676358"
|
||||
}
|
||||
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:5.18.1@aar'
|
||||
}
|
||||
|
||||
//preBuild.dependsOn buildVosk
|
||||
|
||||
mavenPublishing {
|
||||
coordinates("com.alphacephei", archiveName, version)
|
||||
pom {
|
||||
name = pomName
|
||||
description = pomDescription
|
||||
}
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest>
|
||||
</manifest>
|
||||
@@ -1,82 +0,0 @@
|
||||
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_set_grm(Pointer recognizer, String grammar);
|
||||
|
||||
public static native void vosk_recognizer_reset(Pointer recognizer);
|
||||
|
||||
public static native void vosk_recognizer_set_endpointer_mode(Pointer recognizer, int mode);
|
||||
|
||||
public static native void vosk_recognizer_set_endpointer_delays(Pointer recognizer, float t_start_max, float t_end, float t_max);
|
||||
|
||||
public static native void vosk_recognizer_free(Pointer recognizer);
|
||||
|
||||
public static native Pointer vosk_text_processor_new(String verbalizer, String tagger);
|
||||
|
||||
public static native void vosk_text_processor_free(Pointer processor);
|
||||
|
||||
public static native String vosk_text_processor_itn(Pointer processor, String input);
|
||||
|
||||
/**
|
||||
* Set log level for Kaldi messages.
|
||||
*
|
||||
* @param loglevel the level
|
||||
* 0 - default value to print info and error messages but no debug
|
||||
* less than 0 - don't print info messages
|
||||
* greater than 0 - more verbose mode
|
||||
*/
|
||||
public static void setLogLevel(LogLevel loglevel) {
|
||||
vosk_set_log_level(loglevel.getValue());
|
||||
}
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
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());
|
||||
}
|
||||
}
|
||||
@@ -1,275 +0,0 @@
|
||||
package org.vosk;
|
||||
|
||||
import com.sun.jna.PointerType;
|
||||
import java.io.IOException;
|
||||
|
||||
public class Recognizer extends PointerType implements AutoCloseable {
|
||||
/**
|
||||
* 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 sampleRate The sample rate of the audio you are going to feed into the recognizer.
|
||||
* Make sure this rate matches the audio content, it is a common
|
||||
* issue causing accuracy problems.
|
||||
* @throws IOException if the recognizer could not be created
|
||||
*/
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 sampleRate The sample rate of the audio you are going to feed into the recognizer.
|
||||
* Make sure this rate matches the audio content, it is a common
|
||||
* issue causing accuracy problems.
|
||||
* @param spkModel speaker model for speaker identification
|
||||
* @throws IOException if the recognizer could not be created
|
||||
*/
|
||||
public Recognizer(Model model, float sampleRate, SpeakerModel spkModel) throws IOException {
|
||||
super(LibVosk.vosk_recognizer_new_spk(model.getPointer(), sampleRate, spkModel.getPointer()));
|
||||
|
||||
if (getPointer() == null) {
|
||||
throw new IOException("Failed to create a recognizer");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 sampleRate The sample rate of the audio you are 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]"]".
|
||||
* @throws IOException if the recognizer could not be created
|
||||
*/
|
||||
public Recognizer(Model model, float sampleRate, String grammar) throws IOException {
|
||||
super(LibVosk.vosk_recognizer_new_grm(model.getPointer(), sampleRate, grammar));
|
||||
|
||||
if (getPointer() == null) {
|
||||
throw new IOException("Failed to create a recognizer");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 maxAlternatives - maximum alternatives to return from recognition results
|
||||
*/
|
||||
public void setMaxAlternatives(int maxAlternatives) {
|
||||
LibVosk.vosk_recognizer_set_max_alternatives(this.getPointer(), maxAlternatives);
|
||||
}
|
||||
|
||||
/** 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
|
||||
*/
|
||||
public void setWords(boolean words) {
|
||||
LibVosk.vosk_recognizer_set_words(this.getPointer(), words);
|
||||
}
|
||||
|
||||
/**
|
||||
* Like above return words and confidences in partial results.
|
||||
*
|
||||
* @param partial_words - boolean value
|
||||
*/
|
||||
public void setPartialWords(boolean partial_words) {
|
||||
LibVosk.vosk_recognizer_set_partial_words(this.getPointer(), partial_words);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 spkModel Speaker recognition model
|
||||
*/
|
||||
public void setSpeakerModel(SpeakerModel spkModel) {
|
||||
LibVosk.vosk_recognizer_set_spk_model(this.getPointer(), spkModel.getPointer());
|
||||
}
|
||||
|
||||
/**
|
||||
* Accept and process new chunk of voice data.
|
||||
*
|
||||
* @param data - audio data in PCM 16-bit mono format
|
||||
* @param len - length of the audio data
|
||||
* @return 1 if silence is occurred and you can retrieve a new utterance with result method
|
||||
* 0 if decoding continues
|
||||
* -1 if exception occurred
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns speech recognition result
|
||||
*
|
||||
* @return 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 #setMaxAlternatives().
|
||||
*
|
||||
* If word times enabled returns word time, see also #setWordTimes().
|
||||
*/
|
||||
public String getResult() {
|
||||
return LibVosk.vosk_recognizer_result(this.getPointer());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns partial speech recognition.
|
||||
*
|
||||
* @return partial speech recognition text which is not yet finalized.
|
||||
* result may change as recognizer process more data.
|
||||
*
|
||||
* <pre>
|
||||
* {
|
||||
* "partial" : "cyril one eight zero"
|
||||
* }
|
||||
* </pre>
|
||||
*/
|
||||
public String getPartialResult() {
|
||||
return LibVosk.vosk_recognizer_partial_result(this.getPointer());
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @return speech result in JSON format.
|
||||
*/
|
||||
public String getFinalResult() {
|
||||
return LibVosk.vosk_recognizer_final_result(this.getPointer());
|
||||
}
|
||||
|
||||
/**
|
||||
* Reconfigures recognizer to use grammar.
|
||||
*
|
||||
* @param grammar Set of phrases in JSON array of strings or "[]" to use default model graph.
|
||||
* @see #Recognizer(Model, float, String)
|
||||
*/
|
||||
public void setGrammar(String grammar) {
|
||||
LibVosk.vosk_recognizer_set_grm(this.getPointer(), grammar);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the recognizer.
|
||||
* Resets current results so the recognition can continue from scratch.
|
||||
*/
|
||||
public void reset() {
|
||||
LibVosk.vosk_recognizer_reset(this.getPointer());
|
||||
}
|
||||
|
||||
/**
|
||||
* Endpointer delay mode
|
||||
*/
|
||||
public class EndpointerMode {
|
||||
public static final int DEFAULT = 0;
|
||||
public static final int SHORT = 1;
|
||||
public static final int LONG = 2;
|
||||
public static final int VERY_LONG = 3;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures endpointer mode for recognizer
|
||||
*/
|
||||
public void setEndpointerMode(int mode) {
|
||||
LibVosk.vosk_recognizer_set_endpointer_mode(this.getPointer(), mode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set endpointer delays
|
||||
*
|
||||
* @param t_start_max timeout for stopping recognition in case of initial silence (usually around 5.0)
|
||||
* @param t_end timeout for stopping recognition in milliseconds after we recognized something (usually around 0.5 - 1.0)
|
||||
* @param t_max timeout for forcing utterance end in milliseconds (usually around 20-30)
|
||||
**/
|
||||
public void setEndpointerDelays(float t_start_max, float t_end, float t_max) {
|
||||
LibVosk.vosk_recognizer_set_endpointer_delays(this.getPointer(), t_start_max, t_end, t_max);
|
||||
}
|
||||
|
||||
/**
|
||||
* Releases recognizer object.
|
||||
* Underlying model is also unreferenced and if needed, released.
|
||||
*/
|
||||
@Override
|
||||
public void close() {
|
||||
LibVosk.vosk_recognizer_free(this.getPointer());
|
||||
}
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
package org.vosk;
|
||||
|
||||
import com.sun.jna.PointerType;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Helps to initialize speaker recognition for grammar-based recognizer.
|
||||
*/
|
||||
public class SpeakerModel extends PointerType implements AutoCloseable {
|
||||
public SpeakerModel() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads speaker model data from the file.
|
||||
*
|
||||
* The path must contain:
|
||||
* - a config file: mfcc.conf
|
||||
* - kaldi nnet: final.ext.raw
|
||||
* - mean.vec
|
||||
* - transform.mat
|
||||
*
|
||||
* @param path the path of the model on the filesystem
|
||||
* @throws IOException if the model could not be created
|
||||
*
|
||||
* @see <a href="http://kaldi-asr.org/doc/structkaldi_1_1MfccOptions.html">Kaldi MfccOptions</a>
|
||||
* @see <a href="http://kaldi-asr.org/doc/classkaldi_1_1nnet3_1_1Nnet.html">Kaldi Nnet</a>
|
||||
*/
|
||||
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,21 +0,0 @@
|
||||
package org.vosk;
|
||||
|
||||
import com.sun.jna.PointerType;
|
||||
|
||||
public class TextProcessor extends PointerType implements AutoCloseable {
|
||||
public TextProcessor() {
|
||||
}
|
||||
|
||||
public TextProcessor(String verbalizer, String tagger) {
|
||||
super(LibVosk.vosk_text_processor_new(verbalizer, tagger));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
LibVosk.vosk_text_processor_free(this.getPointer());
|
||||
}
|
||||
|
||||
public String itn(String input) {
|
||||
return LibVosk.vosk_text_processor_itn(this.getPointer(), input);
|
||||
}
|
||||
}
|
||||
@@ -1,316 +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.vosk.android;
|
||||
|
||||
import android.media.AudioFormat;
|
||||
import android.media.AudioRecord;
|
||||
import android.media.MediaRecorder.AudioSource;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.annotation.SuppressLint;
|
||||
|
||||
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.
|
||||
*/
|
||||
@SuppressLint("MissingPermission")
|
||||
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.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates speech service with a caller-supplied {@link AudioRecord}.
|
||||
* <p>
|
||||
* Use this when you need to control the audio input device - for example,
|
||||
* to pin recording to the built-in microphone when an external USB device
|
||||
* without a microphone is present:
|
||||
* <pre>
|
||||
* AudioRecord recorder = new AudioRecord.Builder()
|
||||
* .setAudioSource(MediaRecorder.AudioSource.VOICE_RECOGNITION)
|
||||
* .setAudioFormat(format)
|
||||
* .build();
|
||||
* if (Build.VERSION.SDK_INT >= 28) {
|
||||
* AudioManager am = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
|
||||
* for (AudioDeviceInfo d : am.getDevices(AudioManager.GET_DEVICES_INPUTS)) {
|
||||
* if (d.getType() == AudioDeviceInfo.TYPE_BUILTIN_MIC) {
|
||||
* recorder.setPreferredDevice(d);
|
||||
* break;
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* SpeechService service = new SpeechService(recognizer, 16000f, recorder);
|
||||
* </pre>
|
||||
* <p>
|
||||
* The caller retains ownership of {@code recorder}: if this constructor
|
||||
* throws, the recorder is <em>not</em> released. Call
|
||||
* {@link AudioRecord#release()} yourself in that case.
|
||||
*
|
||||
* @param recognizer the Vosk recognizer
|
||||
* @param sampleRate sample rate in Hz; must match {@code recorder}'s configuration
|
||||
* @param recorder a fully-initialised {@link AudioRecord}
|
||||
* @throws IOException if {@code recorder} is in STATE_UNINITIALIZED
|
||||
*/
|
||||
public SpeechService(Recognizer recognizer, float sampleRate, AudioRecord recorder)
|
||||
throws IOException {
|
||||
this.recognizer = recognizer;
|
||||
this.sampleRate = (int) sampleRate;
|
||||
this.recorder = recorder;
|
||||
|
||||
bufferSize = Math.round(this.sampleRate * BUFFER_SIZE_SECONDS);
|
||||
|
||||
if (recorder.getState() == AudioRecord.STATE_UNINITIALIZED) {
|
||||
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();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the audio session ID of the underlying {@link AudioRecord}.
|
||||
* <p>
|
||||
* The session ID can be used to attach audio effects such as
|
||||
* {@link android.media.audiofx.NoiseSuppressor} to the recording session.
|
||||
*
|
||||
* @return audio session ID, or {@link AudioRecord#ERROR} if unavailable
|
||||
*/
|
||||
public int getAudioSessionId() {
|
||||
return recorder.getAudioSessionId();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,165 +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.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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,150 +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.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();
|
||||
}
|
||||
}
|
||||
@@ -1,43 +1,71 @@
|
||||
buildscript {
|
||||
repositories {
|
||||
google()
|
||||
jcenter()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:3.5.3'
|
||||
classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.8.5'
|
||||
}
|
||||
}
|
||||
|
||||
plugins {
|
||||
id "com.jfrog.bintray"
|
||||
}
|
||||
|
||||
def archiveName = "vosk-model-en"
|
||||
def pomName = "Vosk English Model"
|
||||
def pomDescription = "Small English model for Android"
|
||||
def libVersion = "0.3.17"
|
||||
|
||||
allprojects {
|
||||
repositories {
|
||||
google()
|
||||
jcenter()
|
||||
}
|
||||
}
|
||||
|
||||
apply plugin: 'com.android.library'
|
||||
apply plugin: 'maven-publish'
|
||||
|
||||
android {
|
||||
namespace "org.vosk"
|
||||
compileSdkVersion 36
|
||||
compileSdkVersion 29
|
||||
defaultConfig {
|
||||
minSdkVersion 21
|
||||
targetSdkVersion 36
|
||||
versionCode 10
|
||||
versionName = version
|
||||
targetSdkVersion 29
|
||||
versionCode 6
|
||||
versionName = libVersion
|
||||
archivesBaseName = archiveName
|
||||
}
|
||||
buildFeatures {
|
||||
buildConfig = false
|
||||
}
|
||||
|
||||
Properties properties = new Properties()
|
||||
properties.load(project.rootProject.file('local.properties').newDataInputStream())
|
||||
|
||||
bintray {
|
||||
user = properties.getProperty("bintray.user")
|
||||
key = properties.getProperty("bintray.apikey")
|
||||
pkg {
|
||||
repo = 'vosk'
|
||||
name = 'vosk-model-en'
|
||||
userOrg = "alphacep"
|
||||
licenses = ['Apache2.0']
|
||||
websiteUrl = 'https://github.com/alphacep/vosk-api'
|
||||
issueTrackerUrl = 'https://github.com/alphacep/vosk-api/issues'
|
||||
vcsUrl = 'https://github.com/alphacep/vosk-api'
|
||||
version {
|
||||
name = libVersion
|
||||
vcsTag = libVersion
|
||||
}
|
||||
}
|
||||
sourceSets {
|
||||
main {
|
||||
assets.srcDirs += "$buildDir/generated/assets"
|
||||
publications = ['aar']
|
||||
}
|
||||
|
||||
publishing {
|
||||
publications {
|
||||
aar(MavenPublication) {
|
||||
groupId 'com.alphacep'
|
||||
artifactId archiveName
|
||||
version libVersion
|
||||
artifact("$buildDir/outputs/aar/$archiveName-release.aar")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
mavenPublishing {
|
||||
coordinates("com.alphacephei", archiveName, version)
|
||||
pom {
|
||||
name = pomName
|
||||
description = pomDescription
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
<manifest>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="org.kaldi.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 @@
|
||||
0.3.16
|
||||
@@ -1 +1 @@
|
||||
include ':lib', ':model-en'
|
||||
include 'model-en'
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="org.kaldi">
|
||||
</manifest>
|
||||
@@ -0,0 +1,262 @@
|
||||
// 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;
|
||||
}
|
||||
|
||||
}
|
||||
+7
-14
@@ -12,35 +12,28 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package org.vosk.android;
|
||||
package org.kaldi;
|
||||
|
||||
/**
|
||||
* Interface to receive recognition results
|
||||
*/
|
||||
/** Interface to receive recognition results */
|
||||
public interface RecognitionListener {
|
||||
|
||||
/**
|
||||
* Called when partial recognition result is available.
|
||||
*/
|
||||
void onPartialResult(String hypothesis);
|
||||
public void onPartialResult(String hypothesis);
|
||||
|
||||
/**
|
||||
* Called after silence occured.
|
||||
* Called after the recognition is ended.
|
||||
*/
|
||||
void onResult(String hypothesis);
|
||||
|
||||
/**
|
||||
* Called after stream end.
|
||||
*/
|
||||
void onFinalResult(String hypothesis);
|
||||
public void onResult(String hypothesis);
|
||||
|
||||
/**
|
||||
* Called when an error occurs.
|
||||
*/
|
||||
void onError(Exception exception);
|
||||
public void onError(Exception exception);
|
||||
|
||||
/**
|
||||
* Called after timeout expired
|
||||
*/
|
||||
void onTimeout();
|
||||
public void onTimeout();
|
||||
}
|
||||
@@ -0,0 +1,286 @@
|
||||
// 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;
|
||||
|
||||
/**
|
||||
* 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 {
|
||||
|
||||
protected static final String TAG = SpeechService.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 service. Service 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 SpeechService(KaldiRecognizer 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.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
+44
-7
@@ -1,16 +1,53 @@
|
||||
CFLAGS=-I../src
|
||||
LDFLAGS=-L../src -lvosk -ldl -lpthread -Wl,-rpath,../src
|
||||
KALDI_ROOT=$(HOME)/travis/kaldi
|
||||
|
||||
VOSK_SOURCES= \
|
||||
../src/kaldi_recognizer.cc \
|
||||
../src/language_model.cc \
|
||||
../src/model.cc \
|
||||
../src/spk_model.cc \
|
||||
../src/vosk_api.cc
|
||||
|
||||
CFLAGS=-g -O2 -DFST_NO_DYNAMIC_LINKING -I../src -I$(KALDI_ROOT)/src -I$(KALDI_ROOT)/tools/openfst/include
|
||||
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/OpenBLAS/install/lib/libopenblas.a \
|
||||
$(KALDI_ROOT)/tools/OpenBLAS/install/lib/liblapack.a \
|
||||
$(KALDI_ROOT)/tools/OpenBLAS/install/lib/libblas.a \
|
||||
$(KALDI_ROOT)/tools/OpenBLAS/install/lib/libf2c.a \
|
||||
$(KALDI_ROOT)/tools/openfst/lib/libfst.a \
|
||||
$(KALDI_ROOT)/tools/openfst/lib/libfstngram.a
|
||||
|
||||
all: test_vosk test_vosk_speaker
|
||||
|
||||
test_vosk: test_vosk.o
|
||||
gcc $^ -o $@ $(LDFLAGS)
|
||||
test_vosk: test_vosk.o libvosk.a
|
||||
g++ $^ -o $@ $(LIBS) -lpthread
|
||||
|
||||
test_vosk_speaker: test_vosk_speaker.o
|
||||
gcc $^ -o $@ $(LDFLAGS)
|
||||
test_vosk_speaker: test_vosk_speaker.o libvosk.a
|
||||
g++ $^ -o $@ $(LIBS) -lpthread
|
||||
|
||||
libvosk.a: $(VOSK_SOURCES:.cc=.o)
|
||||
ar rcs $@ $^
|
||||
|
||||
%.o: %.c
|
||||
gcc $(CFLAGS) -c -o $@ $<
|
||||
g++ $(CFLAGS) -c -o $@ $<
|
||||
|
||||
%.o: %.cc
|
||||
g++ -std=c++17 $(CFLAGS) -c -o $@ $<
|
||||
|
||||
clean:
|
||||
rm -f *.o *.a test_vosk test_vosk_speaker
|
||||
|
||||
@@ -8,7 +8,7 @@ int main() {
|
||||
|
||||
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);
|
||||
VoskRecognizer *recognizer = vosk_recognizer_new_spk(model, spk_model, 16000.0);
|
||||
|
||||
wavin = fopen("test.wav", "rb");
|
||||
fseek(wavin, 44, SEEK_SET);
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
KALDI_ROOT ?= $(HOME)/kaldi
|
||||
CFLAGS := -std=c++17 -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
|
||||
|
||||
# On Linux
|
||||
MATH_LIBS = \
|
||||
${KALDI_ROOT}/tools/OpenBLAS/install/lib/libopenblas.a \
|
||||
${KALDI_ROOT}/tools/OpenBLAS/install/lib/liblapack.a \
|
||||
${KALDI_ROOT}/tools/OpenBLAS/install/lib/libblas.a \
|
||||
${KALDI_ROOT}/tools/OpenBLAS/install/lib/libf2c.a
|
||||
|
||||
# On OSX
|
||||
# MATH_LIBS = -framework Accelerate
|
||||
|
||||
all: test.exe
|
||||
|
||||
test.exe: libkaldiwrap.so test.cs
|
||||
mcs -out:Vosk.dll -target:library gen/*.cs
|
||||
mcs test.cs -r:Vosk.dll -out:test.exe
|
||||
|
||||
VOSK_SOURCES = \
|
||||
vosk_wrap.c \
|
||||
../src/kaldi_recognizer.cc \
|
||||
../src/language_model.cc \
|
||||
../src/model.cc \
|
||||
../src/spk_model.cc \
|
||||
../src/vosk_api.cc
|
||||
|
||||
VOSK_HEADERS = \
|
||||
../src/kaldi_recognizer.h \
|
||||
../src/language_model.h \
|
||||
../src/model.h \
|
||||
../src/vosk_api.h \
|
||||
../src/spk_model.h
|
||||
|
||||
libkaldiwrap.so: $(VOSK_SOURCES) $(VOSK_HEADERS)
|
||||
$(CXX) -fpermissive $(CFLAGS) $(CPPFLAGS) -shared -o $@ $(VOSK_SOURCES) $(KALDI_LIBS) $(MATH_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 *.dll
|
||||
@@ -1,12 +0,0 @@
|
||||
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.
|
||||
@@ -1,83 +0,0 @@
|
||||
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);
|
||||
|
||||
rec.SetEndpointerMode(EndpointerMode.LONG);
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<RootNamespace>VoskDemo</RootNamespace>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<RestoreSources>$(RestoreSources);../nuget</RestoreSources>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Vosk" Version="0.3.75" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -1,17 +0,0 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<PackageId>Vosk</PackageId>
|
||||
<Version>0.3.75</Version>
|
||||
<authors>Alpha Cephei Inc</authors>
|
||||
<owners>Alpha Cephei Inc</owners>
|
||||
</PropertyGroup>
|
||||
|
||||
<Target Name="CopyFiles" AfterTargets="Build">
|
||||
<Copy SourceFiles="bin/Release/net8.0/Vosk.dll" DestinationFolder="lib/net8.0" />
|
||||
</Target>
|
||||
|
||||
</Project>
|
||||
@@ -2,14 +2,13 @@
|
||||
<package>
|
||||
<metadata>
|
||||
<id>Vosk</id>
|
||||
<version>0.3.75</version>
|
||||
<version>0.3.16</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.
|
||||
<description>Vosk is an offline open source speech recognition toolkit. It enables speech recognition models for 16 languages and dialects - English, Indian English, German, French, Spanish, Portuguese, Chinese, Russian, Turkish, Vietnamese, Italian, Dutch, Catalan, Arabic, Greek, Farsi.
|
||||
|
||||
Vosk models are small (50 Mb) but provide continuous large vocabulary transcription, zero-latency response with streaming API, reconfigurable vocabulary and speaker identification.
|
||||
|
||||
@@ -19,14 +18,10 @@ Vosk supplies speech recognition for chatbots, smart home appliances, virtual as
|
||||
|
||||
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>
|
||||
<copyright>Copyright 2020 Alpha Cephei Inc</copyright>
|
||||
<tags>speech recognition voice stt asr speech-to-text ai offline privacy</tags>
|
||||
<dependencies>
|
||||
<group targetFramework="net8.0"/>
|
||||
<group targetFramework=".NETStandard2.0"/>
|
||||
</dependencies>
|
||||
</metadata>
|
||||
<files>
|
||||
<file src="**" exclude="bin/**;obj/**;build.sh;src/*.cs;*.nupkg;**/.keep-me" />
|
||||
</files>
|
||||
</package>
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
rm -rf bin lib obj
|
||||
/home/shmyrev/local/dotnet/dotnet pack Vosk.csproj -p:NuspecFile=Vosk.nuspec -o .
|
||||
@@ -1,11 +0,0 @@
|
||||
<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,9 @@
|
||||
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<Content Include="$(MSBuildThisFileDirectory)lib/**/**">
|
||||
<Link>%(RecursiveDir)%(Filename)%(Extension)</Link>
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
<Visible>false</Visible>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -1,55 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace Vosk
|
||||
{
|
||||
public class BatchModel : global::System.IDisposable
|
||||
{
|
||||
private global::System.Runtime.InteropServices.HandleRef handle;
|
||||
|
||||
internal BatchModel(global::System.IntPtr cPtr)
|
||||
{
|
||||
handle = new global::System.Runtime.InteropServices.HandleRef(this, cPtr);
|
||||
}
|
||||
|
||||
internal static global::System.Runtime.InteropServices.HandleRef getCPtr(BatchModel obj)
|
||||
{
|
||||
return (obj == null) ? new global::System.Runtime.InteropServices.HandleRef(null, global::System.IntPtr.Zero) : obj.handle;
|
||||
}
|
||||
|
||||
~BatchModel()
|
||||
{
|
||||
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_BatchModel(handle);
|
||||
handle = new global::System.Runtime.InteropServices.HandleRef(null, global::System.IntPtr.Zero);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public BatchModel(string model_path) : this(VoskPINVOKE.new_BatchModel(model_path))
|
||||
{
|
||||
}
|
||||
|
||||
public void WaitForCompletion()
|
||||
{
|
||||
if (handle.Handle != global::System.IntPtr.Zero)
|
||||
{
|
||||
VoskPINVOKE.wait_BatchModel(handle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
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)) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,88 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace Vosk
|
||||
{
|
||||
public class VoskBatchRecognizer : System.IDisposable
|
||||
{
|
||||
private System.Runtime.InteropServices.HandleRef handle;
|
||||
|
||||
internal VoskBatchRecognizer(System.IntPtr cPtr)
|
||||
{
|
||||
handle = new System.Runtime.InteropServices.HandleRef(this, cPtr);
|
||||
}
|
||||
|
||||
internal static System.Runtime.InteropServices.HandleRef getCPtr(VoskBatchRecognizer obj)
|
||||
{
|
||||
return (obj == null) ? new System.Runtime.InteropServices.HandleRef(null, System.IntPtr.Zero) : obj.handle;
|
||||
}
|
||||
|
||||
~VoskBatchRecognizer()
|
||||
{
|
||||
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_VoskBatchRecognizer(handle);
|
||||
handle = new System.Runtime.InteropServices.HandleRef(null, System.IntPtr.Zero);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public VoskBatchRecognizer(BatchModel model, float sample_rate) : this(VoskPINVOKE.new_VoskBatchRecognizer(BatchModel.getCPtr(model), sample_rate))
|
||||
{
|
||||
}
|
||||
|
||||
public bool AcceptWaveform(byte[] data, int len)
|
||||
{
|
||||
return VoskPINVOKE.VoskBatchRecognizer_AcceptWaveform(handle, data, 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 FrontResult()
|
||||
{
|
||||
return PtrToStringUTF8(VoskPINVOKE.VoskBatchRecognizer_FrontResult(handle));
|
||||
}
|
||||
|
||||
public string Result()
|
||||
{
|
||||
string result = PtrToStringUTF8(VoskPINVOKE.VoskBatchRecognizer_FrontResult(handle));
|
||||
VoskPINVOKE.VoskBatchRecognizer_Pop(handle);
|
||||
return result;
|
||||
}
|
||||
|
||||
public int GetNumPendingChunks()
|
||||
{
|
||||
return VoskPINVOKE.VoskBatchRecognizer_GetPendingChunks(handle);
|
||||
}
|
||||
|
||||
public void FinishStream()
|
||||
{
|
||||
VoskPINVOKE.VoskBatchRecognizer_FinishStream(handle);
|
||||
}
|
||||
|
||||
public void SetNLSML(bool nlsml)
|
||||
{
|
||||
VoskPINVOKE.VoskBatchRecognizer_SetNLSML(handle, Convert.ToInt32(nlsml));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,118 +0,0 @@
|
||||
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_recognizer_set_endpointer_mode")]
|
||||
public static extern void VoskRecognizer_SetEndpointerMode(global::System.Runtime.InteropServices.HandleRef jarg1, int jarg2);
|
||||
|
||||
[global::System.Runtime.InteropServices.DllImport("libvosk", EntryPoint="vosk_recognizer_set_endpointer_delays")]
|
||||
public static extern void VoskRecognizer_SetEndpointerDelays(global::System.Runtime.InteropServices.HandleRef jarg1, float jarg2, float jarg3, float jarg4);
|
||||
|
||||
[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();
|
||||
|
||||
[global::System.Runtime.InteropServices.DllImport("libvosk", EntryPoint = "vosk_batch_model_new")]
|
||||
public static extern global::System.IntPtr new_BatchModel(string jarg1);
|
||||
|
||||
[global::System.Runtime.InteropServices.DllImport("libvosk", EntryPoint = "vosk_batch_model_free")]
|
||||
public static extern void delete_BatchModel(global::System.Runtime.InteropServices.HandleRef jarg1);
|
||||
|
||||
[global::System.Runtime.InteropServices.DllImport("libvosk", EntryPoint = "vosk_batch_model_wait")]
|
||||
public static extern void wait_BatchModel(global::System.Runtime.InteropServices.HandleRef jarg1);
|
||||
|
||||
[global::System.Runtime.InteropServices.DllImport("libvosk", EntryPoint = "vosk_batch_recognizer_new")]
|
||||
public static extern global::System.IntPtr new_VoskBatchRecognizer(global::System.Runtime.InteropServices.HandleRef jarg1, float jarg2);
|
||||
|
||||
[global::System.Runtime.InteropServices.DllImport("libvosk", EntryPoint = "vosk_batch_recognizer_free")]
|
||||
public static extern void delete_VoskBatchRecognizer(global::System.Runtime.InteropServices.HandleRef jarg1);
|
||||
|
||||
[global::System.Runtime.InteropServices.DllImport("libvosk", EntryPoint = "vosk_batch_recognizer_accept_waveform")]
|
||||
public static extern bool VoskBatchRecognizer_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_batch_recognizer_set_nlsml")]
|
||||
public static extern void VoskBatchRecognizer_SetNLSML(global::System.Runtime.InteropServices.HandleRef jarg1, int jarg2);
|
||||
|
||||
[global::System.Runtime.InteropServices.DllImport("libvosk", EntryPoint = "vosk_batch_recognizer_finish_stream")]
|
||||
public static extern void VoskBatchRecognizer_FinishStream(global::System.Runtime.InteropServices.HandleRef jarg1);
|
||||
|
||||
[global::System.Runtime.InteropServices.DllImport("libvosk", EntryPoint = "vosk_batch_recognizer_front_result")]
|
||||
public static extern global::System.IntPtr VoskBatchRecognizer_FrontResult(global::System.Runtime.InteropServices.HandleRef jarg1);
|
||||
|
||||
[global::System.Runtime.InteropServices.DllImport("libvosk", EntryPoint = "vosk_batch_recognizer_pop")]
|
||||
public static extern void VoskBatchRecognizer_Pop(global::System.Runtime.InteropServices.HandleRef jarg1);
|
||||
|
||||
[global::System.Runtime.InteropServices.DllImport("libvosk", EntryPoint = "vosk_batch_recognizer_get_pending_chunks")]
|
||||
public static extern int VoskBatchRecognizer_GetPendingChunks(global::System.Runtime.InteropServices.HandleRef jarg1);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,111 +0,0 @@
|
||||
namespace Vosk {
|
||||
|
||||
public enum EndpointerMode {
|
||||
DEFAULT = 0,
|
||||
SHORT = 1,
|
||||
LONG = 2,
|
||||
VERY_LONG = 3
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
public void SetEndpointerMode(EndpointerMode mode) {
|
||||
VoskPINVOKE.VoskRecognizer_SetEndpointerMode(handle, (int) mode);
|
||||
}
|
||||
|
||||
public void SetEndpointerDelays(float t_start_max, float t_end, float t_max) {
|
||||
VoskPINVOKE.VoskRecognizer_SetEndpointerDelays(handle, t_start_max, t_end, t_max);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
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());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
See https://alphacephei.com/vosk/accuracy
|
||||
@@ -0,0 +1 @@
|
||||
See https://alphacephei.com/vosk/adaptation
|
||||
@@ -0,0 +1 @@
|
||||
See https://alphacephei.com/vosk/models
|
||||
-176
@@ -1,176 +0,0 @@
|
||||
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
|
||||
@@ -1 +0,0 @@
|
||||
See example subfolder for instructions how to use the module
|
||||
-99
@@ -1,99 +0,0 @@
|
||||
package vosk
|
||||
|
||||
// #cgo CPPFLAGS: -I ${SRCDIR}/../src
|
||||
// #cgo !windows LDFLAGS: -L ${SRCDIR}/../src -lvosk -ldl -lpthread
|
||||
// #cgo windows LDFLAGS: -L ${SRCDIR}/../src -lvosk -lpthread
|
||||
// #include <stdlib.h>
|
||||
// #include <vosk_api.h>
|
||||
import "C"
|
||||
import "unsafe"
|
||||
|
||||
// VoskBatchModel contains a reference to the C VoskBatchModel
|
||||
type VoskBatchModel struct {
|
||||
model *C.struct_VoskBatchModel
|
||||
}
|
||||
|
||||
// NewBatchModel creates a new VoskBatchModel instance
|
||||
func NewBatchModel(modelPath string) (*VoskBatchModel, error) {
|
||||
cmodelPath := C.CString(modelPath)
|
||||
defer C.free(unsafe.Pointer(cmodelPath))
|
||||
internal := C.vosk_batch_model_new(cmodelPath)
|
||||
model := &VoskBatchModel{model: internal}
|
||||
return model, nil
|
||||
}
|
||||
|
||||
func (m *VoskBatchModel) Free() {
|
||||
C.vosk_batch_model_free(m.model)
|
||||
}
|
||||
|
||||
func (m *VoskBatchModel) Wait() {
|
||||
C.vosk_batch_model_wait(m.model);
|
||||
}
|
||||
|
||||
func freeBatchModel(model *VoskBatchModel) {
|
||||
C.vosk_batch_model_free(model.model)
|
||||
}
|
||||
|
||||
// VoskBatchRecognizer contains a reference to the C VoskBatchRecognizer
|
||||
type VoskBatchRecognizer struct {
|
||||
rec *C.struct_VoskBatchRecognizer
|
||||
}
|
||||
|
||||
func freeBatchRecognizer(recognizer *VoskBatchRecognizer) {
|
||||
C.vosk_batch_recognizer_free(recognizer.rec)
|
||||
}
|
||||
|
||||
func (r *VoskBatchRecognizer) Free() {
|
||||
C.vosk_batch_recognizer_free(r.rec)
|
||||
}
|
||||
|
||||
// NewBatchRecognizer creates a new VoskBatchRecognizer instance
|
||||
func NewBatchRecognizer(model *VoskBatchModel, sampleRate float64) (*VoskBatchRecognizer, error) {
|
||||
internal := C.vosk_batch_recognizer_new(model.model, C.float(sampleRate))
|
||||
rec := &VoskBatchRecognizer{rec: internal}
|
||||
return rec, nil
|
||||
}
|
||||
|
||||
// AcceptWaveform accepts and processes a new chunk of the voice data.
|
||||
func (r *VoskBatchRecognizer) AcceptWaveform(buffer []byte) {
|
||||
cbuf := C.CBytes(buffer)
|
||||
defer C.free(cbuf)
|
||||
C.vosk_batch_recognizer_accept_waveform(r.rec, (*C.char)(cbuf), C.int(len(buffer)))
|
||||
}
|
||||
|
||||
/** Set NLSML output
|
||||
* @param nlsml - boolean value
|
||||
*/
|
||||
//void vosk_batch_recognizer_set_nlsml(VoskBatchRecognizer *recognizer, int nlsml);
|
||||
|
||||
func (r *VoskBatchRecognizer) SetNlsml(nlsml int) {
|
||||
C.vosk_batch_recognizer_set_nlsml(r.rec, C.int(nlsml))
|
||||
}
|
||||
|
||||
/** Closes the stream */
|
||||
//void vosk_batch_recognizer_finish_stream(VoskBatchRecognizer *recognizer);
|
||||
|
||||
func (r *VoskBatchRecognizer) FinishStream() {
|
||||
C.vosk_batch_recognizer_finish_stream(r.rec)
|
||||
}
|
||||
|
||||
/** Return results */
|
||||
//const char *vosk_batch_recognizer_front_result(VoskBatchRecognizer *recognizer);
|
||||
|
||||
func (r *VoskBatchRecognizer) FrontResult() string {
|
||||
return C.GoString(C.vosk_batch_recognizer_front_result(r.rec))
|
||||
}
|
||||
|
||||
/** Release and free first retrieved result */
|
||||
//void vosk_batch_recognizer_pop(VoskBatchRecognizer *recognizer);
|
||||
|
||||
func (r *VoskBatchRecognizer) Pop() {
|
||||
C.vosk_batch_recognizer_pop(r.rec)
|
||||
}
|
||||
|
||||
/** Get amount of pending chunks for more intelligent waiting */
|
||||
//int vosk_batch_recognizer_get_pending_chunks(VoskBatchRecognizer *recognizer);
|
||||
func (r *VoskBatchRecognizer) GetPendingChunks() int {
|
||||
i := C.vosk_batch_recognizer_get_pending_chunks(r.rec)
|
||||
return int(i)
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
This example expects a `s16le` converted audio file and converts it to text in a
|
||||
manner that imitates the Python example of [test_gpu_batch.py](../python/example/test_gpu_batch.py).
|
||||
|
||||
Note that the `libvosk.so` must be in the library path. This was successfully tested on
|
||||
Ubuntu 24.04 with Go 1.18, gcc-11, NVIDIA driver 570.172.08.
|
||||
@@ -1,54 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
vosk "github.com/alphacep/vosk-api/go"
|
||||
)
|
||||
|
||||
func main() {
|
||||
var filename string
|
||||
flag.StringVar(&filename, "f", "", "file to transcribe")
|
||||
flag.Parse()
|
||||
|
||||
vosk.GPUInit()
|
||||
|
||||
model, err := vosk.NewBatchModel("model")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
rec, err := vosk.NewBatchRecognizer(model, 16000.0)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
file, err := os.Open(filename)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
buf := make([]byte, 8000)
|
||||
|
||||
for {
|
||||
if _, err := file.Read(buf); err != nil {
|
||||
if err != io.EOF {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
rec.AcceptWaveform(buf)
|
||||
model.Wait()
|
||||
if rec.FrontResult() != "" {
|
||||
fmt.Println(rec.FrontResult())
|
||||
rec.Pop()
|
||||
}
|
||||
}
|
||||
// Is this needed? rec.FinishStream()
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
// 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
|
||||
@@ -1,31 +0,0 @@
|
||||
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
|
||||
```
|
||||
@@ -1,2 +0,0 @@
|
||||
// Example package for Vosk Go bindings.
|
||||
package main
|
||||
@@ -1,63 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
vosk "github.com/alphacep/vosk-api/go"
|
||||
)
|
||||
|
||||
func main() {
|
||||
var filename string
|
||||
flag.StringVar(&filename, "f", "", "file to transcribe")
|
||||
flag.Parse()
|
||||
|
||||
vosk.GPUInit()
|
||||
|
||||
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()
|
||||
|
||||
buf := make([]byte, 4096)
|
||||
|
||||
for {
|
||||
_, err := file.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"])
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
module github.com/alphacep/vosk-api/go
|
||||
|
||||
go 1.16
|
||||
|
||||
replace (
|
||||
github.com/alphacep/vosk-api/go => ./
|
||||
)
|
||||
-180
@@ -1,180 +0,0 @@
|
||||
package vosk
|
||||
|
||||
// #cgo CPPFLAGS: -I ${SRCDIR}/../src
|
||||
// #cgo !windows LDFLAGS: -L ${SRCDIR}/../src -lvosk -ldl -lpthread
|
||||
// #cgo windows LDFLAGS: -L ${SRCDIR}/../src -lvosk -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)
|
||||
}
|
||||
|
||||
// SetGrm sets which phrases to recognize on an already initialized recognizer.
|
||||
func (r *VoskRecognizer) SetGrm(grammar string) {
|
||||
cgrammar := C.CString(grammar)
|
||||
defer C.free(unsafe.Pointer(cgrammar))
|
||||
C.vosk_recognizer_set_grm(r.rec, cgrammar)
|
||||
}
|
||||
|
||||
// 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))
|
||||
}
|
||||
|
||||
// SetEndpointerDelays sets the recognition timeouts, where startMax
|
||||
// is the timeout for stopping recognition in case of initial silence
|
||||
// (usually around 5), end is the timeout for stopping recognition
|
||||
// in milliseconds after we recognized something (usually around 0.5-1.0),
|
||||
// and max is the timeout for forcing utterance end in milliseconds
|
||||
// (usually around 20-30).
|
||||
func (r *VoskRecognizer) SetEndpointerDelays(startMax, end, max float64) {
|
||||
C.vosk_recognizer_set_endpointer_delays(r.rec, C.float(startMax), C.float(end), C.float(max))
|
||||
}
|
||||
|
||||
// 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,12 +13,11 @@
|
||||
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 */; };
|
||||
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 */; };
|
||||
92D86BD5253F823F0040D53F /* vosk-model-small-en-us-0.4 in Resources */ = {isa = PBXBuildFile; fileRef = 92D86BD3253F823E0040D53F /* vosk-model-small-en-us-0.4 */; };
|
||||
92D86BD6253F823F0040D53F /* vosk-model-spk-0.4 in Resources */ = {isa = PBXBuildFile; fileRef = 92D86BD4253F823F0040D53F /* vosk-model-spk-0.4 */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
@@ -31,14 +30,13 @@
|
||||
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; };
|
||||
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>"; };
|
||||
92D86BD3253F823E0040D53F /* vosk-model-small-en-us-0.4 */ = {isa = PBXFileReference; lastKnownFileType = folder; path = "vosk-model-small-en-us-0.4"; sourceTree = "<group>"; };
|
||||
92D86BD4253F823F0040D53F /* vosk-model-spk-0.4 */ = {isa = PBXFileReference; lastKnownFileType = folder; path = "vosk-model-spk-0.4"; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
@@ -47,9 +45,9 @@
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
92833003273C466E00058B52 /* libc++.tbd in Frameworks */,
|
||||
92375246240C6DC900DD6076 /* libstdc++.tbd in Frameworks */,
|
||||
92375244240C6DAF00DD6076 /* Accelerate.framework in Frameworks */,
|
||||
925527A9273C492C00FFD9CC /* libvosk.a in Frameworks */,
|
||||
9237523C240C642000DD6076 /* libkaldiwrap.a in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@@ -85,7 +83,6 @@
|
||||
9237522A240C550B00DD6076 /* LaunchScreen.storyboard */,
|
||||
9237522D240C550B00DD6076 /* Info.plist */,
|
||||
92375233240C558900DD6076 /* Vosk.swift */,
|
||||
92D6B8D225BDFEAC007FF08D /* VoskModel.swift */,
|
||||
);
|
||||
path = VoskApiTest;
|
||||
sourceTree = "<group>";
|
||||
@@ -93,11 +90,11 @@
|
||||
92375239240C642000DD6076 /* Vosk */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
92D86BD3253F823E0040D53F /* vosk-model-small-en-us-0.4 */,
|
||||
92D86BD4253F823F0040D53F /* vosk-model-spk-0.4 */,
|
||||
928CC50C25BE124400490481 /* vosk-model-small-en-us-0.15 */,
|
||||
925527A8273C492C00FFD9CC /* libvosk.a */,
|
||||
92AA22AD244CDD1200DA464B /* vosk_api.h */,
|
||||
92375256240C6E3D00DD6076 /* 10001-90210-01803.wav */,
|
||||
92AA22AD244CDD1200DA464B /* vosk_api.h */,
|
||||
9237523A240C642000DD6076 /* libkaldiwrap.a */,
|
||||
);
|
||||
name = Vosk;
|
||||
path = VoskApiTest/Vosk;
|
||||
@@ -106,7 +103,7 @@
|
||||
92375242240C6DAF00DD6076 /* Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
92833002273C466E00058B52 /* libc++.tbd */,
|
||||
92375245240C6DC900DD6076 /* libstdc++.tbd */,
|
||||
92375243240C6DAF00DD6076 /* Accelerate.framework */,
|
||||
);
|
||||
name = Frameworks;
|
||||
@@ -145,7 +142,7 @@
|
||||
9237521D240C550B00DD6076 = {
|
||||
CreatedOnToolsVersion = 8.3.2;
|
||||
LastSwiftMigration = 0920;
|
||||
ProvisioningStyle = Manual;
|
||||
ProvisioningStyle = Automatic;
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -172,12 +169,12 @@
|
||||
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 */,
|
||||
92D86BD5253F823F0040D53F /* vosk-model-small-en-us-0.4 in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@@ -190,7 +187,6 @@
|
||||
files = (
|
||||
92375224240C550B00DD6076 /* ViewController.swift in Sources */,
|
||||
92375222240C550B00DD6076 /* AppDelegate.swift in Sources */,
|
||||
92D6B8D325BDFEAC007FF08D /* VoskModel.swift in Sources */,
|
||||
92375234240C558900DD6076 /* Vosk.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
@@ -223,6 +219,7 @@
|
||||
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;
|
||||
@@ -244,7 +241,7 @@
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
@@ -281,6 +278,7 @@
|
||||
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;
|
||||
@@ -302,7 +300,7 @@
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
ENABLE_NS_ASSERTIONS = NO;
|
||||
@@ -317,7 +315,6 @@
|
||||
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";
|
||||
@@ -332,10 +329,7 @@
|
||||
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;
|
||||
ENABLE_BITCODE = YES;
|
||||
INFOPLIST_FILE = VoskApiTest/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||
LIBRARY_SEARCH_PATHS = (
|
||||
@@ -344,13 +338,11 @@
|
||||
);
|
||||
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;
|
||||
};
|
||||
@@ -359,10 +351,7 @@
|
||||
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;
|
||||
ENABLE_BITCODE = YES;
|
||||
INFOPLIST_FILE = VoskApiTest/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||
LIBRARY_SEARCH_PATHS = (
|
||||
@@ -371,12 +360,10 @@
|
||||
);
|
||||
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;
|
||||
};
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Workspace
|
||||
version = "1.0">
|
||||
<FileRef
|
||||
location = "group:../VoskApiTest/Vosk/vosk-model-small-en-us-0.4">
|
||||
</FileRef>
|
||||
<FileRef
|
||||
location = "group:../VoskApiTest/Vosk/vosk-model-spk-0.4">
|
||||
</FileRef>
|
||||
<FileRef
|
||||
location = "self:">
|
||||
</FileRef>
|
||||
|
||||
@@ -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="bdW-KL-Y8Z">
|
||||
<device id="retina5_5" 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="BYZ-38-t0r">
|
||||
<device id="retina4_7" orientation="portrait">
|
||||
<adaptation id="fullscreen"/>
|
||||
</device>
|
||||
<dependencies>
|
||||
@@ -10,52 +10,23 @@
|
||||
</dependencies>
|
||||
<scenes>
|
||||
<!--View Controller-->
|
||||
<scene sceneID="nEc-89-Iqu">
|
||||
<scene sceneID="tne-QT-ifu">
|
||||
<objects>
|
||||
<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>
|
||||
<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"/>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||
</view>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="14"/>
|
||||
<textInputTraits key="textInputTraits" autocapitalizationType="sentences"/>
|
||||
</textView>
|
||||
<connections>
|
||||
<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"/>
|
||||
<outlet property="mainText" destination="CtX-mx-X98" id="oJy-5J-NKp"/>
|
||||
</connections>
|
||||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="nWA-4D-pA6" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="-17.39130434782609" y="-285.32608695652175"/>
|
||||
<point key="canvasLocation" x="32.799999999999997" y="32.833583208395808"/>
|
||||
</scene>
|
||||
</scenes>
|
||||
</document>
|
||||
|
||||
@@ -3,113 +3,30 @@
|
||||
// VoskApiTest
|
||||
//
|
||||
// Created by Niсkolay Shmyrev on 01.03.20.
|
||||
// Copyright © 2020-2021 Alpha Cephei. All rights reserved.
|
||||
// Copyright © 2020 Alpha Cephei. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import AVFoundation
|
||||
|
||||
enum WorkMode {
|
||||
case stopped
|
||||
case microphone
|
||||
case file
|
||||
}
|
||||
|
||||
class ViewController: UIViewController {
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@IBOutlet var mainText: UITextView!
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
setMode(mode: .stopped)
|
||||
processingQueue = DispatchQueue(label: "recognizerQueue")
|
||||
model = VoskModel()
|
||||
}
|
||||
|
||||
DispatchQueue.global(qos: .userInitiated).async {
|
||||
DispatchQueue.main.async {
|
||||
self.mainText.text = "Processing file..."
|
||||
}
|
||||
let vosk = Vosk()
|
||||
let res = vosk.recognizeFile()
|
||||
DispatchQueue.main.async {
|
||||
self.mainText.text = res
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override func didReceiveMemoryWarning() {
|
||||
super.didReceiveMemoryWarning()
|
||||
}
|
||||
|
||||
+21
-33
@@ -3,52 +3,40 @@
|
||||
// VoskApiTest
|
||||
//
|
||||
// Created by Niсkolay Shmyrev on 01.03.20.
|
||||
// Copyright © 2020-2021 Alpha Cephei. All rights reserved.
|
||||
// Copyright © 2020 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 {
|
||||
|
||||
|
||||
// Set to -1 to disable logs
|
||||
vosk_set_log_level(0);
|
||||
|
||||
let modelPath = resourcePath + "/vosk-model-small-en-us-0.4"
|
||||
let spkModelPath = resourcePath + "/vosk-model-spk-0.4"
|
||||
|
||||
let model = vosk_model_new(modelPath)
|
||||
let spkModel = vosk_spk_model_new(spkModelPath)
|
||||
let recognizer = vosk_recognizer_new_spk(model, spkModel, 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!)!;
|
||||
}
|
||||
}
|
||||
|
||||
+36
-117
@@ -1,4 +1,4 @@
|
||||
// Copyright 2020-2021 Alpha Cephei Inc.
|
||||
// Copyright 2020 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.
|
||||
@@ -43,7 +43,7 @@ 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 */
|
||||
@ @returns model object */
|
||||
VoskModel *vosk_model_new(const char *model_path);
|
||||
|
||||
|
||||
@@ -55,18 +55,10 @@ VoskModel *vosk_model_new(const char *model_path);
|
||||
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 */
|
||||
* @returns model object */
|
||||
VoskSpkModel *vosk_spk_model_new(const char *model_path);
|
||||
|
||||
|
||||
@@ -79,13 +71,9 @@ 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 */
|
||||
* The recognizers process the speech and return text using shared model data
|
||||
* @param sample_rate The sample rate of the audio you going to feed into the recognizer
|
||||
* @returns recognizer object */
|
||||
VoskRecognizer *vosk_recognizer_new(VoskModel *model, float sample_rate);
|
||||
|
||||
|
||||
@@ -94,14 +82,10 @@ VoskRecognizer *vosk_recognizer_new(VoskModel *model, float sample_rate);
|
||||
* 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);
|
||||
* @param sample_rate The sample rate of the audio you going to feed into the recognizer
|
||||
* @returns recognizer object */
|
||||
VoskRecognizer *vosk_recognizer_new_spk(VoskModel *model, VoskSpkModel *spk_model, float sample_rate);
|
||||
|
||||
|
||||
/** Creates the recognizer object with the phrase list
|
||||
@@ -114,46 +98,42 @@ VoskRecognizer *vosk_recognizer_new_spk(VoskModel *model, float sample_rate, Vos
|
||||
* 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 sample_rate The sample rate of the audio you going to feed into the recognizer
|
||||
* @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 */
|
||||
* @returns recognizer object */
|
||||
VoskRecognizer *vosk_recognizer_new_grm(VoskModel *model, float sample_rate, const char *grammar);
|
||||
|
||||
|
||||
/** Adds speaker model to already initialized recognizer
|
||||
/** Accept voice data
|
||||
*
|
||||
* Can add speaker recognition model to already created recognizer. Helps to initialize
|
||||
* speaker recognition for grammar-based recognizer.
|
||||
* accept and process new chunk of voice data
|
||||
*
|
||||
* @param spk_model Speaker recognition model */
|
||||
void vosk_recognizer_set_spk_model(VoskRecognizer *recognizer, VoskSpkModel *spk_model);
|
||||
* @param data - audio data in PCM 16-bit mono format
|
||||
* @param length - length of the audio data
|
||||
* @returns true if silence is occured and you can retrieve a new utterance with result method */
|
||||
int vosk_recognizer_accept_waveform(VoskRecognizer *recognizer, const char *data, int length);
|
||||
|
||||
|
||||
/** 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
|
||||
/** 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>
|
||||
* {
|
||||
* "result" : [{
|
||||
* "conf" : 1.000000,
|
||||
* "end" : 1.110000,
|
||||
@@ -176,54 +156,13 @@ void vosk_recognizer_set_max_alternatives(VoskRecognizer *recognizer, int max_al
|
||||
* "word" : "zero"
|
||||
* }, {
|
||||
* "conf" : 1.000000,
|
||||
* "end" : 2.610000,
|
||||
* "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"
|
||||
* "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);
|
||||
|
||||
@@ -235,7 +174,7 @@ const char *vosk_recognizer_result(VoskRecognizer *recognizer);
|
||||
*
|
||||
* <pre>
|
||||
* {
|
||||
* "partial" : "cyril one eight zero"
|
||||
* "partial" : "cyril one eight zero"
|
||||
* }
|
||||
* </pre>
|
||||
*/
|
||||
@@ -251,12 +190,6 @@ const char *vosk_recognizer_partial_result(VoskRecognizer *recognizer);
|
||||
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 */
|
||||
@@ -271,20 +204,6 @@ void vosk_recognizer_free(VoskRecognizer *recognizer);
|
||||
*/
|
||||
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
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
//
|
||||
// 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)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
KALDI_ROOT ?= $(HOME)/kaldi
|
||||
CFLAGS := -g -O2 -DPIC -fPIC -Wno-unused-function -std=c++17
|
||||
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 \
|
||||
|
||||
# On Linux
|
||||
MATH_LIBS = \
|
||||
${KALDI_ROOT}/tools/OpenBLAS/install/lib/libopenblas.a \
|
||||
${KALDI_ROOT}/tools/OpenBLAS/install/lib/liblapack.a \
|
||||
${KALDI_ROOT}/tools/OpenBLAS/install/lib/libblas.a \
|
||||
${KALDI_ROOT}/tools/OpenBLAS/install/lib/libf2c.a
|
||||
|
||||
all: libvosk_jni.so
|
||||
|
||||
VOSK_SOURCES = \
|
||||
vosk_wrap.cc \
|
||||
../src/kaldi_recognizer.cc \
|
||||
../src/kaldi_recognizer.h \
|
||||
../src/language_model.cc \
|
||||
../src/language_model.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) $(MATH_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.15.zip
|
||||
unzip vosk-model-small-en-us-0.3.zip && rm vosk-model-small-en-us-0.15.zip
|
||||
mv vosk-model-small-en-us-0.3.15 model
|
||||
|
||||
model-spk:
|
||||
wget https://alphacephei.com/kaldi/models/vosk-model-spk-0.4.zip
|
||||
unzip vosk-model-spk-0.3.zip && rm vosk-model-spk-0.4.zip
|
||||
mv vosk-model-spk-0.4 model-spk
|
||||
|
||||
run: model model-spk
|
||||
javac test/*.java org/kaldi/*.java
|
||||
java -Djava.library.path=. -cp . test.DecoderTest
|
||||
+16
-4
@@ -1,7 +1,19 @@
|
||||
Java bindings for Vosk API using jnr-ffi
|
||||
Java API sample
|
||||
|
||||
See demo project for details, build it with Gradle.
|
||||
Doesn't work on Windows or Mac yet, help to prepare the packaged jars is welcome.
|
||||
|
||||
Download model and unpack as "model" folder in the demo project.
|
||||
For now to try it:
|
||||
|
||||
Make sure you are using recent JDK and Gradle.
|
||||
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
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
plugins {
|
||||
id 'application'
|
||||
}
|
||||
|
||||
application {
|
||||
mainClass = 'org.vosk.demo.DecoderDemo'
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation group: 'com.alphacephei', name: 'vosk', version: '0.3.75'
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
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());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,71 +0,0 @@
|
||||
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.75'
|
||||
|
||||
mavenPublish {
|
||||
group = 'com.alphacephei'
|
||||
version = version
|
||||
sonatypeHost = 's01'
|
||||
}
|
||||
|
||||
dependencies {
|
||||
api group: 'net.java.dev.jna', name: 'jna', version: '5.18.1'
|
||||
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()
|
||||
}
|
||||
@@ -1,108 +0,0 @@
|
||||
package org.vosk;
|
||||
|
||||
import com.sun.jna.Native;
|
||||
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 {
|
||||
try (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", LibVosk.class.getClassLoader());
|
||||
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_set_grm(Pointer recognizer, String grammar);
|
||||
|
||||
public static native void vosk_recognizer_reset(Pointer recognizer);
|
||||
|
||||
public static native void vosk_recognizer_set_endpointer_mode(Pointer recognizer, int mode);
|
||||
|
||||
public static native void vosk_recognizer_set_endpointer_delays(Pointer recognizer, float t_start_max, float t_end, float t_max);
|
||||
|
||||
public static native void vosk_recognizer_free(Pointer recognizer);
|
||||
|
||||
public static native Pointer vosk_text_processor_new(String verbalizer, String tagger);
|
||||
|
||||
public static native void vosk_text_processor_free(Pointer processor);
|
||||
|
||||
public static native String vosk_text_processor_itn(Pointer processor, String input);
|
||||
|
||||
/**
|
||||
* Set log level for Kaldi messages.
|
||||
*
|
||||
* @param loglevel the level
|
||||
* 0 - default value to print info and error messages but no debug
|
||||
* less than 0 - don't print info messages
|
||||
* greater than 0 - more verbose mode
|
||||
*/
|
||||
public static void setLogLevel(LogLevel loglevel) {
|
||||
vosk_set_log_level(loglevel.getValue());
|
||||
}
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
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());
|
||||
}
|
||||
}
|
||||
@@ -1,275 +0,0 @@
|
||||
package org.vosk;
|
||||
|
||||
import com.sun.jna.PointerType;
|
||||
import java.io.IOException;
|
||||
|
||||
public class Recognizer extends PointerType implements AutoCloseable {
|
||||
/**
|
||||
* 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 sampleRate The sample rate of the audio you are going to feed into the recognizer.
|
||||
* Make sure this rate matches the audio content, it is a common
|
||||
* issue causing accuracy problems.
|
||||
* @throws IOException if the recognizer could not be created
|
||||
*/
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 sampleRate The sample rate of the audio you are going to feed into the recognizer.
|
||||
* Make sure this rate matches the audio content, it is a common
|
||||
* issue causing accuracy problems.
|
||||
* @param spkModel speaker model for speaker identification
|
||||
* @throws IOException if the recognizer could not be created
|
||||
*/
|
||||
public Recognizer(Model model, float sampleRate, SpeakerModel spkModel) throws IOException {
|
||||
super(LibVosk.vosk_recognizer_new_spk(model.getPointer(), sampleRate, spkModel.getPointer()));
|
||||
|
||||
if (getPointer() == null) {
|
||||
throw new IOException("Failed to create a recognizer");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 sampleRate The sample rate of the audio you are 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]"]".
|
||||
* @throws IOException if the recognizer could not be created
|
||||
*/
|
||||
public Recognizer(Model model, float sampleRate, String grammar) throws IOException {
|
||||
super(LibVosk.vosk_recognizer_new_grm(model.getPointer(), sampleRate, grammar));
|
||||
|
||||
if (getPointer() == null) {
|
||||
throw new IOException("Failed to create a recognizer");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 maxAlternatives - maximum alternatives to return from recognition results
|
||||
*/
|
||||
public void setMaxAlternatives(int maxAlternatives) {
|
||||
LibVosk.vosk_recognizer_set_max_alternatives(this.getPointer(), maxAlternatives);
|
||||
}
|
||||
|
||||
/** 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
|
||||
*/
|
||||
public void setWords(boolean words) {
|
||||
LibVosk.vosk_recognizer_set_words(this.getPointer(), words);
|
||||
}
|
||||
|
||||
/**
|
||||
* Like above return words and confidences in partial results.
|
||||
*
|
||||
* @param partial_words - boolean value
|
||||
*/
|
||||
public void setPartialWords(boolean partial_words) {
|
||||
LibVosk.vosk_recognizer_set_partial_words(this.getPointer(), partial_words);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 spkModel Speaker recognition model
|
||||
*/
|
||||
public void setSpeakerModel(SpeakerModel spkModel) {
|
||||
LibVosk.vosk_recognizer_set_spk_model(this.getPointer(), spkModel.getPointer());
|
||||
}
|
||||
|
||||
/**
|
||||
* Accept and process new chunk of voice data.
|
||||
*
|
||||
* @param data - audio data in PCM 16-bit mono format
|
||||
* @param len - length of the audio data
|
||||
* @return 1 if silence is occurred and you can retrieve a new utterance with result method
|
||||
* 0 if decoding continues
|
||||
* -1 if exception occurred
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns speech recognition result
|
||||
*
|
||||
* @return 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 #setMaxAlternatives().
|
||||
*
|
||||
* If word times enabled returns word time, see also #setWordTimes().
|
||||
*/
|
||||
public String getResult() {
|
||||
return LibVosk.vosk_recognizer_result(this.getPointer());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns partial speech recognition.
|
||||
*
|
||||
* @return partial speech recognition text which is not yet finalized.
|
||||
* result may change as recognizer process more data.
|
||||
*
|
||||
* <pre>
|
||||
* {
|
||||
* "partial" : "cyril one eight zero"
|
||||
* }
|
||||
* </pre>
|
||||
*/
|
||||
public String getPartialResult() {
|
||||
return LibVosk.vosk_recognizer_partial_result(this.getPointer());
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @return speech result in JSON format.
|
||||
*/
|
||||
public String getFinalResult() {
|
||||
return LibVosk.vosk_recognizer_final_result(this.getPointer());
|
||||
}
|
||||
|
||||
/**
|
||||
* Reconfigures recognizer to use grammar.
|
||||
*
|
||||
* @param grammar Set of phrases in JSON array of strings or "[]" to use default model graph.
|
||||
* @see #Recognizer(Model, float, String)
|
||||
*/
|
||||
public void setGrammar(String grammar) {
|
||||
LibVosk.vosk_recognizer_set_grm(this.getPointer(), grammar);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the recognizer.
|
||||
* Resets current results so the recognition can continue from scratch.
|
||||
*/
|
||||
public void reset() {
|
||||
LibVosk.vosk_recognizer_reset(this.getPointer());
|
||||
}
|
||||
|
||||
/**
|
||||
* Endpointer delay mode
|
||||
*/
|
||||
public class EndpointerMode {
|
||||
public static final int DEFAULT = 0;
|
||||
public static final int SHORT = 1;
|
||||
public static final int LONG = 2;
|
||||
public static final int VERY_LONG = 3;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures endpointer mode for recognizer
|
||||
*/
|
||||
public void setEndpointerMode(int mode) {
|
||||
LibVosk.vosk_recognizer_set_endpointer_mode(this.getPointer(), mode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set endpointer delays
|
||||
*
|
||||
* @param t_start_max timeout for stopping recognition in case of initial silence (usually around 5.0)
|
||||
* @param t_end timeout for stopping recognition in milliseconds after we recognized something (usually around 0.5 - 1.0)
|
||||
* @param t_max timeout for forcing utterance end in milliseconds (usually around 20-30)
|
||||
**/
|
||||
public void setEndpointerDelays(float t_start_max, float t_end, float t_max) {
|
||||
LibVosk.vosk_recognizer_set_endpointer_delays(this.getPointer(), t_start_max, t_end, t_max);
|
||||
}
|
||||
|
||||
/**
|
||||
* Releases recognizer object.
|
||||
* Underlying model is also unreferenced and if needed, released.
|
||||
*/
|
||||
@Override
|
||||
public void close() {
|
||||
LibVosk.vosk_recognizer_free(this.getPointer());
|
||||
}
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
package org.vosk;
|
||||
|
||||
import com.sun.jna.PointerType;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Helps to initialize speaker recognition for grammar-based recognizer.
|
||||
*/
|
||||
public class SpeakerModel extends PointerType implements AutoCloseable {
|
||||
public SpeakerModel() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads speaker model data from the file.
|
||||
*
|
||||
* The path must contain:
|
||||
* - a config file: mfcc.conf
|
||||
* - kaldi nnet: final.ext.raw
|
||||
* - mean.vec
|
||||
* - transform.mat
|
||||
*
|
||||
* @param path the path of the model on the filesystem
|
||||
* @throws IOException if the model could not be created
|
||||
*
|
||||
* @see <a href="http://kaldi-asr.org/doc/structkaldi_1_1MfccOptions.html">Kaldi MfccOptions</a>
|
||||
* @see <a href="http://kaldi-asr.org/doc/classkaldi_1_1nnet3_1_1Nnet.html">Kaldi Nnet</a>
|
||||
*/
|
||||
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 @@
|
||||
Mainly for work around JNA API
|
||||
@@ -1,120 +0,0 @@
|
||||
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.Recognizer.EndpointerMode;
|
||||
import org.vosk.LibVosk;
|
||||
import org.vosk.Model;
|
||||
import org.vosk.TextProcessor;
|
||||
|
||||
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
|
||||
public void decoderEndpointerDelays() throws IOException, UnsupportedAudioFileException {
|
||||
try (Model model = new Model("model");
|
||||
Recognizer recognizer = new Recognizer(model, 16000)) {
|
||||
recognizer.setEndpointerMode(EndpointerMode.VERY_LONG);
|
||||
recognizer.setEndpointerDelays(5.0f, 3.0f, 50.0f);
|
||||
}
|
||||
Assert.assertTrue(true);
|
||||
}
|
||||
|
||||
@Test(expected = IOException.class)
|
||||
public void decoderTestException() throws IOException {
|
||||
Model model = new Model("model_missing");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testItn() throws IOException {
|
||||
TextProcessor p = new TextProcessor("model/itn/en_itn_tagger.fst", "model/itn/en_itn_verbalizer.fst");
|
||||
System.out.println(p.itn("as easy as one two three"));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
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 {
|
||||
|
||||
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());
|
||||
}
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
.gradle/
|
||||
.idea/
|
||||
build/
|
||||
@@ -1 +0,0 @@
|
||||
Doomsdayrs doomsdayrs@gmail.com
|
||||
@@ -1,8 +0,0 @@
|
||||
# Maintenance
|
||||
|
||||
To maintain this module, please ensure the following.
|
||||
1. Kotlin version is kept up to date.
|
||||
This will be found in the plugins block of the [build.gradle.kts](./build.gradle.kts).
|
||||
Ensure both multiplatform and serialization have the same value.
|
||||
2. Ensure dependencies are up to date
|
||||
3. Ensure that the android min & target sdks are up to date.
|
||||
@@ -1,63 +0,0 @@
|
||||
# vosk-api-kotlin
|
||||
|
||||
The vosk-api wrapped using Kotlin Multiplatform.
|
||||
|
||||
## Usage
|
||||
|
||||
The following are ways to use this wrapper.
|
||||
|
||||
### JVM
|
||||
|
||||
For Java & Android targets.
|
||||
|
||||
```kotlin
|
||||
dependencoes {
|
||||
val voskVersion = "0.4.0-alpha0"
|
||||
|
||||
// Generic
|
||||
implementation("com.alphacephei:vosk-api-kotlin:$voskVersion")
|
||||
|
||||
// Android
|
||||
implementation("com.alphacephei:vosk-api-kotlin-android:$voskVersion")
|
||||
}
|
||||
```
|
||||
|
||||
## Building
|
||||
|
||||
To build this project, follow the following steps.
|
||||
|
||||
1. Install `libvosk`.
|
||||
This can be done from [source][source install] (parent monorepo)
|
||||
or [downloaded][download].
|
||||
2. Download a [Vosk model](https://alphacephei.com/vosk/models) to use.
|
||||
- It is suggested to use a small model to speed up tests.
|
||||
3. Once both are downloaded and placed into a proper location (hopefully following UNIX specification).
|
||||
Set the following environment variables:
|
||||
- `VOSK_MODEL` to the path of the model.
|
||||
- `VOSK_PATH` to the path of `libvosk`.
|
||||
These are used by the various tests to operate.
|
||||
4. Now that the required steps are complete, one can run `./gradlew build`.
|
||||
|
||||
## Kotlin/Native
|
||||
|
||||
Currently, the native target is disabled due to a lack of vosk-api packaging on platforms.
|
||||
Further worsened by the fact that installing the vosk-api on Linux systems is a chore.
|
||||
|
||||
First, either install from [source][source install]
|
||||
or [download][download] and install into the proper unix directories as expected in
|
||||
[libvosk.def](./src/nativeInterop/cinterop/libvosk.def).
|
||||
|
||||
To enable development for Kotlin/Native, do either for the following.
|
||||
- Add `NATIVE_EXPERIMENT=true` to your environment.
|
||||
- Go into [build.gradle.kts](./build.gradle.kts) & find `enableNative` & set the right side to true.
|
||||
|
||||
Afterwards, when syncing the project, the native source sets will become available to work on.
|
||||
It is suggested to run `cinteropLibvoskNative` to generate the Kotlin C bindings to work with.
|
||||
|
||||
## Future
|
||||
|
||||
- Possibly target Kotlin/JS
|
||||
- Possibly target Kotlin/Objective-C (?)
|
||||
|
||||
[source install]: https://alphacephei.com/vosk/install
|
||||
[download]: https://github.com/alphacep/vosk-api/releases/latest
|
||||
@@ -1,204 +0,0 @@
|
||||
import org.jetbrains.dokka.gradle.DokkaTask
|
||||
import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi
|
||||
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
||||
|
||||
/*
|
||||
* Copyright 2020 Alpha Cephei Inc. & Doomsdayrs
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
plugins {
|
||||
kotlin("multiplatform") version "2.0.0"
|
||||
id("com.android.library")
|
||||
`maven-publish`
|
||||
id("org.jetbrains.dokka") version "1.9.20"
|
||||
kotlin("plugin.serialization") version "2.0.0"
|
||||
}
|
||||
|
||||
group = "com.alphacephei"
|
||||
version = "0.3.75"
|
||||
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
val dokkaOutputDir = "$buildDir/dokka"
|
||||
|
||||
tasks.getByName<DokkaTask>("dokkaHtml") {
|
||||
outputDirectory.set(file(dokkaOutputDir))
|
||||
}
|
||||
|
||||
val deleteDokkaOutputDir by tasks.register<Delete>("deleteDokkaOutputDirectory") {
|
||||
delete(dokkaOutputDir)
|
||||
}
|
||||
|
||||
val javadocJar = tasks.register<Jar>("javadocJar") {
|
||||
dependsOn(deleteDokkaOutputDir, tasks.dokkaHtml)
|
||||
archiveClassifier.set("javadoc")
|
||||
from(dokkaOutputDir)
|
||||
}
|
||||
|
||||
fun org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension.native(
|
||||
configure: org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTargetWithHostTests.() -> Unit = {}
|
||||
) {
|
||||
when {
|
||||
org.jetbrains.kotlin.konan.target.HostManager.hostIsMingw -> mingwX64("native")
|
||||
org.jetbrains.kotlin.konan.target.HostManager.hostIsLinux -> linuxX64("native")
|
||||
org.jetbrains.kotlin.konan.target.HostManager.hostIsMac -> if (org.jetbrains.kotlin.konan.target.HostManager.hostArch() == "arm64") {
|
||||
macosArm64("native")
|
||||
} else {
|
||||
macosX64("native")
|
||||
}
|
||||
|
||||
else -> error("Unsupported Host OS: ${org.jetbrains.kotlin.konan.target.HostManager.hostOs()}")
|
||||
}.apply(configure)
|
||||
}
|
||||
|
||||
kotlin {
|
||||
jvm {
|
||||
@OptIn(ExperimentalKotlinGradlePluginApi::class)
|
||||
compilerOptions {
|
||||
jvmTarget.set(JvmTarget.JVM_17)
|
||||
}
|
||||
|
||||
testRuns["test"].executionTask.configure {
|
||||
useJUnitPlatform()
|
||||
environment("MODEL", "VOSK_MODEL")
|
||||
//environment("MODEL", "/home/doomsdayrs/Downloads/vosk-model-small-en-us-0.15/")
|
||||
environment("LIBRARY", "VOSK_PATH")
|
||||
//environment("LIBRARY", "/usr/local/lib64/libvosk/libvosk.so")
|
||||
environment("AUDIO", "$projectDir/../python/example/test.wav")
|
||||
}
|
||||
}
|
||||
|
||||
androidTarget {
|
||||
publishAllLibraryVariants()
|
||||
}
|
||||
|
||||
/**
|
||||
* If native target should be enabled or not.
|
||||
*
|
||||
* Currently disabled as there is no proper packaging distribution currently.
|
||||
*/
|
||||
@Suppress("SimplifyBooleanWithConstants") // Ignore, the false is for overrides
|
||||
val enableNative = System.getenv("NATIVE_EXPERIMENT") == "true" || false
|
||||
|
||||
if (enableNative)
|
||||
native {
|
||||
val main by compilations.getting
|
||||
val libvosk by main.cinterops.creating
|
||||
|
||||
binaries {
|
||||
sharedLib()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@OptIn(ExperimentalKotlinGradlePluginApi::class)
|
||||
applyDefaultHierarchyTemplate {
|
||||
withJvm()
|
||||
withAndroidTarget()
|
||||
|
||||
if (enableNative)
|
||||
withNative()
|
||||
}
|
||||
|
||||
publishing {
|
||||
publications {
|
||||
withType<MavenPublication> {
|
||||
artifact(javadocJar)
|
||||
pom {
|
||||
url.set("http://www.alphacephei.com.com/vosk/")
|
||||
licenses {
|
||||
license {
|
||||
name.set("The Apache License, Version 2.0")
|
||||
url.set("http://www.apache.org/licenses/LICENSE-2.0.txt")
|
||||
}
|
||||
}
|
||||
developers {
|
||||
developer {
|
||||
id.set("com.alphacephei")
|
||||
name.set("Alpha Cephei Inc")
|
||||
email.set("contact@alphacephei.com")
|
||||
}
|
||||
}
|
||||
scm {
|
||||
connection.set("scm:git:git://github.com/alphacep/vosk-api.git")
|
||||
url.set("https://github.com/alphacep/vosk-api/")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val jna_version = "5.14.0"
|
||||
val coroutines_version = "1.7.3"
|
||||
|
||||
sourceSets {
|
||||
val commonMain by getting {
|
||||
dependencies {
|
||||
api("org.jetbrains.kotlinx:kotlinx-serialization-json:1.7.0")
|
||||
api("org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutines_version")
|
||||
}
|
||||
}
|
||||
val commonTest by getting {
|
||||
dependencies {
|
||||
implementation(kotlin("test"))
|
||||
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:$coroutines_version")
|
||||
}
|
||||
}
|
||||
val jvmMain by getting {
|
||||
dependencies {
|
||||
api("net.java.dev.jna:jna:$jna_version")
|
||||
}
|
||||
}
|
||||
val jvmTest by getting
|
||||
if (enableNative) {
|
||||
val nativeMain by getting
|
||||
}
|
||||
val androidMain by getting {
|
||||
dependsOn(jvmMain)
|
||||
dependencies {
|
||||
api("net.java.dev.jna:jna:$jna_version@aar")
|
||||
}
|
||||
}
|
||||
val androidUnitTest by getting {
|
||||
dependencies {
|
||||
implementation("junit:junit:4.13.2")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "com.alphacephei.library"
|
||||
compileSdk = 34
|
||||
sourceSets["main"].manifest.srcFile("src/androidMain/AndroidManifest.xml")
|
||||
defaultConfig {
|
||||
minSdk = 24
|
||||
targetSdk = 34
|
||||
}
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_17
|
||||
targetCompatibility = JavaVersion.VERSION_17
|
||||
}
|
||||
publishing {
|
||||
multipleVariants {
|
||||
withSourcesJar()
|
||||
withJavadocJar()
|
||||
allVariants()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
/*
|
||||
* Copyright 2020 Alpha Cephei Inc. & Doomsdayrs
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
pluginManagement {
|
||||
repositories {
|
||||
google()
|
||||
gradlePluginPortal()
|
||||
mavenCentral()
|
||||
}
|
||||
resolutionStrategy {
|
||||
eachPlugin {
|
||||
if (requested.id.namespace == "com.android") {
|
||||
useModule("com.android.tools.build:gradle:8.3.0")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
rootProject.name = "vosk-api-kotlin"
|
||||
@@ -1,6 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.alphacephei.library">
|
||||
|
||||
<uses-permission android:name="android.permission.RECORD_AUDIO" />
|
||||
</manifest>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user