Compare commits

...

146 Commits

Author SHA1 Message Date
Nickolay Shmyrev cf2560c9f8 Bump debian requirement 2022-12-14 17:58:40 +01:00
Nickolay Shmyrev 800d3d7089 Bump version 2022-12-14 17:38:53 +01:00
Nickolay Shmyrev b471207f7a Protect against segmentation faults with corrupted model folder 2022-12-14 17:32:55 +01:00
Edwin Hendriks 25c59b52e3 add -m (language model) argument to test_microphone.py (#1219)
* added language model as parameter

* fix fore more help explanation
2022-12-07 22:53:21 +03:00
Nickolay Shmyrev 4c72097478 Allow to configure GPU model path. See https://github.com/alphacep/vosk-server/issues/207 2022-11-19 01:13:42 +01:00
Tomas Mehdi 1053cfa0f8 JSON output_type with texts (#1180) 2022-10-26 22:41:04 +03:00
gmat 21a42cb6cd new output type json for gecko project (#1166)
add a output type `json` to get output file compatible with transcription file format for the gecko project annotation https://github.com/gong-io/gecko
2022-10-06 16:27:29 +03:00
Nickolay Shmyrev 16c4a0d985 Bump version 2022-09-25 21:00:24 +02:00
Nickolay Shmyrev 298253401a Update approach to machine detection on cross-compilation
See for details issue #1155
2022-09-25 21:00:24 +02:00
Nickolay Shmyrev 32aa980069 Allow to change grammar of existing recognizer. Probably can be faster though
if we do not reinit feature pipeline
2022-09-13 23:12:13 +02:00
vadimdddd 7b4d396eb1 fix quotes and import's order (#1143) 2022-09-13 21:51:45 +03:00
vadimdddd 36968fbb30 Fix python style issues (#1140)
* pylint_fix v.2

* init: fix quotes
2022-09-12 23:08:59 +03:00
vadimdddd 7474888801 Fix python style issues (#1131) 2022-09-09 00:13:02 +03:00
vadimdddd d46b7a43eb Split results by newlines (#1124) 2022-08-26 17:22:10 +03:00
vadimdddd 2376b32a8a Add SRTResult to KaldiRecognizer (#1080) 2022-08-24 12:48:18 +03:00
vadimdddd b8a88cc30c Add gradio recognizer (#1114)
Demonstrate speech recognition in gradio
2022-08-19 17:31:47 +03:00
Nickolay V. Shmyrev be0b117711 Add training colab 2022-08-17 19:11:45 +02:00
Nickolay V. Shmyrev 449c8ea5af Introduce stop stage espnet style 2022-08-17 14:30:56 +02:00
Nickolay V. Shmyrev 900da76652 Import script locally 2022-08-17 12:50:59 +02:00
Nickolay V. Shmyrev df0ee24084 Add adaptation colab 2022-08-17 12:38:17 +02:00
Nickolay V. Shmyrev d3d8f53156 Add Kaldi build colab 2022-08-17 10:02:43 +02:00
vadimdddd 6eee303d7e Fix decoding files with spaces (#1084)
Fix decoding files with spaces
2022-08-02 20:56:04 +03:00
Nickolay Shmyrev 053d71f5aa Bug fix in notebook 2022-08-01 22:59:38 +02:00
Nickolay Shmyrev 4fbbf5882c Add colab demo 2022-08-01 22:52:57 +02:00
Nickolay Shmyrev 7012103b3b Simplify the go interface. Move from []byte to strings. Fixes #1095 2022-07-31 09:58:33 +02:00
Nickolay Shmyrev 967024d20a Fix precedence of current path for dll load. Fixes issue #1096 2022-07-30 09:15:28 +02:00
Nicolas Dato b966a8078b Fix comment (#1091) 2022-07-26 23:07:25 +03:00
vadimdddd f63b015284 Add new dependency websockets (#1079)
add websockets to dependencies
2022-07-19 19:58:24 +03:00
Nickolay Shmyrev 0a9672d910 Attempt to build manylinux wheel 2022-07-15 00:40:38 +02:00
Nickolay Shmyrev 4ccccd0cd2 Bump version 2022-07-14 21:38:36 +02:00
Nickolay Shmyrev 354fb672a3 Merge branch 'master' of github.com:alphacep/vosk-api 2022-07-13 14:09:05 +02:00
Nickolay Shmyrev 73abf0740a Add Ruby bindings 2022-07-13 14:08:50 +02:00
Nickolay Shmyrev 81c82935ac Add usage example 2022-07-07 01:09:32 +02:00
Nickolay Shmyrev ac3ec56584 Add notes about Go and Rust 2022-07-05 01:18:08 +02:00
Nickolay Shmyrev 2cbe12d4d0 Add link to rust bindings 2022-07-05 01:17:43 +02:00
Nickolay Shmyrev 1475b0e986 Async ffmpeg for trancriber and code refactoring 2022-07-05 01:13:05 +02:00
vadimdddd 8ceab0b9b1 Add vosk-server to transcriber (#1024)
Allow to use vosk-server for transcription
2022-07-04 13:50:38 +03:00
Nickolay Shmyrev 1496b597d3 Fixes issues with models without alignment 2022-07-03 13:00:05 +02:00
vadimdddd 983519e629 Add compatibility with python 3.5 issue(#1021) (#1041)
Added format for python3.5
2022-06-29 18:00:40 +03:00
Nickolay Shmyrev 58fa98ccd7 Wait for microphone to finalize. Fixes crash in issue #1034 2022-06-28 01:01:53 +03:00
Nickolay Shmyrev a7bc5a22d4 We support Polish now 2022-06-05 23:03:57 +02:00
Rémi Bernon 23bbff0b56 build: Add a CMakeLists.txt. (#992) 2022-06-04 17:34:42 +03:00
Kay Gosho a47b58e2f4 Specify device to get microphone input (#993) 2022-06-03 15:33:50 +03:00
Nickolay Shmyrev 8cf64ee93e Add RISCV support 2022-06-02 01:44:16 +02:00
vadimdddd 630edeb3d6 fixed error if parent directory doesn't exist #988 (#990)
Create missing parent directory
2022-06-01 17:52:16 +03:00
Nickolay Shmyrev b1b216d4c8 Bump versions 2022-05-26 15:22:16 +02:00
Nickolay Shmyrev 55dd29b0ff Fix transcriber bugs while processing folder 2022-05-26 14:05:54 +02:00
Nickolay Shmyrev ea0568a38d Change target binary library name and copy header 2022-05-26 13:29:14 +02:00
Nickolay Shmyrev 298c86d0d4 Bump version and add python requirements 2022-05-26 12:46:06 +02:00
Nickolay Shmyrev 859420809b Add extra comments 2022-05-26 11:45:16 +02:00
Nickolay Shmyrev 0fe3a89768 Update to 0.3.40 2022-05-26 11:41:14 +02:00
Nickolay Shmyrev 5b892fbfc5 Architecture should be universal2 2022-05-25 19:52:05 +03:00
Nickolay Shmyrev fb4ed21a7f Shave transcriber code 2022-05-24 22:53:57 +02:00
vadimdddd 4209f3a9fe Vosk model loader(#871) (#941)
* methods get_model_by_name, get_model_by_lang, get_model were added into the model class

* importing modules changed to using components; introduced constant MODELS_HOME_DIR; simplified code

* added new model folders into init; changed samples and transcriber bin for new mode loader

* changed back in cli.py lang arg to args.lang

* added 3 directories instead of 1 to check for models

* cli.py: added 3 args instead of 1 for model; __init__.py: changed script get_model_path for run get_model_by_name/lang inside current directory

* deleted default env var

* cli.py: changed arg_name; __init__.py: changed const name, changed model loading only for last directory

* deleted unused method

* changed by_name, by_lang methods, added download_model method

* deleted env variable initialization

* deleted print()

* deteled unused modules

* added progress_bar, added folder AppData/Local/vosk for model search

* changed download_model methond; added my_hook method
2022-05-24 21:06:13 +03:00
Nickolay Shmyrev ff2c80d5f1 Add x86 crossbuild 2022-05-24 20:00:03 +02:00
Nickolay Shmyrev 3a07a08121 Universal binary in node 2022-05-24 17:30:49 +02:00
Nickolay Shmyrev 592da81a8c Update nuget package for 0.3.38 2022-05-24 17:05:14 +02:00
Nickolay Shmyrev def8c93711 Remove semicolon 2022-05-24 15:23:03 +03:00
Nickolay Shmyrev f73088da58 We create universal binary on OSX 2022-05-24 15:11:05 +03:00
Nickolay Shmyrev 06a761ecbd Bump openblas 2022-05-15 23:58:30 +02:00
Nickolay Shmyrev 97d737a30a Bump version 2022-05-15 23:36:46 +02:00
Nickolay Shmyrev c7bffbf603 No need for g++ in C code, fix osx warning 2022-05-15 23:20:33 +02:00
Nickolay Shmyrev 02c40ea612 Bring back incremental decoder, now works better with kaldi fix 2022-05-15 23:20:19 +02:00
Mark Delk ce5ffb980a use comma instead of equal sign in rpath linker opts (#963)
Using `=` for linker options is a GNU extension which doesn't work with
clang.

Using `,` should work for both gcc and clang.
2022-05-12 00:34:43 +03:00
Nickolay Shmyrev d5ca98a982 Revert incremental decoder patch, leads to very slow decoding in some cases due to lattice blow-up 2022-05-06 00:34:50 +02:00
Nickolay Shmyrev b0146782d6 Reorganize transcriber binary 2022-04-29 03:01:09 +02:00
Nickolay Shmyrev 5aaea8fc90 Simplify makefile 2022-04-29 02:35:17 +02:00
Chupligin Sergey 5d7752b657 Allow build with shared librares (#939) 2022-04-29 03:23:22 +03:00
vadimdddd 9d94746479 Add transcriber tool (#851)
Add transcriber tool
2022-04-20 14:48:22 +03:00
Nickolay V. Shmyrev a87f2e1e07 Czech model 2022-04-13 22:31:45 +02:00
Nickolay Shmyrev 7b7d814484 Introduce incremental decoder with confidences in partial results 2022-04-07 01:07:47 +02:00
Nickolay Shmyrev 2daf67f31c C# style method name 2022-03-29 23:05:27 +02:00
Nickolay V. Shmyrev 62dd631379 Merge pull request #894 from gauthiersornet/patch-1
Update VoskDemo.cs
2022-03-22 22:35:16 +03:00
gauthiersornet 2511192ecb Update VoskDemo.cs
To read shorts from a binary buffer => fbuffer[i] = BitConverter.ToInt16(buffer, n);
2022-03-22 20:29:59 +01:00
Nickolay Shmyrev 22cb90de4a Add Hindi 2022-03-17 20:08:39 +01:00
Nickolay Shmyrev 3951834df2 Move decoding parts to decoding stage. Enable upsampling/downsampling 2022-03-05 14:52:11 +01:00
Nickolay Shmyrev 1ea0de106c Add trainign setup 2022-03-05 00:28:27 +01:00
Nickolay Shmyrev a9bf929ebd Add NLSML output for GPU recognizer 2022-03-03 23:01:49 +01:00
Nickolay Shmyrev 3336cd704b Add resampler 2022-03-03 21:48:25 +01:00
Nickolay Shmyrev a57a84f90e Refactor GPU API to hide the ID and keep it closer to CPU recognizer 2022-03-03 21:09:09 +01:00
Nickolay Shmyrev ad546a8f1a Also read processing options 2022-02-17 10:43:05 +01:00
Nickolay Shmyrev 1f447a8dfc Rename according to Kaldi changes 2022-02-10 20:52:55 +01:00
Nickolay Shmyrev f574d896e9 Emtpy result should be also xml 2022-02-03 23:43:00 +01:00
Nickolay Shmyrev a561c2d6d4 Don't add space before string 2022-02-03 23:26:53 +01:00
Nickolay Shmyrev 79b8395be0 Add NLSML output 2022-02-03 23:08:09 +01:00
Nickolay Shmyrev d2c11a611f Read list of files from arguments 2022-01-30 22:57:36 +01:00
Nickolay Shmyrev b0903413b1 Set soname for Android library 2022-01-21 13:22:26 +01:00
Nickolay Shmyrev 2135223490 Put stream information in a single structure 2022-01-12 14:58:43 +01:00
Nickolay Shmyrev 6f86944a06 Implement wave chunking for cuda decoder 2022-01-12 01:32:20 +01:00
Nickolay Shmyrev 9861be2787 Add libs as dependencies in Makefile 2022-01-10 20:21:24 +01:00
Nickolay Shmyrev c6fab363e6 Don't close channel which not yet started 2022-01-09 15:15:20 +01:00
Nickolay Shmyrev c32099705f Fix branch name and add implib dump 2022-01-07 17:33:47 +01:00
Nickolay Shmyrev a1eac015dc Add Esperanto 2022-01-07 16:27:57 +01:00
Nickolay Shmyrev 64dfc65d51 Merge branch 'master' of github.com:alphacep/vosk-api 2022-01-05 20:32:25 +01:00
Nickolay Shmyrev 70d5cbd0e0 Update README with Japanese 2022-01-05 20:32:08 +01:00
Nickolay Shmyrev 5428d36d16 Round times 2021-12-26 01:12:18 +01:00
Nickolay V. Shmyrev ed4c15b7aa Merge pull request #800 from alphacep/batch
Batch GPU decoding
2021-12-24 03:35:51 +03:00
Nickolay Shmyrev 525b722c44 Compile without CUDA too 2021-12-24 01:35:06 +01:00
Nickolay Shmyrev 72bf210164 Put the demo into main folder 2021-12-24 01:07:38 +01:00
Nickolay Shmyrev 93e81c3bc8 Bigger frames per chunk for our big models 2021-12-24 00:22:42 +01:00
Nickolay Shmyrev cb0f8e6411 Per-stream wait API 2021-12-23 22:34:47 +01:00
Nickolay Shmyrev 848b2dc753 Expose results in Python 2021-12-17 22:57:00 +01:00
Nickolay Shmyrev 60f0396fe0 Reset lattice on endpoint 2021-12-17 01:13:09 +01:00
Nickolay Shmyrev 344e137a61 Decoding works, results are empty yet 2021-12-13 01:21:59 +01:00
Nickolay Shmyrev 6977be7fb7 Batch recognizer draft 2021-12-12 21:37:44 +01:00
Nickolay V. Shmyrev a4721de8aa Merge pull request #759 from bertyhell/patch-1
Output correct SRT format using milliseconds (NodeJS SRT example)
2021-11-11 12:52:00 +03:00
Bert Verhelst 378ba122c8 Output correct SRT format using miliseconds 2021-11-11 10:49:50 +01:00
Nickolay Shmyrev 287160622f Update to latest state 0.3.32 2021-11-10 22:26:31 +03:00
Nickolay Shmyrev a5d788a2e9 One more fix to expose aar dependencies 2021-11-10 00:33:03 +01:00
Nickolay Shmyrev 7f651e1e45 Expose JNA dependency. Fix issue #757 2021-11-09 23:02:17 +01:00
Nickolay Shmyrev ad5bec114d Fix issue with empty lattice 2021-11-08 13:46:35 +01:00
Nickolay Shmyrev f71c62ad0f Publish Java jars on Mavencentral too 2021-11-07 21:26:33 +01:00
Nickolay Shmyrev 44f7dd2d8b Publish to sonatype 2021-11-07 20:41:49 +01:00
Nickolay Shmyrev 15a9508a78 Use ndk directory instead of sdk 2021-11-07 14:32:29 +01:00
Nickolay Shmyrev bdea9a53e8 Bump android version 2021-11-07 14:30:08 +01:00
Nickolay Shmyrev 81f58667ff Update Android build 2021-11-07 13:45:27 +01:00
Nickolay V. Shmyrev 680a2e4c31 Merge pull request #752 from sskorol/cuda-fix
Added missing cuda lib to Vosk compilation flags
2021-11-06 19:04:22 +03:00
sskorol 13993db542 Added missing cuda lib to Vosk compilation flags 2021-11-06 17:24:49 +02:00
Nickolay Shmyrev 59d595a4f0 Replace about in readme 2021-10-31 00:15:52 +02:00
Nickolay Shmyrev 4c562e15a4 Bump version to 0.3.32 2021-10-30 22:45:10 +02:00
Nickolay Shmyrev 5bcdf454ec Add fbank feature support 2021-10-30 22:45:01 +02:00
Nickolay Shmyrev 4ccdda44ac Extend documentation 2021-10-12 22:35:31 +02:00
Nickolay Shmyrev 5e46825474 Add try/catch wrapper for C++ method to raise native exceptions. Python and Java
are implemented, others on the way
2021-10-12 22:31:36 +02:00
Nickolay Shmyrev fcab5a9581 Revert CFFI bump 2021-10-10 22:09:55 +02:00
Nickolay Shmyrev e7f5e0ac23 Bump cffi version 2021-10-10 21:21:32 +02:00
Nickolay Shmyrev 9a3906831b Properly convert lattice. Fixes issue #713 2021-10-08 21:11:00 +02:00
Nickolay Shmyrev 195db43b9e Rename branches in kaldi repo 2021-10-07 01:18:47 +02:00
Nickolay Shmyrev 332553ec1e Revert rescoring to more accurate pure fst composition 2021-10-04 01:05:04 +02:00
Nickolay Shmyrev 646af3f652 Explain more about sample rate in constructor 2021-09-17 11:17:38 +02:00
Nickolay V. Shmyrev f24ac65fcb Merge pull request #688 from scroot/patch-1
Update vosk.go
2021-09-16 18:42:03 +03:00
scroot 14312c93f9 Update vosk.go
* fix partial result
+ Free
2021-09-16 09:55:26 +08:00
Nickolay Shmyrev 4346d4155d Fix go example 2021-09-02 10:48:19 +02:00
Nickolay Shmyrev 7790ac5040 Update versions in bindings 2021-08-31 22:42:42 +02:00
Nickolay Shmyrev 7b3ea0b59d Merge branch 'master' of github.com:alphacep/vosk-api 2021-08-31 21:56:49 +02:00
Nickolay Shmyrev e2af710369 Rework rescoring for faster and more accurate results 2021-08-31 21:56:21 +02:00
Nickolay V. Shmyrev 6b1b620f39 Merge pull request #678 from agorman/go-bindings
Cleaning up and completing go bindings
2021-08-31 22:34:19 +03:00
Andy Gorman 966524da16 gofmt 2021-08-31 12:24:19 -07:00
Andy Gorman 83c999f298 Cleaning up and completing go bindings 2021-08-31 12:18:34 -07:00
Nickolay Shmyrev abff8a4f56 Organize makefile structure 2021-07-29 11:58:31 +02:00
Timur 188575f3a2 Added MKL build options (#647)
Co-authored-by: Kasimov Timur <t.kasimov@cft.ru>
2021-07-29 11:03:39 +03:00
Nickolay Shmyrev 9f4d8ca187 Update doc 2021-07-12 20:29:42 +02:00
Nickolay Shmyrev 72cc8f3a5e Added test doc 2021-07-12 20:23:49 +02:00
Nickolay Shmyrev 915dcab597 Add doc 2021-07-11 15:54:42 +02:00
Nickolay Shmyrev d82052b168 Better have it here 2021-07-11 11:26:32 +02:00
Nickolay Shmyrev dbb65bc71c Unify example 2021-07-11 11:01:56 +02:00
Nickolay Shmyrev 2498bc595b Add module file 2021-07-11 10:22:24 +02:00
115 changed files with 18254 additions and 798 deletions
+1
View File
@@ -10,6 +10,7 @@ gradlew
gradlew.bat
gradle
local.properties
gradle.properties
# Android
android/build
+20
View File
@@ -0,0 +1,20 @@
cmake_minimum_required(VERSION 3.13)
project(vosk-api CXX)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_EXTENSIONS OFF)
add_library(vosk
src/language_model.cc
src/model.cc
src/recognizer.cc
src/spk_model.cc
src/vosk_api.cc
)
find_package(kaldi REQUIRED)
target_link_libraries(vosk PUBLIC kaldi-base kaldi-online2 kaldi-rnnlm fstngram)
include(GNUInstallDirs)
install(TARGETS vosk DESTINATION ${CMAKE_INSTALL_LIBDIR})
install(FILES src/vosk_api.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
+5 -4
View File
@@ -1,17 +1,18 @@
# About
# Vosk Speech Recognition Toolkit
Vosk is an offline open source speech recognition toolkit. It enables
speech recognition models for 18 languages and dialects - English, Indian
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.
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++ and others.
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,
+18 -11
View File
@@ -1,25 +1,36 @@
buildscript {
repositories {
google()
jcenter()
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:4.1.3'
classpath 'com.android.tools.build:gradle:4.2.0'
classpath 'com.vanniktech:gradle-maven-publish-plugin:0.18.0'
}
}
allprojects {
version = '0.3.30'
version = '0.3.45'
}
subprojects {
apply plugin: 'com.android.library'
apply plugin: 'maven-publish'
apply plugin: 'com.vanniktech.maven.publish'
plugins.withId('com.vanniktech.maven.publish') {
mavenPublish {
group = 'com.alphacephei'
version = version
sonatypeHost = 's01'
androidVariantToPublish = 'release'
}
}
repositories {
google()
jcenter()
mavenCentral()
}
publishing {
@@ -29,8 +40,8 @@ subprojects {
version version
pom {
url = 'http://www.alphacephei.com.com/vosk/'
licenses {
license {
licenses {
license {
name = 'The Apache License, Version 2.0'
url = 'http://www.apache.org/licenses/LICENSE-2.0.txt'
}
@@ -49,10 +60,6 @@ subprojects {
}
}
}
repositories {
maven {
url = "$rootDir/repo"
}
}
}
}
+20 -14
View File
@@ -29,7 +29,7 @@ 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=$PATH:$ANDROID_NDK_HOME/toolchains/llvm/prebuilt/${OS_NAME}-x86_64/bin
PATH=$ANDROID_NDK_HOME/toolchains/llvm/prebuilt/${OS_NAME}-x86_64/bin:$PATH
OPENFST_VERSION=1.8.0
for arch in armeabi-v7a arm64-v8a x86_64 x86; do
@@ -40,7 +40,8 @@ case $arch in
armeabi-v7a)
BLAS_ARCH=ARMV7
HOST=arm-linux-androideabi
AR=arm-linux-androideabi-ar
AR=llvm-ar
RANLIB=llvm-ranlib
CC=armv7a-linux-androideabi21-clang
CXX=armv7a-linux-androideabi21-clang++
ARCHFLAGS="-mfloat-abi=softfp -mfpu=neon"
@@ -48,7 +49,8 @@ case $arch in
arm64-v8a)
BLAS_ARCH=ARMV8
HOST=aarch64-linux-android
AR=aarch64-linux-android-ar
AR=llvm-ar
RANLIB=llvm-ranlib
CC=aarch64-linux-android21-clang
CXX=aarch64-linux-android21-clang++
ARCHFLAGS=""
@@ -56,7 +58,8 @@ case $arch in
x86_64)
BLAS_ARCH=ATOM
HOST=x86_64-linux-android
AR=x86_64-linux-android-ar
AR=llvm-ar
RANLIB=llvm-ranlib
CC=x86_64-linux-android21-clang
CXX=x86_64-linux-android21-clang++
ARCHFLAGS=""
@@ -64,7 +67,8 @@ case $arch in
x86)
BLAS_ARCH=ATOM
HOST=i686-linux-android
AR=i686-linux-android-ar
AR=llvm-ar
RANLIB=llvm-ranlib
CC=i686-linux-android21-clang
CXX=i686-linux-android21-clang++
ARCHFLAGS=""
@@ -105,9 +109,9 @@ make install
# Kaldi itself
cd $WORKDIR
git clone -b android-mix --single-branch https://github.com/alphacep/kaldi
git clone -b vosk-android --single-branch https://github.com/alphacep/kaldi
cd $WORKDIR/kaldi/src
CXX=$CXX CXXFLAGS="$ARCHFLAGS -O3 -DFST_NO_DYNAMIC_LINKING" ./configure --use-cuda=no \
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 \
@@ -118,12 +122,14 @@ make -j 8 online2 lm rnnlm
# Vosk-api
cd $WORKDIR
#rm -rf vosk-api
git clone -b master --single-branch https://github.com/alphacep/vosk-api
cd vosk-api/src
make -j 8 KALDI_ROOT=${WORKDIR}/kaldi OPENFST_ROOT=${WORKDIR}/local OPENBLAS_ROOT=${WORKDIR}/local CXX=$CXX EXTRA_LDFLAGS="-llog -static-libstdc++"
# Copy JNI library to sources
cp $WORKDIR/vosk-api/src/libvosk.so $WORKDIR/../../src/main/jniLibs/$arch/libvosk.so
mkdir -p $WORKDIR/vosk
make -j 8 -C ${WORKDIR_BASE}/../../../src \
OUTDIR=$WORKDIR/vosk \
KALDI_ROOT=${WORKDIR}/kaldi \
OPENFST_ROOT=${WORKDIR}/local \
OPENBLAS_ROOT=${WORKDIR}/local \
CXX=$CXX \
EXTRA_LDFLAGS="-llog -static-libstdc++ -Wl,-soname,libvosk.so"
cp $WORKDIR/vosk/libvosk.so $WORKDIR/../../src/main/jniLibs/$arch/libvosk.so
done
+15 -2
View File
@@ -10,6 +10,7 @@ android {
versionCode 6
versionName = version
archivesBaseName = archiveName
ndkVersion = "22.1.7171670"
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
@@ -19,11 +20,11 @@ android {
task buildVosk(type: Exec) {
commandLine './build-vosk.sh'
environment ANDROID_NDK_HOME: android.getSdkDirectory()
environment ANDROID_NDK_HOME: android.getNdkDirectory()
}
dependencies {
implementation 'net.java.dev.jna:jna:4.4.0@aar'
api 'net.java.dev.jna:jna:4.4.0@aar'
}
//preBuild.dependsOn buildVosk
@@ -37,6 +38,18 @@ publishing {
name = pomName
description = pomDescription
}
//generate pom nodes for dependencies
pom.withXml {
def dependenciesNode = asNode().appendNode('dependencies')
configurations.implementation.allDependencies.each { dependency ->
if (dependency.name != 'unspecified') {
def dependencyNode = dependenciesNode.appendNode('dependency')
dependencyNode.appendNode('groupId', dependency.group)
dependencyNode.appendNode('artifactId', dependency.name)
dependencyNode.appendNode('version', dependency.version)
}
}
}
}
}
}
-1
View File
@@ -1 +0,0 @@
include 'model-en'
@@ -36,6 +36,8 @@ public class LibVosk {
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);
@@ -23,6 +23,10 @@ public class Recognizer extends PointerType implements AutoCloseable {
LibVosk.vosk_recognizer_set_words(this.getPointer(), words);
}
public void setPartialWords(boolean partial_words) {
LibVosk.vosk_recognizer_set_partial_words(this.getPointer(), partial_words);
}
public void setSpeakerModel(SpeakerModel spkModel) {
LibVosk.vosk_recognizer_set_spk_model(this.getPointer(), spkModel.getPointer());
}
+4 -4
View File
@@ -1,16 +1,16 @@
CFLAGS=-I../src
LDFLAGS=-L../src -lvosk -ldl -lpthread -Wl,-rpath=../src
LDFLAGS=-L../src -lvosk -ldl -lpthread -Wl,-rpath,../src
all: test_vosk test_vosk_speaker
test_vosk: test_vosk.o
g++ $^ -o $@ $(LDFLAGS)
gcc $^ -o $@ $(LDFLAGS)
test_vosk_speaker: test_vosk_speaker.o
g++ $^ -o $@ $(LDFLAGS)
gcc $^ -o $@ $(LDFLAGS)
%.o: %.c
g++ $(CFLAGS) -c -o $@ $<
gcc $(CFLAGS) -c -o $@ $<
clean:
rm -f *.o *.a test_vosk test_vosk_speaker
+1 -1
View File
@@ -34,7 +34,7 @@ public class VoskDemo
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);
fbuffer[i] = BitConverter.ToInt16(buffer, n);
}
if (rec.AcceptWaveform(fbuffer, fbuffer.Length)) {
Console.WriteLine(rec.Result());
+1 -1
View File
@@ -11,7 +11,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Vosk" Version="0.3.30" />
<PackageReference Include="Vosk" Version="0.3.45" />
</ItemGroup>
</Project>
+5 -3
View File
@@ -2,13 +2,14 @@
<package>
<metadata>
<id>Vosk</id>
<version>0.3.30</version>
<version>0.3.45</version>
<authors>Alpha Cephei Inc</authors>
<owners>Alpha Cephei Inc</owners>
<license type="expression">Apache-2.0</license>
<projectUrl>https://alphacephei.com/vosk/</projectUrl>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<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.
<title>Vosk Speech Recognition Toolkit</title>
<description>Vosk is an offline open source speech recognition toolkit. It enables speech recognition models for 20+ languages and dialects - English, Indian English, German, French, Spanish, Portuguese, Chinese, Russian, Turkish, Vietnamese, Italian, Dutch, Catalan, Arabic, Greek, Farsi, Filipino, Ukrainian, Kazakh, Swedish, Japanese, Esperanto, Hindi, Czech. More to come.
Vosk models are small (50 Mb) but provide continuous large vocabulary transcription, zero-latency response with streaming API, reconfigurable vocabulary and speaker identification.
@@ -18,7 +19,8 @@ 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>
<copyright>Copyright 2020 Alpha Cephei Inc</copyright>
<repository type="git" url="https://github.com/alphacep/vosk-api.git" branch="master"/>
<copyright>Copyright 2020-2050 Alpha Cephei Inc</copyright>
<tags>speech recognition voice stt asr speech-to-text ai offline privacy</tags>
<dependencies>
<group targetFramework=".NETStandard2.0"/>
+1 -1
View File
@@ -2,7 +2,7 @@
<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-x64\*.dylib" Condition="'$([MSBuild]::IsOsPlatform(OSX))'" />
<NativeLibs Include="$(MSBuildThisFileDirectory)\lib\osx-universal\*.dylib" Condition="'$([MSBuild]::IsOsPlatform(OSX))'" />
<None Include="@(NativeLibs)">
<Link>%(FileName)%(Extension)</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+1 -1
View File
@@ -32,7 +32,7 @@ public class Model : global::System.IDisposable {
public Model(string model_path) : this(VoskPINVOKE.new_Model(model_path)) {
}
public int vosk_model_find_word(string word) {
public int FindWord(string word) {
return VoskPINVOKE.Model_vosk_model_find_word(handle, word);
}
+3
View File
@@ -38,6 +38,9 @@ class VoskPINVOKE {
[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);
+4
View File
@@ -46,6 +46,10 @@ public class VoskRecognizer : System.IDisposable {
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));
}
+176
View File
@@ -0,0 +1,176 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. this License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
+1
View File
@@ -0,0 +1 @@
See example subfolder for instructions how to use the module
+7
View File
@@ -0,0 +1,7 @@
// Go bindings for Vosk speech recognition toolkit. Vosk is an offline
// open source speech to text API for Android, iOS, Raspberry Pi and
// servers. It enables speech recognition models for 18 languages and
// dialects - English, Indian English, German, French, Spanish, Portuguese,
// Chinese, Russian, Turkish, Vietnamese, Italian, Dutch, Catalan, Arabic,
// Greek, Farsi, Filipino, Ukrainian.
package vosk
+31
View File
@@ -0,0 +1,31 @@
To try this package do the following steps:
On Linux (we download library and set LD_LIBRARY_PATH)
```
git clone https://github.com/alphacep/vosk-api
cd vosk-api/go/example
wget https://github.com/alphacep/vosk-api/releases/download/v0.3.45/vosk-linux-x86_64-0.3.45.zip
unzip vosk-linux-x86_64-0.3.45.zip
wget https://alphacephei.com/vosk/models/vosk-model-small-en-us-0.15.zip
unzip vosk-model-small-en-us-0.15.zip
mv vosk-model-small-en-us-0.15 model
cp ../../python/example/test.wav .
VOSK_PATH=`pwd`/vosk-linux-x86_64-0.3.45 LD_LIBRARY_PATH=$VOSK_PATH CGO_CPPFLAGS="-I $VOSK_PATH" CGO_LDFLAGS="-L $VOSK_PATH" go run . -f test.wav
```
for Windows (we place DLLs in current folder where linker finds them):
```
git clone https://github.com/alphacep/vosk-api
cd vosk-api/go/example
wget https://github.com/alphacep/vosk-api/releases/download/v0.3.45/vosk-linux-x86_64-0.3.45.zip
unzip vosk-linux-x86_64-0.3.45.zip
cp vosk-linux-x86_64-0.3.45/*.dll .
cp vosk-linux-x86_64-0.3.45/*.h .
wget https://alphacephei.com/vosk/models/vosk-model-small-en-us-0.15.zip
unzip vosk-model-small-en-us-0.15.zip
mv vosk-model-small-en-us-0.15 model
cp ../../python/example/test.wav .
VOSK_PATH=`pwd` LD_LIBRARY_PATH=$VOSK_PATH CGO_CPPFLAGS="-I $VOSK_PATH" CGO_LDFLAGS="-L $VOSK_PATH -lvosk -lpthread -dl" go run . -f test.wav
```
+2
View File
@@ -0,0 +1,2 @@
// Example package for Vosk Go bindings.
package main
-36
View File
@@ -1,36 +0,0 @@
package main
import (
"flag"
"os"
".."
)
func main() {
var filename string
flag.StringVar(&filename, "f", "", "file to transcribe")
flag.Parse()
model, err := vosk.NewModel("model")
rec, err := vosk.NewRecognizer(model)
file, err := os.Open(filename)
if err != nil {
panic(err)
}
defer file.Close()
fileinfo, err := file.Stat()
if err != nil {
panic(err)
}
filesize := fileinfo.Size()
buffer := make([]byte, filesize)
_, err = file.Read(buffer)
if err != nil {
panic(err)
}
println(vosk.VoskFinalResult(rec, buffer))
}
+63
View File
@@ -0,0 +1,63 @@
package main
import (
"bufio"
"flag"
"fmt"
"io"
"log"
"os"
"encoding/json"
vosk "github.com/alphacep/vosk-api/go"
)
func main() {
var filename string
flag.StringVar(&filename, "f", "", "file to transcribe")
flag.Parse()
model, err := vosk.NewModel("model")
if err != nil {
log.Fatal(err)
}
// we can check if word is in the vocabulary
// fmt.Println(model.FindWord("air"))
sampleRate := 16000.0
rec, err := vosk.NewRecognizer(model, sampleRate)
if err != nil {
log.Fatal(err)
}
rec.SetWords(1)
file, err := os.Open(filename)
if err != nil {
panic(err)
}
defer file.Close()
reader := bufio.NewReader(file)
buf := make([]byte, 4096)
for {
_, err := reader.Read(buf)
if err != nil {
if err != io.EOF {
log.Fatal(err)
}
break
}
if rec.AcceptWaveform(buf) != 0 {
fmt.Println(rec.Result())
}
}
// Unmarshal example for final result
var jres map[string]interface{}
json.Unmarshal([]byte(rec.FinalResult()), &jres)
fmt.Println(jres["text"])
}
+7
View File
@@ -0,0 +1,7 @@
module github.com/alphacep/vosk-api/go
go 1.16
replace (
github.com/alphacep/vosk-api/go => ./
)
+136 -36
View File
@@ -5,58 +5,158 @@ package vosk
// #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
}
// VoskSpkModel contains a reference to the C VoskSpkModel
type VoskSpkModel struct {
spkModel *C.struct_VoskSpkModel
}
// VoskRecognizer contains a reference to the C VoskRecognizer
type VoskRecognizer struct {
rec *C.struct_VoskRecognizer
}
func VoskFinalResult(recognizer *VoskRecognizer, buffer []byte) string {
cbuf := C.CBytes(buffer)
defer C.free(cbuf)
_ = C.vosk_recognizer_accept_waveform(recognizer.rec, (*C.char)(cbuf), C.int(len(buffer)))
result := C.GoString(C.vosk_recognizer_final_result(recognizer.rec))
return result
model *C.struct_VoskModel
}
// NewModel creates a new VoskModel instance
func NewModel(modelPath string) (*VoskModel, error) {
var internal *C.struct_VoskModel
internal = C.vosk_model_new(C.CString(modelPath))
model := &VoskModel{model: internal}
return model, nil
cmodelPath := C.CString(modelPath)
defer C.free(unsafe.Pointer(cmodelPath))
internal := C.vosk_model_new(cmodelPath)
model := &VoskModel{model: internal}
return model, nil
}
// NewRecognizer creates a new VoskRecognizer instance
func NewRecognizer(model *VoskModel) (*VoskRecognizer, error) {
var internal *C.struct_VoskRecognizer
internal = C.vosk_recognizer_new(model.model, 16000.0)
rec := &VoskRecognizer{rec: internal}
return rec, nil
func (m *VoskModel) Free() {
C.vosk_model_free(m.model)
}
func freeModel(model *VoskModel) {
C.vosk_model_free(model.model)
C.vosk_model_free(model.model)
}
func freeRecognizer(recognizer *VoskRecognizer) {
C.vosk_recognizer_free(recognizer.rec)
// 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) {
var internal *C.struct_VoskSpkModel
internal = C.vosk_spk_model_new(C.CString(spkModelPath))
spkModel := &VoskSpkModel{spkModel: internal}
return spkModel, nil
cspkModelPath := C.CString(spkModelPath)
defer C.free(unsafe.Pointer(cspkModelPath))
internal := C.vosk_spk_model_new(cspkModelPath)
spkModel := &VoskSpkModel{spkModel: internal}
return spkModel, nil
}
func freeSpkModel(model *VoskSpkModel) {
C.vosk_spk_model_free(model.spkModel)
}
func(s *VoskSpkModel) Free() {
C.vosk_spk_model_free(s.spkModel)
}
// VoskRecognizer contains a reference to the C VoskRecognizer
type VoskRecognizer struct {
rec *C.struct_VoskRecognizer
}
func freeRecognizer(recognizer *VoskRecognizer) {
C.vosk_recognizer_free(recognizer.rec)
}
func (r *VoskRecognizer) Free() {
C.vosk_recognizer_free(r.rec)
}
// NewRecognizer creates a new VoskRecognizer instance
func NewRecognizer(model *VoskModel, sampleRate float64) (*VoskRecognizer, error) {
internal := C.vosk_recognizer_new(model.model, C.float(sampleRate))
rec := &VoskRecognizer{rec: internal}
return rec, nil
}
// NewRecognizerSpk creates a new VoskRecognizer instance with a speaker model.
func NewRecognizerSpk(model *VoskModel, sampleRate float64, spkModel *VoskSpkModel) (*VoskRecognizer, error) {
internal := C.vosk_recognizer_new_spk(model.model, C.float(sampleRate), spkModel.spkModel)
rec := &VoskRecognizer{rec: internal}
return rec, nil
}
// NewRecognizerGrm creates a new VoskRecognizer instance with the phrase list.
func NewRecognizerGrm(model *VoskModel, sampleRate float64, grammar string) (*VoskRecognizer, error) {
cgrammar := C.CString(grammar)
defer C.free(unsafe.Pointer(cgrammar))
internal := C.vosk_recognizer_new_grm(model.model, C.float(sampleRate), cgrammar)
rec := &VoskRecognizer{rec: internal}
return rec, nil
}
// SetSpkModel adds a speaker model to an already initialized recognizer.
func (r *VoskRecognizer) SetSpkModel(spkModel *VoskSpkModel) {
C.vosk_recognizer_set_spk_model(r.rec, spkModel.spkModel)
}
// SetMaxAlternatives configures the recognizer to output n-best results.
func (r *VoskRecognizer) SetMaxAlternatives(maxAlternatives int) {
C.vosk_recognizer_set_max_alternatives(r.rec, C.int(maxAlternatives))
}
// SetWords enables words with times in the ouput.
func (r *VoskRecognizer) SetWords(words int) {
C.vosk_recognizer_set_words(r.rec, C.int(words))
}
// SetPartialWords enables words with times in the partial ouput.
func (r *VoskRecognizer) SetPartialWords(words int) {
C.vosk_recognizer_set_partial_words(r.rec, C.int(words))
}
// AcceptWaveform accepts and processes a new chunk of the voice data.
func (r *VoskRecognizer) AcceptWaveform(buffer []byte) int {
cbuf := C.CBytes(buffer)
defer C.free(cbuf)
i := C.vosk_recognizer_accept_waveform(r.rec, (*C.char)(cbuf), C.int(len(buffer)))
return int(i)
}
// Result returns a speech recognition result.
func (r *VoskRecognizer) Result() string {
return C.GoString(C.vosk_recognizer_result(r.rec))
}
// PartialResult returns a partial speech recognition result.
func (r *VoskRecognizer) PartialResult() string {
return C.GoString(C.vosk_recognizer_partial_result(r.rec))
}
// FinalResult returns a speech recognition result. Same as result, but doesn't wait
// for silence.
func (r *VoskRecognizer) FinalResult() string {
return C.GoString(C.vosk_recognizer_final_result(r.rec))
}
// Reset resets the recognizer.
func (r *VoskRecognizer) Reset() {
C.vosk_recognizer_reset(r.rec)
}
// SetLogLevel sets the log level for Kaldi messages.
func SetLogLevel(logLevel int) {
C.vosk_set_log_level(C.int(logLevel))
}
// GPUInit automatically selects a CUDA device and allows multithreading.
func GPUInit() {
C.vosk_gpu_init()
}
// GPUThreadInit inits CUDA device in a multi-threaded environment.
func GPUThreadInit() {
C.vosk_gpu_thread_init()
}
+26 -17
View File
@@ -13,10 +13,10 @@
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 */; };
92D86BD6253F823F0040D53F /* vosk-model-spk-0.4 in Resources */ = {isa = PBXBuildFile; fileRef = 92D86BD4253F823F0040D53F /* vosk-model-spk-0.4 */; };
@@ -31,10 +31,10 @@
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>"; };
@@ -47,9 +47,9 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
92375246240C6DC900DD6076 /* libstdc++.tbd in Frameworks */,
92833003273C466E00058B52 /* libc++.tbd in Frameworks */,
92375244240C6DAF00DD6076 /* Accelerate.framework in Frameworks */,
9237523C240C642000DD6076 /* libkaldiwrap.a in Frameworks */,
925527A9273C492C00FFD9CC /* libvosk.a in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -93,11 +93,11 @@
92375239240C642000DD6076 /* Vosk */ = {
isa = PBXGroup;
children = (
928CC50C25BE124400490481 /* vosk-model-small-en-us-0.15 */,
92D86BD4253F823F0040D53F /* vosk-model-spk-0.4 */,
92375256240C6E3D00DD6076 /* 10001-90210-01803.wav */,
928CC50C25BE124400490481 /* vosk-model-small-en-us-0.15 */,
925527A8273C492C00FFD9CC /* libvosk.a */,
92AA22AD244CDD1200DA464B /* vosk_api.h */,
9237523A240C642000DD6076 /* libkaldiwrap.a */,
92375256240C6E3D00DD6076 /* 10001-90210-01803.wav */,
);
name = Vosk;
path = VoskApiTest/Vosk;
@@ -106,7 +106,7 @@
92375242240C6DAF00DD6076 /* Frameworks */ = {
isa = PBXGroup;
children = (
92375245240C6DC900DD6076 /* libstdc++.tbd */,
92833002273C466E00058B52 /* libc++.tbd */,
92375243240C6DAF00DD6076 /* Accelerate.framework */,
);
name = Frameworks;
@@ -145,7 +145,7 @@
9237521D240C550B00DD6076 = {
CreatedOnToolsVersion = 8.3.2;
LastSwiftMigration = 0920;
ProvisioningStyle = Automatic;
ProvisioningStyle = Manual;
};
};
};
@@ -223,7 +223,6 @@
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
@@ -245,7 +244,7 @@
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
@@ -282,7 +281,6 @@
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
@@ -304,7 +302,7 @@
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
@@ -319,6 +317,7 @@
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 10.3;
MTL_ENABLE_DEBUG_INFO = NO;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
SWIFT_OBJC_BRIDGING_HEADER = bridging.h;
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
@@ -333,7 +332,10 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
ENABLE_BITCODE = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
CODE_SIGN_STYLE = Manual;
DEVELOPMENT_TEAM = "";
ENABLE_BITCODE = NO;
INFOPLIST_FILE = VoskApiTest/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
LIBRARY_SEARCH_PATHS = (
@@ -342,11 +344,13 @@
);
PRODUCT_BUNDLE_IDENTIFIER = com.alphacephei.VoskApiTest;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_INSTALL_OBJC_HEADER = YES;
SWIFT_OBJC_BRIDGING_HEADER = VoskApiTest/bridging.h;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_SWIFT3_OBJC_INFERENCE = Default;
SWIFT_VERSION = 4.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
};
@@ -355,7 +359,10 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
ENABLE_BITCODE = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
CODE_SIGN_STYLE = Manual;
DEVELOPMENT_TEAM = "";
ENABLE_BITCODE = NO;
INFOPLIST_FILE = VoskApiTest/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
LIBRARY_SEARCH_PATHS = (
@@ -364,10 +371,12 @@
);
PRODUCT_BUNDLE_IDENTIFIER = com.alphacephei.VoskApiTest;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_INSTALL_OBJC_HEADER = YES;
SWIFT_OBJC_BRIDGING_HEADER = VoskApiTest/bridging.h;
SWIFT_SWIFT3_OBJC_INFERENCE = Default;
SWIFT_VERSION = 4.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Release;
};
@@ -1,9 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "group:/Users/shmyrev/Documents/IOS/VoskApiTest/VoskApiTest/Vosk/vosk-model-small-en-us-0.15">
</FileRef>
<FileRef
location = "self:">
</FileRef>
+1 -1
View File
@@ -14,7 +14,7 @@ public final class Vosk {
var recognizer : OpaquePointer!
init(model: VoskModel, sampleRate: Float) {
recognizer = vosk_recognizer_new_spk(model.model, model.spkModel, sampleRate)
recognizer = vosk_recognizer_new_spk(model.model, sampleRate, model.spkModel)
}
deinit {
+117 -36
View File
@@ -1,4 +1,4 @@
// Copyright 2020 Alpha Cephei Inc.
// Copyright 2020-2021 Alpha Cephei Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -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 */
* @returns model object or NULL if problem occured */
VoskModel *vosk_model_new(const char *model_path);
@@ -55,10 +55,18 @@ 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 */
* @returns model object or NULL if problem occured */
VoskSpkModel *vosk_spk_model_new(const char *model_path);
@@ -71,9 +79,13 @@ void vosk_spk_model_free(VoskSpkModel *model);
/** Creates the recognizer object
*
* 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 */
* The recognizers process the speech and return text using shared model data
* @param model VoskModel containing static data for recognizer. Model can be
* shared across recognizers, even running in different threads.
* @param sample_rate The sample rate of the audio you going to feed into the recognizer.
* Make sure this rate matches the audio content, it is a common
* issue causing accuracy problems.
* @returns recognizer object or NULL if problem occured */
VoskRecognizer *vosk_recognizer_new(VoskModel *model, float sample_rate);
@@ -82,10 +94,14 @@ 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
* @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);
* @returns recognizer object or NULL if problem occured */
VoskRecognizer *vosk_recognizer_new_spk(VoskModel *model, float sample_rate, VoskSpkModel *spk_model);
/** Creates the recognizer object with the phrase list
@@ -98,42 +114,46 @@ VoskRecognizer *vosk_recognizer_new_spk(VoskModel *model, VoskSpkModel *spk_mode
* Only recognizers with lookahead models support this type of quick configuration.
* Precompiled HCLG graph models are not supported.
*
* @param sample_rate The sample rate of the audio you going to feed into the recognizer
* @param model VoskModel containing static data for recognizer. Model can be
* shared across recognizers, even running in different threads.
* @param sample_rate The sample rate of the audio you going to feed into the recognizer.
* Make sure this rate matches the audio content, it is a common
* issue causing accuracy problems.
* @param grammar The string with the list of phrases to recognize as JSON array of strings,
* for example "["one two three four five", "[unk]"]".
*
* @returns recognizer object */
* @returns recognizer object or NULL if problem occured */
VoskRecognizer *vosk_recognizer_new_grm(VoskModel *model, float sample_rate, const char *grammar);
/** Accept voice data
/** Adds speaker model to already initialized recognizer
*
* accept and process new chunk of voice data
* Can add speaker recognition model to already created recognizer. Helps to initialize
* speaker recognition for grammar-based recognizer.
*
* @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);
* @param spk_model Speaker recognition model */
void vosk_recognizer_set_spk_model(VoskRecognizer *recognizer, VoskSpkModel *spk_model);
/** 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
/** Configures recognizer to output n-best results
*
* <pre>
* {
* "alternatives": [
* { "text": "one two three four five", "confidence": 0.97 },
* { "text": "one two three for five", "confidence": 0.03 },
* ]
* }
* </pre>
*
* @param max_alternatives - maximum alternatives to return from recognition results
*/
void vosk_recognizer_set_max_alternatives(VoskRecognizer *recognizer, int max_alternatives);
/** Enables words with times in the output
*
* <pre>
* {
* "result" : [{
* "conf" : 1.000000,
* "end" : 1.110000,
@@ -156,13 +176,54 @@ int vosk_recognizer_accept_waveform_f(VoskRecognizer *recognizer, const float *d
* "word" : "zero"
* }, {
* "conf" : 1.000000,
* "end" : 2.610000,
* "end" : 2.610000,
* "start" : 2.340000,
* "word" : "one"
* }],
* "text" : "what zero zero zero one"
* </pre>
*
* @param words - boolean value
*/
void vosk_recognizer_set_words(VoskRecognizer *recognizer, int words);
/** Accept voice data
*
* accept and process new chunk of voice data
*
* @param data - audio data in PCM 16-bit mono format
* @param length - length of the audio data
* @returns 1 if silence is occured and you can retrieve a new utterance with result method
* 0 if decoding continues
* -1 if exception occured */
int vosk_recognizer_accept_waveform(VoskRecognizer *recognizer, const char *data, int length);
/** Same as above but the version with the short data for language bindings where you have
* audio as array of shorts */
int vosk_recognizer_accept_waveform_s(VoskRecognizer *recognizer, const short *data, int length);
/** Same as above but the version with the float data for language bindings where you have
* audio as array of floats */
int vosk_recognizer_accept_waveform_f(VoskRecognizer *recognizer, const float *data, int length);
/** Returns speech recognition result
*
* @returns the result in JSON format which contains decoded line, decoded
* words, times in seconds and confidences. You can parse this result
* with any json parser
*
* <pre>
* {
* "text" : "what zero zero zero one"
* }
* </pre>
*
* If alternatives enabled it returns result with alternatives, see also vosk_recognizer_set_alternatives().
*
* If word times enabled returns word time, see also vosk_recognizer_set_word_times().
*/
const char *vosk_recognizer_result(VoskRecognizer *recognizer);
@@ -174,7 +235,7 @@ const char *vosk_recognizer_result(VoskRecognizer *recognizer);
*
* <pre>
* {
* "partial" : "cyril one eight zero"
* "partial" : "cyril one eight zero"
* }
* </pre>
*/
@@ -190,6 +251,12 @@ 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 */
@@ -204,6 +271,20 @@ 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 -4
View File
@@ -8,12 +8,9 @@ application {
repositories {
mavenCentral()
maven {
url 'https://alphacephei.com/maven/'
}
}
dependencies {
implementation group: 'net.java.dev.jna', name: 'jna', version: '5.7.0'
implementation group: 'com.alphacephei', name: 'vosk', version: '0.3.30+'
implementation group: 'com.alphacephei', name: 'vosk', version: '0.3.40+'
}
+23 -10
View File
@@ -1,18 +1,31 @@
buildscript {
repositories {
mavenCentral()
}
}
plugins {
id 'java-library'
id 'maven-publish'
id 'com.vanniktech.maven.publish' version '0.18.0'
}
archivesBaseName = 'vosk'
group = 'com.alphacephei'
version = '0.3.30'
repositories {
mavenCentral()
}
archivesBaseName = 'vosk'
group = 'com.alphacephei'
version = '0.3.45'
mavenPublish {
group = 'com.alphacephei'
version = version
sonatypeHost = 's01'
}
dependencies {
implementation group: 'net.java.dev.jna', name: 'jna', version: '5.7.0'
api group: 'net.java.dev.jna', name: 'jna', version: '5.7.0'
testImplementation 'junit:junit:4.13'
}
@@ -45,14 +58,14 @@ publishing {
}
}
}
repositories {
maven {
url = "repo"
}
}
}
test {
dependsOn cleanTest
testLogging.showStandardStreams = true
}
java {
withSourcesJar()
withJavadocJar()
}
@@ -62,6 +62,8 @@ public class LibVosk {
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);
+6 -1
View File
@@ -1,13 +1,18 @@
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) {
public Model(String path) throws IOException {
super(LibVosk.vosk_model_new(path));
if (getPointer() == null) {
throw new IOException("Failed to create a model");
}
}
@Override
@@ -1,10 +1,15 @@
package org.vosk;
import com.sun.jna.PointerType;
import java.io.IOException;
public class Recognizer extends PointerType implements AutoCloseable {
public Recognizer(Model model, float sampleRate) {
public Recognizer(Model model, float sampleRate) throws IOException {
super(LibVosk.vosk_recognizer_new(model, sampleRate));
if (getPointer() == null) {
throw new IOException("Failed to create a recognizer");
}
}
public Recognizer(Model model, float sampleRate, SpeakerModel spkModel) {
@@ -23,6 +28,10 @@ public class Recognizer extends PointerType implements AutoCloseable {
LibVosk.vosk_recognizer_set_words(this.getPointer(), words);
}
public void setPartialWords(boolean partial_words) {
LibVosk.vosk_recognizer_set_partial_words(this.getPointer(), partial_words);
}
public void setSpeakerModel(SpeakerModel spkModel) {
LibVosk.vosk_recognizer_set_spk_model(this.getPointer(), spkModel.getPointer());
}
@@ -1,13 +1,18 @@
package org.vosk;
import com.sun.jna.PointerType;
import java.io.IOException;
public class SpeakerModel extends PointerType implements AutoCloseable {
public SpeakerModel() {
}
public SpeakerModel(String path) {
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
@@ -30,6 +30,7 @@ public class DecoderTest {
recognizer.setMaxAlternatives(10);
recognizer.setWords(true);
recognizer.setPartialWords(true);
int nbytes;
byte[] b = new byte[4096];
@@ -93,4 +94,10 @@ public class DecoderTest {
}
Assert.assertTrue(true);
}
@Test(expected = IOException.class)
public void decoderTestException() throws IOException {
Model model = new Model("model_missing");
}
}
+7 -6
View File
@@ -2,18 +2,19 @@ This is an FFI-NAPI wrapper for the Vosk library.
## Usage
It mostly follows Vosk interface, some methods are not yet fully implemented.
Bindings mostly follow Vosk interface, some methods are not yet fully implemented.
To use it you need to compile libvosk library, see Python module build
instructions for details. You can find prebuilt library inside python
wheel.
See [demo folder](https://github.com/alphacep/vosk-api/tree/master/nodejs/demo) for
details.
## About
Vosk is an offline open source speech recognition toolkit. It enables
speech recognition models for 17 languages and dialects - English, Indian
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.
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
+11 -4
View File
@@ -18,11 +18,11 @@ const rec = new vosk.Recognizer({model: model, sampleRate: SAMPLE_RATE});
var micInstance = mic({
rate: String(SAMPLE_RATE),
channels: '1',
debug: false
debug: false,
device: 'default',
});
var micInputStream = micInstance.getAudioStream();
micInstance.start();
micInputStream.on('data', data => {
if (rec.acceptWaveform(data))
@@ -31,9 +31,16 @@ micInputStream.on('data', data => {
console.log(rec.partialResult());
});
process.on('SIGINT', function() {
micInputStream.on('audioProcessExitComplete', function() {
console.log("Cleaning up");
console.log(rec.finalResult());
console.log("\nDone");
rec.free();
model.free();
});
process.on('SIGINT', function() {
console.log("\nStopping");
micInstance.stop();
});
micInstance.start();
+3
View File
@@ -29,10 +29,13 @@ wfReader.on('format', async ({ audioFormat, sampleRate, channels }) => {
const rec = new vosk.Recognizer({model: model, sampleRate: sampleRate});
rec.setMaxAlternatives(10);
rec.setWords(true);
rec.setPartialWords(true);
for await (const data of wfReadable) {
const end_of_speech = rec.acceptWaveform(data);
if (end_of_speech) {
console.log(JSON.stringify(rec.result(), null, 4));
} else {
console.log(JSON.stringify(rec.partialResult(), null, 4));
}
}
console.log(JSON.stringify(rec.finalResult(rec), null, 4));
+6 -6
View File
@@ -46,8 +46,8 @@ ffmpeg_run.on('exit', code => {
subs.push({
type: 'cue',
data: {
start: words[0].start,
end: words[0].end,
start: words[0].start * 1000,
end: words[0].end * 1000,
text: words[0].word
}
});
@@ -61,8 +61,8 @@ ffmpeg_run.on('exit', code => {
subs.push({
type: 'cue',
data: {
start: words[start_index].start,
end: words[i].end,
start: words[start_index].start * 1000,
end: words[i].end * 1000,
text: text.slice(0, text.length-1)
}
});
@@ -74,8 +74,8 @@ ffmpeg_run.on('exit', code => {
subs.push({
type: 'cue',
data: {
start: words[start_index].start,
end: words[words.length-1].end,
start: words[start_index].start * 1000,
end: words[words.length-1].end * 1000,
text: text
}
});
+8 -2
View File
@@ -72,11 +72,11 @@ if (os.platform() == 'win32') {
// Update path to load dependent dlls
let currentPath = process.env.Path;
let dllDirectory = path.resolve(path.join(__dirname, "lib", "win-x86_64"));
process.env.Path = currentPath + path.delimiter + dllDirectory;
process.env.Path = dllDirectory + path.delimiter + currentPath;
soname = path.join(__dirname, "lib", "win-x86_64", "libvosk.dll")
} else if (os.platform() == 'darwin') {
soname = path.join(__dirname, "lib", "osx-x86_64", "libvosk.dylib")
soname = path.join(__dirname, "lib", "osx-universal", "libvosk.dylib")
} else {
soname = path.join(__dirname, "lib", "linux-x86_64", "libvosk.so")
}
@@ -93,6 +93,7 @@ const libvosk = ffi.Library(soname, {
'vosk_recognizer_free': ['void', [vosk_recognizer_ptr]],
'vosk_recognizer_set_max_alternatives': ['void', [vosk_recognizer_ptr, 'int']],
'vosk_recognizer_set_words': ['void', [vosk_recognizer_ptr, 'bool']],
'vosk_recognizer_set_partial_words': ['void', [vosk_recognizer_ptr, 'bool']],
'vosk_recognizer_set_spk_model': ['void', [vosk_recognizer_ptr, vosk_spk_model_ptr]],
'vosk_recognizer_accept_waveform': ['bool', [vosk_recognizer_ptr, 'pointer', 'int']],
'vosk_recognizer_result': ['string', [vosk_recognizer_ptr]],
@@ -301,6 +302,11 @@ class Recognizer {
libvosk.vosk_recognizer_set_words(this.handle, words);
}
/** Same as above, but for partial results*/
setPartialWords(partial_words) {
libvosk.vosk_recognizer_set_partial_words(this.handle, partial_words);
}
/** Adds speaker recognition model to already created recognizer. Helps to initialize
* speaker recognition for grammar-based recognizer.
*
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "vosk",
"version": "0.3.30",
"version": "0.3.45",
"description": "Node binding for continuous offline voice recoginition with Vosk library.",
"repository": {
"type": "git",
+639
View File
@@ -0,0 +1,639 @@
[MAIN]
# Analyse import fallback blocks. This can be used to support both Python 2 and
# 3 compatible code, which means that the block might have code that exists
# only in one or another interpreter, leading to false positives when analysed.
analyse-fallback-blocks=no
# Load and enable all available extensions. Use --list-extensions to see a list
# all available extensions.
#enable-all-extensions=
# In error mode, messages with a category besides ERROR or FATAL are
# suppressed, and no reports are done by default. Error mode is compatible with
# disabling specific errors.
#errors-only=
# Always return a 0 (non-error) status code, even if lint errors are found.
# This is primarily useful in continuous integration scripts.
#exit-zero=
# A comma-separated list of package or module names from where C extensions may
# be loaded. Extensions are loading into the active Python interpreter and may
# run arbitrary code.
extension-pkg-allow-list=
# A comma-separated list of package or module names from where C extensions may
# be loaded. Extensions are loading into the active Python interpreter and may
# run arbitrary code. (This is an alternative name to extension-pkg-allow-list
# for backward compatibility.)
extension-pkg-whitelist=
# Return non-zero exit code if any of these messages/categories are detected,
# even if score is above --fail-under value. Syntax same as enable. Messages
# specified are enabled, while categories only check already-enabled messages.
fail-on=
# Specify a score threshold under which the program will exit with error.
fail-under=10
# Interpret the stdin as a python script, whose filename needs to be passed as
# the module_or_package argument.
#from-stdin=
# Files or directories to be skipped. They should be base names, not paths.
ignore=CVS
# Add files or directories matching the regular expressions patterns to the
# ignore-list. The regex matches against paths and can be in Posix or Windows
# format. Because '\' represents the directory delimiter on Windows systems, it
# can't be used as an escape character.
ignore-paths=
# Files or directories matching the regular expression patterns are skipped.
# The regex matches against base names, not paths. The default value ignores
# Emacs file locks
ignore-patterns=^\.#
# List of module names for which member attributes should not be checked
# (useful for modules/projects where namespaces are manipulated during runtime
# and thus existing member attributes cannot be deduced by static analysis). It
# supports qualified module names, as well as Unix pattern matching.
ignored-modules=
# Python code to execute, usually for sys.path manipulation such as
# pygtk.require().
#init-hook=
# Use multiple processes to speed up Pylint. Specifying 0 will auto-detect the
# number of processors available to use, and will cap the count on Windows to
# avoid hangs.
jobs=1
# Control the amount of potential inferred values when inferring a single
# object. This can help the performance when dealing with large functions or
# complex, nested conditions.
limit-inference-results=100
# List of plugins (as comma separated values of python module names) to load,
# usually to register additional checkers.
load-plugins=
# Pickle collected data for later comparisons.
persistent=yes
# Minimum Python version to use for version dependent checks. Will default to
# the version used to run pylint.
py-version=3.7
# Discover python modules and packages in the file system subtree.
recursive=no
# When enabled, pylint would attempt to guess common misconfiguration and emit
# user-friendly hints instead of false-positive error messages.
suggestion-mode=yes
# Allow loading of arbitrary C extensions. Extensions are imported into the
# active Python interpreter and may run arbitrary code.
unsafe-load-any-extension=no
# In verbose mode, extra non-checker-related info will be displayed.
#verbose=
[REPORTS]
# Python expression which should return a score less than or equal to 10. You
# have access to the variables 'fatal', 'error', 'warning', 'refactor',
# 'convention', and 'info' which contain the number of messages in each
# category, as well as 'statement' which is the total number of statements
# analyzed. This score is used by the global evaluation report (RP0004).
evaluation=max(0, 0 if fatal else 10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10))
# Template used to display messages. This is a python new-style format string
# used to format the message information. See doc for all details.
msg-template=
# Set the output format. Available formats are text, parseable, colorized, json
# and msvs (visual studio). You can also give a reporter class, e.g.
# mypackage.mymodule.MyReporterClass.
#output-format=
# Tells whether to display a full report or only the messages.
reports=no
# Activate the evaluation score.
score=yes
[MESSAGES CONTROL]
# Only show warnings with the listed confidence levels. Leave empty to show
# all. Valid levels: HIGH, CONTROL_FLOW, INFERENCE, INFERENCE_FAILURE,
# UNDEFINED.
confidence=HIGH,
CONTROL_FLOW,
INFERENCE,
INFERENCE_FAILURE,
UNDEFINED
# Disable the message, report, category or checker with the given id(s). You
# can either give multiple identifiers separated by comma (,) or put this
# option multiple times (only on the command line, not in the configuration
# file where it should appear only once). You can also use "--disable=all" to
# disable everything first and then re-enable specific checks. For example, if
# you want to run only the similarities checker, you can use "--disable=all
# --enable=similarities". If you want to run only the classes checker, but have
# no Warning level messages displayed, use "--disable=all --enable=classes
# --disable=W".
disable=raw-checker-failed,
bad-inline-option,
locally-disabled,
file-ignored,
suppressed-message,
useless-suppression,
deprecated-pragma,
use-symbolic-message-instead,
missing-module-docstring, # added
missing-class-docstring, # added
missing-function-docstring, # added
invalid-name, # added
broad-except, # added
wrong-import-order, # added
consider-using-f-string, # added
logging-format-interpolation, # added
no-member, # added
consider-using-with, # added
unused-argument,
no-else-return, # added
too-few-public-methods, # added
import-error, # added
protected-access, # added
unused-variable, # added
R0801 # added (similarity of two scripts)
# Enable the message, report, category or checker with the given id(s). You can
# either give multiple identifier separated by comma (,) or put this option
# multiple time (only on the command line, not in the configuration file where
# it should appear only once). See also the "--disable" option for examples.
enable=c-extension-no-member
[BASIC]
# Naming style matching correct argument names.
argument-naming-style=snake_case
# Regular expression matching correct argument names. Overrides argument-
# naming-style. If left empty, argument names will be checked with the set
# naming style.
#argument-rgx=
# Naming style matching correct attribute names.
attr-naming-style=snake_case
# Regular expression matching correct attribute names. Overrides attr-naming-
# style. If left empty, attribute names will be checked with the set naming
# style.
#attr-rgx=
# Bad variable names which should always be refused, separated by a comma.
bad-names=foo,
bar,
baz,
toto,
tutu,
tata
# Bad variable names regexes, separated by a comma. If names match any regex,
# they will always be refused
bad-names-rgxs=
# Naming style matching correct class attribute names.
class-attribute-naming-style=any
# Regular expression matching correct class attribute names. Overrides class-
# attribute-naming-style. If left empty, class attribute names will be checked
# with the set naming style.
#class-attribute-rgx=
# Naming style matching correct class constant names.
class-const-naming-style=UPPER_CASE
# Regular expression matching correct class constant names. Overrides class-
# const-naming-style. If left empty, class constant names will be checked with
# the set naming style.
#class-const-rgx=
# Naming style matching correct class names.
class-naming-style=PascalCase
# Regular expression matching correct class names. Overrides class-naming-
# style. If left empty, class names will be checked with the set naming style.
#class-rgx=
# Naming style matching correct constant names.
const-naming-style=UPPER_CASE
# Regular expression matching correct constant names. Overrides const-naming-
# style. If left empty, constant names will be checked with the set naming
# style.
#const-rgx=
# Minimum line length for functions/classes that require docstrings, shorter
# ones are exempt.
docstring-min-length=-1
# Naming style matching correct function names.
function-naming-style=snake_case
# Regular expression matching correct function names. Overrides function-
# naming-style. If left empty, function names will be checked with the set
# naming style.
#function-rgx=
# Good variable names which should always be accepted, separated by a comma.
good-names=i,
j,
k,
ex,
Run,
_,
x,
y,
nx,
ny
# Good variable names regexes, separated by a comma. If names match any regex,
# they will always be accepted
good-names-rgxs=
# Include a hint for the correct naming format with invalid-name.
include-naming-hint=no
# Naming style matching correct inline iteration names.
inlinevar-naming-style=any
# Regular expression matching correct inline iteration names. Overrides
# inlinevar-naming-style. If left empty, inline iteration names will be checked
# with the set naming style.
#inlinevar-rgx=
# Naming style matching correct method names.
method-naming-style=snake_case
# Regular expression matching correct method names. Overrides method-naming-
# style. If left empty, method names will be checked with the set naming style.
#method-rgx=
# Naming style matching correct module names.
module-naming-style=snake_case
# Regular expression matching correct module names. Overrides module-naming-
# style. If left empty, module names will be checked with the set naming style.
#module-rgx=
# Colon-delimited sets of names that determine each other's naming style when
# the name regexes allow several styles.
name-group=
# Regular expression which should only match function or class names that do
# not require a docstring.
no-docstring-rgx=^_
# List of decorators that produce properties, such as abc.abstractproperty. Add
# to this list to register other decorators that produce valid properties.
# These decorators are taken in consideration only for invalid-name.
property-classes=abc.abstractproperty
# Regular expression matching correct type variable names. If left empty, type
# variable names will be checked with the set naming style.
#typevar-rgx=
# Naming style matching correct variable names.
variable-naming-style=snake_case
# Regular expression matching correct variable names. Overrides variable-
# naming-style. If left empty, variable names will be checked with the set
# naming style.
#variable-rgx=
[VARIABLES]
# List of additional names supposed to be defined in builtins. Remember that
# you should avoid defining new builtins when possible.
additional-builtins=
# Tells whether unused global variables should be treated as a violation.
allow-global-unused-variables=yes
# List of names allowed to shadow builtins
allowed-redefined-builtins=
# List of strings which can identify a callback function by name. A callback
# name must start or end with one of those strings.
callbacks=cb_,
_cb
# A regular expression matching the name of dummy variables (i.e. expected to
# not be used).
dummy-variables-rgx=_+$|(_[a-zA-Z0-9_]*[a-zA-Z0-9]+?$)|dummy|^ignored_|^unused_
# Argument names that match this expression will be ignored.
ignored-argument-names=_.*|^ignored_|^unused_
# Tells whether we should check for unused import in __init__ files.
init-import=no
# List of qualified module names which can have objects that can redefine
# builtins.
redefining-builtins-modules=six.moves,past.builtins,future.builtins,builtins,io
[SIMILARITIES]
# Comments are removed from the similarity computation
ignore-comments=yes
# Docstrings are removed from the similarity computation
ignore-docstrings=yes
# Imports are removed from the similarity computation
ignore-imports=yes
# Signatures are removed from the similarity computation
ignore-signatures=yes
# Minimum lines number of a similarity.
min-similarity-lines=4
[MISCELLANEOUS]
# List of note tags to take in consideration, separated by a comma.
notes=FIXME,
XXX,
TODO
# Regular expression of note tags to take in consideration.
notes-rgx=
[STRING]
# This flag controls whether inconsistent-quotes generates a warning when the
# character used as a quote delimiter is used inconsistently within a module.
check-quote-consistency=no
# This flag controls whether the implicit-str-concat should generate a warning
# on implicit string concatenation in sequences defined over several lines.
check-str-concat-over-line-jumps=no
[IMPORTS]
# List of modules that can be imported at any level, not just the top level
# one.
allow-any-import-level=
# Allow wildcard imports from modules that define __all__.
allow-wildcard-with-all=no
# Deprecated modules which should not be used, separated by a comma.
deprecated-modules=
# Output a graph (.gv or any supported image format) of external dependencies
# to the given file (report RP0402 must not be disabled).
ext-import-graph=
# Output a graph (.gv or any supported image format) of all (i.e. internal and
# external) dependencies to the given file (report RP0402 must not be
# disabled).
import-graph=
# Output a graph (.gv or any supported image format) of internal dependencies
# to the given file (report RP0402 must not be disabled).
int-import-graph=
# Force import order to recognize a module as part of the standard
# compatibility libraries.
known-standard-library=
# Force import order to recognize a module as part of a third party library.
known-third-party=enchant
# Couples of modules and preferred modules, separated by a comma.
preferred-modules=
[EXCEPTIONS]
# Exceptions that will emit a warning when caught.
overgeneral-exceptions=BaseException,
Exception
[DESIGN]
# List of regular expressions of class ancestor names to ignore when counting
# public methods (see R0903)
exclude-too-few-public-methods=
# List of qualified class names to ignore when counting class parents (see
# R0901)
ignored-parents=
# Maximum number of arguments for function / method.
max-args=5
# Maximum number of attributes for a class (see R0902).
max-attributes=7
# Maximum number of boolean expressions in an if statement (see R0916).
max-bool-expr=5
# Maximum number of branch for function / method body.
max-branches=12
# Maximum number of locals for function / method body.
max-locals=15
# Maximum number of parents for a class (see R0901).
max-parents=7
# Maximum number of public methods for a class (see R0904).
max-public-methods=20
# Maximum number of return / yield for function / method body.
max-returns=6
# Maximum number of statements in function / method body.
max-statements=50
# Minimum number of public methods for a class (see R0903).
min-public-methods=2
[METHOD_ARGS]
# List of qualified names (i.e., library.method) which require a timeout
# parameter e.g. 'requests.api.get,requests.api.post'
timeout-methods=requests.api.delete,requests.api.get,requests.api.head,requests.api.options,requests.api.patch,requests.api.post,requests.api.put,requests.api.request
[LOGGING]
# The type of string formatting that logging methods do. `old` means using %
# formatting, `new` is for `{}` formatting.
logging-format-style=old
# Logging modules to check that the string format arguments are in logging
# function parameter format.
logging-modules=logging
[FORMAT]
# Expected format of line ending, e.g. empty (any line ending), LF or CRLF.
expected-line-ending-format=
# Regexp for a line that is allowed to be longer than the limit.
ignore-long-lines=^\s*(# )?<?https?://\S+>?$
# Number of spaces of indent required inside a hanging or continued line.
indent-after-paren=4
# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1
# tab).
indent-string=' '
# Maximum number of characters on a single line.
max-line-length=100
# Maximum number of lines in a module.
max-module-lines=1000
# Allow the body of a class to be on the same line as the declaration if body
# contains single statement.
single-line-class-stmt=no
# Allow the body of an if to be on the same line as the test if there is no
# else.
single-line-if-stmt=no
[REFACTORING]
# Maximum number of nested blocks for function / method body
max-nested-blocks=5
# Complete name of functions that never returns. When checking for
# inconsistent-return-statements if a never returning function is called then
# it will be considered as an explicit return statement and no message will be
# printed.
never-returning-functions=sys.exit,argparse.parse_error
[TYPECHECK]
# List of decorators that produce context managers, such as
# contextlib.contextmanager. Add to this list to register other decorators that
# produce valid context managers.
contextmanager-decorators=contextlib.contextmanager
# List of members which are set dynamically and missed by pylint inference
# system, and so shouldn't trigger E1101 when accessed. Python regular
# expressions are accepted.
generated-members=
# Tells whether to warn about missing members when the owner of the attribute
# is inferred to be None.
ignore-none=yes
# This flag controls whether pylint should warn about no-member and similar
# checks whenever an opaque object is returned when inferring. The inference
# can return multiple potential results while evaluating a Python object, but
# some branches might not be evaluated, which results in partial inference. In
# that case, it might be useful to still emit no-member and other checks for
# the rest of the inferred objects.
ignore-on-opaque-inference=yes
# List of symbolic message names to ignore for Mixin members.
ignored-checks-for-mixins=no-member,
not-async-context-manager,
not-context-manager,
attribute-defined-outside-init
# List of class names for which member attributes should not be checked (useful
# for classes with dynamically set attributes). This supports the use of
# qualified names.
ignored-classes=optparse.Values,thread._local,_thread._local,argparse.Namespace
# Show a hint with possible names when a member name was not found. The aspect
# of finding the hint is based on edit distance.
missing-member-hint=yes
# The minimum edit distance a name should have in order to be considered a
# similar match for a missing member name.
missing-member-hint-distance=1
# The total number of similar names that should be taken in consideration when
# showing a hint for a missing member.
missing-member-max-choices=1
# Regex pattern to define which classes are considered mixins.
mixin-class-rgx=.*[Mm]ixin
# List of decorators that change the signature of a decorated function.
signature-mutators=
[CLASSES]
# Warn about protected attribute access inside special methods
check-protected-access-in-special-methods=no
# List of method names used to declare (i.e. assign) instance attributes.
defining-attr-methods=__init__,
__new__,
setUp,
__post_init__
# List of member names, which should be excluded from the protected access
# warning.
exclude-protected=_asdict,
_fields,
_replace,
_source,
_make
# List of valid names for the first argument in a class method.
valid-classmethod-first-arg=cls
# List of valid names for the first argument in a metaclass class method.
valid-metaclass-classmethod-first-arg=cls
[SPELLING]
# Limits count of emitted suggestions for spelling mistakes.
max-spelling-suggestions=4
# Spelling dictionary name. Available dictionaries: none. To make it work,
# install the 'python-enchant' package.
spelling-dict=
# List of comma separated words that should be considered directives if they
# appear at the beginning of a comment and should not be checked.
spelling-ignore-comment-directives=fmt: on,fmt: off,noqa:,noqa,nosec,isort:skip,mypy:
# List of comma separated words that should not be checked.
spelling-ignore-words=
# A path to a file that contains the private dictionary; one word per line.
spelling-private-dict-file=
# Tells whether to store unknown words to the private dictionary (see the
# --spelling-private-dict-file option) instead of raising a message.
spelling-store-unknown-words=no
+4 -2
View File
@@ -1,9 +1,11 @@
This is a Python module for Vosk.
Vosk is an offline open source speech recognition toolkit. It enables
speech recognition models for 17 languages and dialects - English, Indian
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.
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
File diff suppressed because one or more lines are too long
+558
View File
@@ -0,0 +1,558 @@
{
"nbformat": 4,
"nbformat_minor": 0,
"metadata": {
"colab": {
"name": "Vosk Adaptation",
"provenance": [],
"collapsed_sections": []
},
"kernelspec": {
"name": "python3",
"display_name": "Python 3"
},
"language_info": {
"name": "python"
},
"accelerator": "GPU",
"gpuClass": "standard"
},
"cells": [
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "URzWMmv50-Ba",
"outputId": "0e096a99-74dd-42e2-efb1-9cba784c3664"
},
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"/content\n",
"--2022-08-17 09:48:52-- https://alphacephei.com/vosk-colab/kaldi.tar.gz\n",
"Resolving alphacephei.com (alphacephei.com)... 188.40.21.16, 2a01:4f8:13a:279f::2\n",
"Connecting to alphacephei.com (alphacephei.com)|188.40.21.16|:443... connected.\n",
"HTTP request sent, awaiting response... 200 OK\n",
"Length: 809174554 (772M) [application/octet-stream]\n",
"Saving to: kaldi.tar.gz\n",
"\n",
"kaldi.tar.gz 100%[===================>] 771.69M 20.3MB/s in 40s \n",
"\n",
"2022-08-17 09:49:33 (19.4 MB/s) - kaldi.tar.gz saved [809174554/809174554]\n",
"\n"
]
}
],
"source": [
"%cd /content\n",
"!wget -c https://alphacephei.com/vosk-colab/kaldi.tar.gz\n",
"!tar xzf kaldi.tar.gz"
]
},
{
"cell_type": "code",
"source": [
"%cd /content/kaldi/egs/ac\n",
"!wget -c https://alphacephei.com/vosk-colab/vosk-model-small-en-us-0.15-compile-colab.tar.gz\n",
"!rm -rf vosk-model-small-en-us-0.15-compile-colab\n",
"!tar xf vosk-model-small-en-us-0.15-compile-colab.tar.gz"
],
"metadata": {
"id": "-065p7WC2SHh",
"colab": {
"base_uri": "https://localhost:8080/"
},
"outputId": "241c7473-7464-48d5-b48d-dc6e3bf4971d"
},
"execution_count": 8,
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"/content/kaldi/egs/ac\n",
"--2022-08-17 10:28:26-- https://alphacephei.com/vosk-colab/vosk-model-small-en-us-0.15-compile-colab.tar.gz\n",
"Resolving alphacephei.com (alphacephei.com)... 188.40.21.16, 2a01:4f8:13a:279f::2\n",
"Connecting to alphacephei.com (alphacephei.com)|188.40.21.16|:443... connected.\n",
"HTTP request sent, awaiting response... 200 OK\n",
"Length: 59618100 (57M) [application/octet-stream]\n",
"Saving to: vosk-model-small-en-us-0.15-compile-colab.tar.gz\n",
"\n",
"vosk-model-small-en 100%[===================>] 56.86M 18.6MB/s in 3.6s \n",
"\n",
"2022-08-17 10:28:30 (15.7 MB/s) - vosk-model-small-en-us-0.15-compile-colab.tar.gz saved [59618100/59618100]\n",
"\n"
]
}
]
},
{
"cell_type": "code",
"source": [
"%cd /content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab\n",
"!ls\n",
"!cat compile-graph.sh\n",
"!bash compile-graph.sh"
],
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "wuDjvNbd2sf9",
"outputId": "34a1d2fe-d443-4574-e25d-824e38eb3a78"
},
"execution_count": 9,
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab\n",
"compile-graph.sh data_test decode.sh\texp\t local path.sh steps\n",
"conf\t\t db\t dict.py\tget_vocab.py mfcc RESULTS utils\n",
"#!/bin/bash\n",
"\n",
"set -x\n",
"\n",
". path.sh\n",
"\n",
"pip3 install phonetisaurus\n",
"\n",
"rm -rf data\n",
"rm -rf exp/tdnn/lgraph\n",
"rm -rf exp/tdnn/lgraph_orig\n",
"\n",
"mkdir -p data/dict\n",
"cp db/phone/* data/dict\n",
"./dict.py > data/dict/lexicon.txt\n",
"\n",
"python3 ./get_vocab.py > data/mix.vocab\n",
"ngramsymbols data/mix.vocab data/mix.syms\n",
"farcompilestrings --fst_type=compact --symbols=data/mix.syms --keep_symbols --unknown_symbol=\"[unk]\" db/extra.txt data/extra.far\n",
"ngramcount --order=3 data/extra.far - |\n",
" ngramprint --integers | grep -v \"<unk>\" | ngramread |\n",
" ngramshrink --method=count_prune --count_pattern=\"3+:3\" |\n",
" ngrammake --method=witten_bell - data/extra.mod\n",
"gunzip -c db/en-50k-0.4-android.lm.gz | ngramread --renormalize_arpa --ARPA --symbols=data/mix.syms - data/en-us.mod\n",
"ngrammerge --method=\"bayes_model_merge\" --normalize --alpha=0.95 --beta=0.05 data/en-us.mod data/extra.mod data/en-us-mix.mod\n",
"ngramprint --ARPA data/en-us-mix.mod | gzip -c > data/en-us-mix.lm.gz\n",
"\n",
"# Prune for the first stage if needed\n",
"# ngramshrink --method=relative_entropy --theta=2e-8 data/en-us-mix.mod data/en-us-mix-prune.mod\n",
"# ngramprint --ARPA data/en-us-mix-prune.mod | gzip -c > data/en-us-mix-small.lm.gz\n",
"\n",
"utils/prepare_lang.sh data/dict \"[unk]\" data/lang_local data/lang\n",
"utils/format_lm.sh data/lang db/en-50k-0.4-android.lm.gz data/dict/lexicon.txt data/lang_test\n",
"utils/format_lm.sh data/lang data/en-us-mix.lm.gz data/dict/lexicon.txt data/lang_test_adapt\n",
"\n",
"utils/mkgraph.sh --self-loop-scale 1.0 data/lang_test exp/tdnn exp/tdnn/graph\n",
"utils/mkgraph.sh --self-loop-scale 1.0 data/lang_test_adapt exp/tdnn exp/tdnn/graph_adapt\n",
"\n",
"# Lookahead part goes OOM\n",
"#utils/mkgraph_lookahead.sh \\\n",
"# --self-loop-scale 1.0 data/lang \\\n",
"# exp/tdnn data/en-us-mix.lm.gz exp/tdnn/lgraph\n",
"#utils/mkgraph_lookahead.sh \\\n",
"# --self-loop-scale 1.0 data/lang \\\n",
"# exp/tdnn db/en-50k-0.4-android.lm.gz exp/tdnn/lgraph_orig\n",
"+ . path.sh\n",
"+++ pwd\n",
"++ export KALDI_ROOT=/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../..\n",
"++ KALDI_ROOT=/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../..\n",
"++ export PATH=/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/utils:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../../src/bin:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../../tools/openfst/bin:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../../src/fstbin:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../../src/gmmbin:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../../src/featbin:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../../src/lm:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../../src/sgmmbin:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../../src/sgmm2bin:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../../src/fgmmbin:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../../src/latbin:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../../src/nnetbin:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../../src/nnet2bin:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../../src/online2bin:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../../src/ivectorbin:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../../src/lmbin:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../../src/chainbin:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../../src/nnet3bin:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab:/opt/bin:/usr/local/nvidia/bin:/usr/local/cuda/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/tools/node/bin:/tools/google-cloud-sdk/bin:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../../tools/sph2pipe_v2.5\n",
"++ PATH=/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/utils:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../../src/bin:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../../tools/openfst/bin:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../../src/fstbin:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../../src/gmmbin:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../../src/featbin:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../../src/lm:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../../src/sgmmbin:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../../src/sgmm2bin:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../../src/fgmmbin:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../../src/latbin:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../../src/nnetbin:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../../src/nnet2bin:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../../src/online2bin:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../../src/ivectorbin:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../../src/lmbin:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../../src/chainbin:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../../src/nnet3bin:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab:/opt/bin:/usr/local/nvidia/bin:/usr/local/cuda/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/tools/node/bin:/tools/google-cloud-sdk/bin:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../../tools/sph2pipe_v2.5\n",
"++ export PATH=/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../../tools/ngram-1.3.7/src/bin:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/utils:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../../src/bin:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../../tools/openfst/bin:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../../src/fstbin:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../../src/gmmbin:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../../src/featbin:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../../src/lm:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../../src/sgmmbin:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../../src/sgmm2bin:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../../src/fgmmbin:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../../src/latbin:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../../src/nnetbin:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../../src/nnet2bin:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../../src/online2bin:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../../src/ivectorbin:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../../src/lmbin:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../../src/chainbin:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../../src/nnet3bin:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab:/opt/bin:/usr/local/nvidia/bin:/usr/local/cuda/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/tools/node/bin:/tools/google-cloud-sdk/bin:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../../tools/sph2pipe_v2.5\n",
"++ PATH=/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../../tools/ngram-1.3.7/src/bin:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/utils:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../../src/bin:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../../tools/openfst/bin:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../../src/fstbin:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../../src/gmmbin:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../../src/featbin:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../../src/lm:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../../src/sgmmbin:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../../src/sgmm2bin:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../../src/fgmmbin:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../../src/latbin:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../../src/nnetbin:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../../src/nnet2bin:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../../src/online2bin:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../../src/ivectorbin:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../../src/lmbin:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../../src/chainbin:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../../src/nnet3bin:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab:/opt/bin:/usr/local/nvidia/bin:/usr/local/cuda/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/tools/node/bin:/tools/google-cloud-sdk/bin:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../../tools/sph2pipe_v2.5\n",
"++ export LD_LIBRARY_PATH=/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../../tools/openfst/lib/fst/\n",
"++ LD_LIBRARY_PATH=/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../../tools/openfst/lib/fst/\n",
"++ export LC_ALL=C\n",
"++ LC_ALL=C\n",
"+ pip3 install phonetisaurus\n",
"Looking in indexes: https://pypi.org/simple, https://us-python.pkg.dev/colab-wheels/public/simple/\n",
"Requirement already satisfied: phonetisaurus in /usr/local/lib/python3.7/dist-packages (0.3.0)\n",
"+ rm -rf data\n",
"+ rm -rf exp/tdnn/lgraph\n",
"+ rm -rf exp/tdnn/lgraph_orig\n",
"+ mkdir -p data/dict\n",
"+ cp db/phone/extra_questions.txt db/phone/nonsilence_phones.txt db/phone/optional_silence.txt db/phone/silence_phones.txt data/dict\n",
"+ ./dict.py\n",
"+ python3 ./get_vocab.py\n",
"+ ngramsymbols data/mix.vocab data/mix.syms\n",
"+ farcompilestrings --fst_type=compact --symbols=data/mix.syms --keep_symbols '--unknown_symbol=[unk]' db/extra.txt data/extra.far\n",
"+ ngramcount --order=3 data/extra.far -\n",
"+ ngrammake --method=witten_bell - data/extra.mod\n",
"+ ngramshrink --method=count_prune --count_pattern=3+:3\n",
"+ ngramread\n",
"+ ngramprint --integers\n",
"+ grep -v '<unk>'\n",
"+ ngramread --renormalize_arpa --ARPA --symbols=data/mix.syms - data/en-us.mod\n",
"+ gunzip -c db/en-50k-0.4-android.lm.gz\n",
"+ ngrammerge --method=bayes_model_merge --normalize --alpha=0.95 --beta=0.05 data/en-us.mod data/extra.mod data/en-us-mix.mod\n",
"+ ngramprint --ARPA data/en-us-mix.mod\n",
"+ gzip -c\n",
"+ utils/prepare_lang.sh data/dict '[unk]' data/lang_local data/lang\n",
"utils/prepare_lang.sh data/dict [unk] data/lang_local data/lang\n",
"Checking data/dict/silence_phones.txt ...\n",
"--> reading data/dict/silence_phones.txt\n",
"--> text seems to be UTF-8 or ASCII, checking whitespaces\n",
"--> text contains only allowed whitespaces\n",
"--> data/dict/silence_phones.txt is OK\n",
"\n",
"Checking data/dict/optional_silence.txt ...\n",
"--> reading data/dict/optional_silence.txt\n",
"--> text seems to be UTF-8 or ASCII, checking whitespaces\n",
"--> text contains only allowed whitespaces\n",
"--> data/dict/optional_silence.txt is OK\n",
"\n",
"Checking data/dict/nonsilence_phones.txt ...\n",
"--> reading data/dict/nonsilence_phones.txt\n",
"--> text seems to be UTF-8 or ASCII, checking whitespaces\n",
"--> text contains only allowed whitespaces\n",
"--> data/dict/nonsilence_phones.txt is OK\n",
"\n",
"Checking disjoint: silence_phones.txt, nonsilence_phones.txt\n",
"--> disjoint property is OK.\n",
"\n",
"Checking data/dict/lexicon.txt\n",
"--> reading data/dict/lexicon.txt\n",
"--> text seems to be UTF-8 or ASCII, checking whitespaces\n",
"--> text contains only allowed whitespaces\n",
"--> data/dict/lexicon.txt is OK\n",
"\n",
"Checking data/dict/extra_questions.txt ...\n",
"--> data/dict/extra_questions.txt is empty (this is OK)\n",
"--> SUCCESS [validating dictionary directory data/dict]\n",
"\n",
"**Creating data/dict/lexiconp.txt from data/dict/lexicon.txt\n",
"fstaddselfloops data/lang/phones/wdisambig_phones.int data/lang/phones/wdisambig_words.int \n",
"prepare_lang.sh: validating output directory\n",
"utils/validate_lang.pl data/lang\n",
"Checking existence of separator file\n",
"separator file data/lang/subword_separator.txt is empty or does not exist, deal in word case.\n",
"Checking data/lang/phones.txt ...\n",
"--> text seems to be UTF-8 or ASCII, checking whitespaces\n",
"--> text contains only allowed whitespaces\n",
"--> data/lang/phones.txt is OK\n",
"\n",
"Checking words.txt: #0 ...\n",
"--> text seems to be UTF-8 or ASCII, checking whitespaces\n",
"--> text contains only allowed whitespaces\n",
"--> data/lang/words.txt is OK\n",
"\n",
"Checking disjoint: silence.txt, nonsilence.txt, disambig.txt ...\n",
"--> silence.txt and nonsilence.txt are disjoint\n",
"--> silence.txt and disambig.txt are disjoint\n",
"--> disambig.txt and nonsilence.txt are disjoint\n",
"--> disjoint property is OK\n",
"\n",
"Checking sumation: silence.txt, nonsilence.txt, disambig.txt ...\n",
"--> found no unexplainable phones in phones.txt\n",
"\n",
"Checking data/lang/phones/context_indep.{txt, int, csl} ...\n",
"--> text seems to be UTF-8 or ASCII, checking whitespaces\n",
"--> text contains only allowed whitespaces\n",
"--> 10 entry/entries in data/lang/phones/context_indep.txt\n",
"--> data/lang/phones/context_indep.int corresponds to data/lang/phones/context_indep.txt\n",
"--> data/lang/phones/context_indep.csl corresponds to data/lang/phones/context_indep.txt\n",
"--> data/lang/phones/context_indep.{txt, int, csl} are OK\n",
"\n",
"Checking data/lang/phones/nonsilence.{txt, int, csl} ...\n",
"--> text seems to be UTF-8 or ASCII, checking whitespaces\n",
"--> text contains only allowed whitespaces\n",
"--> 156 entry/entries in data/lang/phones/nonsilence.txt\n",
"--> data/lang/phones/nonsilence.int corresponds to data/lang/phones/nonsilence.txt\n",
"--> data/lang/phones/nonsilence.csl corresponds to data/lang/phones/nonsilence.txt\n",
"--> data/lang/phones/nonsilence.{txt, int, csl} are OK\n",
"\n",
"Checking data/lang/phones/silence.{txt, int, csl} ...\n",
"--> text seems to be UTF-8 or ASCII, checking whitespaces\n",
"--> text contains only allowed whitespaces\n",
"--> 10 entry/entries in data/lang/phones/silence.txt\n",
"--> data/lang/phones/silence.int corresponds to data/lang/phones/silence.txt\n",
"--> data/lang/phones/silence.csl corresponds to data/lang/phones/silence.txt\n",
"--> data/lang/phones/silence.{txt, int, csl} are OK\n",
"\n",
"Checking data/lang/phones/optional_silence.{txt, int, csl} ...\n",
"--> text seems to be UTF-8 or ASCII, checking whitespaces\n",
"--> text contains only allowed whitespaces\n",
"--> 1 entry/entries in data/lang/phones/optional_silence.txt\n",
"--> data/lang/phones/optional_silence.int corresponds to data/lang/phones/optional_silence.txt\n",
"--> data/lang/phones/optional_silence.csl corresponds to data/lang/phones/optional_silence.txt\n",
"--> data/lang/phones/optional_silence.{txt, int, csl} are OK\n",
"\n",
"Checking data/lang/phones/disambig.{txt, int, csl} ...\n",
"--> text seems to be UTF-8 or ASCII, checking whitespaces\n",
"--> text contains only allowed whitespaces\n",
"--> 14 entry/entries in data/lang/phones/disambig.txt\n",
"--> data/lang/phones/disambig.int corresponds to data/lang/phones/disambig.txt\n",
"--> data/lang/phones/disambig.csl corresponds to data/lang/phones/disambig.txt\n",
"--> data/lang/phones/disambig.{txt, int, csl} are OK\n",
"\n",
"Checking data/lang/phones/roots.{txt, int} ...\n",
"--> text seems to be UTF-8 or ASCII, checking whitespaces\n",
"--> text contains only allowed whitespaces\n",
"--> 41 entry/entries in data/lang/phones/roots.txt\n",
"--> data/lang/phones/roots.int corresponds to data/lang/phones/roots.txt\n",
"--> data/lang/phones/roots.{txt, int} are OK\n",
"\n",
"Checking data/lang/phones/sets.{txt, int} ...\n",
"--> text seems to be UTF-8 or ASCII, checking whitespaces\n",
"--> text contains only allowed whitespaces\n",
"--> 41 entry/entries in data/lang/phones/sets.txt\n",
"--> data/lang/phones/sets.int corresponds to data/lang/phones/sets.txt\n",
"--> data/lang/phones/sets.{txt, int} are OK\n",
"\n",
"Checking data/lang/phones/extra_questions.{txt, int} ...\n",
"--> text seems to be UTF-8 or ASCII, checking whitespaces\n",
"--> text contains only allowed whitespaces\n",
"--> 9 entry/entries in data/lang/phones/extra_questions.txt\n",
"--> data/lang/phones/extra_questions.int corresponds to data/lang/phones/extra_questions.txt\n",
"--> data/lang/phones/extra_questions.{txt, int} are OK\n",
"\n",
"Checking data/lang/phones/word_boundary.{txt, int} ...\n",
"--> text seems to be UTF-8 or ASCII, checking whitespaces\n",
"--> text contains only allowed whitespaces\n",
"--> 166 entry/entries in data/lang/phones/word_boundary.txt\n",
"--> data/lang/phones/word_boundary.int corresponds to data/lang/phones/word_boundary.txt\n",
"--> data/lang/phones/word_boundary.{txt, int} are OK\n",
"\n",
"Checking optional_silence.txt ...\n",
"--> reading data/lang/phones/optional_silence.txt\n",
"--> data/lang/phones/optional_silence.txt is OK\n",
"\n",
"Checking disambiguation symbols: #0 and #1\n",
"--> data/lang/phones/disambig.txt has \"#0\" and \"#1\"\n",
"--> data/lang/phones/disambig.txt is OK\n",
"\n",
"Checking topo ...\n",
"\n",
"Checking word_boundary.txt: silence.txt, nonsilence.txt, disambig.txt ...\n",
"--> data/lang/phones/word_boundary.txt doesn't include disambiguation symbols\n",
"--> data/lang/phones/word_boundary.txt is the union of nonsilence.txt and silence.txt\n",
"--> data/lang/phones/word_boundary.txt is OK\n",
"\n",
"Checking word-level disambiguation symbols...\n",
"--> data/lang/phones/wdisambig.txt exists (newer prepare_lang.sh)\n",
"Checking word_boundary.int and disambig.int\n",
"--> generating a 98 word/subword sequence\n",
"--> resulting phone sequence from L.fst corresponds to the word sequence\n",
"--> L.fst is OK\n",
"--> generating a 49 word/subword sequence\n",
"--> resulting phone sequence from L_disambig.fst corresponds to the word sequence\n",
"--> L_disambig.fst is OK\n",
"\n",
"Checking data/lang/oov.{txt, int} ...\n",
"--> text seems to be UTF-8 or ASCII, checking whitespaces\n",
"--> text contains only allowed whitespaces\n",
"--> 1 entry/entries in data/lang/oov.txt\n",
"--> data/lang/oov.int corresponds to data/lang/oov.txt\n",
"--> data/lang/oov.{txt, int} are OK\n",
"\n",
"--> data/lang/L.fst is olabel sorted\n",
"--> data/lang/L_disambig.fst is olabel sorted\n",
"--> SUCCESS [validating lang directory data/lang]\n",
"+ utils/format_lm.sh data/lang db/en-50k-0.4-android.lm.gz data/dict/lexicon.txt data/lang_test\n",
"Converting 'db/en-50k-0.4-android.lm.gz' to FST\n",
"arpa2fst --disambig-symbol=#0 --read-symbol-table=data/lang_test/words.txt - data/lang_test/G.fst \n",
"LOG (arpa2fst[5.5.1046~1-76cd5]:Read():arpa-file-parser.cc:94) Reading \\data\\ section.\n",
"LOG (arpa2fst[5.5.1046~1-76cd5]:Read():arpa-file-parser.cc:149) Reading \\1-grams: section.\n",
"LOG (arpa2fst[5.5.1046~1-76cd5]:Read():arpa-file-parser.cc:149) Reading \\2-grams: section.\n",
"LOG (arpa2fst[5.5.1046~1-76cd5]:Read():arpa-file-parser.cc:149) Reading \\3-grams: section.\n",
"LOG (arpa2fst[5.5.1046~1-76cd5]:RemoveRedundantStates():arpa-lm-compiler.cc:359) Reduced num-states from 1217362 to 185036\n",
"fstisstochastic data/lang_test/G.fst \n",
"0.476411 -3.03779\n",
"Succeeded in formatting LM: 'db/en-50k-0.4-android.lm.gz'\n",
"+ utils/format_lm.sh data/lang data/en-us-mix.lm.gz data/dict/lexicon.txt data/lang_test_adapt\n",
"Converting 'data/en-us-mix.lm.gz' to FST\n",
"arpa2fst --disambig-symbol=#0 --read-symbol-table=data/lang_test_adapt/words.txt - data/lang_test_adapt/G.fst \n",
"LOG (arpa2fst[5.5.1046~1-76cd5]:Read():arpa-file-parser.cc:94) Reading \\data\\ section.\n",
"LOG (arpa2fst[5.5.1046~1-76cd5]:Read():arpa-file-parser.cc:149) Reading \\1-grams: section.\n",
"LOG (arpa2fst[5.5.1046~1-76cd5]:Read():arpa-file-parser.cc:149) Reading \\2-grams: section.\n",
"LOG (arpa2fst[5.5.1046~1-76cd5]:Read():arpa-file-parser.cc:149) Reading \\3-grams: section.\n",
"LOG (arpa2fst[5.5.1046~1-76cd5]:RemoveRedundantStates():arpa-lm-compiler.cc:359) Reduced num-states from 1217646 to 185095\n",
"fstisstochastic data/lang_test_adapt/G.fst \n",
"6.81902e-07 -3.03779\n",
"Succeeded in formatting LM: 'data/en-us-mix.lm.gz'\n",
"+ utils/mkgraph.sh --self-loop-scale 1.0 data/lang_test exp/tdnn exp/tdnn/graph\n",
"tree-info exp/tdnn/tree \n",
"tree-info exp/tdnn/tree \n",
"fstdeterminizestar --use-log=true \n",
"fsttablecompose data/lang_test/L_disambig.fst data/lang_test/G.fst \n",
"fstminimizeencoded \n",
"fstpushspecial \n",
"fstisstochastic data/lang_test/tmp/LG.fst \n",
"-0.145498 -0.146281\n",
"[info]: LG not stochastic.\n",
"fstcomposecontext --context-size=2 --central-position=1 --read-disambig-syms=data/lang_test/phones/disambig.int --write-disambig-syms=data/lang_test/tmp/disambig_ilabels_2_1.int data/lang_test/tmp/ilabels_2_1.905 data/lang_test/tmp/LG.fst \n",
"fstisstochastic data/lang_test/tmp/CLG_2_1.fst \n",
"-0.145498 -0.146281\n",
"[info]: CLG not stochastic.\n",
"make-h-transducer --disambig-syms-out=exp/tdnn/graph/disambig_tid.int --transition-scale=1.0 data/lang_test/tmp/ilabels_2_1 exp/tdnn/tree exp/tdnn/final.mdl \n",
"fstrmepslocal \n",
"fsttablecompose exp/tdnn/graph/Ha.fst data/lang_test/tmp/CLG_2_1.fst \n",
"fstdeterminizestar --use-log=true \n",
"fstminimizeencoded \n",
"fstrmsymbols exp/tdnn/graph/disambig_tid.int \n",
"fstisstochastic exp/tdnn/graph/HCLGa.fst \n",
"-0.109817 -0.571742\n",
"HCLGa is not stochastic\n",
"add-self-loops --self-loop-scale=1.0 --reorder=true exp/tdnn/final.mdl exp/tdnn/graph/HCLGa.fst \n",
"fstisstochastic exp/tdnn/graph/HCLG.fst \n",
"1.90465e-09 -0.415046\n",
"[info]: final HCLG is not stochastic.\n",
"+ utils/mkgraph.sh --self-loop-scale 1.0 data/lang_test_adapt exp/tdnn exp/tdnn/graph_adapt\n",
"tree-info exp/tdnn/tree \n",
"tree-info exp/tdnn/tree \n",
"fstdeterminizestar --use-log=true \n",
"fsttablecompose data/lang_test_adapt/L_disambig.fst data/lang_test_adapt/G.fst \n",
"fstminimizeencoded \n",
"fstpushspecial \n",
"fstisstochastic data/lang_test_adapt/tmp/LG.fst \n",
"-0.148474 -0.149181\n",
"[info]: LG not stochastic.\n",
"fstcomposecontext --context-size=2 --central-position=1 --read-disambig-syms=data/lang_test_adapt/phones/disambig.int --write-disambig-syms=data/lang_test_adapt/tmp/disambig_ilabels_2_1.int data/lang_test_adapt/tmp/ilabels_2_1.979 data/lang_test_adapt/tmp/LG.fst \n",
"fstisstochastic data/lang_test_adapt/tmp/CLG_2_1.fst \n",
"-0.148474 -0.149181\n",
"[info]: CLG not stochastic.\n",
"make-h-transducer --disambig-syms-out=exp/tdnn/graph_adapt/disambig_tid.int --transition-scale=1.0 data/lang_test_adapt/tmp/ilabels_2_1 exp/tdnn/tree exp/tdnn/final.mdl \n",
"fstrmepslocal \n",
"fsttablecompose exp/tdnn/graph_adapt/Ha.fst data/lang_test_adapt/tmp/CLG_2_1.fst \n",
"fstdeterminizestar --use-log=true \n",
"fstminimizeencoded \n",
"fstrmsymbols exp/tdnn/graph_adapt/disambig_tid.int \n",
"fstisstochastic exp/tdnn/graph_adapt/HCLGa.fst \n",
"-0.113907 -0.5857\n",
"HCLGa is not stochastic\n",
"add-self-loops --self-loop-scale=1.0 --reorder=true exp/tdnn/final.mdl exp/tdnn/graph_adapt/HCLGa.fst \n",
"fstisstochastic exp/tdnn/graph_adapt/HCLG.fst \n",
"1.90465e-09 -0.423618\n",
"[info]: final HCLG is not stochastic.\n"
]
}
]
},
{
"cell_type": "code",
"source": [
"!cat decode.sh\n",
"!bash decode.sh"
],
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "Sl3QBI1MXpc-",
"outputId": "affac8a3-782f-4000-e31f-81bfed47a37a"
},
"execution_count": 10,
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"#!/bin/bash\n",
"\n",
". path.sh\n",
"\n",
"steps/make_mfcc.sh --nj 10 data_test/test_small exp/make_mfcc/test mfcc\n",
"steps/compute_cmvn_stats.sh data_test/test_small exp/make_mfcc/test mfcc\n",
"utils/fix_data_dir.sh data_test/test_small\n",
"\n",
"steps/online/nnet2/extract_ivectors_online.sh --nj 4 \\\n",
" data_test/test_small exp/extractor \\\n",
" exp/ivectors_test\n",
"\n",
"steps/nnet3/decode.sh --nj 4 \\\n",
" --acwt 1.0 --post-decode-acwt 10.0 \\\n",
" --online-ivector-dir exp/ivectors_test \\\n",
" exp/tdnn/graph_adapt data_test/test_small exp/tdnn/decode_test_adapt\n",
"\n",
"steps/nnet3/decode.sh --nj 4 \\\n",
" --acwt 1.0 --post-decode-acwt 10.0 \\\n",
" --online-ivector-dir exp/ivectors_test \\\n",
" exp/tdnn/graph data_test/test_small exp/tdnn/decode_test\n",
"\n",
"#steps/nnet3/decode_lookahead.sh --nj 4 \\\n",
"# --acwt 1.0 --post-decode-acwt 10.0 \\\n",
"# --online-ivector-dir exp/ivectors_test \\\n",
"# exp/tdnn/lgraph data_test/test_small exp/tdnn/decode_test_adapt\n",
"#steps/nnet3/decode_lookahead.sh --nj 4 \\\n",
"# --acwt 1.0 --post-decode-acwt 10.0 \\\n",
"# --online-ivector-dir exp/ivectors_test \\\n",
"# exp/tdnn/lgraph_orig data_test/test_small exp/tdnn/decode_test\n",
"steps/make_mfcc.sh --nj 10 data_test/test_small exp/make_mfcc/test mfcc\n",
"steps/make_mfcc.sh: moving data_test/test_small/feats.scp to data_test/test_small/.backup\n",
"utils/validate_data_dir.sh: Successfully validated data-directory data_test/test_small\n",
"steps/make_mfcc.sh: [info]: no segments file exists: assuming wav.scp indexed by utterance.\n",
"steps/make_mfcc.sh: Succeeded creating MFCC features for test_small\n",
"steps/compute_cmvn_stats.sh data_test/test_small exp/make_mfcc/test mfcc\n",
"Succeeded creating CMVN stats for test_small\n",
"fix_data_dir.sh: kept all 50 utterances.\n",
"fix_data_dir.sh: old files are kept in data_test/test_small/.backup\n",
"steps/online/nnet2/extract_ivectors_online.sh --nj 4 data_test/test_small exp/extractor exp/ivectors_test\n",
"steps/online/nnet2/extract_ivectors_online.sh: extracting iVectors\n",
"steps/online/nnet2/extract_ivectors_online.sh: combining iVectors across jobs\n",
"steps/online/nnet2/extract_ivectors_online.sh: done extracting (online) iVectors to exp/ivectors_test using the extractor in exp/extractor.\n",
"steps/nnet3/decode.sh --nj 4 --acwt 1.0 --post-decode-acwt 10.0 --online-ivector-dir exp/ivectors_test exp/tdnn/graph_adapt data_test/test_small exp/tdnn/decode_test_adapt\n",
"steps/nnet2/check_ivectors_compatible.sh: WARNING: One of the directories do not contain iVector ID.\n",
"steps/nnet2/check_ivectors_compatible.sh: WARNING: That means it's you who's reponsible for keeping \n",
"steps/nnet2/check_ivectors_compatible.sh: WARNING: the directories compatible\n",
"steps/nnet3/decode.sh: feature type is raw\n",
"steps/diagnostic/analyze_lats.sh --cmd run.pl --iter final exp/tdnn/graph_adapt exp/tdnn/decode_test_adapt\n",
"steps/diagnostic/analyze_lats.sh: see stats in exp/tdnn/decode_test_adapt/log/analyze_alignments.log\n",
"Overall, lattice depth (10,50,90-percentile)=(1,1,4) and mean=2.4\n",
"steps/diagnostic/analyze_lats.sh: see stats in exp/tdnn/decode_test_adapt/log/analyze_lattice_depth_stats.log\n",
"score best paths\n",
"local/score.sh --cmd run.pl data_test/test_small exp/tdnn/graph_adapt exp/tdnn/decode_test_adapt\n",
"local/score.sh: scoring with word insertion penalty=0.0,0.5,1.0\n",
"score confidence and timing with sclite\n",
"Decoding done.\n",
"steps/nnet3/decode.sh --nj 4 --acwt 1.0 --post-decode-acwt 10.0 --online-ivector-dir exp/ivectors_test exp/tdnn/graph data_test/test_small exp/tdnn/decode_test\n",
"steps/nnet2/check_ivectors_compatible.sh: WARNING: One of the directories do not contain iVector ID.\n",
"steps/nnet2/check_ivectors_compatible.sh: WARNING: That means it's you who's reponsible for keeping \n",
"steps/nnet2/check_ivectors_compatible.sh: WARNING: the directories compatible\n",
"steps/nnet3/decode.sh: feature type is raw\n",
"steps/diagnostic/analyze_lats.sh --cmd run.pl --iter final exp/tdnn/graph exp/tdnn/decode_test\n",
"steps/diagnostic/analyze_lats.sh: see stats in exp/tdnn/decode_test/log/analyze_alignments.log\n",
"Overall, lattice depth (10,50,90-percentile)=(1,5,23) and mean=10.4\n",
"steps/diagnostic/analyze_lats.sh: see stats in exp/tdnn/decode_test/log/analyze_lattice_depth_stats.log\n",
"score best paths\n",
"local/score.sh --cmd run.pl data_test/test_small exp/tdnn/graph exp/tdnn/decode_test\n",
"local/score.sh: scoring with word insertion penalty=0.0,0.5,1.0\n",
"score confidence and timing with sclite\n",
"Decoding done.\n"
]
}
]
},
{
"cell_type": "code",
"source": [
"!bash RESULTS"
],
"metadata": {
"id": "ABtcNyUDX4S8",
"outputId": "d5e50be7-3293-4a59-94b8-9bfa46736481",
"colab": {
"base_uri": "https://localhost:8080/"
}
},
"execution_count": 11,
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"%WER 11.77 [ 107 / 909, 13 ins, 7 del, 87 sub ] exp/tdnn/decode_test/wer_7_1.0\n",
"%WER 0.22 [ 2 / 909, 0 ins, 1 del, 1 sub ] exp/tdnn/decode_test_adapt/wer_10_1.0\n"
]
}
]
}
]
}
File diff suppressed because it is too large Load Diff
File diff suppressed because one or more lines are too long
+6 -10
View File
@@ -1,23 +1,19 @@
#!/usr/bin/env python3
from vosk import Model, KaldiRecognizer, SetLogLevel
import sys
import os
import wave
import sys
import json
from vosk import Model, KaldiRecognizer, SetLogLevel
SetLogLevel(0)
if not os.path.exists("model"):
print ("Please download the model from https://alphacephei.com/vosk/models and unpack as 'model' in the current folder.")
exit (1)
wf = wave.open(sys.argv[1], "rb")
if wf.getnchannels() != 1 or wf.getsampwidth() != 2 or wf.getcomptype() != "NONE":
print ("Audio file must be WAV format mono PCM.")
exit (1)
print("Audio file must be WAV format mono PCM.")
sys.exit(1)
model = Model("model")
model = Model(lang="en-us")
rec = KaldiRecognizer(model, wf.getframerate())
rec.SetMaxAlternatives(10)
rec.SetWords(True)
+4 -4
View File
@@ -1,11 +1,11 @@
#!/usr/bin/env python3
from vosk import Model, KaldiRecognizer
import sys
import json
model = Model("model")
from vosk import Model, KaldiRecognizer
model = Model(lang="en-us")
rec = KaldiRecognizer(model, 8000)
res = json.loads(rec.FinalResult())
print (res)
print(res)
+19 -23
View File
@@ -1,33 +1,29 @@
#!/usr/bin/env python3
from vosk import Model, KaldiRecognizer, SetLogLevel
import sys
import os
import wave
import subprocess
import sys
from vosk import Model, KaldiRecognizer, SetLogLevel
SAMPLE_RATE = 16000
SetLogLevel(0)
if not os.path.exists("model"):
print ("Please download the model from https://alphacephei.com/vosk/models and unpack as 'model' in the current folder.")
exit (1)
model = Model(lang="en-us")
rec = KaldiRecognizer(model, SAMPLE_RATE)
sample_rate=16000
model = Model("model")
rec = KaldiRecognizer(model, sample_rate)
process = subprocess.Popen(['ffmpeg', '-loglevel', 'quiet', '-i',
with subprocess.Popen(["ffmpeg", "-loglevel", "quiet", "-i",
sys.argv[1],
'-ar', str(sample_rate) , '-ac', '1', '-f', 's16le', '-'],
stdout=subprocess.PIPE)
"-ar", str(SAMPLE_RATE) , "-ac", "1", "-f", "s16le", "-"],
stdout=subprocess.PIPE) as process:
while True:
data = process.stdout.read(4000)
if len(data) == 0:
break
if rec.AcceptWaveform(data):
print(rec.Result())
else:
print(rec.PartialResult())
while True:
data = process.stdout.read(4000)
if len(data) == 0:
break
if rec.AcceptWaveform(data):
print(rec.Result())
else:
print(rec.PartialResult())
print(rec.FinalResult())
print(rec.FinalResult())
+61
View File
@@ -0,0 +1,61 @@
#!/usr/bin/env python3
import sys
import json
from vosk import BatchModel, BatchRecognizer, GpuInit
from timeit import default_timer as timer
TOT_SAMPLES = 0
GpuInit()
model = BatchModel("model")
with open(sys.argv[1]) as fn:
fnames = fn.readlines()
fds = [open(x.strip(), "rb") for x in fnames]
uids = [fname.strip().split("/")[-1][:-4] for fname in fnames]
recs = [BatchRecognizer(model, 16000) for x in fnames]
results = [""] * len(fnames)
ended = set()
start_time = timer()
while True:
# Feed in the data
for i, fd in enumerate(fds):
if i in ended:
continue
data = fd.read(8000)
if len(data) == 0:
recs[i].FinishStream()
ended.add(i)
continue
recs[i].AcceptWaveform(data)
TOT_SAMPLES += len(data)
# Wait for results from CUDA
model.Wait()
# Retrieve and add results
for i, fd in enumerate(fds):
res = recs[i].Result()
if len(res) != 0:
results[i] = results[i] + " " + json.loads(res)["text"]
if len(ended) == len(fds):
break
end_time = timer()
for i, res in enumerate(results):
print(uids[i], res.strip())
print("Processed %.3f seconds of audio in %.3f seconds (%.3f xRT)"
% (TOT_SAMPLES / 16000.0 / 2,
end_time - start_time,
(TOT_SAMPLES / 16000.0 / 2 / (end_time - start_time))),
file=sys.stderr)
+40
View File
@@ -0,0 +1,40 @@
#!/usr/bin/env python3
import json
import gradio as gr
from vosk import KaldiRecognizer, Model
model = Model(lang="en-us")
def transcribe(data, state):
sample_rate, audio_data = data
audio_data = (audio_data >> 16).astype("int16").tobytes()
if state is None:
rec = KaldiRecognizer(model, sample_rate)
result = []
else:
rec, result = state
if rec.AcceptWaveform(audio_data):
text_result = json.loads(rec.Result())["text"]
if text_result != "":
result.append(text_result)
partial_result = ""
else:
partial_result = json.loads(rec.PartialResult())["partial"] + " "
return "\n".join(result) + "\n" + partial_result, (rec, result)
gr.Interface(
fn=transcribe,
inputs=[
gr.Audio(source="microphone", type="numpy", streaming=True),
"state"
],
outputs=[
"textbox",
"state"
],
live=True).launch(share=True)
+38 -38
View File
@@ -1,11 +1,15 @@
#!/usr/bin/env python3
# prerequisites: as described in https://alphacephei.com/vosk/install and also python module `sounddevice` (simply run command `pip install sounddevice`)
# Example usage using Dutch (nl) recognition model: `python test_microphone.py -m nl`
# For more help run: `python test_microphone.py -h`
import argparse
import os
import queue
import sounddevice as sd
import vosk
import sys
import sounddevice as sd
from vosk import Model, KaldiRecognizer
q = queue.Queue()
@@ -24,8 +28,8 @@ def callback(indata, frames, time, status):
parser = argparse.ArgumentParser(add_help=False)
parser.add_argument(
'-l', '--list-devices', action='store_true',
help='show list of audio devices and exit')
"-l", "--list-devices", action="store_true",
help="show list of audio devices and exit")
args, remaining = parser.parse_known_args()
if args.list_devices:
print(sd.query_devices())
@@ -35,55 +39,51 @@ parser = argparse.ArgumentParser(
formatter_class=argparse.RawDescriptionHelpFormatter,
parents=[parser])
parser.add_argument(
'-f', '--filename', type=str, metavar='FILENAME',
help='audio file to store recording to')
"-f", "--filename", type=str, metavar="FILENAME",
help="audio file to store recording to")
parser.add_argument(
'-m', '--model', type=str, metavar='MODEL_PATH',
help='Path to the model')
"-d", "--device", type=int_or_str,
help="input device (numeric ID or substring)")
parser.add_argument(
'-d', '--device', type=int_or_str,
help='input device (numeric ID or substring)')
"-r", "--samplerate", type=int, help="sampling rate")
parser.add_argument(
'-r', '--samplerate', type=int, help='sampling rate')
"-m", "--model", type=str, help="language model; e.g. en-us, fr, nl; default is en-us")
args = parser.parse_args(remaining)
try:
if args.model is None:
args.model = "model"
if not os.path.exists(args.model):
print ("Please download a model for your language from https://alphacephei.com/vosk/models")
print ("and unpack as 'model' in the current folder.")
parser.exit(0)
if args.samplerate is None:
device_info = sd.query_devices(args.device, 'input')
device_info = sd.query_devices(args.device, "input")
# soundfile expects an int, sounddevice provides a float:
args.samplerate = int(device_info['default_samplerate'])
model = vosk.Model(args.model)
args.samplerate = int(device_info["default_samplerate"])
if args.model is None:
model = Model(lang="en-us")
else:
model = Model(lang=args.model)
if args.filename:
dump_fn = open(args.filename, "wb")
else:
dump_fn = None
with sd.RawInputStream(samplerate=args.samplerate, blocksize = 8000, device=args.device, dtype='int16',
channels=1, callback=callback):
print('#' * 80)
print('Press Ctrl+C to stop the recording')
print('#' * 80)
with sd.RawInputStream(samplerate=args.samplerate, blocksize = 8000, device=args.device,
dtype="int16", channels=1, callback=callback):
print("#" * 80)
print("Press Ctrl+C to stop the recording")
print("#" * 80)
rec = vosk.KaldiRecognizer(model, args.samplerate)
while True:
data = q.get()
if rec.AcceptWaveform(data):
print(rec.Result())
else:
print(rec.PartialResult())
if dump_fn is not None:
dump_fn.write(data)
rec = KaldiRecognizer(model, args.samplerate)
while True:
data = q.get()
if rec.AcceptWaveform(data):
print(rec.Result())
else:
print(rec.PartialResult())
if dump_fn is not None:
dump_fn.write(data)
except KeyboardInterrupt:
print('\nDone')
print("\nDone")
parser.exit(0)
except Exception as e:
parser.exit(type(e).__name__ + ': ' + str(e))
parser.exit(type(e).__name__ + ": " + str(e))
+27
View File
@@ -0,0 +1,27 @@
#!/usr/bin/env python3
import wave
import sys
from vosk import Model, KaldiRecognizer, SetLogLevel
SetLogLevel(0)
wf = wave.open(sys.argv[1], "rb")
if wf.getnchannels() != 1 or wf.getsampwidth() != 2 or wf.getcomptype() != "NONE":
print("Audio file must be WAV format mono PCM.")
sys.exit(1)
model = Model(lang="en-us")
rec = KaldiRecognizer(model, wf.getframerate())
rec.SetMaxAlternatives(10)
rec.SetNLSML(True)
while True:
data = wf.readframes(4000)
if len(data) == 0:
break
if rec.AcceptWaveform(data):
print(rec.Result())
print(rec.FinalResult())
+9 -13
View File
@@ -1,23 +1,19 @@
#!/usr/bin/env python3
from vosk import Model, KaldiRecognizer, SetLogLevel
import sys
import os
import wave
import sys
import json
from vosk import Model, KaldiRecognizer, SetLogLevel
SetLogLevel(0)
if not os.path.exists("model"):
print ("Please download the model from https://alphacephei.com/vosk/models and unpack as 'model' in the current folder.")
exit (1)
wf = wave.open(sys.argv[1], "rb")
if wf.getnchannels() != 1 or wf.getsampwidth() != 2 or wf.getcomptype() != "NONE":
print ("Audio file must be WAV format mono PCM.")
exit (1)
print("Audio file must be WAV format mono PCM.")
sys.exit(1)
model = Model("model")
model = Model(lang="en-us")
rec = KaldiRecognizer(model, wf.getframerate())
while True:
@@ -26,12 +22,12 @@ while True:
break
if rec.AcceptWaveform(data):
print(rec.Result())
break
sys.exit(1)
else:
jres = json.loads(rec.PartialResult())
print(jres)
if jres['partial'] == "one zero zero zero":
if jres["partial"] == "one zero zero zero":
print("We can reset recognizer here and start over")
rec.Reset();
rec.Reset()
+13 -10
View File
@@ -1,24 +1,27 @@
#!/usr/bin/env python3
from vosk import Model, KaldiRecognizer, SetLogLevel
import sys
import os
import wave
import sys
from vosk import Model, KaldiRecognizer, SetLogLevel
# You can set log level to -1 to disable debug messages
SetLogLevel(0)
if not os.path.exists("model"):
print ("Please download the model from https://alphacephei.com/vosk/models and unpack as 'model' in the current folder.")
exit (1)
wf = wave.open(sys.argv[1], "rb")
if wf.getnchannels() != 1 or wf.getsampwidth() != 2 or wf.getcomptype() != "NONE":
print ("Audio file must be WAV format mono PCM.")
exit (1)
print("Audio file must be WAV format mono PCM.")
sys.exit(1)
model = Model(lang="en-us")
# You can also init model by name or with a folder path
# model = Model(model_name="vosk-model-en-us-0.21")
# model = Model("models/en")
model = Model("model")
rec = KaldiRecognizer(model, wf.getframerate())
rec.SetWords(True)
rec.SetPartialWords(True)
while True:
data = wf.readframes(4000)
+44 -26
View File
@@ -1,38 +1,54 @@
#!/usr/bin/env python3
from vosk import Model, KaldiRecognizer, SpkModel
import os
import sys
import wave
import json
import os
import numpy as np
model_path = "model"
spk_model_path = "model-spk"
from vosk import Model, KaldiRecognizer, SpkModel
if not os.path.exists(model_path):
print ("Please download the model from https://alphacephei.com/vosk/models and unpack as {} in the current folder.".format(model_path))
exit (1)
SPK_MODEL_PATH = "model-spk"
if not os.path.exists(spk_model_path):
print ("Please download the speaker model from https://alphacephei.com/vosk/models and unpack as {} in the current folder.".format(spk_model_path))
exit (1)
if not os.path.exists(SPK_MODEL_PATH):
print("Please download the speaker model from "
"https://alphacephei.com/vosk/models and unpack as {SPK_MODEL_PATH} "
"in the current folder.")
sys.exit(1)
wf = wave.open(sys.argv[1], "rb")
if wf.getnchannels() != 1 or wf.getsampwidth() != 2 or wf.getcomptype() != "NONE":
print ("Audio file must be WAV format mono PCM.")
exit (1)
print("Audio file must be WAV format mono PCM.")
sys.exit(1)
# Large vocabulary free form recognition
model = Model(model_path)
spk_model = SpkModel(spk_model_path)
model = Model(lang="en-us")
spk_model = SpkModel(SPK_MODEL_PATH)
#rec = KaldiRecognizer(model, wf.getframerate(), spk_model)
rec = KaldiRecognizer(model, wf.getframerate())
rec.SetSpkModel(spk_model)
# We compare speakers with cosine distance. We can keep one or several fingerprints for the speaker in a database
# We compare speakers with cosine distance.
# We can keep one or several fingerprints for the speaker in a database
# to distingusih among users.
spk_sig = [-1.110417,0.09703002,1.35658,0.7798632,-0.305457,-0.339204,0.6186931,-0.4521213,0.3982236,-0.004530723,0.7651616,0.6500852,-0.6664245,0.1361499,0.1358056,-0.2887807,-0.1280468,-0.8208137,-1.620276,-0.4628615,0.7870904,-0.105754,0.9739769,-0.3258137,-0.7322628,-0.6212429,-0.5531687,-0.7796484,0.7035915,1.056094,-0.4941756,-0.6521456,-0.2238328,-0.003737517,0.2165709,1.200186,-0.7737719,0.492015,1.16058,0.6135428,-0.7183084,0.3153541,0.3458071,-1.418189,-0.9624157,0.4168292,-1.627305,0.2742135,-0.6166027,0.1962581,-0.6406527,0.4372789,-0.4296024,0.4898657,-0.9531326,-0.2945702,0.7879696,-1.517101,-0.9344181,-0.5049928,-0.005040941,-0.4637912,0.8223695,-1.079849,0.8871287,-0.9732434,-0.5548235,1.879138,-1.452064,-0.1975368,1.55047,0.5941782,-0.52897,1.368219,0.6782904,1.202505,-0.9256122,-0.9718158,-0.9570228,-0.5563112,-1.19049,-1.167985,2.606804,-2.261825,0.01340385,0.2526799,-1.125458,-1.575991,-0.363153,0.3270262,1.485984,-1.769565,1.541829,0.7293826,0.1743717,-0.4759418,1.523451,-2.487134,-1.824067,-0.626367,0.7448186,-1.425648,0.3524166,-0.9903384,3.339342,0.4563958,-0.2876643,1.521635,0.9508078,-0.1398541,0.3867955,-0.7550205,0.6568405,0.09419366,-1.583935,1.306094,-0.3501927,0.1794427,-0.3768163,0.9683866,-0.2442541,-1.696921,-1.8056,-0.6803037,-1.842043,0.3069353,0.9070363,-0.486526]
spk_sig = [-1.110417,0.09703002,1.35658,0.7798632,-0.305457,-0.339204,0.6186931,
-0.4521213,0.3982236,-0.004530723,0.7651616,0.6500852,-0.6664245,0.1361499,
0.1358056,-0.2887807,-0.1280468,-0.8208137,-1.620276,-0.4628615,0.7870904,
-0.105754,0.9739769,-0.3258137,-0.7322628,-0.6212429,-0.5531687,-0.7796484,
0.7035915,1.056094,-0.4941756,-0.6521456,-0.2238328,-0.003737517,0.2165709,
1.200186,-0.7737719,0.492015,1.16058,0.6135428,-0.7183084,0.3153541,0.3458071,
-1.418189,-0.9624157,0.4168292,-1.627305,0.2742135,-0.6166027,0.1962581,
-0.6406527,0.4372789,-0.4296024,0.4898657,-0.9531326,-0.2945702,0.7879696,
-1.517101,-0.9344181,-0.5049928,-0.005040941,-0.4637912,0.8223695,-1.079849,
0.8871287,-0.9732434,-0.5548235,1.879138,-1.452064,-0.1975368,1.55047,
0.5941782,-0.52897,1.368219,0.6782904,1.202505,-0.9256122,-0.9718158,
-0.9570228,-0.5563112,-1.19049,-1.167985,2.606804,-2.261825,0.01340385,
0.2526799,-1.125458,-1.575991,-0.363153,0.3270262,1.485984,-1.769565,
1.541829,0.7293826,0.1743717,-0.4759418,1.523451,-2.487134,-1.824067,
-0.626367,0.7448186,-1.425648,0.3524166,-0.9903384,3.339342,0.4563958,
-0.2876643,1.521635,0.9508078,-0.1398541,0.3867955,-0.7550205,0.6568405,
0.09419366,-1.583935,1.306094,-0.3501927,0.1794427,-0.3768163,0.9683866,
-0.2442541,-1.696921,-1.8056,-0.6803037,-1.842043,0.3069353,0.9070363,-0.486526]
def cosine_dist(x, y):
nx = np.array(x)
@@ -45,16 +61,18 @@ while True:
break
if rec.AcceptWaveform(data):
res = json.loads(rec.Result())
print ("Text:", res['text'])
if 'spk' in res:
print ("X-vector:", res['spk'])
print ("Speaker distance:", cosine_dist(spk_sig, res['spk']), "based on", res['spk_frames'], "frames")
print("Text:", res["text"])
if "spk" in res:
print("X-vector:", res["spk"])
print("Speaker distance:", cosine_dist(spk_sig, res["spk"]),
"based on", res["spk_frames"], "frames")
print ("Note that second distance is not very reliable because utterance is too short. Utterances longer than 4 seconds give better xvector")
print("Note that second distance is not very reliable because utterance is too short. "
"Utterances longer than 4 seconds give better xvector")
res = json.loads(rec.FinalResult())
print ("Text:", res['text'])
if 'spk' in res:
print ("X-vector:", res['spk'])
print ("Speaker distance:", cosine_dist(spk_sig, res['spk']), "based on", res['spk_frames'], "frames")
print("Text:", res["text"])
if "spk" in res:
print("X-vector:", res["spk"])
print("Speaker distance:", cosine_dist(spk_sig, res["spk"]),
"based on", res["spk_frames"], "frames")
+11 -46
View File
@@ -1,56 +1,21 @@
#!/usr/bin/env python3
from vosk import Model, KaldiRecognizer, SetLogLevel
import sys
import os
import wave
import subprocess
import srt
import json
import datetime
import sys
from vosk import Model, KaldiRecognizer, SetLogLevel
SAMPLE_RATE = 16000
SetLogLevel(-1)
if not os.path.exists("model"):
print ("Please download the model from https://alphacephei.com/vosk/models and unpack as 'model' in the current folder.")
exit (1)
sample_rate=16000
model = Model("model")
rec = KaldiRecognizer(model, sample_rate)
model = Model(lang="en-us")
rec = KaldiRecognizer(model, SAMPLE_RATE)
rec.SetWords(True)
process = subprocess.Popen(['ffmpeg', '-loglevel', 'quiet', '-i',
with subprocess.Popen(["ffmpeg", "-loglevel", "quiet", "-i",
sys.argv[1],
'-ar', str(sample_rate) , '-ac', '1', '-f', 's16le', '-'],
stdout=subprocess.PIPE)
"-ar", str(SAMPLE_RATE) , "-ac", "1", "-f", "s16le", "-"],
stdout=subprocess.PIPE).stdout as stream:
WORDS_PER_LINE = 7
def transcribe():
results = []
subs = []
while True:
data = process.stdout.read(4000)
if len(data) == 0:
break
if rec.AcceptWaveform(data):
results.append(rec.Result())
results.append(rec.FinalResult())
for i, res in enumerate(results):
jres = json.loads(res)
if not 'result' in jres:
continue
words = jres['result']
for j in range(0, len(words), WORDS_PER_LINE):
line = words[j : j + WORDS_PER_LINE]
s = srt.Subtitle(index=len(subs),
content=" ".join([l['word'] for l in line]),
start=datetime.timedelta(seconds=line[0]['start']),
end=datetime.timedelta(seconds=line[-1]['end']))
subs.append(s)
return subs
print (srt.compose(transcribe()))
print(rec.SrtResult(stream))
+13 -18
View File
@@ -1,16 +1,11 @@
#!/usr/bin/env python3
from vosk import Model, KaldiRecognizer
import sys
import json
import os
if not os.path.exists("model"):
print ("Please download the model from https://alphacephei.com/vosk/models and unpack as 'model' in the current folder.")
exit (1)
from vosk import Model, KaldiRecognizer
model = Model("model")
model = Model(lang="en-us")
# Large vocabulary free form recognition
rec = KaldiRecognizer(model, 16000)
@@ -18,16 +13,16 @@ rec = KaldiRecognizer(model, 16000)
# You can also specify the possible word list
#rec = KaldiRecognizer(model, 16000, "zero oh one two three four five six seven eight nine")
wf = open(sys.argv[1], "rb")
wf.read(44) # skip header
with open(sys.argv[1], "rb") as wf:
wf.read(44) # skip header
while True:
data = wf.read(4000)
if len(data) == 0:
break
if rec.AcceptWaveform(data):
res = json.loads(rec.Result())
print (res['text'])
while True:
data = wf.read(4000)
if len(data) == 0:
break
if rec.AcceptWaveform(data):
res = json.loads(rec.Result())
print(res["text"])
res = json.loads(rec.FinalResult())
print (res['text'])
res = json.loads(rec.FinalResult())
print(res["text"])
+41 -46
View File
@@ -1,72 +1,67 @@
#!/usr/bin/env python3
from vosk import Model, KaldiRecognizer, SetLogLevel
from webvtt import WebVTT, Caption
import sys
import os
import subprocess
import json
import textwrap
SetLogLevel(-1)
if not os.path.exists('model'):
print('Please download the model from https://alphacephei.com/vosk/models'
' and unpack as `model` in the current folder.')
exit(1)
sample_rate = 16000
model = Model('model')
rec = KaldiRecognizer(model, sample_rate)
rec.SetWords(True)
from webvtt import WebVTT, Caption
from vosk import Model, KaldiRecognizer, SetLogLevel
SAMPLE_RATE = 16000
WORDS_PER_LINE = 7
SetLogLevel(-1)
def timeString(seconds):
model = Model(lang="en-us")
rec = KaldiRecognizer(model, SAMPLE_RATE)
rec.SetWords(True)
def timestring(seconds):
minutes = seconds / 60
seconds = seconds % 60
hours = int(minutes / 60)
minutes = int(minutes % 60)
return '%i:%02i:%06.3f' % (hours, minutes, seconds)
return "%i:%02i:%06.3f" % (hours, minutes, seconds)
def transcribe():
command = ['ffmpeg', '-nostdin', '-loglevel', 'quiet', '-i', sys.argv[1],
'-ar', str(sample_rate), '-ac', '1', '-f', 's16le', '-']
process = subprocess.Popen(command, stdout=subprocess.PIPE)
command = ["ffmpeg", "-nostdin", "-loglevel", "quiet", "-i", sys.argv[1],
"-ar", str(SAMPLE_RATE), "-ac", "1", "-f", "s16le", "-"]
with subprocess.Popen(command, stdout=subprocess.PIPE) as process:
results = []
while True:
data = process.stdout.read(4000)
if len(data) == 0:
break
if rec.AcceptWaveform(data):
results.append(rec.Result())
results.append(rec.FinalResult())
results = []
while True:
data = process.stdout.read(4000)
if len(data) == 0:
break
if rec.AcceptWaveform(data):
results.append(rec.Result())
results.append(rec.FinalResult())
vtt = WebVTT()
for i, res in enumerate(results):
words = json.loads(res).get('result')
if not words:
continue
vtt = WebVTT()
for _, res in enumerate(results):
words = json.loads(res).get("result")
if not words:
continue
start = timeString(words[0]['start'])
end = timeString(words[-1]['end'])
content = ' '.join([w['word'] for w in words])
start = timestring(words[0]["start"])
end = timestring(words[-1]["end"])
content = " ".join([w["word"] for w in words])
caption = Caption(start, end, textwrap.fill(content))
vtt.captions.append(caption)
caption = Caption(start, end, textwrap.fill(content))
vtt.captions.append(caption)
# save or return webvtt
if len(sys.argv) > 2:
vtt.save(sys.argv[2])
else:
print(vtt.content)
# save or return webvtt
if len(sys.argv) > 2:
vtt.save(sys.argv[2])
else:
print(vtt.content)
if __name__ == '__main__':
if not (1 < len(sys.argv) < 4):
print(f'Usage: {sys.argv[0]} audiofile [output file]')
exit(1)
if __name__ == "__main__":
if not 1 < len(sys.argv) < 4:
print("Usage: {} audiofile [output file]".format(sys.argv[0]))
sys.exit(1)
transcribe()
+11 -11
View File
@@ -1,23 +1,22 @@
#!/usr/bin/env python3
from vosk import Model, KaldiRecognizer
import sys
import os
import wave
import sys
if not os.path.exists("model"):
print ("Please download the model from https://alphacephei.com/vosk/models and unpack as 'model' in the current folder.")
exit (1)
from vosk import Model, KaldiRecognizer
wf = wave.open(sys.argv[1], "rb")
if wf.getnchannels() != 1 or wf.getsampwidth() != 2 or wf.getcomptype() != "NONE":
print ("Audio file must be WAV format mono PCM.")
exit (1)
print("Audio file must be WAV format mono PCM.")
sys.exit(1)
model = Model("model")
model = Model(lang="en-us")
# You can also specify the possible word or phrase list as JSON list, the order doesn't have to be strict
rec = KaldiRecognizer(model, wf.getframerate(), '["oh one two three four five six seven eight nine zero", "[unk]"]')
# You can also specify the possible word or phrase list as JSON list,
# the order doesn't have to be strict
rec = KaldiRecognizer(model,
wf.getframerate(),
'["oh one two three", "four five six", "seven eight nine zero", "[unk]"]')
while True:
data = wf.readframes(4000)
@@ -25,6 +24,7 @@ while True:
break
if rec.AcceptWaveform(data):
print(rec.Result())
rec.SetGrammar('["one zero one two three oh", "four five six", "seven eight nine zero", "[unk]"]')
else:
print(rec.PartialResult())
+16 -12
View File
@@ -1,14 +1,15 @@
import os
import sys
import setuptools
import shutil
import glob
import platform
# Figure out environment for cross-compile
vosk_source = os.getenv("VOSK_SOURCE", os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
system = os.environ.get('VOSK_PLATFORM', platform.system())
vosk_source = os.getenv("VOSK_SOURCE", os.path.abspath(os.path.join(os.path.dirname(__file__),
"..")))
system = os.environ.get('VOSK_SYSTEM', platform.system())
architecture = os.environ.get('VOSK_ARCHITECTURE', platform.architecture()[0])
machine = os.environ.get('VOSK_MACHINE', platform.machine())
# Copy precompmilled libraries
for lib in glob.glob(os.path.join(vosk_source, "src/lib*.*")):
@@ -25,26 +26,26 @@ else:
def get_tag(self):
abi = 'none'
if system == 'Darwin':
oses = 'macosx_10_6_x86_64'
oses = 'macosx_10_6_universal2'
elif system == 'Windows' and architecture == '32bit':
oses = 'win32'
elif system == 'Windows' and architecture == '64bit':
oses = 'win_amd64'
elif system == 'Linux' and architecture == '64bit':
oses = 'linux_x86_64'
elif system == 'Linux' and machine == 'aarch64' and architecture == '64bit':
oses = 'manylinux2014_aarch64'
elif system == 'Linux':
oses = 'linux_' + architecture
oses = 'linux_' + machine
else:
raise TypeError("Unknown build environment")
return 'py3', abi, oses
cmdclass = {'bdist_wheel': bdist_wheel_tag_name}
with open("README.md", "r") as fh:
long_description = fh.read()
with open("README.md", "rb") as fh:
long_description = fh.read().decode("utf-8")
setuptools.setup(
name="vosk",
version="0.3.30",
version="0.3.45",
author="Alpha Cephei Inc",
author_email="contact@alphacephei.com",
description="Offline open source speech recognition API based on Kaldi and Vosk",
@@ -53,6 +54,9 @@ setuptools.setup(
url="https://github.com/alphacep/vosk-api",
packages=setuptools.find_packages(),
package_data = {'vosk': ['*.so', '*.dll', '*.dyld']},
entry_points = {
'console_scripts': ['vosk-transcriber=vosk.transcriber.cli:main'],
},
include_package_data=True,
classifiers=[
'Programming Language :: Python :: 3',
@@ -65,7 +69,7 @@ setuptools.setup(
cmdclass=cmdclass,
python_requires='>=3',
zip_safe=False, # Since we load so file from the filesystem, we can not run from zip file
setup_requires=['cffi>=1.0'],
install_requires=['cffi>=1.0'],
setup_requires=['cffi>=1.0', 'requests', 'tqdm', 'srt', 'websockets'],
install_requires=['cffi>=1.0', 'requests', 'tqdm', 'srt', 'websockets'],
cffi_modules=['vosk_builder.py:ffibuilder'],
)
+9 -10
View File
@@ -1,14 +1,13 @@
#!/usr/bin/env python3
import wave
import json
import sys
from multiprocessing.dummy import Pool
from vosk import Model, KaldiRecognizer
import sys
import os
import wave
import json
model = Model("model")
model = Model("en-us")
def recognize(line):
uid, fn = line.split()
@@ -22,14 +21,14 @@ def recognize(line):
break
if rec.AcceptWaveform(data):
jres = json.loads(rec.Result())
text = text + " " + jres['text']
text = text + " " + jres["text"]
jres = json.loads(rec.FinalResult())
text = text + " " + jres['text']
return (uid + text)
text = text + " " + jres["text"]
return uid + text
def main():
p = Pool(8)
texts = p.map(recognize, open(sys.argv[1]).readlines())
texts = p.map(recognize, open(sys.argv[1], encoding="uft-8").readlines())
print ("\n".join(texts))
main()
+204 -20
View File
@@ -1,56 +1,162 @@
import os
import sys
import srt
import datetime
import json
import requests
from urllib.request import urlretrieve
from zipfile import ZipFile
from re import match
from pathlib import Path
from .vosk_cffi import ffi as _ffi
from tqdm import tqdm
# Remote location of the models and local folders
MODEL_PRE_URL = "https://alphacephei.com/vosk/models/"
MODEL_LIST_URL = MODEL_PRE_URL + "model-list.json"
MODEL_DIRS = [os.getenv("VOSK_MODEL_PATH"), Path("/usr/share/vosk"),
Path.home() / "AppData/Local/vosk", Path.home() / ".cache/vosk"]
def open_dll():
dlldir = os.path.abspath(os.path.dirname(__file__))
if sys.platform == 'win32':
if sys.platform == "win32":
# We want to load dependencies too
os.environ["PATH"] = dlldir + os.pathsep + os.environ['PATH']
if hasattr(os, 'add_dll_directory'):
os.environ["PATH"] = dlldir + os.pathsep + os.environ["PATH"]
if hasattr(os, "add_dll_directory"):
os.add_dll_directory(dlldir)
return _ffi.dlopen(os.path.join(dlldir, "libvosk.dll"))
elif sys.platform == 'linux':
elif sys.platform == "linux":
return _ffi.dlopen(os.path.join(dlldir, "libvosk.so"))
elif sys.platform == 'darwin':
elif sys.platform == "darwin":
return _ffi.dlopen(os.path.join(dlldir, "libvosk.dyld"))
else:
raise TypeError("Unsupported platform")
_c = open_dll()
class Model(object):
def list_models():
response = requests.get(MODEL_LIST_URL, timeout=10)
for model in response.json():
print(model["name"])
def __init__(self, model_path):
self._handle = _c.vosk_model_new(model_path.encode('utf-8'))
def list_languages():
response = requests.get(MODEL_LIST_URL, timeout=10)
languages = {m["lang"] for m in response.json()}
for lang in languages:
print (lang)
class Model:
def __init__(self, model_path=None, model_name=None, lang=None):
if model_path is not None:
self._handle = _c.vosk_model_new(model_path.encode("utf-8"))
else:
model_path = self.get_model_path(model_name, lang)
self._handle = _c.vosk_model_new(model_path.encode("utf-8"))
if self._handle == _ffi.NULL:
raise Exception("Failed to create a model")
def __del__(self):
_c.vosk_model_free(self._handle)
def vosk_model_find_word(self, word):
return _c.vosk_model_find_word(self._handle, word.encode('utf-8'))
return _c.vosk_model_find_word(self._handle, word.encode("utf-8"))
class SpkModel(object):
def get_model_path(self, model_name, lang):
if model_name is None:
model_path = self.get_model_by_lang(lang)
else:
model_path = self.get_model_by_name(model_name)
return str(model_path)
def get_model_by_name(self, model_name):
for directory in MODEL_DIRS:
if directory is None or not Path(directory).exists():
continue
model_file_list = os.listdir(directory)
model_file = [model for model in model_file_list if model == model_name]
if model_file != []:
return Path(directory, model_file[0])
response = requests.get(MODEL_LIST_URL, timeout=10)
result_model = [model["name"] for model in response.json() if model["name"] == model_name]
if result_model == []:
print("model name %s does not exist" % (model_name))
sys.exit(1)
else:
self.download_model(Path(directory, result_model[0]))
return Path(directory, result_model[0])
def get_model_by_lang(self, lang):
for directory in MODEL_DIRS:
if directory is None or not Path(directory).exists():
continue
model_file_list = os.listdir(directory)
model_file = [model for model in model_file_list if
match(r"vosk-model(-small)?-{}".format(lang), model)]
if model_file != []:
return Path(directory, model_file[0])
response = requests.get(MODEL_LIST_URL, timeout=10)
result_model = [model["name"] for model in response.json() if
model["lang"] == lang and model["type"] == "small" and model["obsolete"] == "false"]
if result_model == []:
print("lang %s does not exist" % (lang))
sys.exit(1)
else:
self.download_model(Path(directory, result_model[0]))
return Path(directory, result_model[0])
def download_model(self, model_name):
if not (model_name.parent).exists():
(model_name.parent).mkdir(parents=True)
with tqdm(unit="B", unit_scale=True, unit_divisor=1024, miniters=1,
desc=(MODEL_PRE_URL + str(model_name.name) + ".zip").rsplit("/",
maxsplit=1)[-1]) as t:
reporthook = self.download_progress_hook(t)
urlretrieve(MODEL_PRE_URL + str(model_name.name) + ".zip",
str(model_name) + ".zip", reporthook=reporthook, data=None)
t.total = t.n
with ZipFile(str(model_name) + ".zip", "r") as model_ref:
model_ref.extractall(model_name.parent)
Path(str(model_name) + ".zip").unlink()
def download_progress_hook(self, t):
last_b = [0]
def update_to(b=1, bsize=1, tsize=None):
if tsize not in (None, -1):
t.total = tsize
displayed = t.update((b - last_b[0]) * bsize)
last_b[0] = b
return displayed
return update_to
class SpkModel:
def __init__(self, model_path):
self._handle = _c.vosk_spk_model_new(model_path.encode('utf-8'))
self._handle = _c.vosk_spk_model_new(model_path.encode("utf-8"))
if self._handle == _ffi.NULL:
raise Exception("Failed to create a speaker model")
def __del__(self):
_c.vosk_spk_model_free(self._handle)
class KaldiRecognizer(object):
class KaldiRecognizer:
def __init__(self, *args):
if len(args) == 2:
self._handle = _c.vosk_recognizer_new(args[0]._handle, args[1])
elif len(args) == 3 and type(args[2]) is SpkModel:
self._handle = _c.vosk_recognizer_new_spk(args[0]._handle, args[1], args[2]._handle)
elif len(args) == 3 and type(args[2]) is str:
self._handle = _c.vosk_recognizer_new_grm(args[0]._handle, args[1], args[2].encode('utf-8'))
elif len(args) == 3 and isinstance(args[2], SpkModel):
self._handle = _c.vosk_recognizer_new_spk(args[0]._handle,
args[1], args[2]._handle)
elif len(args) == 3 and isinstance(args[2], str):
self._handle = _c.vosk_recognizer_new_grm(args[0]._handle,
args[1], args[2].encode("utf-8"))
else:
raise TypeError("Unknown arguments")
if self._handle == _ffi.NULL:
raise Exception("Failed to create a recognizer")
def __del__(self):
_c.vosk_recognizer_free(self._handle)
@@ -60,24 +166,62 @@ class KaldiRecognizer(object):
def SetWords(self, enable_words):
_c.vosk_recognizer_set_words(self._handle, 1 if enable_words else 0)
def SetPartialWords(self, enable_partial_words):
_c.vosk_recognizer_set_partial_words(self._handle, 1 if enable_partial_words else 0)
def SetNLSML(self, enable_nlsml):
_c.vosk_recognizer_set_nlsml(self._handle, 1 if enable_nlsml else 0)
def SetSpkModel(self, spk_model):
_c.vosk_recognizer_set_spk_model(self._handle, spk_model._handle)
def SetGrammar(self, grammar):
_c.vosk_recognizer_set_grm(self._handle, grammar.encode("utf-8"))
def AcceptWaveform(self, data):
return _c.vosk_recognizer_accept_waveform(self._handle, data, len(data))
res = _c.vosk_recognizer_accept_waveform(self._handle, data, len(data))
if res < 0:
raise Exception("Failed to process waveform")
return res
def Result(self):
return _ffi.string(_c.vosk_recognizer_result(self._handle)).decode('utf-8')
return _ffi.string(_c.vosk_recognizer_result(self._handle)).decode("utf-8")
def PartialResult(self):
return _ffi.string(_c.vosk_recognizer_partial_result(self._handle)).decode('utf-8')
return _ffi.string(_c.vosk_recognizer_partial_result(self._handle)).decode("utf-8")
def FinalResult(self):
return _ffi.string(_c.vosk_recognizer_final_result(self._handle)).decode('utf-8')
return _ffi.string(_c.vosk_recognizer_final_result(self._handle)).decode("utf-8")
def Reset(self):
return _c.vosk_recognizer_reset(self._handle)
def SrtResult(self, stream, words_per_line = 7):
results = []
while True:
data = stream.read(4000)
if len(data) == 0:
break
if self.AcceptWaveform(data):
results.append(self.Result())
results.append(self.FinalResult())
subs = []
for res in results:
jres = json.loads(res)
if not "result" in jres:
continue
words = jres["result"]
for j in range(0, len(words), words_per_line):
line = words[j : j + words_per_line]
s = srt.Subtitle(index=len(subs),
content=" ".join([l["word"] for l in line]),
start=datetime.timedelta(seconds=line[0]["start"]),
end=datetime.timedelta(seconds=line[-1]["end"]))
subs.append(s)
return srt.compose(subs)
def SetLogLevel(level):
return _c.vosk_set_log_level(level)
@@ -89,3 +233,43 @@ def GpuInit():
def GpuThreadInit():
_c.vosk_gpu_thread_init()
class BatchModel:
def __init__(self, model_path, *args):
self._handle = _c.vosk_batch_model_new(model_path.encode('utf-8'))
if self._handle == _ffi.NULL:
raise Exception("Failed to create a model")
def __del__(self):
_c.vosk_batch_model_free(self._handle)
def Wait(self):
_c.vosk_batch_model_wait(self._handle)
class BatchRecognizer:
def __init__(self, *args):
self._handle = _c.vosk_batch_recognizer_new(args[0]._handle, args[1])
if self._handle == _ffi.NULL:
raise Exception("Failed to create a recognizer")
def __del__(self):
_c.vosk_batch_recognizer_free(self._handle)
def AcceptWaveform(self, data):
res = _c.vosk_batch_recognizer_accept_waveform(self._handle, data, len(data))
def Result(self):
ptr = _c.vosk_batch_recognizer_front_result(self._handle)
res = _ffi.string(ptr).decode("utf-8")
_c.vosk_batch_recognizer_pop(self._handle)
return res
def FinishStream(self):
_c.vosk_batch_recognizer_finish_stream(self._handle)
def GetPendingChunks(self):
return _c.vosk_batch_recognizer_get_pending_chunks(self._handle)
View File
+89
View File
@@ -0,0 +1,89 @@
#!/usr/bin/env python3
import argparse
import logging
import sys
import os
from pathlib import Path
from vosk import list_models, list_languages
from vosk.transcriber.transcriber import Transcriber
parser = argparse.ArgumentParser(
description = "Transcribe audio file and save result in selected format")
parser.add_argument(
"--model", "-m", type=str,
help="model path")
parser.add_argument(
"--server", "-s", const="ws://localhost:2700", action="store_const",
help="use server for recognition")
parser.add_argument(
"--list-models", default=False, action="store_true",
help="list available models")
parser.add_argument(
"--list-languages", default=False, action="store_true",
help="list available languages")
parser.add_argument(
"--model-name", "-n", type=str,
help="select model by name")
parser.add_argument(
"--lang", "-l", default="en-us", type=str,
help="select model by language")
parser.add_argument(
"--input", "-i", type=str,
help="audiofile")
parser.add_argument(
"--output", "-o", default="", type=str,
help="optional output filename path")
parser.add_argument(
"--output-type", "-t", default="txt", type=str,
help="optional arg output data type")
parser.add_argument(
"--tasks", "-ts", default=10, type=int,
help="number of parallel recognition tasks")
parser.add_argument(
"--log-level", default="INFO",
help="logging level")
def main():
args = parser.parse_args()
log_level = args.log_level.upper()
logging.getLogger().setLevel(log_level)
if args.list_models is True:
list_models()
return
if args.list_languages is True:
list_languages()
return
if not args.input:
logging.info("Please specify input file or directory")
sys.exit(1)
if not Path(args.input).exists():
logging.info("File/folder {args.input} does not exist, "\
"please specify an existing file/directory")
sys.exit(1)
transcriber = Transcriber(args)
if Path(args.input).is_dir():
task_list = [(Path(args.input, fn),
Path(args.output,
Path(fn).stem).with_suffix("." + args.output_type)) for fn in os.listdir(args.input)]
elif Path(args.input).is_file():
if args.output == "":
task_list = [(Path(args.input), args.output)]
else:
task_list = [(Path(args.input), Path(args.output))]
else:
logging.info("Wrong arguments")
sys.exit(1)
transcriber.process_task_list(task_list)
if __name__ == "__main__":
main()
+195
View File
@@ -0,0 +1,195 @@
import json
import logging
import asyncio
import websockets
import srt
import datetime
import shlex
import subprocess
from vosk import KaldiRecognizer, Model
from queue import Queue
from timeit import default_timer as timer
from multiprocessing.dummy import Pool
CHUNK_SIZE = 4000
SAMPLE_RATE = 16000.0
class Transcriber:
def __init__(self, args):
self.model = Model(model_path=args.model, model_name=args.model_name, lang=args.lang)
self.args = args
self.queue = Queue()
def recognize_stream(self, rec, stream):
tot_samples = 0
result = []
while True:
data = stream.stdout.read(CHUNK_SIZE)
if len(data) == 0:
break
tot_samples += len(data)
if rec.AcceptWaveform(data):
jres = json.loads(rec.Result())
logging.info(jres)
result.append(jres)
else:
jres = json.loads(rec.PartialResult())
if jres["partial"] != "":
logging.info(jres)
jres = json.loads(rec.FinalResult())
result.append(jres)
return result, tot_samples
async def recognize_stream_server(self, proc):
async with websockets.connect(self.args.server) as websocket:
tot_samples = 0
result = []
await websocket.send('{ "config" : { "sample_rate" : %f } }' % (SAMPLE_RATE))
while True:
data = await proc.stdout.read(CHUNK_SIZE)
tot_samples += len(data)
if len(data) == 0:
break
await websocket.send(data)
jres = json.loads(await websocket.recv())
logging.info(jres)
if not "partial" in jres:
result.append(jres)
await websocket.send('{"eof" : 1}')
jres = json.loads(await websocket.recv())
logging.info(jres)
result.append(jres)
return result, tot_samples
def format_result(self, result, words_per_line=7):
processed_result = ""
if self.args.output_type == "srt":
subs = []
for _, res in enumerate(result):
if not "result" in res:
continue
words = res["result"]
for j in range(0, len(words), words_per_line):
line = words[j : j + words_per_line]
s = srt.Subtitle(index=len(subs),
content = " ".join([l["word"] for l in line]),
start=datetime.timedelta(seconds=line[0]["start"]),
end=datetime.timedelta(seconds=line[-1]["end"]))
subs.append(s)
processed_result = srt.compose(subs)
elif self.args.output_type == "txt":
for part in result:
if part["text"] != "":
processed_result += part["text"] + "\n"
elif self.args.output_type == "json":
monologues = {"schemaVersion":"2.0", "monologues":[], "text":[]}
for part in result:
if part["text"] != "":
monologue["text"] += part["text"]
for _, res in enumerate(result):
if not "result" in res:
continue
monologue = { "speaker": {"id": "unknown", "name": None}, "start": 0, "end": 0, "terms": []}
monologue["start"] = res["result"][0]["start"]
monologue["end"] = res["result"][-1]["end"]
monologue["terms"] = [{"confidence": t["conf"], "start": t["start"], "end": t["end"], "text": t["word"], "type": "WORD" } for t in res["result"]]
monologues["monologues"].append(monologue)
processed_result = json.dumps(monologues)
return processed_result
def resample_ffmpeg(self, infile):
cmd = shlex.split("ffmpeg -nostdin -loglevel quiet "
"-i \'{}\' -ar {} -ac 1 -f s16le -".format(str(infile), SAMPLE_RATE))
stream = subprocess.Popen(cmd, stdout=subprocess.PIPE)
return stream
async def resample_ffmpeg_async(self, infile):
cmd = "ffmpeg -nostdin -loglevel quiet "\
"-i \'{}\' -ar {} -ac 1 -f s16le -".format(str(infile), SAMPLE_RATE)
return await asyncio.create_subprocess_shell(cmd, stdout=subprocess.PIPE)
async def server_worker(self):
while True:
try:
input_file, output_file = self.queue.get_nowait()
except Exception:
break
logging.info("Recognizing {}".format(input_file))
start_time = timer()
proc = await self.resample_ffmpeg_async(input_file)
result, tot_samples = await self.recognize_stream_server(proc)
processed_result = self.format_result(result)
if output_file != "":
logging.info("File {} processing complete".format(output_file))
with open(output_file, "w", encoding="utf-8") as fh:
fh.write(processed_result)
else:
print(processed_result)
await proc.wait()
elapsed = timer() - start_time
logging.info("Execution time: {:.3f} sec; "\
"xRT {:.3f}".format(elapsed, float(elapsed) * (2 * SAMPLE_RATE) / tot_samples))
self.queue.task_done()
def pool_worker(self, inputdata):
logging.info("Recognizing {}".format(inputdata[0]))
start_time = timer()
try:
stream = self.resample_ffmpeg(inputdata[0])
except FileNotFoundError as e:
print(e, "Missing FFMPEG, please install and try again")
return
except Exception as e:
logging.info(e)
return
rec = KaldiRecognizer(self.model, SAMPLE_RATE)
rec.SetWords(True)
result, tot_samples = self.recognize_stream(rec, stream)
processed_result = self.format_result(result)
if inputdata[1] != "":
logging.info("File {} processing complete".format(inputdata[1]))
with open(inputdata[1], "w", encoding="utf-8") as fh:
fh.write(processed_result)
else:
print(processed_result)
elapsed = timer() - start_time
logging.info("Execution time: {:.3f} sec; "\
"xRT {:.3f}".format(elapsed, float(elapsed) * (2 * SAMPLE_RATE) / tot_samples))
async def process_task_list_server(self, task_list):
for x in task_list:
self.queue.put(x)
workers = [asyncio.create_task(self.server_worker()) for i in range(self.args.tasks)]
await asyncio.gather(*workers)
def process_task_list_pool(self, task_list):
with Pool() as pool:
pool.map(self.pool_worker, task_list)
def process_task_list(self, task_list):
if self.args.server is None:
self.process_task_list_pool(task_list)
else:
asyncio.run(self.process_task_list_server(task_list))
+5
View File
@@ -0,0 +1,5 @@
class Vosk
def self.hi
puts "Hello world!"
end
end
+12
View File
@@ -0,0 +1,12 @@
Gem::Specification.new do |s|
s.name = "vosk"
s.version = "0.3.45"
s.summary = "Offline speech recognition API"
s.description = "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."
s.authors = ["Alpha Cephei Inc"]
s.email = "contact@alphacphei.com"
s.files = ["lib/vosk.rb"]
s.homepage =
"https://rubygems.org/gems/vosk"
s.license = "Apache 2.0"
end
+5
View File
@@ -0,0 +1,5 @@
See
https://github.com/Bear-03/vosk-rs
https://crates.io/crates/vosk
+84 -41
View File
@@ -1,71 +1,114 @@
# Locations of the dependencies
KALDI_ROOT?=$(HOME)/travis/kaldi
OPENFST_ROOT?=$(KALDI_ROOT)/tools/openfst
OPENBLAS_ROOT?=$(KALDI_ROOT)/tools/OpenBLAS/install
HAVE_CUDA?=0
MKL_ROOT?=/opt/intel/mkl
CUDA_ROOT?=/usr/local/cuda
EXT?=so
CXX?=g++
HAVE_OPENBLAS_CLAPACK=1
USE_SHARED?=0
# Math libraries
HAVE_OPENBLAS_CLAPACK?=1
HAVE_MKL?=0
HAVE_ACCELERATE=0
EXTRA_CFLGAS?=
HAVE_CUDA?=0
# Compiler
CXX?=g++
EXT?=so
# Extra
EXTRA_CFLAGS?=
EXTRA_LDFLAGS?=
OUTDIR?=.
VOSK_SOURCES= \
kaldi_recognizer.cc \
recognizer.cc \
language_model.cc \
model.cc \
spk_model.cc \
vosk_api.cc
CFLAGS=-g -O2 -std=c++17 -fPIC -DFST_NO_DYNAMIC_LINKING $(EXTRA_CFLAGS) \
-I. -I$(KALDI_ROOT)/src -I$(OPENFST_ROOT)/include -I$(OPENBLAS_ROOT)/include
VOSK_HEADERS= \
recognizer.h \
language_model.h \
model.h \
spk_model.h \
vosk_api.h
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/rnnlm/kaldi-rnnlm.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 \
$(OPENFST_ROOT)/lib/libfst.a \
$(OPENFST_ROOT)/lib/libfstngram.a
CFLAGS=-g -O3 -std=c++17 -Wno-deprecated-declarations -fPIC -DFST_NO_DYNAMIC_LINKING \
-I. -I$(KALDI_ROOT)/src -I$(OPENFST_ROOT)/include $(EXTRA_CFLAGS)
LDFLAGS=
ifeq ($(USE_SHARED), 0)
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/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/rnnlm/kaldi-rnnlm.a \
$(KALDI_ROOT)/src/hmm/kaldi-hmm.a \
$(KALDI_ROOT)/src/nnet3/kaldi-nnet3.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 \
$(OPENFST_ROOT)/lib/libfst.a \
$(OPENFST_ROOT)/lib/libfstngram.a
else
LDFLAGS += \
-L$(KALDI_ROOT)/libs \
-lkaldi-online2 -lkaldi-decoder -lkaldi-ivector -lkaldi-gmm -lkaldi-tree \
-lkaldi-feat -lkaldi-lat -lkaldi-lm -lkaldi-rnnlm -lkaldi-hmm -lkaldi-nnet3 \
-lkaldi-transform -lkaldi-cudamatrix -lkaldi-matrix -lkaldi-fstext \
-lkaldi-util -lkaldi-base -lfst -lfstngram
endif
ifeq ($(HAVE_OPENBLAS_CLAPACK), 1)
LIBS += \
$(OPENBLAS_ROOT)/lib/libopenblas.a \
$(OPENBLAS_ROOT)/lib/liblapack.a \
$(OPENBLAS_ROOT)/lib/libblas.a \
$(OPENBLAS_ROOT)/lib/libf2c.a
CFLAGS += -I$(OPENBLAS_ROOT)/include
ifeq ($(USE_SHARED), 0)
LIBS += \
$(OPENBLAS_ROOT)/lib/libopenblas.a \
$(OPENBLAS_ROOT)/lib/liblapack.a \
$(OPENBLAS_ROOT)/lib/libblas.a \
$(OPENBLAS_ROOT)/lib/libf2c.a
else
LDFLAGS += -lopenblas -llapack -lblas -lf2c
endif
endif
ifeq ($(HAVE_MKL), 1)
CFLAGS += -DHAVE_MKL=1 -I$(MKL_ROOT)/include
LDFLAGS += -L$(MKL_ROOT)/lib/intel64 -Wl,-rpath=$(MKL_ROOT)/lib/intel64 -lmkl_rt -lmkl_intel_lp64 -lmkl_core -lmkl_sequential
endif
ifeq ($(HAVE_ACCELERATE), 1)
LIBS += \
-framework Accelerate
LDFLAGS += -framework Accelerate
endif
ifeq ($(HAVE_CUDA), 1)
CFLAGS+=-DHAVE_CUDA=1 -I$(CUDA_ROOT)/include
LIBS+=-L$(CUDA_ROOT)/lib64 -lcublas -lcusparse -lcudart -lcurand -lcufft -lcusolver -lnvToolsExt
VOSK_SOURCES += batch_recognizer.cc batch_model.cc
VOSK_HEADERS += batch_recognizer.h batch_model.h
CFLAGS+=-DHAVE_CUDA=1 -I$(CUDA_ROOT)/include
LIBS := \
$(KALDI_ROOT)/src/cudadecoder/kaldi-cudadecoder.a \
$(KALDI_ROOT)/src/cudafeat/kaldi-cudafeat.a \
$(LIBS)
LDFLAGS += -L$(CUDA_ROOT)/lib64 -lcuda -lcublas -lcusparse -lcudart -lcurand -lcufft -lcusolver -lnvToolsExt
endif
all: libvosk.$(EXT)
all: $(OUTDIR)/libvosk.$(EXT)
libvosk.$(EXT): $(VOSK_SOURCES:.cc=.o)
$(CXX) --shared -s -o $@ $^ $(LIBS) -lm -latomic $(EXTRA_LDFLAGS)
$(OUTDIR)/libvosk.$(EXT): $(VOSK_SOURCES:%.cc=$(OUTDIR)/%.o) $(LIBS)
$(CXX) --shared -s -o $@ $^ $(LDFLAGS) $(EXTRA_LDFLAGS)
%.o: %.cc
$(OUTDIR)/%.o: %.cc $(VOSK_HEADERS)
$(CXX) $(CFLAGS) -c -o $@ $<
clean:
+135
View File
@@ -0,0 +1,135 @@
// Copyright 2019-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.
// 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.
#include "batch_model.h"
#include <sys/stat.h>
using namespace fst;
using namespace kaldi::nnet3;
using CorrelationID = CudaOnlinePipelineDynamicBatcher::CorrelationID;
BatchModel::BatchModel(const char *model_path) : model_path_str_(model_path) {
BatchedThreadedNnet3CudaOnlinePipelineConfig batched_decoder_config;
kaldi::ParseOptions po("something");
batched_decoder_config.Register(&po);
po.ReadConfigFile(model_path_str_ + "/conf/model.conf");
struct stat buffer;
string nnet3_rxfilename_ = model_path_str_ + "/am/final.mdl";
string hclg_fst_rxfilename_ = model_path_str_ + "/graph/HCLG.fst";
string word_syms_rxfilename_ = model_path_str_ + "/graph/words.txt";
string winfo_rxfilename_ = model_path_str_ + "/graph/phones/word_boundary.int";
string std_fst_rxfilename_ = model_path_str_ + "/rescore/G.fst";
string carpa_rxfilename_ = model_path_str_ + "/rescore/G.carpa";
trans_model_ = new kaldi::TransitionModel();
nnet_ = new kaldi::nnet3::AmNnetSimple();
{
bool binary;
kaldi::Input ki(nnet3_rxfilename_, &binary);
trans_model_->Read(ki.Stream(), binary);
nnet_->Read(ki.Stream(), binary);
SetBatchnormTestMode(true, &(nnet_->GetNnet()));
SetDropoutTestMode(true, &(nnet_->GetNnet()));
nnet3::CollapseModel(nnet3::CollapseModelConfig(), &(nnet_->GetNnet()));
}
if (stat(hclg_fst_rxfilename_.c_str(), &buffer) == 0) {
KALDI_LOG << "Loading HCLG from " << hclg_fst_rxfilename_;
hclg_fst_ = fst::ReadFstKaldiGeneric(hclg_fst_rxfilename_);
}
KALDI_LOG << "Loading words from " << word_syms_rxfilename_;
if (!(word_syms_ = fst::SymbolTable::ReadText(word_syms_rxfilename_))) {
KALDI_ERR << "Could not read symbol table from file "
<< word_syms_rxfilename_;
}
KALDI_ASSERT(word_syms_);
if (stat(winfo_rxfilename_.c_str(), &buffer) == 0) {
KALDI_LOG << "Loading winfo " << winfo_rxfilename_;
kaldi::WordBoundaryInfoNewOpts opts;
winfo_ = new kaldi::WordBoundaryInfo(opts, winfo_rxfilename_);
}
batched_decoder_config.num_worker_threads = -1;
batched_decoder_config.max_batch_size = 32;
batched_decoder_config.num_channels = 600;
batched_decoder_config.reset_on_endpoint = true;
batched_decoder_config.use_gpu_feature_extraction = true;
feature_info_.feature_type = "mfcc";
ReadConfigFromFile(model_path_str_ + "/conf/mfcc.conf", &feature_info_.mfcc_opts);
feature_info_.silence_weighting_config.silence_weight = 1e-3;
feature_info_.silence_weighting_config.silence_phones_str = batched_decoder_config.decoder_opts.endpointing_config.silence_phones;
OnlineIvectorExtractionConfig ivector_extraction_opts;
ivector_extraction_opts.splice_config_rxfilename = model_path_str_ + "/ivector/splice.conf";
ivector_extraction_opts.cmvn_config_rxfilename = model_path_str_ + "/ivector/online_cmvn.conf";
ivector_extraction_opts.lda_mat_rxfilename = model_path_str_ + "/ivector/final.mat";
ivector_extraction_opts.global_cmvn_stats_rxfilename = model_path_str_ + "/ivector/global_cmvn.stats";
ivector_extraction_opts.diag_ubm_rxfilename = model_path_str_ + "/ivector/final.dubm";
ivector_extraction_opts.ivector_extractor_rxfilename = model_path_str_ + "/ivector/final.ie";
ivector_extraction_opts.max_count = 100;
feature_info_.use_ivectors = true;
feature_info_.ivector_extractor_info.Init(ivector_extraction_opts);
batched_decoder_config.decoder_opts.max_active = 7000;
batched_decoder_config.decoder_opts.default_beam = 13.0;
batched_decoder_config.decoder_opts.lattice_beam = 6.0;
batched_decoder_config.compute_opts.acoustic_scale = 1.0;
batched_decoder_config.compute_opts.frame_subsampling_factor = 3;
int32 nnet_left_context, nnet_right_context;
nnet3::ComputeSimpleNnetContext(nnet_->GetNnet(), &nnet_left_context,
&nnet_right_context);
batched_decoder_config.compute_opts.frames_per_chunk = std::max(51, (nnet_right_context + 3 - nnet_right_context % 3));
cuda_pipeline_ = new BatchedThreadedNnet3CudaOnlinePipeline
(batched_decoder_config, feature_info_, *hclg_fst_, *nnet_, *trans_model_);
cuda_pipeline_->SetSymbolTable(*word_syms_);
CudaOnlinePipelineDynamicBatcherConfig dynamic_batcher_config;
dynamic_batcher_ = new CudaOnlinePipelineDynamicBatcher(dynamic_batcher_config,
*cuda_pipeline_);
samples_per_chunk_ = cuda_pipeline_->GetNSampsPerChunk();
last_id_ = 0;
}
uint64_t BatchModel::GetID(BatchRecognizer *recognizer) {
return last_id_++;
}
BatchModel::~BatchModel() {
delete trans_model_;
delete nnet_;
delete word_syms_;
delete winfo_;
delete hclg_fst_;
delete cuda_pipeline_;
delete dynamic_batcher_;
}
void BatchModel::WaitForCompletion()
{
dynamic_batcher_->WaitForCompletion();
}
+71
View File
@@ -0,0 +1,71 @@
// 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.
#ifndef VOSK_BATCH_MODEL_H
#define VOSK_BATCH_MODEL_H
#include "base/kaldi-common.h"
#include "util/common-utils.h"
#include "fstext/fstext-lib.h"
#include "fstext/fstext-utils.h"
#include "decoder/lattice-faster-decoder.h"
#include "feat/feature-mfcc.h"
#include "lat/kaldi-lattice.h"
#include "lat/word-align-lattice.h"
#include "lat/compose-lattice-pruned.h"
#include "nnet3/am-nnet-simple.h"
#include "nnet3/nnet-am-decodable-simple.h"
#include "nnet3/nnet-utils.h"
#include "cudadecoder/cuda-online-pipeline-dynamic-batcher.h"
#include "cudadecoder/batched-threaded-nnet3-cuda-online-pipeline.h"
#include "cudadecoder/batched-threaded-nnet3-cuda-pipeline2.h"
#include "cudadecoder/cuda-pipeline-common.h"
#include "model.h"
using namespace kaldi;
using namespace kaldi::cuda_decoder;
class BatchRecognizer;
class BatchModel {
public:
BatchModel(const char *model_path);
~BatchModel();
uint64_t GetID(BatchRecognizer *recognizer);
void WaitForCompletion();
private:
friend class BatchRecognizer;
std::string model_path_str_;
OnlineNnet2FeaturePipelineInfo feature_info_;
kaldi::TransitionModel *trans_model_ = nullptr;
kaldi::nnet3::AmNnetSimple *nnet_ = nullptr;
const fst::SymbolTable *word_syms_ = nullptr;
fst::Fst<fst::StdArc> *hclg_fst_ = nullptr;
kaldi::WordBoundaryInfo *winfo_ = nullptr;
BatchedThreadedNnet3CudaOnlinePipeline *cuda_pipeline_ = nullptr;
CudaOnlinePipelineDynamicBatcher *dynamic_batcher_ = nullptr;
int32 samples_per_chunk_;
uint64_t last_id_;
};
#endif /* VOSK_BATCH_MODEL_H */
+202
View File
@@ -0,0 +1,202 @@
// Copyright 2019-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.
// 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.
#include "batch_recognizer.h"
#include "fstext/fstext-utils.h"
#include "lat/sausages.h"
#include "json.h"
BatchRecognizer::BatchRecognizer(BatchModel *model, float
sample_frequency) : model_(model), sample_frequency_(sample_frequency),
initialized_(false), callbacks_set_(false), nlsml_(false) {
id_ = model->GetID(this);
resampler_ = new LinearResample(
sample_frequency, 16000.0f,
std::min(sample_frequency / 2, 16000.0f / 2), 6);
}
BatchRecognizer::~BatchRecognizer() {
delete resampler_;
// Drop the ID
}
void BatchRecognizer::FinishStream()
{
SubVector<BaseFloat> chunk = buffer_.Range(0, buffer_.Dim());
model_->dynamic_batcher_->Push(id_, !initialized_, true, chunk);
}
void BatchRecognizer::PushLattice(CompactLattice &clat, BaseFloat offset)
{
fst::ScaleLattice(fst::GraphLatticeScale(0.9), &clat);
CompactLattice aligned_lat;
WordAlignLattice(clat, *model_->trans_model_, *model_->winfo_, 0, &aligned_lat);
MinimumBayesRisk mbr(aligned_lat);
const vector<BaseFloat> &conf = mbr.GetOneBestConfidences();
const vector<int32> &words = mbr.GetOneBest();
const vector<pair<BaseFloat, BaseFloat> > &times =
mbr.GetOneBestTimes();
int size = words.size();
if (nlsml_) {
std::stringstream ss;
std::stringstream text;
ss << "<?xml version=\"1.0\"?>\n";
ss << "<result grammar=\"default\">\n";
BaseFloat confidence = 0.0;
for (int i = 0; i < size; i++) {
if (i) {
text << " ";
}
confidence += conf[i];
text << model_->word_syms_->Find(words[i]);
}
confidence /= size;
ss << "<interpretation grammar=\"default\" confidence=\"" << confidence << "\">\n";
ss << "<input mode=\"speech\">" << text.str() << "</input>\n";
ss << "<instance>" << text.str() << "</instance>\n";
ss << "</interpretation>\n";
ss << "</result>\n";
results_.push(ss.str());
} else {
json::JSON obj;
stringstream text;
// Create JSON object
for (int i = 0; i < size; i++) {
json::JSON word;
word["word"] = model_->word_syms_->Find(words[i]);
word["start"] = round(times[i].first) * 0.03 + offset;
word["end"] = round(times[i].second) * 0.03 + offset;
word["conf"] = conf[i];
obj["result"].append(word);
if (i) {
text << " ";
}
text << model_->word_syms_->Find(words[i]);
}
obj["text"] = text.str();
// KALDI_LOG << "Result " << id << " " << obj.dump();
results_.push(obj.dump());
}
}
void BatchRecognizer::SetNLSML(bool nlsml)
{
nlsml_ = nlsml;
}
void BatchRecognizer::AcceptWaveform(const char *data, int len)
{
uint64_t id = id_;
if (!callbacks_set_) {
// Define the callback for results.
#if 0
model_->cuda_pipeline_->SetBestPathCallback(
id,
[&, id](const std::string &str, bool partial,
bool endpoint_detected) {
if (partial) {
KALDI_LOG << "id #" << id << " [partial] : " << str << ":";
}
if (endpoint_detected) {
KALDI_LOG << "id #" << id << " [endpoint detected]";
}
if (!partial) {
KALDI_LOG << "id #" << id << " : " << str;
}
});
#endif
model_->cuda_pipeline_->SetLatticeCallback(
id,
[&, id](SegmentedLatticeCallbackParams& params) {
if (params.results.empty()) {
KALDI_WARN << "Empty result for callback";
return;
}
CompactLattice *clat = params.results[0].GetLatticeResult();
BaseFloat offset = params.results[0].GetTimeOffsetSeconds();
PushLattice(*clat, offset);
},
CudaPipelineResult::RESULT_TYPE_LATTICE);
callbacks_set_ = true;
}
Vector<BaseFloat> input_wave(len / 2);
for (int i = 0; i < len / 2; i++)
input_wave(i) = *(((short *)data) + i);
Vector<BaseFloat> resampled_wave;
resampler_->Resample(input_wave, true, &resampled_wave);
int32 end = buffer_.Dim();
buffer_.Resize(end + resampled_wave.Dim(), kCopyData);
buffer_.Range(end, resampled_wave.Dim()).CopyFromVec(resampled_wave);
// Pick chunks and submit them to the batcher
int32 i = 0;
while (i + model_->samples_per_chunk_ <= buffer_.Dim()) {
model_->dynamic_batcher_->Push(id_, !initialized_, false,
buffer_.Range(i, model_->samples_per_chunk_));
initialized_ = true;
i += model_->samples_per_chunk_;
}
// Keep remaining data
if (i > 0) {
int32 tail = buffer_.Dim() - i;
for (int j = 0; j < tail; j++) {
buffer_(j) = buffer_(i + j);
}
buffer_.Resize(tail, kCopyData);
}
}
const char* BatchRecognizer::FrontResult()
{
if (results_.empty()) {
return "";
}
return results_.front().c_str();
}
void BatchRecognizer::Pop()
{
if (results_.empty()) {
return;
}
results_.pop();
}
int BatchRecognizer::GetNumPendingChunks()
{
return model_->dynamic_batcher_->GetNumPendingChunks(id_);
}
+55
View File
@@ -0,0 +1,55 @@
// 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.
#ifndef VOSK_BATCH_RECOGNIZER_H
#define VOSK_BATCH_RECOGNIZER_H
#include "base/kaldi-common.h"
#include "util/common-utils.h"
#include "feat/resample.h"
#include <queue>
#include "batch_model.h"
using namespace kaldi;
class BatchRecognizer {
public:
BatchRecognizer(BatchModel *model, float sample_frequency);
~BatchRecognizer();
void AcceptWaveform(const char *data, int len);
int GetNumPendingChunks();
const char *FrontResult();
void Pop();
void FinishStream();
void SetNLSML(bool nlsml);
private:
void PushLattice(CompactLattice &clat, BaseFloat offset);
BatchModel *model_;
uint64_t id_;
bool initialized_;
bool callbacks_set_;
bool nlsml_;
float sample_frequency_;
std::queue<std::string> results_;
LinearResample *resampler_;
kaldi::Vector<BaseFloat> buffer_;
};
#endif /* VOSK_BATCH_RECOGNIZER_H */
+4 -4
View File
@@ -424,7 +424,7 @@ class JSON
Class Type = Class::Null;
};
JSON Array() {
inline JSON Array() {
return JSON::Make( JSON::Class::Array );
}
@@ -435,11 +435,11 @@ JSON Array( T... args ) {
return arr;
}
JSON Object() {
inline JSON Object() {
return JSON::Make( JSON::Class::Object );
}
std::ostream& operator<<( std::ostream &os, const JSON &json ) {
inline std::ostream& operator<<( std::ostream &os, const JSON &json ) {
os << json.dump();
return os;
}
@@ -647,7 +647,7 @@ namespace {
}
}
JSON JSON::Load( const string &str ) {
inline JSON JSON::Load( const string &str ) {
size_t offset = 0;
return parse_next( str, offset );
}
+36 -23
View File
@@ -109,10 +109,10 @@ Model::Model(const char *model_path) : model_path_str_(model_path) {
struct stat buffer;
string am_v2_path = model_path_str_ + "/am/final.mdl";
string mfcc_v2_path = model_path_str_ + "/conf/mfcc.conf";
string model_conf_v2_path = model_path_str_ + "/conf/model.conf";
string am_v1_path = model_path_str_ + "/final.mdl";
string mfcc_v1_path = model_path_str_ + "/mfcc.conf";
if (stat(am_v2_path.c_str(), &buffer) == 0 && stat(mfcc_v2_path.c_str(), &buffer) == 0) {
if (stat(am_v2_path.c_str(), &buffer) == 0 && stat(model_conf_v2_path.c_str(), &buffer) == 0) {
ConfigureV2();
ReadDataFiles();
} else if (stat(am_v1_path.c_str(), &buffer) == 0 && stat(mfcc_v1_path.c_str(), &buffer) == 0) {
@@ -168,13 +168,13 @@ void Model::ConfigureV1()
std_fst_rxfilename_ = model_path_str_ + "/rescore/G.fst";
final_ie_rxfilename_ = model_path_str_ + "/ivector/final.ie";
mfcc_conf_rxfilename_ = model_path_str_ + "/mfcc.conf";
fbank_conf_rxfilename_ = model_path_str_ + "/fbank.conf";
global_cmvn_stats_rxfilename_ = model_path_str_ + "/global_cmvn.stats";
pitch_conf_rxfilename_ = model_path_str_ + "/pitch.conf";
rnnlm_word_feats_rxfilename_ = model_path_str_ + "/rnnlm/word_feats.txt";
rnnlm_feat_embedding_rxfilename_ = model_path_str_ + "/rnnlm/feat_embedding.final.mat";
rnnlm_config_rxfilename_ = model_path_str_ + "/rnnlm/special_symbol_opts.conf";
rnnlm_lm_rxfilename_ = model_path_str_ + "/rnnlm/final.raw";
rnnlm_lm_fst_rxfilename_ = model_path_str_ + "/rescore/G.fst";
}
void Model::ConfigureV2()
@@ -197,13 +197,13 @@ void Model::ConfigureV2()
std_fst_rxfilename_ = model_path_str_ + "/rescore/G.fst";
final_ie_rxfilename_ = model_path_str_ + "/ivector/final.ie";
mfcc_conf_rxfilename_ = model_path_str_ + "/conf/mfcc.conf";
fbank_conf_rxfilename_ = model_path_str_ + "/conf/fbank.conf";
global_cmvn_stats_rxfilename_ = model_path_str_ + "/am/global_cmvn.stats";
pitch_conf_rxfilename_ = model_path_str_ + "/conf/pitch.conf";
rnnlm_word_feats_rxfilename_ = model_path_str_ + "/rnnlm/word_feats.txt";
rnnlm_feat_embedding_rxfilename_ = model_path_str_ + "/rnnlm/feat_embedding.final.mat";
rnnlm_config_rxfilename_ = model_path_str_ + "/rnnlm/special_symbol_opts.conf";
rnnlm_lm_rxfilename_ = model_path_str_ + "/rnnlm/final.raw";
rnnlm_lm_fst_rxfilename_ = model_path_str_ + "/rescore/G.fst";
}
void Model::ReadDataFiles()
@@ -215,9 +215,17 @@ void Model::ReadDataFiles()
" lattice-beam=" << nnet3_decoding_config_.lattice_beam;
KALDI_LOG << "Silence phones " << endpoint_config_.silence_phones;
feature_info_.feature_type = "mfcc";
ReadConfigFromFile(mfcc_conf_rxfilename_, &feature_info_.mfcc_opts);
feature_info_.mfcc_opts.frame_opts.allow_downsample = true; // It is safe to downsample
if (stat(mfcc_conf_rxfilename_.c_str(), &buffer) == 0) {
feature_info_.feature_type = "mfcc";
ReadConfigFromFile(mfcc_conf_rxfilename_, &feature_info_.mfcc_opts);
feature_info_.mfcc_opts.frame_opts.allow_downsample = true; // It is safe to downsample
} else if (stat(fbank_conf_rxfilename_.c_str(), &buffer) == 0) {
feature_info_.feature_type = "fbank";
ReadConfigFromFile(fbank_conf_rxfilename_, &feature_info_.fbank_opts);
feature_info_.fbank_opts.frame_opts.allow_downsample = true; // It is safe to downsample
} else {
KALDI_ERR << "Failed to find feature config file";
}
feature_info_.silence_weighting_config.silence_weight = 1e-3;
feature_info_.silence_weighting_config.silence_phones_str = endpoint_config_.silence_phones;
@@ -233,9 +241,9 @@ void Model::ReadDataFiles()
SetDropoutTestMode(true, &(nnet_->GetNnet()));
nnet3::CollapseModel(nnet3::CollapseModelConfig(), &(nnet_->GetNnet()));
}
decodable_info_ = new nnet3::DecodableNnetSimpleLoopedInfo(decodable_opts_,
nnet_);
if (stat(final_ie_rxfilename_.c_str(), &buffer) == 0) {
KALDI_LOG << "Loading i-vector extractor from " << final_ie_rxfilename_;
@@ -263,7 +271,8 @@ void Model::ReadDataFiles()
if (stat(pitch_conf_rxfilename_.c_str(), &buffer) == 0) {
KALDI_LOG << "Using pitch in feature pipeline";
feature_info_.add_pitch = true;
ReadConfigFromFile(pitch_conf_rxfilename_, &feature_info_.pitch_opts);
ReadConfigsFromFile(pitch_conf_rxfilename_,
&feature_info_.pitch_opts, &feature_info_.pitch_process_opts);
}
if (stat(hclg_fst_rxfilename_.c_str(), &buffer) == 0) {
@@ -273,7 +282,10 @@ void Model::ReadDataFiles()
KALDI_LOG << "Loading HCL and G from " << hcl_fst_rxfilename_ << " " << g_fst_rxfilename_;
hcl_fst_ = fst::StdFst::Read(hcl_fst_rxfilename_);
g_fst_ = fst::StdFst::Read(g_fst_rxfilename_);
ReadIntegerVectorSimple(disambig_rxfilename_, &disambig_);
if (!ReadIntegerVectorSimple(disambig_rxfilename_, &disambig_)) {
KALDI_ERR << "Could not read disambig symbol table from file "
<< disambig_rxfilename_;
}
}
if (hclg_fst_ && hclg_fst_->OutputSymbols()) {
@@ -288,7 +300,9 @@ void Model::ReadDataFiles()
<< word_syms_rxfilename_;
word_syms_loaded_ = word_syms_;
}
KALDI_ASSERT(word_syms_);
if (!word_syms_) {
KALDI_ERR << "Word symbol table empty";
}
if (stat(winfo_rxfilename_.c_str(), &buffer) == 0) {
KALDI_LOG << "Loading winfo " << winfo_rxfilename_;
@@ -296,12 +310,19 @@ void Model::ReadDataFiles()
winfo_ = new kaldi::WordBoundaryInfo(opts, winfo_rxfilename_);
}
if (stat(carpa_rxfilename_.c_str(), &buffer) == 0) {
KALDI_LOG << "Loading subtract G.fst model from " << std_fst_rxfilename_;
graph_lm_fst_ = fst::ReadAndPrepareLmFst(std_fst_rxfilename_);
KALDI_LOG << "Loading CARPA model from " << carpa_rxfilename_;
ReadKaldiObject(carpa_rxfilename_, &const_arpa_);
}
// RNNLM Rescoring
if (stat(rnnlm_lm_rxfilename_.c_str(), &buffer) == 0) {
KALDI_LOG << "Loading RNNLM model from " << rnnlm_lm_rxfilename_;
ReadKaldiObject(rnnlm_lm_rxfilename_, &rnnlm);
rnnlm_lm_fst_ = fst::ReadAndPrepareLmFst(rnnlm_lm_fst_rxfilename_);
Matrix<BaseFloat> feature_embedding_mat;
ReadKaldiObject(rnnlm_feat_embedding_rxfilename_, &feature_embedding_mat);
SparseMatrix<BaseFloat> word_feature_mat;
@@ -319,17 +340,9 @@ void Model::ReadDataFiles()
ReadConfigFromFile(rnnlm_config_rxfilename_, &rnnlm_compute_opts);
} else if (stat(carpa_rxfilename_.c_str(), &buffer) == 0) {
KALDI_LOG << "Loading CARPA model from " << carpa_rxfilename_;
std_lm_fst_ = fst::ReadFstKaldi(std_fst_rxfilename_);
fst::Project(std_lm_fst_, fst::ProjectType::OUTPUT);
if (std_lm_fst_->Properties(fst::kILabelSorted, true) == 0) {
fst::ILabelCompare<fst::StdArc> ilabel_comp;
fst::ArcSort(std_lm_fst_, ilabel_comp);
}
ReadKaldiObject(carpa_rxfilename_, &const_arpa_);
rnnlm_enabled_ = true;
}
}
void Model::Ref()
@@ -363,5 +376,5 @@ Model::~Model() {
delete hclg_fst_;
delete hcl_fst_;
delete g_fst_;
delete std_lm_fst_;
delete graph_lm_fst_;
}
+7 -7
View File
@@ -21,7 +21,7 @@
#include "online2/onlinebin-util.h"
#include "online2/online-timing.h"
#include "online2/online-endpoint.h"
#include "online2/online-nnet3-decoding.h"
#include "online2/online-nnet3-incremental-decoding.h"
#include "online2/online-feature-pipeline.h"
#include "lat/lattice-functions.h"
#include "lat/sausages.h"
@@ -36,7 +36,7 @@
using namespace kaldi;
using namespace std;
class KaldiRecognizer;
class Recognizer;
class Model {
@@ -52,7 +52,7 @@ protected:
void ConfigureV2();
void ReadDataFiles();
friend class KaldiRecognizer;
friend class Recognizer;
string model_path_str_;
string nnet3_rxfilename_;
@@ -66,17 +66,17 @@ protected:
string std_fst_rxfilename_;
string final_ie_rxfilename_;
string mfcc_conf_rxfilename_;
string fbank_conf_rxfilename_;
string global_cmvn_stats_rxfilename_;
string pitch_conf_rxfilename_;
string rnnlm_word_feats_rxfilename_;
string rnnlm_feat_embedding_rxfilename_;
string rnnlm_config_rxfilename_;
string rnnlm_lm_fst_rxfilename_;
string rnnlm_lm_rxfilename_;
kaldi::OnlineEndpointConfig endpoint_config_;
kaldi::LatticeFasterDecoderConfig nnet3_decoding_config_;
kaldi::LatticeIncrementalDecoderConfig nnet3_decoding_config_;
kaldi::nnet3::NnetSimpleLoopedComputationOptions decodable_opts_;
kaldi::OnlineNnet2FeaturePipelineInfo feature_info_;
@@ -92,13 +92,13 @@ protected:
fst::Fst<fst::StdArc> *hcl_fst_ = nullptr;
fst::Fst<fst::StdArc> *g_fst_ = nullptr;
fst::VectorFst<fst::StdArc> *std_lm_fst_ = nullptr;
fst::VectorFst<fst::StdArc> *graph_lm_fst_ = nullptr;
kaldi::ConstArpaLm const_arpa_;
kaldi::rnnlm::RnnlmComputeStateComputationOptions rnnlm_compute_opts;
CuMatrix<BaseFloat> word_embedding_mat;
fst::VectorFst<fst::StdArc> *rnnlm_lm_fst_ = NULL;
kaldi::nnet3::Nnet rnnlm;
bool rnnlm_enabled_ = false;
std::atomic<int> ref_cnt_;
};
+340 -145
View File
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#include "kaldi_recognizer.h"
#include "recognizer.h"
#include "json.h"
#include "fstext/fstext-utils.h"
#include "lat/sausages.h"
@@ -21,7 +21,7 @@
using namespace fst;
using namespace kaldi::nnet3;
KaldiRecognizer::KaldiRecognizer(Model *model, float sample_frequency) : model_(model), spk_model_(0), sample_frequency_(sample_frequency) {
Recognizer::Recognizer(Model *model, float sample_frequency) : model_(model), spk_model_(0), sample_frequency_(sample_frequency) {
model_->Ref();
@@ -36,7 +36,7 @@ KaldiRecognizer::KaldiRecognizer(Model *model, float sample_frequency) : model_(
}
}
decoder_ = new kaldi::SingleUtteranceNnet3Decoder(model_->nnet3_decoding_config_,
decoder_ = new kaldi::SingleUtteranceNnet3IncrementalDecoder(model_->nnet3_decoding_config_,
*model_->trans_model_,
*model_->decodable_info_,
model_->hclg_fst_ ? *model_->hclg_fst_ : *decode_fst_,
@@ -46,7 +46,7 @@ KaldiRecognizer::KaldiRecognizer(Model *model, float sample_frequency) : model_(
InitRescoring();
}
KaldiRecognizer::KaldiRecognizer(Model *model, float sample_frequency, char const *grammar) : model_(model), spk_model_(0), sample_frequency_(sample_frequency)
Recognizer::Recognizer(Model *model, float sample_frequency, char const *grammar) : model_(model), spk_model_(0), sample_frequency_(sample_frequency)
{
model_->Ref();
@@ -54,50 +54,12 @@ KaldiRecognizer::KaldiRecognizer(Model *model, float sample_frequency, char cons
silence_weighting_ = new kaldi::OnlineSilenceWeighting(*model_->trans_model_, model_->feature_info_.silence_weighting_config, 3);
if (model_->hcl_fst_) {
json::JSON obj;
obj = json::JSON::Load(grammar);
if (obj.length() <= 0) {
KALDI_WARN << "Expecting array of strings, got: '" << grammar << "'";
} else {
KALDI_LOG << obj;
LanguageModelOptions opts;
opts.ngram_order = 2;
opts.discount = 0.5;
LanguageModelEstimator estimator(opts);
for (int i = 0; i < obj.length(); i++) {
bool ok;
string line = obj[i].ToString(ok);
if (!ok) {
KALDI_ERR << "Expecting array of strings, got: '" << obj << "'";
}
std::vector<int32> sentence;
stringstream ss(line);
string token;
while (getline(ss, token, ' ')) {
int32 id = model_->word_syms_->Find(token);
if (id == kNoSymbol) {
KALDI_WARN << "Ignoring word missing in vocabulary: '" << token << "'";
} else {
sentence.push_back(id);
}
}
estimator.AddCounts(sentence);
}
g_fst_ = new StdVectorFst();
estimator.Estimate(g_fst_);
decode_fst_ = LookaheadComposeFst(*model_->hcl_fst_, *g_fst_, model_->disambig_);
}
UpdateGrammarFst(grammar);
} else {
KALDI_WARN << "Runtime graphs are not supported by this model";
}
decoder_ = new kaldi::SingleUtteranceNnet3Decoder(model_->nnet3_decoding_config_,
decoder_ = new kaldi::SingleUtteranceNnet3IncrementalDecoder(model_->nnet3_decoding_config_,
*model_->trans_model_,
*model_->decodable_info_,
model_->hclg_fst_ ? *model_->hclg_fst_ : *decode_fst_,
@@ -107,7 +69,7 @@ KaldiRecognizer::KaldiRecognizer(Model *model, float sample_frequency, char cons
InitRescoring();
}
KaldiRecognizer::KaldiRecognizer(Model *model, float sample_frequency, SpkModel *spk_model) : model_(model), spk_model_(spk_model), sample_frequency_(sample_frequency) {
Recognizer::Recognizer(Model *model, float sample_frequency, SpkModel *spk_model) : model_(model), spk_model_(spk_model), sample_frequency_(sample_frequency) {
model_->Ref();
spk_model->Ref();
@@ -123,7 +85,7 @@ KaldiRecognizer::KaldiRecognizer(Model *model, float sample_frequency, SpkModel
}
}
decoder_ = new kaldi::SingleUtteranceNnet3Decoder(model_->nnet3_decoding_config_,
decoder_ = new kaldi::SingleUtteranceNnet3IncrementalDecoder(model_->nnet3_decoding_config_,
*model_->trans_model_,
*model_->decodable_info_,
model_->hclg_fst_ ? *model_->hclg_fst_ : *decode_fst_,
@@ -135,27 +97,27 @@ KaldiRecognizer::KaldiRecognizer(Model *model, float sample_frequency, SpkModel
InitRescoring();
}
KaldiRecognizer::~KaldiRecognizer() {
Recognizer::~Recognizer() {
delete decoder_;
delete feature_pipeline_;
delete silence_weighting_;
delete g_fst_;
delete decode_fst_;
delete spk_feature_;
delete lm_fst_;
delete info;
delete lm_to_subtract_det_backoff;
delete lm_to_subtract_det_scale;
delete lm_to_add_orig;
delete lm_to_add;
delete lm_to_subtract_;
delete carpa_to_add_;
delete carpa_to_add_scale_;
delete rnnlm_info_;
delete rnnlm_to_add_;
delete rnnlm_to_add_scale_;
model_->Unref();
if (spk_model_)
spk_model_->Unref();
}
void KaldiRecognizer::InitState()
void Recognizer::InitState()
{
frame_offset_ = 0;
samples_processed_ = 0;
@@ -164,27 +126,28 @@ void KaldiRecognizer::InitState()
state_ = RECOGNIZER_INITIALIZED;
}
void KaldiRecognizer::InitRescoring()
void Recognizer::InitRescoring()
{
if (model_->rnnlm_lm_fst_) {
float lm_scale = 0.5;
int lm_order = 4;
if (model_->graph_lm_fst_) {
info = new kaldi::rnnlm::RnnlmComputeStateInfo(model_->rnnlm_compute_opts, model_->rnnlm, model_->word_embedding_mat);
lm_to_subtract_det_backoff = new fst::BackoffDeterministicOnDemandFst<fst::StdArc>(*model_->rnnlm_lm_fst_);
lm_to_subtract_det_scale = new fst::ScaleDeterministicOnDemandFst(-lm_scale, lm_to_subtract_det_backoff);
lm_to_add_orig = new kaldi::rnnlm::KaldiRnnlmDeterministicFst(lm_order, *info);
lm_to_add = new fst::ScaleDeterministicOnDemandFst(lm_scale, lm_to_add_orig);
} else if (model_->std_lm_fst_) {
fst::CacheOptions cache_opts(true, 50000);
fst::CacheOptions cache_opts(true, -1);
fst::ArcMapFstOptions mapfst_opts(cache_opts);
fst::StdToLatticeMapper<kaldi::BaseFloat> mapper;
lm_fst_ = new fst::ArcMapFst<fst::StdArc, kaldi::LatticeArc, fst::StdToLatticeMapper<kaldi::BaseFloat> >(*model_->std_lm_fst_, mapper, mapfst_opts);
fst::StdToLatticeMapper<BaseFloat> mapper;
lm_to_subtract_ = new fst::ArcMapFst<fst::StdArc, LatticeArc, fst::StdToLatticeMapper<BaseFloat> >(*model_->graph_lm_fst_, mapper, mapfst_opts);
carpa_to_add_ = new ConstArpaLmDeterministicFst(model_->const_arpa_);
if (model_->rnnlm_enabled_) {
int lm_order = 4;
rnnlm_info_ = new kaldi::rnnlm::RnnlmComputeStateInfo(model_->rnnlm_compute_opts, model_->rnnlm, model_->word_embedding_mat);
rnnlm_to_add_ = new kaldi::rnnlm::KaldiRnnlmDeterministicFst(lm_order, *rnnlm_info_);
rnnlm_to_add_scale_ = new fst::ScaleDeterministicOnDemandFst(0.5, rnnlm_to_add_);
carpa_to_add_scale_ = new fst::ScaleDeterministicOnDemandFst(-0.5, carpa_to_add_);
}
}
}
void KaldiRecognizer::CleanUp()
void Recognizer::CleanUp()
{
delete silence_weighting_;
silence_weighting_ = new kaldi::OnlineSilenceWeighting(*model_->trans_model_, model_->feature_info_.silence_weighting_config, 3);
@@ -207,7 +170,7 @@ void KaldiRecognizer::CleanUp()
delete feature_pipeline_;
feature_pipeline_ = new kaldi::OnlineNnet2FeaturePipeline (model_->feature_info_);
decoder_ = new kaldi::SingleUtteranceNnet3Decoder(model_->nnet3_decoding_config_,
decoder_ = new kaldi::SingleUtteranceNnet3IncrementalDecoder(model_->nnet3_decoding_config_,
*model_->trans_model_,
*model_->decodable_info_,
model_->hclg_fst_ ? *model_->hclg_fst_ : *decode_fst_,
@@ -222,7 +185,7 @@ void KaldiRecognizer::CleanUp()
}
}
void KaldiRecognizer::UpdateSilenceWeights()
void Recognizer::UpdateSilenceWeights()
{
if (silence_weighting_->Active() && feature_pipeline_->NumFramesReady() > 0 &&
feature_pipeline_->IvectorFeature() != nullptr) {
@@ -235,17 +198,27 @@ void KaldiRecognizer::UpdateSilenceWeights()
}
}
void KaldiRecognizer::SetMaxAlternatives(int max_alternatives)
void Recognizer::SetMaxAlternatives(int max_alternatives)
{
max_alternatives_ = max_alternatives;
}
void KaldiRecognizer::SetWords(bool words)
void Recognizer::SetWords(bool words)
{
words_ = words;
}
void KaldiRecognizer::SetSpkModel(SpkModel *spk_model)
void Recognizer::SetPartialWords(bool partial_words)
{
partial_words_ = partial_words;
}
void Recognizer::SetNLSML(bool nlsml)
{
nlsml_ = nlsml;
}
void Recognizer::SetSpkModel(SpkModel *spk_model)
{
if (state_ == RECOGNIZER_RUNNING) {
KALDI_ERR << "Can't add speaker model to already running recognizer";
@@ -256,7 +229,97 @@ void KaldiRecognizer::SetSpkModel(SpkModel *spk_model)
spk_feature_ = new OnlineMfcc(spk_model_->spkvector_mfcc_opts);
}
bool KaldiRecognizer::AcceptWaveform(const char *data, int len)
void Recognizer::SetGrm(char const *grammar)
{
if (state_ == RECOGNIZER_RUNNING) {
KALDI_ERR << "Can't add speaker model to already running recognizer";
return;
}
if (!model_->hcl_fst_) {
KALDI_WARN << "Runtime graphs are not supported by this model";
return;
}
delete decode_fst_;
if (!strcmp(grammar, "[]")) {
decode_fst_ = LookaheadComposeFst(*model_->hcl_fst_, *model_->g_fst_, model_->disambig_);
} else {
UpdateGrammarFst(grammar);
}
samples_round_start_ += samples_processed_;
samples_processed_ = 0;
frame_offset_ = 0;
delete decoder_;
delete feature_pipeline_;
delete silence_weighting_;
silence_weighting_ = new kaldi::OnlineSilenceWeighting(*model_->trans_model_, model_->feature_info_.silence_weighting_config, 3);
feature_pipeline_ = new kaldi::OnlineNnet2FeaturePipeline (model_->feature_info_);
decoder_ = new kaldi::SingleUtteranceNnet3IncrementalDecoder(model_->nnet3_decoding_config_,
*model_->trans_model_,
*model_->decodable_info_,
*decode_fst_,
feature_pipeline_);
if (spk_model_) {
delete spk_feature_;
spk_feature_ = new OnlineMfcc(spk_model_->spkvector_mfcc_opts);
}
state_ = RECOGNIZER_INITIALIZED;
}
void Recognizer::UpdateGrammarFst(char const *grammar)
{
json::JSON obj;
obj = json::JSON::Load(grammar);
if (obj.length() <= 0) {
KALDI_WARN << "Expecting array of strings, got: '" << grammar << "'";
return;
}
KALDI_LOG << obj;
LanguageModelOptions opts;
opts.ngram_order = 2;
opts.discount = 0.5;
LanguageModelEstimator estimator(opts);
for (int i = 0; i < obj.length(); i++) {
bool ok;
string line = obj[i].ToString(ok);
if (!ok) {
KALDI_ERR << "Expecting array of strings, got: '" << obj << "'";
}
std::vector<int32> sentence;
stringstream ss(line);
string token;
while (getline(ss, token, ' ')) {
int32 id = model_->word_syms_->Find(token);
if (id == kNoSymbol) {
KALDI_WARN << "Ignoring word missing in vocabulary: '" << token << "'";
} else {
sentence.push_back(id);
}
}
estimator.AddCounts(sentence);
}
g_fst_ = new StdVectorFst();
estimator.Estimate(g_fst_);
decode_fst_ = LookaheadComposeFst(*model_->hcl_fst_, *g_fst_, model_->disambig_);
}
bool Recognizer::AcceptWaveform(const char *data, int len)
{
Vector<BaseFloat> wave;
wave.Resize(len / 2, kUndefined);
@@ -265,7 +328,7 @@ bool KaldiRecognizer::AcceptWaveform(const char *data, int len)
return AcceptWaveform(wave);
}
bool KaldiRecognizer::AcceptWaveform(const short *sdata, int len)
bool Recognizer::AcceptWaveform(const short *sdata, int len)
{
Vector<BaseFloat> wave;
wave.Resize(len, kUndefined);
@@ -274,7 +337,7 @@ bool KaldiRecognizer::AcceptWaveform(const short *sdata, int len)
return AcceptWaveform(wave);
}
bool KaldiRecognizer::AcceptWaveform(const float *fdata, int len)
bool Recognizer::AcceptWaveform(const float *fdata, int len)
{
Vector<BaseFloat> wave;
wave.Resize(len, kUndefined);
@@ -283,7 +346,7 @@ bool KaldiRecognizer::AcceptWaveform(const float *fdata, int len)
return AcceptWaveform(wave);
}
bool KaldiRecognizer::AcceptWaveform(Vector<BaseFloat> &wdata)
bool Recognizer::AcceptWaveform(Vector<BaseFloat> &wdata)
{
// Cleanup if we finalized previous utterance or the whole feature pipeline
if (!(state_ == RECOGNIZER_RUNNING || state_ == RECOGNIZER_INITIALIZED)) {
@@ -342,7 +405,7 @@ static void RunNnetComputation(const MatrixBase<BaseFloat> &features,
#define MIN_SPK_FEATS 50
bool KaldiRecognizer::GetSpkVector(Vector<BaseFloat> &out_xvector, int *num_spk_frames)
bool Recognizer::GetSpkVector(Vector<BaseFloat> &out_xvector, int *num_spk_frames)
{
vector<int32> nonsilence_frames;
if (silence_weighting_->Active() && feature_pipeline_->NumFramesReady() > 0) {
@@ -407,14 +470,23 @@ bool KaldiRecognizer::GetSpkVector(Vector<BaseFloat> &out_xvector, int *num_spk_
return true;
}
const char *KaldiRecognizer::MbrResult(CompactLattice &rlat)
// If we can't align, we still need to prepare for MBR
static void CopyLatticeForMbr(CompactLattice &lat, CompactLattice *lat_out)
{
*lat_out = lat;
RmEpsilon(lat_out, true);
fst::CreateSuperFinal(lat_out);
TopSortCompactLatticeIfNeeded(lat_out);
}
const char *Recognizer::MbrResult(CompactLattice &rlat)
{
CompactLattice aligned_lat;
if (model_->winfo_) {
WordAlignLattice(rlat, *model_->trans_model_, *model_->winfo_, 0, &aligned_lat);
} else {
aligned_lat = rlat;
CopyLatticeForMbr(rlat, &aligned_lat);
}
MinimumBayesRisk mbr(aligned_lat);
@@ -522,7 +594,7 @@ static bool CompactLatticeToWordAlignmentWeight(const CompactLattice &clat,
}
const char *KaldiRecognizer::NbestResult(CompactLattice &clat)
const char *Recognizer::NbestResult(CompactLattice &clat)
{
Lattice lat;
Lattice nbest_lat;
@@ -533,15 +605,15 @@ const char *KaldiRecognizer::NbestResult(CompactLattice &clat)
fst::ConvertNbestToVector(nbest_lat, &nbest_lats);
json::JSON obj;
std::stringstream ss;
for (int k = 0; k < nbest_lats.size(); k++) {
Lattice nlat = nbest_lats[k];
RmEpsilon(&nlat);
CompactLattice nclat;
CompactLattice aligned_nclat;
ConvertLattice(nlat, &nclat);
CompactLattice nclat;
fst::Invert(&nlat);
DeterminizeLattice(nlat, &nclat);
CompactLattice aligned_nclat;
if (model_->winfo_) {
WordAlignLattice(nclat, *model_->trans_model_, *model_->winfo_, 0, &aligned_nclat);
} else {
@@ -559,7 +631,7 @@ const char *KaldiRecognizer::NbestResult(CompactLattice &clat)
stringstream text;
json::JSON entry;
for (int i = 0; i < words.size(); i++) {
for (int i = 0, first = 1; i < words.size(); i++) {
json::JSON word;
if (words[i] == 0)
continue;
@@ -569,8 +641,12 @@ const char *KaldiRecognizer::NbestResult(CompactLattice &clat)
word["end"] = samples_round_start_ / sample_frequency_ + (frame_offset_ + begin_times[i] + lengths[i]) * 0.03;
entry["result"].append(word);
}
if (i)
if (first)
first = 0;
else
text << " ";
text << model_->word_syms_->Find(words[i]);
}
@@ -582,56 +658,122 @@ const char *KaldiRecognizer::NbestResult(CompactLattice &clat)
return StoreReturn(obj.dump());
}
const char* KaldiRecognizer::GetResult()
const char *Recognizer::NlsmlResult(CompactLattice &clat)
{
Lattice lat;
Lattice nbest_lat;
std::vector<Lattice> nbest_lats;
ConvertLattice (clat, &lat);
fst::ShortestPath(lat, &nbest_lat, max_alternatives_);
fst::ConvertNbestToVector(nbest_lat, &nbest_lats);
std::stringstream ss;
ss << "<?xml version=\"1.0\"?>\n";
ss << "<result grammar=\"default\">\n";
for (int k = 0; k < nbest_lats.size(); k++) {
Lattice nlat = nbest_lats[k];
CompactLattice nclat;
fst::Invert(&nlat);
DeterminizeLattice(nlat, &nclat);
CompactLattice aligned_nclat;
if (model_->winfo_) {
WordAlignLattice(nclat, *model_->trans_model_, *model_->winfo_, 0, &aligned_nclat);
} else {
aligned_nclat = nclat;
}
std::vector<int32> words;
std::vector<int32> begin_times;
std::vector<int32> lengths;
CompactLattice::Weight weight;
CompactLatticeToWordAlignmentWeight(aligned_nclat, &words, &begin_times, &lengths, &weight);
float likelihood = -(weight.Weight().Value1() + weight.Weight().Value2());
stringstream text;
for (int i = 0, first = 1; i < words.size(); i++) {
if (words[i] == 0)
continue;
if (first)
first = 0;
else
text << " ";
text << model_->word_syms_->Find(words[i]);
}
ss << "<interpretation grammar=\"default\" confidence=\"" << likelihood << "\">\n";
ss << "<input mode=\"speech\">" << text.str() << "</input>\n";
ss << "<instance>" << text.str() << "</instance>\n";
ss << "</interpretation>\n";
}
ss << "</result>\n";
return StoreReturn(ss.str());
}
const char* Recognizer::GetResult()
{
if (decoder_->NumFramesDecoded() == 0) {
return StoreEmptyReturn();
}
kaldi::CompactLattice clat;
kaldi::CompactLattice rlat;
decoder_->GetLattice(true, &clat);
// Original from decoder, subtracted graph weight, rescored with carpa, rescored with rnnlm
CompactLattice clat, slat, tlat, rlat;
if (model_->rnnlm_lm_fst_) {
kaldi::ComposeLatticePrunedOptions compose_opts;
compose_opts.lattice_compose_beam = 3.0;
compose_opts.max_arcs = 3000;
clat = decoder_->GetLattice(decoder_->NumFramesDecoded(), true);
TopSortCompactLatticeIfNeeded(&clat);
fst::ComposeDeterministicOnDemandFst<fst::StdArc> combined_lms(lm_to_subtract_det_scale, lm_to_add);
CompactLattice composed_clat;
ComposeCompactLatticePruned(compose_opts, clat,
&combined_lms, &rlat);
lm_to_add_orig->Clear();
} else if (model_->std_lm_fst_) {
Lattice lat1;
if (lm_to_subtract_ && carpa_to_add_) {
Lattice lat, composed_lat;
ConvertLattice(clat, &lat1);
fst::ScaleLattice(fst::GraphLatticeScale(-1.0), &lat1);
fst::ArcSort(&lat1, fst::OLabelCompare<kaldi::LatticeArc>());
kaldi::Lattice composed_lat;
fst::Compose(lat1, *lm_fst_, &composed_lat);
// Delete old score
ConvertLattice(clat, &lat);
fst::ScaleLattice(fst::GraphLatticeScale(-1.0), &lat);
fst::Compose(lat, *lm_to_subtract_, &composed_lat);
fst::Invert(&composed_lat);
kaldi::CompactLattice determinized_lat;
DeterminizeLattice(composed_lat, &determinized_lat);
fst::ScaleLattice(fst::GraphLatticeScale(-1), &determinized_lat);
fst::ArcSort(&determinized_lat, fst::OLabelCompare<kaldi::CompactLatticeArc>());
DeterminizeLattice(composed_lat, &slat);
fst::ScaleLattice(fst::GraphLatticeScale(-1.0), &slat);
kaldi::ConstArpaLmDeterministicFst const_arpa_fst(model_->const_arpa_);
kaldi::CompactLattice composed_clat;
kaldi::ComposeCompactLatticeDeterministic(determinized_lat, &const_arpa_fst, &composed_clat);
kaldi::Lattice composed_lat1;
ConvertLattice(composed_clat, &composed_lat1);
fst::Invert(&composed_lat1);
DeterminizeLattice(composed_lat1, &rlat);
// Add CARPA score
TopSortCompactLatticeIfNeeded(&slat);
ComposeCompactLatticeDeterministic(slat, carpa_to_add_, &tlat);
// Rescore with RNNLM score on top if needed
if (rnnlm_to_add_scale_) {
ComposeLatticePrunedOptions compose_opts;
compose_opts.lattice_compose_beam = 3.0;
compose_opts.max_arcs = 3000;
fst::ComposeDeterministicOnDemandFst<StdArc> combined_rnnlm(carpa_to_add_scale_, rnnlm_to_add_scale_);
TopSortCompactLatticeIfNeeded(&tlat);
ComposeCompactLatticePruned(compose_opts, tlat,
&combined_rnnlm, &rlat);
rnnlm_to_add_->Clear();
} else {
rlat = tlat;
}
} else {
rlat = clat;
}
fst::ScaleLattice(fst::GraphLatticeScale(0.9), &rlat); // Apply rescoring weight
// Pruned composition can return empty lattice. It should be rare
if (rlat.Start() != 0) {
return StoreEmptyReturn();
}
// Apply rescoring weight
fst::ScaleLattice(fst::GraphLatticeScale(0.9), &rlat);
if (max_alternatives_ == 0) {
return MbrResult(rlat);
} else if (nlsml_) {
return NlsmlResult(rlat);
} else {
return NbestResult(rlat);
}
@@ -639,7 +781,7 @@ const char* KaldiRecognizer::GetResult()
}
const char* KaldiRecognizer::PartialResult()
const char* Recognizer::PartialResult()
{
if (state_ != RECOGNIZER_RUNNING) {
return StoreEmptyReturn();
@@ -647,30 +789,75 @@ const char* KaldiRecognizer::PartialResult()
json::JSON res;
if (decoder_->NumFramesDecoded() == 0) {
res["partial"] = "";
return StoreReturn(res.dump());
}
if (partial_words_) {
kaldi::Lattice lat;
decoder_->GetBestPath(false, &lat);
vector<kaldi::int32> alignment, words;
LatticeWeight weight;
GetLinearSymbolSequence(lat, &alignment, &words, &weight);
ostringstream text;
for (size_t i = 0; i < words.size(); i++) {
if (i) {
text << " ";
if (decoder_->NumFramesInLattice() == 0) {
res["partial"] = "";
return StoreReturn(res.dump());
}
text << model_->word_syms_->Find(words[i]);
CompactLattice clat;
CompactLattice aligned_lat;
clat = decoder_->GetLattice(decoder_->NumFramesInLattice(), false);
if (model_->winfo_) {
WordAlignLatticePartial(clat, *model_->trans_model_, *model_->winfo_, 0, &aligned_lat);
} else {
CopyLatticeForMbr(clat, &aligned_lat);
}
MinimumBayesRisk mbr(aligned_lat);
const vector<BaseFloat> &conf = mbr.GetOneBestConfidences();
const vector<int32> &words = mbr.GetOneBest();
const vector<pair<BaseFloat, BaseFloat> > &times = mbr.GetOneBestTimes();
int size = words.size();
stringstream text;
// Create JSON object
for (int i = 0; i < size; i++) {
json::JSON word;
word["word"] = model_->word_syms_->Find(words[i]);
word["start"] = samples_round_start_ / sample_frequency_ + (frame_offset_ + times[i].first) * 0.03;
word["end"] = samples_round_start_ / sample_frequency_ + (frame_offset_ + times[i].second) * 0.03;
word["conf"] = conf[i];
res["partial_result"].append(word);
if (i) {
text << " ";
}
text << model_->word_syms_->Find(words[i]);
}
res["partial"] = text.str();
} else {
if (decoder_->NumFramesDecoded() == 0) {
res["partial"] = "";
return StoreReturn(res.dump());
}
Lattice lat;
decoder_->GetBestPath(false, &lat);
vector<kaldi::int32> alignment, words;
LatticeWeight weight;
GetLinearSymbolSequence(lat, &alignment, &words, &weight);
ostringstream text;
for (size_t i = 0; i < words.size(); i++) {
if (i) {
text << " ";
}
text << model_->word_syms_->Find(words[i]);
}
res["partial"] = text.str();
}
res["partial"] = text.str();
return StoreReturn(res.dump());
}
const char* KaldiRecognizer::Result()
const char* Recognizer::Result()
{
if (state_ != RECOGNIZER_RUNNING) {
return StoreEmptyReturn();
@@ -680,7 +867,7 @@ const char* KaldiRecognizer::Result()
return GetResult();
}
const char* KaldiRecognizer::FinalResult()
const char* Recognizer::FinalResult()
{
if (state_ != RECOGNIZER_RUNNING) {
return StoreEmptyReturn();
@@ -708,7 +895,7 @@ const char* KaldiRecognizer::FinalResult()
return last_result_.c_str();
}
void KaldiRecognizer::Reset()
void Recognizer::Reset()
{
if (state_ == RECOGNIZER_RUNNING) {
decoder_->FinalizeDecoding();
@@ -717,17 +904,25 @@ void KaldiRecognizer::Reset()
state_ = RECOGNIZER_ENDPOINT;
}
const char *KaldiRecognizer::StoreEmptyReturn()
const char *Recognizer::StoreEmptyReturn()
{
if (!max_alternatives_) {
return StoreReturn("{\"text\": \"\"}");
} else if (nlsml_) {
return StoreReturn("<?xml version=\"1.0\"?>\n"
"<result grammar=\"default\">\n"
"<interpretation confidence=\"1.0\">\n"
"<instance/>\n"
"<input><noinput/></input>\n"
"</interpretation>\n"
"</result>\n");
} else {
return StoreReturn("{\"alternatives\" : [{\"text\": \"\", \"confidence\" : 1.0}] }");
}
}
// Store result in recognizer and return as const string
const char *KaldiRecognizer::StoreReturn(const string &res)
const char *Recognizer::StoreReturn(const string &res)
{
last_result_ = res;
return last_result_.c_str();
+23 -15
View File
@@ -33,22 +33,25 @@
using namespace kaldi;
enum KaldiRecognizerState {
enum RecognizerState {
RECOGNIZER_INITIALIZED,
RECOGNIZER_RUNNING,
RECOGNIZER_ENDPOINT,
RECOGNIZER_FINALIZED
};
class KaldiRecognizer {
class Recognizer {
public:
KaldiRecognizer(Model *model, float sample_frequency);
KaldiRecognizer(Model *model, float sample_frequency, SpkModel *spk_model);
KaldiRecognizer(Model *model, float sample_frequency, char const *grammar);
~KaldiRecognizer();
Recognizer(Model *model, float sample_frequency);
Recognizer(Model *model, float sample_frequency, SpkModel *spk_model);
Recognizer(Model *model, float sample_frequency, char const *grammar);
~Recognizer();
void SetMaxAlternatives(int max_alternatives);
void SetSpkModel(SpkModel *spk_model);
void SetGrm(char const *grammar);
void SetWords(bool words);
void SetPartialWords(bool partial_words);
void SetNLSML(bool nlsml);
bool AcceptWaveform(const char *data, int len);
bool AcceptWaveform(const short *sdata, int len);
bool AcceptWaveform(const float *fdata, int len);
@@ -62,6 +65,7 @@ class KaldiRecognizer {
void InitRescoring();
void CleanUp();
void UpdateSilenceWeights();
void UpdateGrammarFst(char const *grammar);
bool AcceptWaveform(Vector<BaseFloat> &wdata);
bool GetSpkVector(Vector<BaseFloat> &out_xvector, int *frames);
const char *GetResult();
@@ -69,9 +73,10 @@ class KaldiRecognizer {
const char *StoreReturn(const string &res);
const char *MbrResult(CompactLattice &clat);
const char *NbestResult(CompactLattice &clat);
const char *NlsmlResult(CompactLattice &clat);
Model *model_ = nullptr;
SingleUtteranceNnet3Decoder *decoder_ = nullptr;
SingleUtteranceNnet3IncrementalDecoder *decoder_ = nullptr;
fst::LookaheadFst<fst::StdArc, int32> *decode_fst_ = nullptr;
fst::StdVectorFst *g_fst_ = nullptr; // dynamically constructed grammar
OnlineNnet2FeaturePipeline *feature_pipeline_ = nullptr;
@@ -82,17 +87,20 @@ class KaldiRecognizer {
OnlineBaseFeature *spk_feature_ = nullptr;
// Rescoring
fst::ArcMapFst<fst::StdArc, kaldi::LatticeArc, fst::StdToLatticeMapper<kaldi::BaseFloat> > *lm_fst_ = nullptr;
fst::ArcMapFst<fst::StdArc, LatticeArc, fst::StdToLatticeMapper<BaseFloat> > *lm_to_subtract_ = nullptr;
kaldi::ConstArpaLmDeterministicFst *carpa_to_add_ = nullptr;
fst::ScaleDeterministicOnDemandFst *carpa_to_add_scale_ = nullptr;
// RNNLM rescoring
kaldi::rnnlm::RnnlmComputeStateInfo *info = nullptr;
fst::ScaleDeterministicOnDemandFst *lm_to_subtract_det_scale = nullptr;
fst::BackoffDeterministicOnDemandFst<fst::StdArc> *lm_to_subtract_det_backoff = nullptr;
kaldi::rnnlm::KaldiRnnlmDeterministicFst* lm_to_add_orig = nullptr;
fst::DeterministicOnDemandFst<fst::StdArc> *lm_to_add = nullptr;
kaldi::rnnlm::KaldiRnnlmDeterministicFst* rnnlm_to_add_ = nullptr;
fst::DeterministicOnDemandFst<fst::StdArc> *rnnlm_to_add_scale_ = nullptr;
kaldi::rnnlm::RnnlmComputeStateInfo *rnnlm_info_ = nullptr;
// Other
int max_alternatives_ = 0; // Disable alternatives by default
bool words_ = false;
bool partial_words_ = false;
bool nlsml_ = false;
float sample_frequency_;
int32 frame_offset_;
@@ -100,7 +108,7 @@ class KaldiRecognizer {
int64 samples_processed_;
int64 samples_round_start_;
KaldiRecognizerState state_;
RecognizerState state_;
string last_result_;
};
+2 -2
View File
@@ -22,7 +22,7 @@
using namespace kaldi;
class KaldiRecognizer;
class Recognizer;
class SpkModel {
@@ -32,7 +32,7 @@ public:
void Unref();
protected:
friend class KaldiRecognizer;
friend class Recognizer;
~SpkModel() {};
kaldi::nnet3::Nnet speaker_nnet;
+166 -17
View File
@@ -13,12 +13,14 @@
// limitations under the License.
#include "vosk_api.h"
#include "kaldi_recognizer.h"
#include "recognizer.h"
#include "model.h"
#include "spk_model.h"
#if HAVE_CUDA
#include "cudamatrix/cu-device.h"
#include "batch_recognizer.h"
#endif
#include <string.h>
@@ -27,11 +29,18 @@ using namespace kaldi;
VoskModel *vosk_model_new(const char *model_path)
{
return (VoskModel *)new Model(model_path);
try {
return (VoskModel *)new Model(model_path);
} catch (...) {
return nullptr;
}
}
void vosk_model_free(VoskModel *model)
{
if (model == nullptr) {
return;
}
((Model *)model)->Unref();
}
@@ -42,82 +51,134 @@ int vosk_model_find_word(VoskModel *model, const char *word)
VoskSpkModel *vosk_spk_model_new(const char *model_path)
{
return (VoskSpkModel *)new SpkModel(model_path);
try {
return (VoskSpkModel *)new SpkModel(model_path);
} catch (...) {
return nullptr;
}
}
void vosk_spk_model_free(VoskSpkModel *model)
{
if (model == nullptr) {
return;
}
((SpkModel *)model)->Unref();
}
VoskRecognizer *vosk_recognizer_new(VoskModel *model, float sample_rate)
{
return (VoskRecognizer *)new KaldiRecognizer((Model *)model, sample_rate);
try {
return (VoskRecognizer *)new Recognizer((Model *)model, sample_rate);
} catch (...) {
return nullptr;
}
}
VoskRecognizer *vosk_recognizer_new_spk(VoskModel *model, float sample_rate, VoskSpkModel *spk_model)
{
return (VoskRecognizer *)new KaldiRecognizer((Model *)model, sample_rate, (SpkModel *)spk_model);
try {
return (VoskRecognizer *)new Recognizer((Model *)model, sample_rate, (SpkModel *)spk_model);
} catch (...) {
return nullptr;
}
}
VoskRecognizer *vosk_recognizer_new_grm(VoskModel *model, float sample_rate, const char *grammar)
{
return (VoskRecognizer *)new KaldiRecognizer((Model *)model, sample_rate, grammar);
try {
return (VoskRecognizer *)new Recognizer((Model *)model, sample_rate, grammar);
} catch (...) {
return nullptr;
}
}
void vosk_recognizer_set_max_alternatives(VoskRecognizer *recognizer, int max_alternatives)
{
((KaldiRecognizer *)recognizer)->SetMaxAlternatives(max_alternatives);
((Recognizer *)recognizer)->SetMaxAlternatives(max_alternatives);
}
void vosk_recognizer_set_words(VoskRecognizer *recognizer, int words)
{
((KaldiRecognizer *)recognizer)->SetWords((bool)words);
((Recognizer *)recognizer)->SetWords((bool)words);
}
void vosk_recognizer_set_partial_words(VoskRecognizer *recognizer, int partial_words)
{
((Recognizer *)recognizer)->SetPartialWords((bool)partial_words);
}
void vosk_recognizer_set_nlsml(VoskRecognizer *recognizer, int nlsml)
{
((Recognizer *)recognizer)->SetNLSML((bool)nlsml);
}
void vosk_recognizer_set_spk_model(VoskRecognizer *recognizer, VoskSpkModel *spk_model)
{
((KaldiRecognizer *)recognizer)->SetSpkModel((SpkModel *)spk_model);
if (recognizer == nullptr || spk_model == nullptr) {
return;
}
((Recognizer *)recognizer)->SetSpkModel((SpkModel *)spk_model);
}
void vosk_recognizer_set_grm(VoskRecognizer *recognizer, char const *grammar)
{
if (recognizer == nullptr) {
return;
}
((Recognizer *)recognizer)->SetGrm(grammar);
}
int vosk_recognizer_accept_waveform(VoskRecognizer *recognizer, const char *data, int length)
{
return ((KaldiRecognizer *)(recognizer))->AcceptWaveform(data, length);
try {
return ((Recognizer *)(recognizer))->AcceptWaveform(data, length);
} catch (...) {
return -1;
}
}
int vosk_recognizer_accept_waveform_s(VoskRecognizer *recognizer, const short *data, int length)
{
return ((KaldiRecognizer *)(recognizer))->AcceptWaveform(data, length);
try {
return ((Recognizer *)(recognizer))->AcceptWaveform(data, length);
} catch (...) {
return -1;
}
}
int vosk_recognizer_accept_waveform_f(VoskRecognizer *recognizer, const float *data, int length)
{
return ((KaldiRecognizer *)(recognizer))->AcceptWaveform(data, length);
try {
return ((Recognizer *)(recognizer))->AcceptWaveform(data, length);
} catch (...) {
return -1;
}
}
const char *vosk_recognizer_result(VoskRecognizer *recognizer)
{
return ((KaldiRecognizer *)recognizer)->Result();
return ((Recognizer *)recognizer)->Result();
}
const char *vosk_recognizer_partial_result(VoskRecognizer *recognizer)
{
return ((KaldiRecognizer *)recognizer)->PartialResult();
return ((Recognizer *)recognizer)->PartialResult();
}
const char *vosk_recognizer_final_result(VoskRecognizer *recognizer)
{
return ((KaldiRecognizer *)recognizer)->FinalResult();
return ((Recognizer *)recognizer)->FinalResult();
}
void vosk_recognizer_reset(VoskRecognizer *recognizer)
{
((KaldiRecognizer *)recognizer)->Reset();
((Recognizer *)recognizer)->Reset();
}
void vosk_recognizer_free(VoskRecognizer *recognizer)
{
delete (KaldiRecognizer *)(recognizer);
delete (Recognizer *)(recognizer);
}
void vosk_set_log_level(int log_level)
@@ -128,6 +189,8 @@ void vosk_set_log_level(int log_level)
void vosk_gpu_init()
{
#if HAVE_CUDA
// kaldi::CuDevice::EnableTensorCores(true);
// kaldi::CuDevice::EnableTf32Compute(true);
kaldi::CuDevice::Instantiate().SelectGpuId("yes");
kaldi::CuDevice::Instantiate().AllowMultithreading();
#endif
@@ -139,3 +202,89 @@ void vosk_gpu_thread_init()
kaldi::CuDevice::Instantiate();
#endif
}
VoskBatchModel *vosk_batch_model_new(const char *model_path)
{
#if HAVE_CUDA
return (VoskBatchModel *)(new BatchModel(model_path));
#else
return NULL;
#endif
}
void vosk_batch_model_free(VoskBatchModel *model)
{
#if HAVE_CUDA
delete ((BatchModel *)model);
#endif
}
void vosk_batch_model_wait(VoskBatchModel *model)
{
#if HAVE_CUDA
((BatchModel *)model)->WaitForCompletion();
#endif
}
VoskBatchRecognizer *vosk_batch_recognizer_new(VoskBatchModel *model, float sample_rate)
{
#if HAVE_CUDA
return (VoskBatchRecognizer *)(new BatchRecognizer((BatchModel *)model, sample_rate));
#else
return NULL;
#endif
}
void vosk_batch_recognizer_free(VoskBatchRecognizer *recognizer)
{
#if HAVE_CUDA
delete ((BatchRecognizer *)recognizer);
#endif
}
void vosk_batch_recognizer_accept_waveform(VoskBatchRecognizer *recognizer, const char *data, int length)
{
#if HAVE_CUDA
((BatchRecognizer *)recognizer)->AcceptWaveform(data, length);
#endif
}
void vosk_batch_recognizer_set_nlsml(VoskBatchRecognizer *recognizer, int nlsml)
{
#if HAVE_CUDA
((BatchRecognizer *)recognizer)->SetNLSML((bool)nlsml);
#endif
}
void vosk_batch_recognizer_finish_stream(VoskBatchRecognizer *recognizer)
{
#if HAVE_CUDA
((BatchRecognizer *)recognizer)->FinishStream();
#endif
}
const char *vosk_batch_recognizer_front_result(VoskBatchRecognizer *recognizer)
{
#if HAVE_CUDA
return ((BatchRecognizer *)recognizer)->FrontResult();
#else
return NULL;
#endif
}
void vosk_batch_recognizer_pop(VoskBatchRecognizer *recognizer)
{
#if HAVE_CUDA
((BatchRecognizer *)recognizer)->Pop();
#endif
}
int vosk_batch_recognizer_get_pending_chunks(VoskBatchRecognizer *recognizer)
{
#if HAVE_CUDA
return ((BatchRecognizer *)recognizer)->GetNumPendingChunks();
#else
return 0;
#endif
}
+94 -11
View File
@@ -40,10 +40,21 @@ typedef struct VoskSpkModel VoskSpkModel;
typedef struct VoskRecognizer VoskRecognizer;
/**
* Batch model object
*/
typedef struct VoskBatchModel VoskBatchModel;
/**
* Batch recognizer object
*/
typedef struct VoskBatchRecognizer VoskBatchRecognizer;
/** 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 */
* @returns model object or NULL if problem occured */
VoskModel *vosk_model_new(const char *model_path);
@@ -66,7 +77,7 @@ 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 */
* @returns model object or NULL if problem occured */
VoskSpkModel *vosk_spk_model_new(const char *model_path);
@@ -79,9 +90,13 @@ void vosk_spk_model_free(VoskSpkModel *model);
/** Creates the recognizer object
*
* 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 */
* The recognizers process the speech and return text using shared model data
* @param model VoskModel containing static data for recognizer. Model can be
* shared across recognizers, even running in different threads.
* @param sample_rate The sample rate of the audio you going to feed into the recognizer.
* Make sure this rate matches the audio content, it is a common
* issue causing accuracy problems.
* @returns recognizer object or NULL if problem occured */
VoskRecognizer *vosk_recognizer_new(VoskModel *model, float sample_rate);
@@ -90,9 +105,13 @@ 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 sample_rate The sample rate of the audio you going to feed into the recognizer
* @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 */
* @returns recognizer object or NULL if problem occured */
VoskRecognizer *vosk_recognizer_new_spk(VoskModel *model, float sample_rate, VoskSpkModel *spk_model);
@@ -106,11 +125,15 @@ 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 sample_rate The sample rate of the audio you going to feed into the recognizer
* @param model VoskModel containing static data for recognizer. Model can be
* shared across recognizers, even running in different threads.
* @param sample_rate The sample rate of the audio you going to feed into the recognizer.
* Make sure this rate matches the audio content, it is a common
* issue causing accuracy problems.
* @param grammar The string with the list of phrases to recognize as JSON array of strings,
* for example "["one two three four five", "[unk]"]".
*
* @returns recognizer object */
* @returns recognizer object or NULL if problem occured */
VoskRecognizer *vosk_recognizer_new_grm(VoskModel *model, float sample_rate, const char *grammar);
@@ -123,6 +146,15 @@ VoskRecognizer *vosk_recognizer_new_grm(VoskModel *model, float sample_rate, con
void vosk_recognizer_set_spk_model(VoskRecognizer *recognizer, VoskSpkModel *spk_model);
/** Reconfigures recognizer to use grammar
*
* @param recognizer Already running VoskRecognizer
* @param grammar Set of phrases in JSON array of strings or "[]" to use default model graph.
* See also vosk_recognizer_new_grm
*/
void vosk_recognizer_set_grm(VoskRecognizer *recognizer, char const *grammar);
/** Configures recognizer to output n-best results
*
* <pre>
@@ -174,6 +206,17 @@ void vosk_recognizer_set_max_alternatives(VoskRecognizer *recognizer, int max_al
*/
void vosk_recognizer_set_words(VoskRecognizer *recognizer, int words);
/** Like above return words and confidences in partial results
*
* @param partial_words - boolean value
*/
void vosk_recognizer_set_partial_words(VoskRecognizer *recognizer, int partial_words);
/** Set NLSML output
* @param nlsml - boolean value
*/
void vosk_recognizer_set_nlsml(VoskRecognizer *recognizer, int nlsml);
/** Accept voice data
*
@@ -181,7 +224,9 @@ void vosk_recognizer_set_words(VoskRecognizer *recognizer, int words);
*
* @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 */
* @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);
@@ -207,7 +252,7 @@ int vosk_recognizer_accept_waveform_f(VoskRecognizer *recognizer, const float *d
* }
* </pre>
*
* If alternatives enabled it returns result with alternatives, see also vosk_recognizer_set_alternatives().
* If alternatives enabled it returns result with alternatives, see also vosk_recognizer_set_max_alternatives().
*
* If word times enabled returns word time, see also vosk_recognizer_set_word_times().
*/
@@ -271,6 +316,44 @@ void vosk_gpu_init();
*/
void vosk_gpu_thread_init();
/** Creates the batch recognizer object
*
* @returns model object or NULL if problem occured */
VoskBatchModel *vosk_batch_model_new(const char *model_path);
/** Releases batch model object */
void vosk_batch_model_free(VoskBatchModel *model);
/** Wait for the processing */
void vosk_batch_model_wait(VoskBatchModel *model);
/** Creates batch recognizer object
* @returns recognizer object or NULL if problem occured */
VoskBatchRecognizer *vosk_batch_recognizer_new(VoskBatchModel *model, float sample_rate);
/** Releases batch recognizer object */
void vosk_batch_recognizer_free(VoskBatchRecognizer *recognizer);
/** Accept batch voice data */
void vosk_batch_recognizer_accept_waveform(VoskBatchRecognizer *recognizer, const char *data, int length);
/** Set NLSML output
* @param nlsml - boolean value
*/
void vosk_batch_recognizer_set_nlsml(VoskBatchRecognizer *recognizer, int nlsml);
/** Closes the stream */
void vosk_batch_recognizer_finish_stream(VoskBatchRecognizer *recognizer);
/** Return results */
const char *vosk_batch_recognizer_front_result(VoskBatchRecognizer *recognizer);
/** Release and free first retrieved result */
void vosk_batch_recognizer_pop(VoskBatchRecognizer *recognizer);
/** Get amount of pending chunks for more intelligent waiting */
int vosk_batch_recognizer_get_pending_chunks(VoskBatchRecognizer *recognizer);
#ifdef __cplusplus
}
#endif
+3
View File
@@ -0,0 +1,3 @@
A proper simple setup to train a Vosk model
More documentation later
+2
View File
@@ -0,0 +1,2 @@
#!/bin/bash
for x in exp/*/decode* exp/chain*/*/decode*; do [ -d $x ] && [[ $x =~ "$1" ]] && grep WER $x/wer_* | utils/best_wer.sh; done
+2
View File
@@ -0,0 +1,2 @@
%WER 14.10 [ 2839 / 20138, 214 ins, 487 del, 2138 sub ] exp/chain/tdnn/decode_test/wer_11_0.0
%WER 12.67 [ 2552 / 20138, 215 ins, 406 del, 1931 sub ] exp/chain/tdnn/decode_test_rescore/wer_11_0.0
+5
View File
@@ -0,0 +1,5 @@
export train_cmd="run.pl"
export decode_cmd="run.pl"
export mkgraph_cmd="run.pl"
export cuda_cmd="run.pl"
export get_egs_cmd="run.pl"
+7
View File
@@ -0,0 +1,7 @@
--use-energy=false
--num-mel-bins=40
--num-ceps=40
--low-freq=20
--high-freq=-400
--allow-upsample=true
--allow-downsample=true
View File
+86
View File
@@ -0,0 +1,86 @@
#!/bin/bash
set -euo pipefail
# This script is called from local/chain_${suffix}/run_tdnn.sh and
# local/chain_${suffix}/run_tdnn.sh (and may eventually be called by more
# scripts). It contains the common feature preparation and
# iVector-related parts of the script. See those scripts for examples
# of usage.
stage=0
train_set=train
gmm=tri3
suffix=""
. ./cmd.sh
. ./path.sh
. utils/parse_options.sh
gmm_dir=exp/${gmm}
ali_dir=exp/${gmm}_ali
if [ $stage -le 4 ]; then
echo "$0: computing a subset of data to train the diagonal UBM."
# We'll use about a quarter of the data.
mkdir -p exp/chain${suffix}/diag_ubm
temp_data_root=exp/chain${suffix}/diag_ubm
num_utts_total=$(wc -l <data/${train_set}/utt2spk)
num_utts=$[$num_utts_total/4]
utils/data/subset_data_dir.sh data/${train_set} \
$num_utts ${temp_data_root}/${train_set}_subset
echo "$0: computing a PCA transform from the data."
steps/online/nnet2/get_pca_transform.sh --cmd "$train_cmd" \
--splice-opts "--left-context=3 --right-context=3" \
--max-utts 10000 --subsample 2 \
${temp_data_root}/${train_set}_subset \
exp/chain${suffix}/pca_transform
echo "$0: training the diagonal UBM."
# Use 512 Gaussians in the UBM.
steps/online/nnet2/train_diag_ubm.sh --cmd "$train_cmd" --nj 10 \
--num-frames 700000 \
--num-threads 8 \
${temp_data_root}/${train_set}_subset 512 \
exp/chain${suffix}/pca_transform exp/chain${suffix}/diag_ubm
fi
if [ $stage -le 5 ]; then
# Train the iVector extractor. Use all of the speed-perturbed data since iVector extractors
# can be sensitive to the amount of data. The script defaults to an iVector dimension of
# 100.
echo "$0: training the iVector extractor"
steps/online/nnet2/train_ivector_extractor.sh --cmd "$train_cmd" --nj 2 \
--ivector-dim 40 \
data/${train_set} exp/chain${suffix}/diag_ubm \
exp/chain${suffix}/extractor || exit 1;
fi
if [ $stage -le 6 ]; then
# We extract iVectors on the speed-perturbed training data after combining
# short segments, which will be what we train the system on. With
# --utts-per-spk-max 2, the script pairs the utterances into twos, and treats
# each of these pairs as one speaker; this gives more diversity in iVectors..
# Note that these are extracted 'online'.
# note, we don't encode the 'max2' in the name of the ivectordir even though
# that's the data we extract the ivectors from, as it's still going to be
# valid for the non-'max2' data, the utterance list is the same.
ivectordir=exp/chain${suffix}/ivectors_${train_set}
# having a larger number of speakers is helpful for generalization, and to
# handle per-utterance decoding well (iVector starts at zero).
temp_data_root=${ivectordir}
utils/data/modify_speaker_info.sh --utts-per-spk-max 2 \
data/${train_set} ${temp_data_root}/${train_set}_max2
steps/online/nnet2/extract_ivectors_online.sh --cmd "$train_cmd" --nj 10 \
${temp_data_root}/${train_set}_max2 \
exp/chain${suffix}/extractor $ivectordir
fi
exit 0
+165
View File
@@ -0,0 +1,165 @@
#!/bin/bash
# Set -e here so that we catch if any executable fails immediately
set -euo pipefail
# (some of which are also used in this script directly).
stage=-1
decode_nj=10
train_set=train
gmm=tri3
nnet3_affix=
suffix=
# The rest are configs specific to this script. Most of the parameters
# are just hardcoded at this level, in the commands below.
affix= # affix for the TDNN directory name
tree_affix=
train_stage=-10
get_egs_stage=-10
decode_iter=
# training options
# training chunk-options
chunk_width=140,100,160
common_egs_dir=
xent_regularize=0.1
dropout_schedule='0,0@0.20,0.5@0.50,0'
# training options
srand=0
remove_egs=true
# End configuration section.
echo "$0 $@" # Print the command line for logging
. ./cmd.sh
. ./path.sh
. ./utils/parse_options.sh
# Problem: We have removed the "train_" prefix of our training set in
# the alignment directory names! Bad!
gmm_dir=exp/$gmm
ali_dir=exp/${gmm}_ali
tree_dir=exp/chain${suffix}/tree${tree_affix:+_$tree_affix}
lang=data/lang_chain${suffix}
lat_dir=exp/chain${suffix}/${gmm}_${train_set}_lats
dir=exp/chain${suffix}/tdnn${affix}
train_data_dir=data/${train_set}
for f in $gmm_dir/final.mdl $train_data_dir/feats.scp $ali_dir/ali.1.gz; do
[ ! -f $f ] && echo "$0: expected file $f to exist" && exit 1
done
if [ $stage -le 9 ]; then
local/chain/run_ivector_common.sh \
--train-set ${train_set} \
--gmm ${gmm} \
--suffix "${suffix}"
fi
if [ $stage -le 10 ]; then
echo "$0: creating lang directory $lang with chain-type topology"
rm -rf $lang
cp -r data/lang $lang
silphonelist=$(cat $lang/phones/silence.csl)
nonsilphonelist=$(cat $lang/phones/nonsilence.csl)
steps/nnet3/chain/gen_topo.py $nonsilphonelist $silphonelist >$lang/topo
fi
if [ $stage -le 11 ]; then
steps/align_fmllr_lats.sh --nj 20 --cmd "$train_cmd" ${train_data_dir} \
data/lang $gmm_dir $lat_dir
fi
if [ $stage -le 12 ]; then
steps/nnet3/chain/build_tree.sh \
--frame-subsampling-factor 3 \
--context-opts "--context-width=2 --central-position=1" \
--cmd "$train_cmd" 2500 ${train_data_dir} \
$lang $ali_dir $tree_dir
fi
if [ $stage -le 13 ]; then
echo "$0: creating neural net configs using the xconfig parser";
num_targets=$(tree-info $tree_dir/tree | grep num-pdfs | awk '{print $2}')
learning_rate_factor=$(echo "print (0.5/$xent_regularize)" | python)
affine_opts="l2-regularize=0.008 dropout-proportion=0.0 dropout-per-dim=true dropout-per-dim-continuous=true"
tdnnf_opts="l2-regularize=0.008 dropout-proportion=0.0 bypass-scale=0.75"
linear_opts="l2-regularize=0.008 orthonormal-constraint=-1.0"
prefinal_opts="l2-regularize=0.008"
output_opts="l2-regularize=0.002"
mkdir -p $dir/configs
cat <<EOF > $dir/configs/network.xconfig
input dim=40 name=ivector
input dim=40 name=input
idct-layer name=idct input=input dim=40 cepstral-lifter=22 affine-transform-file=$dir/configs/idct.mat
batchnorm-component name=batchnorm0 input=idct
spec-augment-layer name=spec-augment freq-max-proportion=0.5 time-zeroed-proportion=0.2 time-mask-max-frames=20
delta-layer name=delta input=spec-augment
no-op-component name=input2 input=Append(delta, ReplaceIndex(ivector, t, 0))
# the first splicing is moved before the lda layer, so no splicing here
relu-batchnorm-dropout-layer name=tdnn1 $affine_opts dim=512 input=input2
tdnnf-layer name=tdnnf2 $tdnnf_opts dim=512 bottleneck-dim=96 time-stride=1
tdnnf-layer name=tdnnf3 $tdnnf_opts dim=512 bottleneck-dim=96 time-stride=1
tdnnf-layer name=tdnnf4 $tdnnf_opts dim=512 bottleneck-dim=96 time-stride=1
tdnnf-layer name=tdnnf5 $tdnnf_opts dim=512 bottleneck-dim=96 time-stride=0
tdnnf-layer name=tdnnf6 $tdnnf_opts dim=512 bottleneck-dim=96 time-stride=3
tdnnf-layer name=tdnnf7 $tdnnf_opts dim=512 bottleneck-dim=96 time-stride=3
tdnnf-layer name=tdnnf8 $tdnnf_opts dim=512 bottleneck-dim=96 time-stride=3
tdnnf-layer name=tdnnf9 $tdnnf_opts dim=512 bottleneck-dim=96 time-stride=3
tdnnf-layer name=tdnnf10 $tdnnf_opts dim=512 bottleneck-dim=96 time-stride=3
tdnnf-layer name=tdnnf11 $tdnnf_opts dim=512 bottleneck-dim=96 time-stride=3
tdnnf-layer name=tdnnf12 $tdnnf_opts dim=512 bottleneck-dim=96 time-stride=3
linear-component name=prefinal-l dim=192 $linear_opts
## adding the layers for chain branch
prefinal-layer name=prefinal-chain input=prefinal-l $prefinal_opts small-dim=192 big-dim=512
output-layer name=output include-log-softmax=false dim=$num_targets $output_opts
# adding the layers for xent branch
prefinal-layer name=prefinal-xent input=prefinal-l $prefinal_opts small-dim=192 big-dim=512
output-layer name=output-xent dim=$num_targets learning-rate-factor=$learning_rate_factor $output_opts
EOF
steps/nnet3/xconfig_to_configs.py --xconfig-file $dir/configs/network.xconfig --config-dir $dir/configs/
fi
if [ $stage -le 14 ]; then
steps/nnet3/chain/train.py --stage $train_stage \
--cmd "$cuda_cmd" \
--feat.online-ivector-dir exp/chain${suffix}/ivectors_${train_set} \
--feat.cmvn-opts "--norm-means=false --norm-vars=false" \
--chain.xent-regularize $xent_regularize \
--chain.leaky-hmm-coefficient 0.1 \
--chain.l2-regularize 0.0 \
--chain.apply-deriv-weights false \
--chain.lm-opts="--num-extra-lm-states=2000" \
--egs.cmd "$get_egs_cmd" \
--egs.dir "$common_egs_dir" \
--egs.stage $get_egs_stage \
--egs.opts "--frames-overlap-per-eg 0 --constrained false" \
--egs.chunk-width $chunk_width \
--trainer.dropout-schedule $dropout_schedule \
--trainer.add-option="--optimization.memory-compression-level=2" \
--trainer.num-chunk-per-minibatch 64 \
--trainer.frames-per-iter 2500000 \
--trainer.num-epochs 20 \
--trainer.optimization.num-jobs-initial 1 \
--trainer.optimization.num-jobs-final 1 \
--trainer.optimization.initial-effective-lrate 0.001 \
--trainer.optimization.final-effective-lrate 0.0001 \
--trainer.max-param-change 2.0 \
--cleanup.remove-egs $remove_egs \
--feat-dir $train_data_dir \
--tree-dir $tree_dir \
--lat-dir $lat_dir \
--dir $dir || exit 1;
fi
+85
View File
@@ -0,0 +1,85 @@
#!/usr/bin/env bash
# Copyright 2014 Vassil Panayotov
# 2014 Johns Hopkins University (author: Daniel Povey)
# Apache 2.0
if [ "$#" -ne 2 ]; then
echo "Usage: $0 <src-dir> <dst-dir>"
echo "e.g.: $0 /export/a15/vpanayotov/data/LibriSpeech/dev-clean data/dev-clean"
exit 1
fi
src=$1
dst=$2
# all utterances are FLAC compressed
if ! which flac >&/dev/null; then
echo "Please install 'flac' on ALL worker nodes!"
exit 1
fi
spk_file=$src/../SPEAKERS.TXT
mkdir -p $dst || exit 1;
[ ! -d $src ] && echo "$0: no such directory $src" && exit 1;
[ ! -f $spk_file ] && echo "$0: expected file $spk_file to exist" && exit 1;
wav_scp=$dst/wav.scp; [[ -f "$wav_scp" ]] && rm $wav_scp
trans=$dst/text; [[ -f "$trans" ]] && rm $trans
utt2spk=$dst/utt2spk; [[ -f "$utt2spk" ]] && rm $utt2spk
spk2gender=$dst/spk2gender; [[ -f $spk2gender ]] && rm $spk2gender
for reader_dir in $(find -L $src -mindepth 1 -maxdepth 1 -type d | sort); do
reader=$(basename $reader_dir)
if ! [ $reader -eq $reader ]; then # not integer.
echo "$0: unexpected subdirectory name $reader"
exit 1;
fi
reader_gender=$(egrep "^$reader[ ]+\|" $spk_file | awk -F'|' '{gsub(/[ ]+/, ""); print tolower($2)}')
if [ "$reader_gender" != 'm' ] && [ "$reader_gender" != 'f' ]; then
echo "Unexpected gender: '$reader_gender'"
exit 1;
fi
for chapter_dir in $(find -L $reader_dir/ -mindepth 1 -maxdepth 1 -type d | sort); do
chapter=$(basename $chapter_dir)
if ! [ "$chapter" -eq "$chapter" ]; then
echo "$0: unexpected chapter-subdirectory name $chapter"
exit 1;
fi
find -L $chapter_dir/ -iname "*.flac" | sort | xargs -I% basename % .flac | \
awk -v "dir=$chapter_dir" '{printf "%s flac -c -d -s %s/%s.flac |\n", $0, dir, $0}' >>$wav_scp|| exit 1
chapter_trans=$chapter_dir/${reader}-${chapter}.trans.txt
[ ! -f $chapter_trans ] && echo "$0: expected file $chapter_trans to exist" && exit 1
cat $chapter_trans >>$trans
# NOTE: For now we are using per-chapter utt2spk. That is each chapter is considered
# to be a different speaker. This is done for simplicity and because we want
# e.g. the CMVN to be calculated per-chapter
awk -v "reader=$reader" -v "chapter=$chapter" '{printf "%s %s-%s\n", $1, reader, chapter}' \
<$chapter_trans >>$utt2spk || exit 1
# reader -> gender map (again using per-chapter granularity)
echo "${reader}-${chapter} $reader_gender" >>$spk2gender
done
done
spk2utt=$dst/spk2utt
utils/utt2spk_to_spk2utt.pl <$utt2spk >$spk2utt || exit 1
ntrans=$(wc -l <$trans)
nutt2spk=$(wc -l <$utt2spk)
! [ "$ntrans" -eq "$nutt2spk" ] && \
echo "Inconsistent #transcripts($ntrans) and #utt2spk($nutt2spk)" && exit 1;
utils/validate_data_dir.sh --no-feats $dst || exit 1;
echo "$0: successfully prepared data in $dst"
exit 0
+100
View File
@@ -0,0 +1,100 @@
#!/usr/bin/env bash
# Copyright 2014 Johns Hopkins University (author: Daniel Povey)
# 2017 Luminar Technologies, Inc. (author: Daniel Galvez)
# Apache 2.0
remove_archive=false
if [ "$1" == --remove-archive ]; then
remove_archive=true
shift
fi
if [ $# -ne 3 ]; then
echo "Usage: $0 [--remove-archive] <data-base> <url-base> <corpus-part>"
echo "e.g.: $0 /export/a05/dgalvez/ www.openslr.org/resources/31 dev-clean-2"
echo "With --remove-archive it will remove the archive after successfully un-tarring it."
echo "<corpus-part> can be one of: dev-clean-2, test-clean-5, dev-other, test-other,"
echo " train-clean-100, train-clean-360, train-other-500."
fi
data=$1
url=$2
part=$3
if [ ! -d "$data" ]; then
echo "$0: no such directory $data"
exit 1;
fi
data=$(readlink -f $data)
part_ok=false
list="dev-clean-2 train-clean-5"
for x in $list; do
if [ "$part" == $x ]; then part_ok=true; fi
done
if ! $part_ok; then
echo "$0: expected <corpus-part> to be one of $list, but got '$part'"
exit 1;
fi
if [ -z "$url" ]; then
echo "$0: empty URL base."
exit 1;
fi
if [ -f $data/LibriSpeech/$part/.complete ]; then
echo "$0: data part $part was already successfully extracted, nothing to do."
exit 0;
fi
#sizes="126046265 332747356"
sizes="126046265 332954390"
if [ -f $data/$part.tar.gz ]; then
size=$(/bin/ls -l $data/$part.tar.gz | awk '{print $5}')
size_ok=false
for s in $sizes; do if [ $s == $size ]; then size_ok=true; fi; done
if ! $size_ok; then
echo "$0: removing existing file $data/$part.tar.gz because its size in bytes $size"
echo "does not equal the size of one of the archives."
rm $data/$part.tar.gz
else
echo "$data/$part.tar.gz exists and appears to be complete."
fi
fi
if [ ! -f $data/$part.tar.gz ]; then
if ! which wget >/dev/null; then
echo "$0: wget is not installed."
exit 1;
fi
full_url=$url/$part.tar.gz
echo "$0: downloading data from $full_url. This may take some time, please be patient."
cd $data
if ! wget --no-check-certificate $full_url; then
echo "$0: error executing wget $full_url"
exit 1;
fi
cd -
fi
cd $data
if ! tar -xvzf $part.tar.gz; then
echo "$0: error un-tarring archive $data/$part.tar.gz"
exit 1;
fi
touch $data/LibriSpeech/$part/.complete
echo "$0: Successfully downloaded and un-tarred $data/$part.tar.gz"
if $remove_archive; then
echo "$0: removing $data/$part.tar.gz file since --remove-archive option was supplied."
rm $data/$part.tar.gz
fi
+73
View File
@@ -0,0 +1,73 @@
#!/usr/bin/env bash
# Copyright 2014 Vassil Panayotov
# 2017 Daniel Povey
# Apache 2.0
if [ $# -ne "3" ]; then
echo "Usage: $0 <base-url> <download_dir> <local?"
echo "e.g.: $0 http://www.openslr.org/resources/11 ./corpus/ data/local/lm"
exit 1
fi
base_url=$1
dst_dir=$2
local_dir=$3
# given a filename returns the corresponding file size in bytes
# The switch cases below can be autogenerated by entering the data directory and running:
# for f in *; do echo "\"$f\") echo \"$(du -b $f | awk '{print $1}')\";;"; done
function filesize() {
case $1 in
"3-gram.arpa.gz") echo "759636181";;
"3-gram.pruned.1e-7.arpa.gz") echo "34094057";;
"3-gram.pruned.3e-7.arpa.gz") echo "13654242";;
"librispeech-lexicon.txt") echo "5627653";;
"librispeech-vocab.txt") echo "1737588";;
*) echo "";;
esac
}
function check_and_download () {
[[ $# -eq 1 ]] || { echo "check_and_download() expects exactly one argument!"; return 1; }
fname=$1
echo "Downloading file '$fname' into '$dst_dir'..."
expect_size="$(filesize $fname)"
[[ ! -z "$expect_size" ]] || { echo "Unknown file size for '$fname'"; return 1; }
if [[ -s $dst_dir/$fname ]]; then
# In the following statement, the first version works on linux, and the part
# after '||' works on Linux.
f=$dst_dir/$fname
fsize=$(set -o pipefail; du -b $f 2>/dev/null | awk '{print $1}' || stat '-f %z' $f)
if [[ "$fsize" -eq "$expect_size" ]]; then
echo "'$fname' already exists and appears to be complete"
return 0
else
echo "WARNING: '$fname' exists, but the size is wrong - re-downloading ..."
fi
fi
wget --no-check-certificate -O $dst_dir/$fname $base_url/$fname || {
echo "Error while trying to download $fname!"
return 1
}
f=$dst_dir/$fname
# In the following statement, the first version works on linux, and the part after '||'
# works on Linux.
fsize=$(set -o pipefail; du -b $f 2>/dev/null | awk '{print $1}' || stat '-f %z' $f)
[[ "$fsize" -eq "$expect_size" ]] || { echo "$fname: file size mismatch!"; return 1; }
return 0
}
mkdir -p $dst_dir $local_dir
for f in 3-gram.pruned.1e-7.arpa.gz 3-gram.pruned.3e-7.arpa.gz \
librispeech-vocab.txt librispeech-lexicon.txt; do
check_and_download $f || exit 1
done
dst_dir=$(readlink -f $dst_dir)
ln -sf $dst_dir/3-gram.pruned.1e-7.arpa.gz $local_dir/lm_tgmed.arpa.gz
ln -sf $dst_dir/3-gram.pruned.3e-7.arpa.gz $local_dir/lm_tgsmall.arpa.gz
ln -sf $dst_dir/librispeech-lexicon.txt $local_dir/librispeech-lexicon.txt
ln -sf $dst_dir/librispeech-vocab.txt $local_dir/librispeech-vocab.txt
exit 0
+18
View File
@@ -0,0 +1,18 @@
#!/usr/bin/env bash
echo "Preparing phone lists and lexicon"
src_dir=$1
dst_dir=$2
mkdir -p $dst_dir
cat $src_dir/librispeech-lexicon.txt | sed 's:[012]::g' > $dst_dir/lexicon_raw_nosil.txt
(echo SIL; echo SPN;) > $dst_dir/silence_phones.txt
echo SIL > $dst_dir/optional_silence.txt
echo "" > $dst_dir/extra_questions
cat $dst_dir/lexicon_raw_nosil.txt | awk '{ for(n=2;n<=NF;n++){ phones[$n] = 1; }} END{for (p in phones) print p;}' | \
grep -v SIL | sort > $dst_dir/nonsilence_phones.txt
(echo '!SIL SIL'; echo '<UNK> SPN'; ) | cat - $dst_dir/lexicon_raw_nosil.txt | sort | uniq >$dst_dir/lexicon.txt
+155
View File
@@ -0,0 +1,155 @@
#!/bin/bash
# Copyright 2012-2014 Johns Hopkins University (Author: Daniel Povey, Yenda Trmal)
# Apache 2.0
[ -f ./path.sh ] && . ./path.sh
# begin configuration section.
cmd=run.pl
stage=0
decode_mbr=false
reverse=false
stats=true
beam=6
word_ins_penalty=0.0
min_lmwt=7
max_lmwt=13
iter=final
#end configuration section.
echo "$0 $@" # Print the command line for logging
[ -f ./path.sh ] && . ./path.sh
. parse_options.sh || exit 1;
if [ $# -ne 3 ]; then
echo "Usage: local/score.sh [--cmd (run.pl|queue.pl...)] <data-dir> <lang-dir|graph-dir> <decode-dir>"
echo " Options:"
echo " --cmd (run.pl|queue.pl...) # specify how to run the sub-processes."
echo " --stage (0|1|2) # start scoring script from part-way through."
echo " --decode_mbr (true/false) # maximum bayes risk decoding (confusion network)."
echo " --min_lmwt <int> # minumum LM-weight for lattice rescoring "
echo " --max_lmwt <int> # maximum LM-weight for lattice rescoring "
echo " --reverse (true/false) # score with time reversed features "
exit 1;
fi
data=$1
lang_or_graph=$2
dir=$3
symtab=$lang_or_graph/words.txt
for f in $symtab $dir/lat.1.gz $data/text; do
[ ! -f $f ] && echo "score.sh: no such file $f" && exit 1;
done
ref_filtering_cmd="cat"
[ -x local/wer_output_filter ] && ref_filtering_cmd="local/wer_output_filter"
[ -x local/wer_ref_filter ] && ref_filtering_cmd="local/wer_ref_filter"
hyp_filtering_cmd="cat"
[ -x local/wer_output_filter ] && hyp_filtering_cmd="local/wer_output_filter"
[ -x local/wer_hyp_filter ] && hyp_filtering_cmd="local/wer_hyp_filter"
if $decode_mbr ; then
echo "$0: scoring with MBR, word insertion penalty=$word_ins_penalty"
else
echo "$0: scoring with word insertion penalty=$word_ins_penalty"
fi
mkdir -p $dir/scoring_kaldi
cat $data/text | $ref_filtering_cmd > $dir/scoring_kaldi/test_filt.txt || exit 1;
if [ $stage -le 0 ]; then
for wip in $(echo $word_ins_penalty | sed 's/,/ /g'); do
mkdir -p $dir/scoring_kaldi/penalty_$wip/log
if $decode_mbr ; then
$cmd LMWT=$min_lmwt:$max_lmwt $dir/scoring_kaldi/penalty_$wip/log/best_path.LMWT.log \
acwt=\`perl -e \"print 1.0/LMWT\"\`\; \
lattice-scale --inv-acoustic-scale=LMWT "ark:gunzip -c $dir/lat.*.gz|" ark:- \| \
lattice-add-penalty --word-ins-penalty=$wip ark:- ark:- \| \
lattice-prune --beam=$beam ark:- ark:- \| \
lattice-mbr-decode --word-symbol-table=$symtab \
ark:- ark,t:- \| \
utils/int2sym.pl -f 2- $symtab \| \
$hyp_filtering_cmd '>' $dir/scoring_kaldi/penalty_$wip/LMWT.txt || exit 1;
else
$cmd LMWT=$min_lmwt:$max_lmwt $dir/scoring_kaldi/penalty_$wip/log/best_path.LMWT.log \
lattice-scale --inv-acoustic-scale=LMWT "ark:gunzip -c $dir/lat.*.gz|" ark:- \| \
lattice-add-penalty --word-ins-penalty=$wip ark:- ark:- \| \
lattice-best-path --word-symbol-table=$symtab ark:- ark,t:- \| \
utils/int2sym.pl -f 2- $symtab \| \
$hyp_filtering_cmd '>' $dir/scoring_kaldi/penalty_$wip/LMWT.txt || exit 1;
fi
if $reverse; then # rarely-used option, ignore this.
for lmwt in `seq $min_lmwt $max_lmwt`; do
mv $dir/scoring_kaldi/penalty_$wip/$lmwt.txt $dir/scoring_kaldi/penalty_$wip/$lmwt.txt.orig
awk '{ printf("%s ",$1); for(i=NF; i>1; i--){ printf("%s ",$i); } printf("\n"); }' \
<$dir/scoring_kaldi/penalty_$wip/$lmwt.txt.orig >$dir/scoring_kaldi/penalty_$wip/$lmwt.txt
done
fi
$cmd LMWT=$min_lmwt:$max_lmwt $dir/scoring_kaldi/penalty_$wip/log/score.LMWT.log \
cat $dir/scoring_kaldi/penalty_$wip/LMWT.txt \| \
compute-wer --text --mode=present \
ark:$dir/scoring_kaldi/test_filt.txt ark,p:- ">&" $dir/wer_LMWT_$wip || exit 1;
done
fi
if [ $stage -le 1 ]; then
for wip in $(echo $word_ins_penalty | sed 's/,/ /g'); do
for lmwt in $(seq $min_lmwt $max_lmwt); do
# adding /dev/null to the command list below forces grep to output the filename
grep WER $dir/wer_${lmwt}_${wip} /dev/null
done
done | utils/best_wer.sh >& $dir/scoring_kaldi/best_wer || exit 1
best_wer_file=$(awk '{print $NF}' $dir/scoring_kaldi/best_wer)
best_wip=$(echo $best_wer_file | awk -F_ '{print $NF}')
best_lmwt=$(echo $best_wer_file | awk -F_ '{N=NF-1; print $N}')
if [ -z "$best_lmwt" ]; then
echo "$0: we could not get the details of the best WER from the file $dir/wer_*. Probably something went wrong."
exit 1;
fi
if $stats; then
mkdir -p $dir/scoring_kaldi/wer_details
echo $best_lmwt > $dir/scoring_kaldi/wer_details/lmwt # record best language model weight
echo $best_wip > $dir/scoring_kaldi/wer_details/wip # record best word insertion penalty
$cmd $dir/scoring_kaldi/log/stats1.log \
cat $dir/scoring_kaldi/penalty_$best_wip/$best_lmwt.txt \| \
align-text --special-symbol="'***'" ark:$dir/scoring_kaldi/test_filt.txt ark:- ark,t:- \| \
utils/scoring/wer_per_utt_details.pl --special-symbol "'***'" \| tee $dir/scoring_kaldi/wer_details/per_utt \|\
utils/scoring/wer_per_spk_details.pl $data/utt2spk \> $dir/scoring_kaldi/wer_details/per_spk || exit 1;
$cmd $dir/scoring_kaldi/log/stats2.log \
cat $dir/scoring_kaldi/wer_details/per_utt \| \
utils/scoring/wer_ops_details.pl --special-symbol "'***'" \| \
sort -b -i -k 1,1 -k 4,4rn -k 2,2 -k 3,3 \> $dir/scoring_kaldi/wer_details/ops || exit 1;
$cmd $dir/scoring_kaldi/log/wer_bootci.log \
compute-wer-bootci --mode=present \
ark:$dir/scoring_kaldi/test_filt.txt ark:$dir/scoring_kaldi/penalty_$best_wip/$best_lmwt.txt \
'>' $dir/scoring_kaldi/wer_details/wer_bootci || exit 1;
fi
fi
# If we got here, the scoring was successful.
# As a small aid to prevent confusion, we remove all wer_{?,??} files;
# these originate from the previous version of the scoring files
rm $dir/wer_{?,??} 2>/dev/null
exit 0;
+6
View File
@@ -0,0 +1,6 @@
export KALDI_ROOT=`pwd`/../../..
[ -f $KALDI_ROOT/tools/env.sh ] && . $KALDI_ROOT/tools/env.sh
export PATH=$PWD/utils/:$KALDI_ROOT/tools/openfst/bin:$PWD:$PATH
[ ! -f $KALDI_ROOT/tools/config/common_path.sh ] && echo >&2 "The standard file $KALDI_ROOT/tools/config/common_path.sh is not present -> Exit!" && exit 1
. $KALDI_ROOT/tools/config/common_path.sh
export LC_ALL=C

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