Compare commits

...

417 Commits

Author SHA1 Message Date
Nickolay Shmyrev ea5177e22d Fix memory leak 2026-06-03 07:37:02 +02:00
Nickolay Shmyrev cdb9a183e7 Pure gpu setup 2025-07-23 10:11:59 +02:00
Nickolay Shmyrev ee0eac64f3 Upgrade onnxruntime
Optimal threads configuration for runtime, a bit faster
2025-02-24 10:27:42 +01:00
Nickolay Shmyrev 7303e38495 Fixes VAD for long segments 2024-11-03 03:55:22 +01:00
Nickolay Shmyrev 241123ccd2 Implement configurable endpointer delays 2024-08-12 13:43:53 +02:00
Nickolay Shmyrev 692717c500 Since we add short context, don't expect segment length to be exactly 20s 2024-07-18 16:27:38 +02:00
Nickolay Shmyrev 742c112ba4 Lenght bucketing in batching. Speedup 2024-06-17 16:36:56 +02:00
Nickolay Shmyrev 7a9a8cd6a1 Add more processing threads and simplify java multistream test 2024-06-06 09:09:18 +02:00
Nickolay Shmyrev cf76d35985 Refactor batching to use max size. Add multi test 2024-06-06 08:17:19 +02:00
Nickolay Shmyrev b72fdf9248 Add C test, fix memory leak 2024-06-03 10:07:01 +02:00
Nickolay Shmyrev 34ebc48539 One more threshold tuning 2024-05-20 17:00:25 +02:00
Nickolay Shmyrev 8409db4c15 Tune VAD accuracy 2024-05-20 09:50:47 +02:00
Nickolay Shmyrev dad4ba398b Temporary fixes 2024-04-15 21:31:55 +02:00
Nickolay Shmyrev 762bb60a73 Add flush for the speech tail 2024-04-01 22:53:20 +02:00
Nickolay Shmyrev 32b099bf85 Fix lock from randomly initialized field 2024-03-28 01:06:48 +01:00
Nickolay Shmyrev 030ad8d7a0 Add multitest 2024-03-27 09:56:12 +01:00
Nickolay Shmyrev a82c11bb11 Add new result retrieval API for java 2024-03-26 08:26:33 +01:00
Nickolay Shmyrev 57bfc1a96a Even better batching 2024-03-26 00:45:15 +01:00
Nickolay Shmyrev 1e71ad78f6 Implement arbitrary chunk size 2024-03-25 13:12:32 +01:00
Nickolay Shmyrev ba19a2bd37 Add multi test 2024-03-04 14:43:30 +01:00
Nickolay Shmyrev 6db8bfa0f5 Add batch API 2024-02-08 08:58:12 +01:00
Nickolay Shmyrev f4832ca301 Add batch processing 2024-02-05 16:09:48 +01:00
Nickolay Shmyrev aabd069cff Fix crash 2024-01-22 06:37:04 +01:00
Nickolay Shmyrev 02690ac087 New bindings 2024-01-15 02:39:56 +01:00
Nickolay Shmyrev aba84973b1 Update to latest gradio 2023-12-15 00:23:37 +01:00
Nickolay Shmyrev 339b1c5d00 Fix t_max endpointer config and introduce t_start_max for silent inputs. 2023-12-13 04:45:30 +01:00
Nickolay Shmyrev a35728fa67 Simplify server argument 2023-11-27 15:40:58 +01:00
Nickolay Shmyrev 322a57b512 Fix json output format 2023-11-27 15:39:49 +01:00
Gonzalo a21d1ad051 feat: enable usage of remote server and not only localhost (#1375) 2023-11-27 17:22:59 +03:00
Nickolay Shmyrev a47fa9147b Add endpointer delays parameter 2023-11-27 15:29:31 +01:00
Wire dfb76f0126 go: do not include libdl if building on Windows (#1464) 2023-11-12 01:26:17 +03:00
Nickolay Shmyrev c3e3fc002f Add online endpointer reconfiguration 2023-10-16 13:40:32 +02:00
Nickolay Shmyrev 62fc308bdf Sometimes python interpreter destroys the handle on exit before
the object. See

https://foss.heptapod.net/pypy/cffi/-/issues/340
2023-09-04 00:33:42 +02:00
Dmitri Soloviev 60c0e33901 simplify go example (#1430) 2023-08-12 23:04:46 +03:00
jsbxyyx 5d09ee8b2b close source input stream (#1) (#1428) 2023-08-11 00:55:33 +03:00
JohnGebbie 12f29a3415 go: Add SetGrm (#1346) 2023-04-29 05:35:01 +03:00
Nickolay Shmyrev d56221bcf2 Add mkl docker build 2023-04-06 02:38:06 +02:00
Nickolay Shmyrev a2eb39d2d7 Set number of MKL threads 2023-04-06 02:10:53 +02:00
Anton Romanov 128c216c61 Fix vosk-transcriber cli json format (#1304)
Fix error UnboundLocalError: local variable 'monologue' referenced before assignment in transcriber.py line 102
2023-03-29 03:12:16 +03:00
mason-acronode da87d0dad3 c# supports GPU inference (#1307)
* Added Batch P/Invoke to be able to use GPU based inference

* Modified Result method

* Modified Result method

* Fixed code to call BatchModel

---------

Co-authored-by: minsik.sim <minsik.sim@cyara.com>
2023-03-25 12:28:14 +03:00
Nickolay Shmyrev 9cee2899bb Update publishing plugin and simplify gradle build 2023-03-15 22:50:42 +01:00
Doomsdayrs a40742bbaf Kotlin: Numerous Improvements (#1274)
* kotlin: bump version for future release

* kotlin: Unify dependency versions

* kotlin: Add AUTHORS

* kotlin: Add MAINTENANCE.md

* kotlin: Add README.md

* kotlin: Update common/Vosk.kt documentation

* kotlin: Fix indentation in build.gradle.kts

* kotlin: Add .idea files to unify how IDEs work

* kotlin: Reformat all code

* kotlin: Update all code headers

* kotlin: Add @JvmStatic to objects

* kotlin: Use Consumer instead of Kotlin lambda in StorageService.kt

* kotlin: Annotate SpeechService.kt as throwing IOException

* kotlin: Add gradle migration settings

* kotlin: Add Deprecated annotation to jvm/LibVosk.setLogLevel

* kotlin: Overhaul exceptions

* Update documentation

* Add function for creating a Recognizer with a proper list

* Update documentation
2023-03-03 19:12:54 +03:00
Doomsdayrs d714dff8d3 Add Kotlin Multiplatform (#1241)
* Init

* Implement common

* Implement JVM target

* Ignore build/ directory

* Implement android

* Disable kotlin/native targets

* Remove jCenter()

* Upgrade android tools to 7.3.0

* Enforce not null for essential windows operations

* Add license headers

* Add maven-publish

* Add dokka for javadoc

* Add disabled kotlin/native for linux

This is disabled currently due to vosk
 not having a proper installation method on linux yet.

* Add empty constructors for JVM classes

* Fix library initialization for JVM

* Add DecoderDemo

* Add serialization support

* Convert no arg functions with returns to val properties

* Properly implement DecoderTest

* Import other android classes

* Properly name json parser as voskJson

* Add license headers to native target

* Unify free functions with Freeable interface

This makes it easy for Kotlin/Native to use a "use" function to
 auto free after use of a Freeable.

* Document JVM default constructor

* Add JVM specific constructors with NIO/IO

* Inline default constructors in Common

* Properly handle invalid paths

* Add android publishing block

* Merge serialization into common

* Update to Kotlin 1.8.10

* Update to JNA 5.13.0

* Put native target behind environment variable or override.

Due to a lack of official packaging,
 this is not possible currently.

* Properly handle Recognizer.acceptWaveform exceptions

* Add flow extension for converting (InputStream->Recognizer) to a flow.

* Use ByteFlow for waveform flow instead.

This allows custom readers, and more advanced backends,
 such as the Dicio example in DecoderTest.kt .
2023-02-17 03:47:20 +03:00
Nickolay Shmyrev 790c564d14 Ignore non-audio files in folder transcription 2023-02-10 00:57:14 +01:00
Nickolay Shmyrev 988ba3f257 Bump JNA dependency everywhere 2023-02-08 22:55:22 +01:00
Nickolay Shmyrev 2796da0a50 Sync android lib to java lib, throw exceptions on errors 2023-02-08 22:07:02 +01:00
Nickolay Shmyrev d9c6fc4958 Check if ivector folder is required 2023-02-08 21:00:15 +01:00
Nickolay Shmyrev fff40ce1a1 Gradle doesn't understand + in version 2022-12-18 22:25:26 +01:00
Nicholas Albion 2deb7de1dd Fixed #883 - use right class loader (#1228) 2022-12-17 14:45:15 +03:00
Nicholas Albion 7d87267c31 copied documentation to javadoc (#1230)
Co-authored-by: Nickolay V. Shmyrev <nshmyrev@gmail.com>
2022-12-17 14:41:33 +03:00
Nicholas Albion 64d84b01b1 added Recognizer.setGrammar(String) for java (#1229) 2022-12-17 11:43:27 +03:00
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
Nickolay Shmyrev fee63d712f Add go bindings 2021-07-10 23:44:15 +02:00
Nickolay Shmyrev 5965ea30e6 Fetch_sub returns original value 2021-07-06 12:11:12 +02:00
Nickolay Shmyrev 5c75495c03 Atomic refcounting for thread safety. See
https://github.com/alphacep/vosk-api/issues/606

for more details
2021-07-06 11:09:34 +02:00
Nickolay Shmyrev 98ae3c66cb Bump version 2021-06-28 02:09:06 +02:00
Nickolay Shmyrev 942e15484a Protect against multiple resets 2021-06-28 01:59:43 +02:00
Lars Kiesow 2349e66a97 Subtitles require word times (#607)
This is a port of the recent addition of commit 7ccf743, adding
`KaldiRecognizer.SetWords(True)` to the other examples dealing with
subtitles to the WebVTT example.

Without this, the example will not work with the most recent `vosk`
(0.3.30) Python package.
2021-06-27 18:06:11 +03:00
Nickolay Shmyrev 5750e3224f Merge branch 'master' of github.com:alphacep/vosk-api 2021-06-24 20:09:12 +02:00
Nickolay Shmyrev 558b4dd69e Don't align lattice twice 2021-06-24 20:08:54 +02:00
Lars Kiesow 02ef49f67e Allow Saving WebVTT (#605)
This patch is a small extension to the WebVTT example which allows to
directly save the WebVTT output to a file like this:

    ./test_webvtt.py test.wav out.vtt
2021-06-24 19:35:30 +03:00
Lars Kiesow 7cdf8f1d03 Add Python WebVTT Example (#601)
This patch adds an example for using webvtt-py to generate WebVTT files
from Vosk output. This is similar to the SRT example but still very
useful for generating an example video subtitle usable in web contexts.
2021-06-23 01:16:33 +03:00
Nickolay Shmyrev 7f894f784a Better check for the model files 2021-06-21 00:19:55 +02:00
Nickolay Shmyrev a770251151 Bump nodejs version to 0.3.30 2021-06-20 21:30:58 +02:00
Nickolay Shmyrev 7ccf743bb6 SRT requires word times 2021-06-10 10:37:15 +02:00
Nickolay Shmyrev 75bedfe06d Add a method to show/hide words and their times 2021-06-07 01:04:37 +02:00
Nickolay Shmyrev 4ea9885d44 Bump version 2021-05-30 21:59:46 +02:00
Nickolay Shmyrev 0930def9ec Fix critical accuracy issues, actually use rescoring 2021-05-30 21:58:06 +02:00
Nickolay Shmyrev 6aa5af7640 Add reset test 2021-05-26 21:22:07 +02:00
Nickolay Shmyrev e74fe5edf7 Reset reset flag 2021-05-26 21:01:27 +02:00
Nickolay Shmyrev cbb5d0fcdf Implement reset method in SpeechService 2021-05-26 20:51:44 +02:00
Nickolay Shmyrev d489bc40e4 Update Java part too 2021-05-26 00:59:08 +02:00
Nickolay Shmyrev 499b2f183a Introduce new API to set speaker model to already initialized recognizer.
Introduce a method to reset recognizer results to start from scratch without
computation of the result.
2021-05-26 00:46:32 +02:00
Nickolay Shmyrev f57b926f66 Unpack library, loading works on Windows 2021-05-23 01:29:29 +02:00
Nickolay Shmyrev 733dca5aea Attempt to fix loading on Windows 2021-05-22 00:41:30 +02:00
Nickolay Shmyrev 1ab7e8ca87 Use older API for backward compatibility 2021-05-20 20:48:21 +02:00
Nickolay Shmyrev 53426f794c Add note about Ukrainian 2021-05-20 01:29:15 +02:00
Nickolay Shmyrev f8189685e5 Add max alternatives output 2021-05-19 18:47:25 +02:00
Nickolay Shmyrev e6bd200c85 Proper path delimiter on windows 2021-05-13 09:36:56 +02:00
Nickolay Shmyrev 4a4c33fd9d Load dependent libraries 2021-05-10 01:39:26 +02:00
Nickolay Shmyrev dc44922b92 Add code to expand path 2021-05-10 01:26:05 +02:00
Nickolay Shmyrev 67896cfba7 Bump bindings versions 2021-05-05 20:50:25 +02:00
Nickolay Shmyrev ee8663e6d9 RNNLM rescoring support 2021-05-05 19:34:30 +02:00
Nickolay Shmyrev 652c05f052 Reformat and simplify code a bit 2021-05-02 21:44:54 +02:00
Nickolay Shmyrev 193d129d9e Model can be a symlink 2021-04-30 11:58:11 +02:00
Sadra Sabouri e995df4bc3 New Examples for Nodejs (Base on Python Examples) (#515) 2021-04-30 10:16:36 +03:00
Sergey Korol f6a30c3626 Added missing GPU methods to nuget package. (#514) 2021-04-29 20:57:56 +03:00
Nickolay Shmyrev ee95420f8f Fix spk model initialization 2021-04-27 23:24:10 +02:00
Nickolay Shmyrev 2221cd0209 Reorganize methods a bit 2021-04-26 23:25:05 +02:00
Nickolay Shmyrev 8368d831aa Add async processing demo 2021-04-26 22:34:33 +02:00
Nickolay Shmyrev 91a128b3ed Fix package name 2021-04-15 13:54:48 +02:00
Nickolay Shmyrev 83d8b63351 Merge branch 'master' of github.com:alphacep/vosk-api 2021-04-15 12:57:17 +02:00
Nickolay Shmyrev 0881aa856f Update for new API 2021-04-15 12:56:59 +02:00
Nickolay Shmyrev 77abb02ada Bump version 2021-04-12 12:23:11 +02:00
François Billioud 119c6cd0dc Update ffi-napi and improve dependency to ref-napi (#483)
With this change, we will use the same version of ref-napi than the one declared in ffi-napi, which reduce dependency tree, bundle size, risks of errors that could be fixed in one version and not in the other, etc
2021-04-01 22:12:14 +03:00
Nickolay Shmyrev cc649aa78e Fixes demo 2021-04-01 20:33:01 +02:00
François Billioud 0b0c9fce0e Types for typescript projects, and jsDoc (#461)
Implement Javascript API
2021-04-01 21:25:55 +03:00
Peter Kronenberg d845e2107f Allow libvosk.dll to get loaded on Windows (#478)
* Allow libvosk.dll to get loaded on Windows

Authored-by: Peter Kronenberg <peter.kronenberg@torch.ai>
2021-03-31 18:55:45 +03:00
Nickolay V. Shmyrev 69992a1b39 Android through JNA framework unified with Java (#476)
* Move to JNA

* Update for android

* Joint publishing

* Fix the developer id

* Added grammar constructor for Java
2021-03-30 23:36:26 +03:00
Nickolay Shmyrev f351280f49 Allow extra cflags 2021-03-30 19:24:00 +02:00
Nickolay Shmyrev c15065b044 Wrap short and float 2021-03-30 17:34:00 +02:00
Nickolay Shmyrev db41f16051 Docs are on the separate project 2021-03-29 00:38:54 +02:00
Nickolay Shmyrev e276de61be Give jar a name and fix OSX library path 2021-03-29 00:20:52 +02:00
Nickolay Shmyrev 43c786ab18 Move jars to our own maven and create a demo 2021-03-28 23:38:43 +02:00
Nickolay Shmyrev 1199f39d55 Move project to a library 2021-03-28 13:02:32 +02:00
Nickolay Shmyrev 70aab86257 Remove test code 2021-03-28 10:11:53 +02:00
Nickolay Shmyrev e4b0af7a8e Update nuget package, add osx library 2021-03-27 23:40:02 +01:00
Martin Mende a120d06f6b Fixed nodejs platform check (#464) 2021-03-24 17:08:39 +03:00
Nickolay Shmyrev e01ca37e45 Add osx module 2021-03-18 00:46:51 +01:00
Nickolay Shmyrev f5c3d898e2 Option to compile on Mac 2021-03-18 02:10:56 +03:00
Nickolay Shmyrev a5a3697b7c Copy data before queue, original data can be destroyed.
Fixes issue #444

Thanks to Alexander Zatvornitsky
2021-03-01 21:25:41 +01:00
Nickolay Shmyrev 4be9874ba2 Reformat 2021-02-28 22:40:40 +01:00
Peter Kronenberg 5647a2dc6a Java example with better type safety, Java objects and support for au… (#429)
Java example with better type safety, Java objects and support for autoclosable

Authored-by: Peter Kronenberg <peter.kronenberg@torch.ai>
2021-03-01 00:35:31 +03:00
Nickolay Shmyrev a067a5b5f3 Enable pitch if conf/pitch.conf is present
Fixes issue #442
2021-02-26 20:34:17 +01:00
Nickolay Shmyrev 475b0ecfc1 Free symbol table only if we loaded it 2021-02-25 19:17:13 +01:00
Nickolay V. Shmyrev 2a9a605144 Merge pull request #436 from sskorol/435-sync-master-with-gpu
Sync master with GPU
2021-02-22 13:41:37 +03:00
sskorol eea7ca571b - Added an optional HAVE_CUDA flag to build Vosk with GPU support.
- Added missing GpuInit/ThreadInit python wrappers.
2021-02-22 12:30:46 +02:00
Nickolay Shmyrev 11c16610ee Fix memory leaks for big models. Fixes issue #425. 2021-02-19 19:25:07 +01:00
Nickolay Shmyrev 2f4124eb66 Demonstrate speaker 2021-02-16 17:40:21 +01:00
Nickolay Shmyrev db3e31d7ce Automatically put .so file in resources 2021-02-16 17:33:09 +01:00
Nickolay Shmyrev d917af21ab Better search for Windows dlls 2021-02-12 22:24:54 +01:00
Nickolay Shmyrev 948c8c7cfd Update to 0.3.21 2021-02-12 10:13:50 +01:00
Nickolay Shmyrev dec8e1acf3 Move to JNA instead of JNR-FFI for better Android compatibility 2021-02-11 00:04:19 +01:00
Nickolay Shmyrev 3c020526ce No need for byte buffer 2021-02-10 23:32:47 +01:00
Nickolay Shmyrev b0da19b07f Java API through JNR-FFI 2021-02-10 23:16:20 +01:00
Nickolay Shmyrev 70bcd9b018 Platform is hardcoded now 2021-02-09 23:43:56 +01:00
Nickolay Shmyrev d2604b609d Copy out lib files too 2021-02-09 23:36:32 +01:00
Nickolay Shmyrev e960e0cacf Proper libraries and demo 2021-02-09 21:43:21 +01:00
Nickolay Shmyrev 77b114d434 Rework node bindings to use node-ffi-napi 2021-02-08 23:00:56 +01:00
Nickolay V. Shmyrev 2860092cef Merge pull request #401 from proger/patch-1
test_microphone: AcceptWaveform wants bytes
2021-02-08 01:50:07 +03:00
Vlad Ki c6119c4835 test_microphone: AcceptWavform wants bytes 2021-02-08 00:38:28 +02:00
Nickolay Shmyrev 415d1927e0 Add win32 wheel 2021-02-07 23:08:27 +01:00
Nickolay Shmyrev 3d1e21d242 Update dockcross build to cffi 2021-02-07 21:33:39 +01:00
Nickolay Shmyrev 7a75e1c3f4 Add arm architectures 2021-02-07 20:28:14 +01:00
Nickolay Shmyrev c3430e448a Add build for Windows wheel 2021-02-07 18:11:38 +01:00
Nickolay Shmyrev ae49ea60d2 Rework manylinux to use cffi 2021-02-07 15:26:23 +01:00
Nickolay Shmyrev 02dc0ce0c8 Take source location from environment 2021-02-07 15:17:35 +01:00
Nickolay Shmyrev 15697a18e8 Platform-specific wheel 2021-02-07 14:58:10 +01:00
Nickolay Shmyrev 481881e59d Fully move python to cffi 2021-02-07 13:23:03 +01:00
Nickolay Shmyrev 11a25b26a7 Add cffi wrapper 2021-02-07 10:57:57 +01:00
Nickolay Shmyrev bf87358f6c Add win32 build 2021-02-07 10:12:32 +01:00
Nickolay Shmyrev 04a8242230 Add vosk-api build 2021-02-07 01:55:47 +01:00
Nickolay Shmyrev ceb96c301c Separate openfst root 2021-02-07 01:48:45 +01:00
Nickolay Shmyrev 307df8fdc0 Add Win cross-compilation docker 2021-02-07 01:41:37 +01:00
Nickolay Shmyrev c44875a2dd Build libvosk in src folder, will be used for many bindings 2021-02-07 00:42:25 +01:00
Nickolay Shmyrev 31f11990ca Rename voskrecognizer 2021-02-07 00:32:47 +01:00
Nickolay Shmyrev b79b85856d Refactor C# API to properly work with nuget and precompiled libvosk.so library 2021-02-06 23:10:51 +01:00
Nickolay Shmyrev 6e861c6a19 Add nuget platforms 2021-02-05 00:39:27 +01:00
Nickolay Shmyrev cf62296a51 Update flags to hopefully fix the build 2021-02-02 14:43:12 +01:00
Nickolay Shmyrev 387b132814 Fix model version in Makefile. Thanks to Peter Kronenberg. 2021-01-26 23:19:34 +01:00
Nickolay Shmyrev de83de8624 Implement decoding on iOS with a microphone 2021-01-24 21:59:21 +01:00
Nickolay Shmyrev fe91b5a717 Preprocess header on the fly 2021-01-10 21:11:35 +01:00
Nickolay Shmyrev a052506a5d Add cffi example 2021-01-10 20:53:43 +01:00
Nickolay Shmyrev 6f2d6d0d69 Proper microphone recognizer with the queue 2021-01-09 23:16:35 +01:00
Nickolay Shmyrev a8ae6025bd Add a bit more optimization 2021-01-09 16:16:32 +01:00
Nickolay Shmyrev 1b8332a609 Raise to 0.3.17 2021-01-09 02:29:56 +01:00
Nickolay Shmyrev fe16ec7e57 Set lowest arch for linux 2021-01-09 01:47:23 +01:00
Nickolay Shmyrev c5ce5e46bd Add c++17 2021-01-09 01:46:13 +01:00
Nickolay Shmyrev 99d66670bc Update Android build 2021-01-08 13:30:28 +01:00
Nickolay Shmyrev 072b42cac6 Use our own dockcross 2021-01-08 00:56:18 +01:00
Nickolay Shmyrev acadb5b4c2 Add optimization 2021-01-07 23:41:13 +01:00
Nickolay Shmyrev 2e1de6c3af No need for python 3.5 anymore 2021-01-07 23:20:54 +01:00
Nickolay Shmyrev 67de30908b Use openblas_clapack 2021-01-07 22:37:56 +01:00
Nickolay Shmyrev 9d398eff0e Remove armv6, too slow anyway. Build with new dockcross. 2021-01-06 16:26:23 +01:00
Nickolay Shmyrev 02b7312a00 Build with openblas-clapack to avoid fortran 2021-01-06 01:11:03 +01:00
Nickolay Shmyrev 08c35e84f3 Update demo with spk vector check 2020-12-23 22:21:12 +01:00
Nickolay Shmyrev b92a5c1fc7 Update wheels to openfst 1.8.0 2020-12-22 23:27:01 +01:00
Nickolay Shmyrev 8997a587c5 Fix warnings in preparation for openfst 1.8.0 2020-12-22 21:16:20 +01:00
Nickolay Shmyrev dc3d03d742 Make sure we have result field in json 2020-11-29 19:19:50 +01:00
Nickolay Shmyrev 84df40715c Add Filipino 2020-11-19 14:53:46 +01:00
Nickolay Shmyrev b639fb501a Package US model on bintray 2020-11-04 22:08:03 +01:00
Nickolay Shmyrev 746ff47757 Split long lines in subtitles 2020-11-03 15:13:16 +01:00
Nickolay Shmyrev 2d62db8118 Update link in readme 2020-10-31 10:03:25 +01:00
Nickolay Shmyrev 7a2adcd9ba Add nuget wraps 2020-10-26 15:57:40 +01:00
Nickolay Shmyrev 9ccf3ef0e8 Introduce CMVN in feature pipeline 2020-10-22 23:56:36 +02:00
Nickolay V. Shmyrev 0edab6d558 Merge pull request #259 from ThThoma/add-valid-word-check
add isWordRecognizable()
2020-10-22 15:53:04 +03:00
Athanasios Thoma 4b344f0cd8 rename 2020-10-22 14:09:50 +03:00
Athanasios Thoma 17ddc4d5ba add isWordRecognizable() 2020-10-21 10:00:39 +03:00
Nickolay Shmyrev 3e33860c47 Update to latest 0.3.15 changes 2020-10-20 23:50:39 +03:00
Nickolay Shmyrev 0269a10833 Critical ivector bugfix and version 0.3.15 2020-10-13 01:28:46 +02:00
Nickolay Shmyrev 7af3e9a334 Add srt example 2020-10-07 13:43:09 +02:00
Nickolay Shmyrev d666876be1 Revert compile flags 2020-10-05 01:24:32 +02:00
Nickolay Shmyrev 564fab7ec1 Update documentation 2020-10-05 00:16:15 +02:00
Nickolay Shmyrev 155c6c2a2a Don't crash with grammar and static graphs 2020-10-05 00:13:25 +02:00
Nickolay Shmyrev d43cbe9344 Add 0.3.14 2020-10-04 23:45:34 +02:00
Nickolay Shmyrev 57cc474c9f Build bigram language model from grammars 2020-10-04 23:42:50 +02:00
Nickolay Shmyrev 586603f8e1 Add Farsi 2020-10-03 12:42:54 +02:00
Nickolay Shmyrev d57887d22a Add Greek 2020-09-30 13:42:20 +02:00
Nickolay Shmyrev 6183bcfc5f Add Arabic model 2020-09-30 13:29:43 +02:00
Nickolay Shmyrev 65f6113b4d Add Catalan 2020-09-27 11:57:20 +02:00
Nickolay Shmyrev 8d88b89db1 Fix reserved identifier violation. Closes issue #226
Thanks to Markus Elfring
2020-09-23 21:16:34 +02:00
Nickolay Shmyrev 62885e8963 Update descriptions 2020-09-22 11:30:27 +02:00
qo6xup6 9fc094a5da Update kaldi_recognizer.h 2020-09-22 10:11:29 +08:00
Nickolay Shmyrev f97383c17f Update models location 2020-09-22 00:00:24 +02:00
Nickolay Shmyrev 4b892ec5e7 Bump version 2020-09-21 23:49:55 +02:00
Nickolay Shmyrev 41035485db Fix x-vectors, now they actually work. Requires new version spk-model-0.4 with whitening transform matrix 2020-09-21 23:31:30 +02:00
Nickolay Shmyrev 9696f4c917 Mention Dutch 2020-09-18 17:31:17 +02:00
Nickolay Shmyrev 55abf5f5ac Dynamic arch in pip module 2020-09-13 22:08:36 +02:00
Nickolay Shmyrev a1b2e41710 Mention we support Indian English 2020-09-09 23:28:55 +02:00
Nickolay Shmyrev dff4ab26e4 Library is now vosk_jni 2020-09-01 18:27:54 +02:00
Nickolay Shmyrev 83b6e1cdf7 Speech service for more flexible recognizer initialization
Publish repo on jcenter
2020-09-01 16:32:15 +02:00
Nickolay Shmyrev 38dbaa15ea Load JNI inside library itself 2020-09-01 12:44:44 +02:00
Nickolay Shmyrev 0e531b6061 Organize Makefile 2020-08-23 20:10:48 +02:00
Nickolay Shmyrev de94ef5537 Add speaker C demo 2020-08-14 11:33:41 +02:00
Nickolay Shmyrev 6ef9d13877 Add Italian 2020-08-03 01:31:30 +02:00
Nickolay Shmyrev 1c7b94757d Don't attempt to resize to empty matrix 2020-07-31 16:45:05 +02:00
Nickolay Shmyrev 83486e0bef Spk vector fix 2020-07-31 12:58:23 +02:00
Nickolay Shmyrev 9787e8a53f No .html in links 2020-07-21 10:15:27 +02:00
Nickolay Shmyrev f59d6685ad Fix confidences 2020-07-15 10:24:08 +02:00
Nickolay Shmyrev 8a986ef384 Require KALDI_MKL to be 1 2020-07-12 11:03:22 +02:00
Nickolay Shmyrev 78f9f55e14 Fixes description 2020-07-12 10:37:56 +02:00
Nickolay Shmyrev c2e006f664 Some probably helpful flags for npm on osx 2020-07-11 20:54:13 +02:00
Nickolay V. Shmyrev 4b8e9737a5 Update README.md 2020-07-08 01:38:03 +02:00
Nickolay Shmyrev 722b09eaa4 Ignore words missing in the vocabulary 2020-07-08 01:35:50 +02:00
Nickolay Shmyrev f5b4f5a1f2 Added C test 2020-07-08 01:35:50 +02:00
Nickolay Shmyrev 4407d8da55 Another timestamp bugfix 2020-07-08 01:35:50 +02:00
Nickolay Shmyrev 73b73527cd Release memory when final result is received to reduce memory pressure. 2020-07-08 01:35:50 +02:00
Nickolay Shmyrev 4df0e3a741 Actually show the distance 2020-07-08 01:35:49 +02:00
He1nr1chK 1a771e0172 Added cosine distance function 2020-06-23 20:10:14 +02:00
Nickolay Shmyrev 4333c3c242 Build dockcross with python 3.8 2020-06-23 01:11:07 +02:00
Nickolay V. Shmyrev b831c9ad57 Merge pull request #151 from hviana/patch-1
Add support for speaker model in Android
2020-06-23 00:07:26 +03:00
Nickolay Shmyrev e5c08f7710 Allow to continue after final result. See for discussion
https://github.com/alphacep/vosk-api/issues/146
2020-06-22 23:04:09 +02:00
Henrique Emanoel Viana 51c2968595 fix 2020-06-22 18:01:42 -03:00
Henrique Emanoel Viana 5993376322 Add suport to SpkModel 2020-06-22 17:51:55 -03:00
Nickolay Shmyrev c4281622b9 Added node speaker demo 2020-06-22 21:45:50 +02:00
Nickolay Shmyrev 9d26014de2 Use wider beams by default to avoid accuracy confusion 2020-06-19 16:10:30 +02:00
Nickolay Shmyrev f6c115d215 Update to 0.3.9 and use our fork of openfst based on openfst 1.7.7 2020-06-16 19:13:57 +02:00
Nickolay Shmyrev 6bd102d778 Fixes timing issue #125 2020-06-15 02:04:02 +02:00
Nickolay Shmyrev d3d6af5712 Define os in a single place 2020-06-12 10:35:58 +02:00
Nickolay V. Shmyrev 876093446f Merge pull request #127 from nnkalita/master
added android build support on macOS
2020-06-12 11:32:27 +03:00
Nickolay Shmyrev 336f219f09 No Qr code for wechat anymore 2020-06-11 12:57:32 +02:00
Nickolay Shmyrev 584251cbdc Remove chinese-only 2020-06-11 09:18:34 +02:00
Nickolay Shmyrev a34995a788 Process audio in chunks for imporved accuracy 2020-06-11 01:06:11 +02:00
Nickolay Shmyrev 998e5da227 Update rules for joining wechat 2020-06-10 15:07:10 +02:00
Nickolay Shmyrev e04c15e367 Fix 2020-06-06 11:15:23 +02:00
Nickolay Shmyrev b81f69d407 Avoid overflow. See issue #128 2020-06-06 10:43:44 +02:00
Nickolay Shmyrev 0ac2064281 Few more words 2020-06-06 10:07:12 +02:00
Nickolay Shmyrev 1d00bd244e Multithreaded testing 2020-06-06 00:40:15 +02:00
Nickolay Shmyrev 8f5efc58c9 Disable ivector pipeline without ivectors 2020-06-05 19:34:04 +02:00
Nayan Kalita a0c5ae1b5e Update build-kaldi.sh
added android build from mac.
2020-06-05 20:10:28 +05:30
Nayan Kalita 99f48f9de1 Merge remote-tracking branch 'upstream/master' 2020-06-05 19:15:42 +05:30
Nayan Kalita 1948b23f32 Update build-kaldi.sh
Updated based feedback.
2020-06-05 18:03:01 +05:30
Nickolay Shmyrev c9eb572fc5 Move documentation to our website 2020-06-05 01:31:13 +02:00
Nickolay Shmyrev 7d9895ff81 Add API documentation in the header 2020-06-04 22:47:54 +02:00
Nickolay Shmyrev d507210ef8 Add npmignore and pure javascript package 2020-06-03 17:47:15 +02:00
Nickolay Shmyrev db0a3d23d5 Add repackages librispeech model 2020-06-03 17:24:28 +02:00
Nickolay Shmyrev f4f920f3cd Proper cross-compilation with setuptools 2020-06-02 00:36:16 +02:00
Nickolay Shmyrev 75993ea276 Update to 0.3.8 2020-06-01 00:55:28 +02:00
Nickolay Shmyrev ee9bacb092 Reset pipeline to save memory 2020-06-01 00:54:47 +02:00
Nickolay Shmyrev b1e775c67b Better speaker identification without silence frames 2020-05-31 23:46:42 +02:00
Nickolay Shmyrev d631e567aa Add README 2020-05-31 02:21:07 +02:00
Nickolay Shmyrev 9edf45be42 Sync version 2020-05-31 02:08:35 +02:00
Nickolay Shmyrev 8b4b3c646a Nodejs support 2020-05-31 02:07:01 +02:00
Nickolay Shmyrev 31bb0557d9 Add mkl support 2020-05-30 23:22:14 +02:00
Nickolay Shmyrev 4593183cf9 Create gen if doens't exist. Thanks to BMI24. 2020-05-25 00:32:42 +02:00
Nickolay Shmyrev fdc45f1187 Better link on wechat 2020-05-23 21:13:23 +02:00
Nickolay Shmyrev b3d3c6d12c Fix mistake, thanks to Shaheen 2020-05-23 01:12:31 +02:00
Nickolay Shmyrev f5f0794def Add logo 2020-05-22 15:24:57 +02:00
Nickolay Shmyrev 55664fcca0 Add wechat group 2020-05-22 09:56:54 +02:00
Nickolay Shmyrev afbf330f16 Make ivector extractor optional 2020-05-18 23:56:42 +02:00
Nickolay Shmyrev 25aadf61bc Fix travis 2020-05-09 17:36:55 +02:00
Nickolay Shmyrev be47056467 Updates for osx 2020-05-09 15:49:24 +03:00
Nickolay Shmyrev 8f623e0aea Get rid of broken cmake 2020-05-09 14:32:46 +02:00
Nickolay Shmyrev 78025435e4 Windows is fully supported now 2020-05-09 09:18:10 +02:00
Nickolay Shmyrev 910455802e Don't link to the library explicitely to allow relocations 2020-05-08 15:16:50 +03:00
Nickolay Shmyrev b12914955c Add x86 build 2020-05-08 11:33:25 +02:00
Nickolay Shmyrev dfe11eaf83 Linux build fix 2020-05-08 09:04:14 +02:00
Nickolay Shmyrev 37fbe1a52b OSX build 2020-05-08 09:55:15 +03:00
Nickolay Shmyrev af11bb2361 No need for external dependencies 2020-05-07 21:48:03 +02:00
Nickolay Shmyrev 8da8697c1e Add ffmpeg test 2020-05-07 21:07:17 +02:00
Nickolay V. Shmyrev 944dc87531 Update readme to mention aarch64 2020-05-05 00:55:29 +02:00
Nayan Kalita bca0b86e37 android build support on macOS 2020-03-06 20:57:56 +05:30
172 changed files with 15324 additions and 4802 deletions
+50 -16
View File
@@ -1,19 +1,25 @@
# Built application files
*.apk
*.ap_
# Object files
*.o
# Java class files
*.class
# Temp files
nohup.out
# Gradle files
.gradle/
build/
gradlew
gradlew.bat
gradle
# Local configuration file (sdk path, etc)
local.properties
gradle.properties
# Android
android/build
android/lib/build
android/model-en/build
android/model-en/src/main/assets/model-en-us
android/repo
*.apk
*.ap_
# Cmake
.cxx
@@ -23,15 +29,43 @@ cmake_install.cmake
wheelhouse
__pycache__
*.egg-info
python/dist
bindings/python/dist
bindings/python/build
bindings/python/vosk/*.so
python/test/db
python/test/hyp
python/test/model
python/test/ref
python/test/result.txt
python/test/wav.scp
# Java
*.so
java/org
java/model-en
java/*.cc
*.class
bindings/java/lib/model
bindings/java/demo/model
bindings/java/lib/build
bindings/java/demo/build
# CSharp
csharp/gen
csharp/*.exe
csharp/*.c
*.dll
*.so
*.dylib
*.nupkg
csharp/demo/model
csharp/demo/test.wav
csharp/demo/bin
csharp/demo/obj
# Node
nodejs/demo/model
nodejs/demo/model-spk
nodejs/demo/test.wav
nodejs/node_modules
nodejs/package-lock.json
# C
c/test_vosk
c/test_vosk_speaker
c/oprofile_data
c/model
c/test.wav
-17
View File
@@ -1,17 +0,0 @@
notifications:
email: false
matrix:
include:
- sudo: required
services:
- docker
env: DOCKER_IMAGE=alphacep/kaldi-manylinux:latest
PLAT=manylinux2010_x86_64
install:
- docker pull $DOCKER_IMAGE
script:
- docker run --rm -e PLAT=$PLAT -v `pwd`:/io $DOCKER_IMAGE $PRE_CMD /io/travis/build-wheels.sh
- ls wheelhouse/
+20 -135
View File
@@ -1,142 +1,27 @@
[![Build Status](https://travis-ci.com/alphacep/vosk-api.svg?branch=master)](https://travis-ci.com/alphacep/vosk-api)
# Vosk Speech Recognition Toolkit
[РУС](README.ru.md)
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.
[中文](README.zh.md)
Vosk models are small (50 Mb) but provide continuous large vocabulary
transcription, zero-latency response with streaming API, reconfigurable
vocabulary and speaker identification.
Vosk is a speech recognition toolkit. The best things in Vosk are:
Speech recognition bindings implemented for various programming languages
like Python, Java, Node.JS, C#, C++, Rust, Go and others.
1. Supports 9 languages - English, German, French, Spanish, Portuguese, Chinese, Russian, Turkish, Vietnamese. More to come.
1. Works offline, even on lightweight devices - Raspberry Pi, Android, iOS
1. Installs with simple `pip3 install vosk`
1. Portable per-language models are only 50Mb each, but there are much bigger server models available.
1. Provides streaming API for the best user experience (unlike popular speech-recognition python packages)
1. There are bindings for different programming languages, too - java/csharp/javascript etc.
1. Allows quick reconfiguration of vocabulary for best accuracy.
1. Supports speaker identification beside simple speech recognition.
Vosk supplies speech recognition for chatbots, smart home appliances,
virtual assistants. It can also create subtitles for movies,
transcription for lectures and interviews.
## Android build
Vosk scales from small devices like Raspberry Pi or Android smartphone to
big clusters.
```
cd android
gradle build
```
# Documentation
Please note that medium blog post about 64-bit is not relevant anymore, the script builds x86, arm64 and armv7 libraries automatically without any modifications.
For example of Android application using Vosk-API check https://github.com/alphacep/kaldi-android-demo project
## iOS build
Available on request. Drop as a mail at [contact@alphacephei.com](mailto:contact@alphacephei.com).
## Python installation from Pypi
The easiest way to install vosk api is with pip. You do not have to compile anything. We currently support only Linux on x86_64 and Raspberry Pi. Other systems (windows, mac) will come soon.
Make sure you have newer pip and python:
* Python version >= 3.4
* pip version >= 19.0
Uprade python and pip if needed. Then install vosk on Linux with a simple command
```
pip3 install vosk
```
## Websocket Server and GRPC server
We also provide a websocket server and grpc server which can be used in telephony and other applications. With bigger models adapted for 8khz audio it provides more accuracy.
The server is installed with docker and can run with a single command:
```
docker run -d -p 2700:2700 alphacep/kaldi-en:latest
```
For details see https://github.com/alphacep/vosk-server
## Compilation from source
If you still want to build from scratch, you can compile Kaldi and Vosk yourself. The compilation is straightforward but might be a little confusing for newbie. In case you want to follow this, please watch the errors.
#### Kaldi compilation for local python, node and java modules
```
git clone -b lookahead --single-branch https://github.com/alphacep/kaldi
cd kaldi/tools
make
```
install all dependencies and repeat `make` if needed
```
extras/install_openblas.sh
cd ../src
./configure --mathlib=OPENBLAS --shared --use-cuda=no
make -j 10
```
#### Python module build
Then build the python module
```
export KALDI_ROOT=<KALDI_ROOT>
cd python
python3 setup.py install
```
#### Running the example code with python
Run like this:
```
cd vosk-api/python/example
wget https://alphacephei.com/kaldi/models/vosk-model-small-en-us-0.3.zip
unzip alphacep-model-android-en-us-0.3.zip
mv alphacep-model-android-en-us-0.3 model-en
python3 ./test_simple.py test.wav
```
To run with your audio file make sure it has proper format - PCM 16khz 16bit mono, otherwise decoding will not work.
You can find other examples of using a microphone, decoding with a fixed small vocabulary or speaker identification setup in [python/example subfolder](https://github.com/alphacep/vosk-api/tree/master/python/example)
#### Java example API build
Or Java
```
cd java && KALDI_ROOT=<KALDI_ROOT> make
wget https://alphacephei.com/kaldi/models/vosk-model-small-en-us-0.3.zip
unzip alphacep-model-android-en-us-0.3.zip
mv alphacep-model-android-en-us-0.3 model
make run
```
#### C# build
Or C#
```
cd csharp && KALDI_ROOT=<KALDI_ROOT> make
wget https://alphacephei.com/kaldi/models/vosk-model-small-en-us-0.3.zip
unzip alphacep-model-android-en-us-0.3.zip
mv alphacep-model-android-en-us-0.3 model
mono test.exe
```
## Models for different languages
For information about models see [the documentation on available models](https://github.com/alphacep/vosk-api/blob/master/doc/models.md).
## Contact Us
If you have any questions, feel free to
* Post an issue here on github
* Send us an e-mail at [contact@alphacephei.com](mailto:contact@alphacephei.com)
* Join our group dedicated to speech recognition on Telegram [@speech_recognition](https://t.me/speech_recognition)
For installation instructions, examples and documentation visit [Vosk
Website](https://alphacephei.com/vosk).
-149
View File
@@ -1,149 +0,0 @@
[![Build Status](https://travis-ci.com/alphacep/vosk-api.svg?branch=master)](https://travis-ci.com/alphacep/vosk-api)
[EN](README.md)
[中文](README.zh.md)
Библитека для распознавания речи "Воск". Преимущества библиотеки:
1. Поддерживает 9 языков - русский, английский, немецкий, французский, португальский, испанский, китайский, турецкий, вьетнамский. В скором времени будут добавлены и другие
1. Работает без доступа к сети даже на мобильных устройствах - Raspberry Pi, Android, iOS
1. Устанавливается с помощью простой команды `pip3 install vosk` без дополнительных шагов
1. Модели для каждого языка занимают всего 50Мб, но есть и гораздо более точные большие модели для более точного распознавания
1. Сделана для потоковой обработки звука, что позволяет реализовать мгновенную реакцию на команды
1. Поддерживает несколько популярных языков программирования - Java, C#, Javascript
1. Позволяет быстро настраивать словарь распознавания для улучшения точности распознавания
1. Позволяет идентифицировать говорящего
## Сборка для Android
```
cd android
gradle build
```
Сборка включает платформы x86, armv7, arm64
Для примера приложения, созданного с помощью библиотеки "Воск" смотрите [демо проект](https://github.com/alphacep/kaldi-android-demo).
## Сборка для iOS
Доступна позапросу. Напишите нам [contact@alphacephei.com](mailto:contact@alphacephei.com).
## Установка для работы Python из Pypi
Проще всего установить "Воск" с помощью pip. Собирать ничего не нужно. Мы поддерживаем платформы Linux, RPi и Windows. Сборка для OSX будет скоро доступна.
Для начала убедитесь, что используются достаточно новые версии pip и Python:
* Python версия >= 3.5
* pip версия >= 19.0
Обновите Python и Pip если нужно, а затем установите "Воск" такой командой:
```
pip3 install vosk
```
Для использования "Воск" смотрите примеры ниже.
## Сервер для протоколов Websocket и GRPC
We also provide a websocket server and grpc server which can be used in telephony and other applications. With bigger models adapted for 8khz audio it provides more accuracy.
The server is installed with docker and can run with a single command:
```
docker run -d -p 2700:2700 alphacep/kaldi-en:latest
```
Смотрите проект https://github.com/alphacep/vosk-server
## Сборка из исходников
Если нужно собрать проект из исходного кода, необходимо будет собрать
Kaldi самостоятельно. Сборка досаточно простая и прямолинейная, но может
быть непривычной для начинающих. Обращайте внимания на сообщения об ошибках
в процессе сборки.
#### Сборка Kaldi для модулей на Python, Java, C#
```
git clone https://github.com/kaldi-asr/kaldi
cd kaldi/tools
make
```
установите все рекомандуемые пакеты и повторите `make` если потребуется.
```
extras/install_openblas.sh
cd ../src
./configure --mathlib=OPENBLAS --shared --use-cuda=no
make -j 10
```
#### Сборка модуля на Python
После Kaldi можно собрать модуль Python
```
export KALDI_ROOT=<KALDI_ROOT>
cd python
python3 setup.py install
```
#### Запуск примера для Python
Выполните следующие команды:
```
cd vosk-api/python/example
wget https://alphacephei.com/kaldi/models/vosk-model-small-en-us-0.3.zip
unzip alphacep-model-android-en-us-0.3.zip
mv alphacep-model-android-en-us-0.3 model-en
python3 ./test_simple.py test.wav
```
Для того, чтобы распознавать другой файл, переведите его в нужный формат - PCM 16кГц 16бит 1канал. Это можно сделать с помощью ffmpeg.
Другие примеры, в том числе использования микрофона, распознавание с небольшим словарём и распознавание говорящего можно найти в [каталоге python/example](https://github.com/alphacep/vosk-api/tree/master/python/example)
#### Сборка для Java
Перейдите в каталог Java и запустите сборку
```
cd java && KALDI_ROOT=<KALDI_ROOT> make
wget https://alphacephei.com/kaldi/models/vosk-model-small-en-us-0.3.zip
unzip alphacep-model-android-en-us-0.3.zip
mv alphacep-model-android-en-us-0.3 model
make run
```
#### Сборка для C#
Для сборки в среде Mono.
```
cd csharp && KALDI_ROOT=<KALDI_ROOT> make
wget https://alphacephei.com/kaldi/models/vosk-model-small-en-us-0.3.zip
unzip alphacep-model-android-en-us-0.3.zip
mv alphacep-model-android-en-us-0.3 model
mono test.exe
```
.NET тоже должен работать, хотя мы не пробовали.
## Модели для разных языков
По информации о моделях смотрите соответствующую [страницу документации](https://github.com/alphacep/vosk-api/blob/master/doc/models.md).
## Contact Us
Если возникли вопросы, свяжитесь с нами:
* Создайте проблему тут на github
* Напишите нам по почте [contact@alphacephei.com](mailto:contact@alphacephei.com)
* Заходите в нашу группу в Телеграмме [@speech_recognition_ru](https://t.me/speech_recognition_ru)
-12
View File
@@ -1,12 +0,0 @@
[![Build Status](https://travis-ci.com/alphacep/vosk-api.svg?branch=master)](https://travis-ci.com/alphacep/vosk-api)
Vosk是言语识别工具包。Vosk最好的事情是:
1. 支持九种语言 - 中文, 英语,德语,法语,西班牙语,葡萄牙语,俄语,土耳其语,越南语
1. 移动设备上脱机工作-Raspberry PiAndroidiOS
1. 使用简单的 pip3 install vosk 安装
1. 每种语言的手提式模型只有是50Mb, 但还有更大的服务器模型可用
1. 提供流媒体API,以提供最佳用户体验(与流行的语音识别python包不同)
1. 还有用于不同编程语言的包装器-java / csharp / javascript等
1. 可以快速重新配置词汇以实现最佳准确性
1. 支持说话人识别
-64
View File
@@ -1,64 +0,0 @@
# Vosk CMake File
cmake_minimum_required(VERSION 3.4.1)
if ("x${ANDROID_ABI}" STREQUAL "xarmeabi-v7a")
set(OPENBLAS_ARCH "armv7")
set(KALDI_SUFFIX "arm_32")
elseif ("x${ANDROID_ABI}" STREQUAL "xarm64-v8a")
set(OPENBLAS_ARCH "armv8")
set(KALDI_SUFFIX "arm_64")
else ("x${ANDROID_ABI}" STREQUAL "xarmeabi-v7a")
set(OPENBLAS_ARCH "atom")
set(KALDI_SUFFIX "x86_64")
endif ("x${ANDROID_ABI}" STREQUAL "xarmeabi-v7a")
set(KALDI_ROOT "${PROJECT_SOURCE_DIR}/build/kaldi_${KALDI_SUFFIX}/kaldi")
set(LIB_ROOT "${PROJECT_SOURCE_DIR}/build/kaldi_${KALDI_SUFFIX}/local")
set(API_SOURCES
"${PROJECT_SOURCE_DIR}/../src/kaldi_recognizer.cc"
"${PROJECT_SOURCE_DIR}/../src/kaldi_recognizer.h"
"${PROJECT_SOURCE_DIR}/../src/model.cc"
"${PROJECT_SOURCE_DIR}/../src/model.h"
"${PROJECT_SOURCE_DIR}/../src/spk_model.cc"
"${PROJECT_SOURCE_DIR}/../src/spk_model.h"
"${PROJECT_SOURCE_DIR}/../src/vosk_api.cc"
"${PROJECT_SOURCE_DIR}/../src/vosk_api.h"
)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O3 -DFST_NO_DYNAMIC_LINKING")
add_library( kaldi_jni SHARED
build/generated-src/cpp/vosk_wrap.cc
${API_SOURCES}
)
include_directories("${PROJECT_SOURCE_DIR}/../src" "build/kaldi_${KALDI_SUFFIX}/kaldi/src" "build/kaldi_${KALDI_SUFFIX}/local/include")
target_link_libraries( kaldi_jni
${KALDI_ROOT}/src/online2/kaldi-online2.a
${KALDI_ROOT}/src/decoder/kaldi-decoder.a
${KALDI_ROOT}/src/ivector/kaldi-ivector.a
${KALDI_ROOT}/src/gmm/kaldi-gmm.a
${KALDI_ROOT}/src/nnet3/kaldi-nnet3.a
${KALDI_ROOT}/src/tree/kaldi-tree.a
${KALDI_ROOT}/src/feat/kaldi-feat.a
${KALDI_ROOT}/src/lat/kaldi-lat.a
${KALDI_ROOT}/src/lm/kaldi-lm.a
${KALDI_ROOT}/src/hmm/kaldi-hmm.a
${KALDI_ROOT}/src/transform/kaldi-transform.a
${KALDI_ROOT}/src/cudamatrix/kaldi-cudamatrix.a
${KALDI_ROOT}/src/matrix/kaldi-matrix.a
${KALDI_ROOT}/src/fstext/kaldi-fstext.a
${KALDI_ROOT}/src/util/kaldi-util.a
${KALDI_ROOT}/src/base/kaldi-base.a
${LIB_ROOT}/lib/libfst.a
${LIB_ROOT}/lib/libfstngram.a
${LIB_ROOT}/lib/libopenblas_${OPENBLAS_ARCH}-r0.3.7.a
${LIB_ROOT}/lib/libclapack.a
${LIB_ROOT}/lib/liblapack.a
${LIB_ROOT}/lib/libblas.a
${LIB_ROOT}/lib/libf2c.a
log
)
-22
View File
@@ -1,22 +0,0 @@
This is still work in progress, more to come
## TODO
* Optimize graph construction, current one is below accuracy
* Load model from the AAR (mmap them in tflite style)
* Add decoding speed measurement
* Add wakeup word
* Add speakerid
* Integrate proper hardware optimized neural network library. Candidates are:
* https://github.com/XiaoMi/mace
* https://github.com/Tencent/ncnn
* https://developer.android.com/ndk/guides/neuralnetworks/ (since API level 27)
* https://github.com/google/XNNPACK
* Quantization for the models
-130
View File
@@ -1,130 +0,0 @@
#!/bin/bash
# Copyright 2019 Alpha Cephei Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
if [ "x$ANDROID_SDK_HOME" == "x" ]; then
echo "ANDROID_SDK_HOME environment variable is undefined, define it with local.properties or with export"
exit 1
fi
if [ ! -d "$ANDROID_SDK_HOME" ]; then
echo "ANDROID_SDK_HOME ($ANDROID_SDK_HOME) is missing. Make sure you have sdk installed"
exit 1
fi
if [ ! -d "$ANDROID_SDK_HOME/ndk-bundle" ]; then
echo "$ANDROID_SDK_HOME/ndk-bundle is missing. Make sure you have ndk installed within sdk"
exit 1
fi
set -x
ANDROID_NDK_HOME=$ANDROID_SDK_HOME/ndk-bundle
ANDROID_TOOLCHAIN_PATH=$ANDROID_NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64
WORKDIR_X86_64=`pwd`/build/kaldi_x86_64
WORKDIR_ARM32=`pwd`/build/kaldi_arm_32
WORKDIR_ARM64=`pwd`/build/kaldi_arm_64
PATH=$PATH:$ANDROID_NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin
OPENFST_VERSION=1.6.7
mkdir -p $WORKDIR_ARM64/local/lib $WORKDIR_ARM32/local/lib $WORKDIR_X86_64/local/lib
# Build standalone CLAPACK since gfortran is missing
cd build
git clone https://github.com/simonlynen/android_libs
cd android_libs/lapack
sed -i 's/APP_STL := gnustl_static/APP_STL := c++_static/g' jni/Application.mk && \
sed -i 's/android-10/android-21/g' project.properties && \
sed -i 's/APP_ABI := armeabi armeabi-v7a/APP_ABI := armeabi-v7a arm64-v8a x86_64/g' jni/Application.mk && \
sed -i 's/LOCAL_MODULE:= testlapack/#LOCAL_MODULE:= testlapack/g' jni/Android.mk && \
sed -i 's/LOCAL_SRC_FILES:= testclapack.cpp/#LOCAL_SRC_FILES:= testclapack.cpp/g' jni/Android.mk && \
sed -i 's/LOCAL_STATIC_LIBRARIES := lapack/#LOCAL_STATIC_LIBRARIES := lapack/g' jni/Android.mk && \
sed -i 's/include $(BUILD_SHARED_LIBRARY)/#include $(BUILD_SHARED_LIBRARY)/g' jni/Android.mk && \
${ANDROID_NDK_HOME}/ndk-build && \
cp obj/local/armeabi-v7a/*.a ${WORKDIR_ARM32}/local/lib && \
cp obj/local/arm64-v8a/*.a ${WORKDIR_ARM64}/local/lib
cp obj/local/x86_64/*.a ${WORKDIR_X86_64}/local/lib
# Architecture-specific part
for arch in arm32 arm64 x86_64; do
#for arch in x86_64; do
case $arch in
arm32)
BLAS_ARCH=ARMV7
WORKDIR=$WORKDIR_ARM32
HOST=arm-linux-androideabi
AR=arm-linux-androideabi-ar
CC=armv7a-linux-androideabi21-clang
CXX=armv7a-linux-androideabi21-clang++
ARCHFLAGS="-mfloat-abi=softfp -mfpu=neon"
;;
arm64)
BLAS_ARCH=ARMV8
WORKDIR=$WORKDIR_ARM64
HOST=aarch64-linux-android
AR=aarch64-linux-android-ar
CC=aarch64-linux-android21-clang
CXX=aarch64-linux-android21-clang++
ARCHFLAGS=""
;;
x86_64)
BLAS_ARCH=ATOM
WORKDIR=$WORKDIR_X86_64
HOST=x86_64-linux-android
AR=x86_64-linux-android-ar
CC=x86_64-linux-android21-clang
CXX=x86_64-linux-android21-clang++
ARCHFLAGS=""
;;
esac
# openblas first
cd $WORKDIR
git clone -b v0.3.7 --single-branch https://github.com/xianyi/OpenBLAS
make -C OpenBLAS TARGET=$BLAS_ARCH ONLY_CBLAS=1 AR=$AR CC=$CC HOSTCC=gcc ARM_SOFTFP_ABI=1 USE_THREAD=0 NUM_THREADS=1 -j4
make -C OpenBLAS install PREFIX=$WORKDIR/local
# tools directory --> we'll only compile OpenFST
cd $WORKDIR
wget -c -T 10 -t 1 http://www.openfst.org/twiki/pub/FST/FstDownload/openfst-${OPENFST_VERSION}.tar.gz || \
wget -c -T 10 -t 3 http://www.openslr.org/resources/2/openfst-${OPENFST_VERSION}.tar.gz
tar -zxvf openfst-${OPENFST_VERSION}.tar.gz
cd openfst-${OPENFST_VERSION}
CXX=$CXX CXXFLAGS="$ARCHFLAGS -O3 -DFST_NO_DYNAMIC_LINKING" ./configure --prefix=${WORKDIR}/local \
--enable-shared --enable-static --with-pic --disable-bin \
--enable-lookahead-fsts --enable-ngram-fsts --host=$HOST --build=x86-linux-gnu
make -j 8
make install
# Kaldi itself
cd $WORKDIR
git clone -b android-mix --single-branch https://github.com/alphacep/kaldi
cd $WORKDIR/kaldi/src
CXX=$CXX CXXFLAGS="$ARCHFLAGS -O3 -DFST_NO_DYNAMIC_LINKING" ./configure --use-cuda=no \
--mathlib=OPENBLAS --shared \
--android-incdir=${ANDROID_TOOLCHAIN_PATH}/sysroot/usr/include \
--host=$HOST --openblas-root=${WORKDIR}/local \
--fst-root=${WORKDIR}/local --fst-version=${OPENFST_VERSION}
make -j 8 depend
make -j 8 online2 lm
done
-68
View File
@@ -1,68 +0,0 @@
buildscript {
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.5.3'
}
}
allprojects {
repositories {
google()
jcenter()
}
}
apply plugin: 'com.android.library'
android {
compileSdkVersion 29
defaultConfig {
minSdkVersion 21
targetSdkVersion 29
versionCode 5
versionName "5.2"
setProperty("archivesBaseName", "kaldi-android-$versionName")
externalNativeBuild {
cmake {
arguments "-DCMAKE_VERBOSE_MAKEFILE=ON", "-DANDROID_ARM_NEON=TRUE", "-DCMAKE_CXX_FLAGS_RELEASE=-O3"
}
}
ndk {
abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86_64'
}
}
sourceSets {
main {
java.srcDirs = ['src/main/java', 'build/generated-src']
}
}
externalNativeBuild {
cmake {
path "CMakeLists.txt"
}
}
}
task swig {
doLast {
mkdir 'build/generated-src/java'
mkdir 'build/generated-src/cpp'
exec {
commandLine 'swig',
"-c++",
"-java", "-package", "org.kaldi",
"-outdir", "build/generated-src/java", "-o", "build/generated-src/cpp/vosk_wrap.cc",
"../src/vosk.i"
}
}
}
task kaldi(type: Exec) {
commandLine './build-kaldi.sh'
environment ANDROID_SDK_HOME: android.getSdkDirectory()
}
preBuild.dependsOn kaldi, swig
-3
View File
@@ -1,3 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="org.kaldi">
</manifest>
-262
View File
@@ -1,262 +0,0 @@
// Copyright 2019 Alpha Cephei Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package org.kaldi;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.io.Reader;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import android.content.Context;
import android.content.res.AssetManager;
import android.os.Environment;
import android.util.Log;
/**
* Provides utility methods to keep asset files to external storage to allow
* further JNI code access assets from a filesystem.
*
* There must be special file {@value #ASSET_LIST_NAME} among the application
* assets containing relative paths of assets to synchronize. If the
* corresponding path does not exist on the external storage it is copied. If
* the path exists checksums are compared and the asset is copied only if there
* is a mismatch. Checksum is stored in a separate asset with the name that
* consists of the original name and a suffix that depends on the checksum
* algorithm (e.g. MD5). Checksum files are copied along with the corresponding
* asset files.
*
* @author Alexander Solovets
*/
public class Assets {
protected static final String TAG = Assets.class.getSimpleName();
public static final String ASSET_LIST_NAME = "assets.lst";
public static final String SYNC_DIR = "sync";
public static final String HASH_EXT = ".md5";
private final AssetManager assetManager;
private final File externalDir;
/**
* Creates new instance for asset synchronization
*
* @param context
* application context
*
* @throws IOException
* if the directory does not exist
*
* @see android.content.Context#getExternalFilesDir
* @see android.os.Environment#getExternalStorageState
*/
public Assets(Context context) throws IOException {
File appDir = context.getExternalFilesDir(null);
if (null == appDir)
throw new IOException("cannot get external files dir, "
+ "external storage state is " + Environment.getExternalStorageState());
externalDir = new File(appDir, SYNC_DIR);
assetManager = context.getAssets();
}
/**
* Creates new instance with specified destination for assets
*
* @param context
* application context to retrieve the assets
* @param path
* path to sync the files
*/
public Assets(Context context, String dest) {
externalDir = new File(dest);
assetManager = context.getAssets();
}
/**
* Returns destination path on external storage where assets are copied.
*
* @return path to application directory or null if it does not exists
*/
public File getExternalDir() {
return externalDir;
}
/**
* Returns the map of asset paths to the files checksums.
*
* @return path to the root of resources directory on external storage
* @throws IOException
* if an I/O error occurs or "assets.lst" is missing
*/
public Map<String, String> getItems() throws IOException {
Map<String, String> items = new HashMap<String, String>();
for (String path : readLines(openAsset(ASSET_LIST_NAME))) {
Reader reader = new InputStreamReader(openAsset(path + HASH_EXT));
items.put(path, new BufferedReader(reader).readLine());
}
return items;
}
/**
* Returns path to hash mappings for the previously copied files. This
* method can be used to find out assets which must be updated.
*/
public Map<String, String> getExternalItems() {
try {
Map<String, String> items = new HashMap<String, String>();
File assetFile = new File(externalDir, ASSET_LIST_NAME);
for (String line : readLines(new FileInputStream(assetFile))) {
String[] fields = line.split(" ");
items.put(fields[0], fields[1]);
}
return items;
} catch (IOException e) {
return Collections.emptyMap();
}
}
/**
* In case you want to create more smart sync implementation, this method
* returns the list of items which must be synchronized.
*/
public Collection<String> getItemsToCopy(String path) throws IOException {
Collection<String> items = new ArrayList<String>();
Queue<String> queue = new ArrayDeque<String>();
queue.offer(path);
while (!queue.isEmpty()) {
path = queue.poll();
String[] list = assetManager.list(path);
for (String nested : list)
queue.offer(nested);
if (list.length == 0)
items.add(path);
}
return items;
}
private List<String> readLines(InputStream source) throws IOException {
List<String> lines = new ArrayList<String>();
BufferedReader br = new BufferedReader(new InputStreamReader(source));
String line;
while (null != (line = br.readLine()))
lines.add(line);
return lines;
}
private InputStream openAsset(String asset) throws IOException {
return assetManager.open(new File(SYNC_DIR, asset).getPath());
}
/**
* Saves the list of synchronized items. The list is stored as a two-column
* space-separated list of items in a text file. The file is located at the
* root of synchronization directory in the external storage.
*
* @param items
* the items
* @throws IOException
* if an I/O error occurs
*/
public void updateItemList(Map<String, String> items) throws IOException {
File assetListFile = new File(externalDir, ASSET_LIST_NAME);
PrintWriter pw = new PrintWriter(new FileOutputStream(assetListFile));
for (Map.Entry<String, String> entry : items.entrySet())
pw.format("%s %s\n", entry.getKey(), entry.getValue());
pw.close();
}
/**
* Copies raw asset resource to external storage of the device.
*
* @param path
* path of the asset to copy
* @throws IOException
* if an I/O error occurs
*/
public File copy(String asset) throws IOException {
InputStream source = openAsset(asset);
File destinationFile = new File(externalDir, asset);
destinationFile.getParentFile().mkdirs();
OutputStream destination = new FileOutputStream(destinationFile);
byte[] buffer = new byte[1024];
int nread;
while ((nread = source.read(buffer)) != -1) {
if (nread == 0) {
nread = source.read();
if (nread < 0)
break;
destination.write(nread);
continue;
}
destination.write(buffer, 0, nread);
}
destination.close();
return destinationFile;
}
/**
* Performs the sync of assets in the application and on the external
* storage
*
* @return The folder on external storage with data
* @throws IOException
*/
public File syncAssets() throws IOException {
Collection<String> newItems = new ArrayList<String>();
Collection<String> unusedItems = new ArrayList<String>();
Map<String, String> items = getItems();
Map<String, String> externalItems = getExternalItems();
for (String path : items.keySet()) {
if (!items.get(path).equals(externalItems.get(path))
|| !(new File(externalDir, path).exists()))
newItems.add(path);
}
unusedItems.addAll(externalItems.keySet());
unusedItems.removeAll(items.keySet());
for (String path : newItems) {
File file = copy(path);
}
for (String path : unusedItems) {
File file = new File(externalDir, path);
file.delete();
}
updateItemList(items);
return externalDir;
}
}
@@ -1,39 +0,0 @@
// Copyright 2019 Alpha Cephei Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package org.kaldi;
/** Interface to receive recognition results */
public interface RecognitionListener {
/**
* Called when partial recognition result is available.
*/
public void onPartialResult(String hypothesis);
/**
* Called after the recognition is ended.
*/
public void onResult(String hypothesis);
/**
* Called when an error occurs.
*/
public void onError(Exception exception);
/**
* Called after timeout expired
*/
public void onTimeout();
}
@@ -1,286 +0,0 @@
// Copyright 2019 Alpha Cephei Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package org.kaldi;
import static java.lang.String.format;
import java.io.File;
import java.io.IOException;
import java.util.Collection;
import java.util.HashSet;
import android.media.AudioFormat;
import android.media.AudioRecord;
import android.media.MediaRecorder.AudioSource;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
/**
* Main class to access recognizer functions. After configuration this class
* starts a listener thread which records the data and recognizes it using
* VOSK engine. Recognition events are passed to a client using
* {@link RecognitionListener}
*
*/
public class SpeechRecognizer {
protected static final String TAG = SpeechRecognizer.class.getSimpleName();
private final KaldiRecognizer recognizer;
private final int sampleRate;
private final static float BUFFER_SIZE_SECONDS = 0.4f;
private int bufferSize;
private final AudioRecord recorder;
private Thread recognizerThread;
private final Handler mainHandler = new Handler(Looper.getMainLooper());
private final Collection<RecognitionListener> listeners = new HashSet<RecognitionListener>();
/**
* Creates speech recognizer. Recognizer holds the AudioRecord object, so you
* need to call {@link release} in order to properly finalize it.
*
* @throws IOException thrown if audio recorder can not be created for some reason.
*/
public SpeechRecognizer(Model model) throws IOException {
recognizer = new KaldiRecognizer(model, 16000.0f);
sampleRate = 16000;
bufferSize = Math.round(sampleRate * BUFFER_SIZE_SECONDS);
recorder = new AudioRecord(
AudioSource.VOICE_RECOGNITION, sampleRate,
AudioFormat.CHANNEL_IN_MONO,
AudioFormat.ENCODING_PCM_16BIT, bufferSize * 2);
if (recorder.getState() == AudioRecord.STATE_UNINITIALIZED) {
recorder.release();
throw new IOException(
"Failed to initialize recorder. Microphone might be already in use.");
}
}
/**
* Adds listener.
*/
public void addListener(RecognitionListener listener) {
synchronized (listeners) {
listeners.add(listener);
}
}
/**
* Removes listener.
*/
public void removeListener(RecognitionListener listener) {
synchronized (listeners) {
listeners.remove(listener);
}
}
/**
* Starts recognition. Does nothing if recognition is active.
*
* @return true if recognition was actually started
*/
public boolean startListening() {
if (null != recognizerThread)
return false;
recognizerThread = new RecognizerThread();
recognizerThread.start();
return true;
}
/**
* Starts recognition. After specified timeout listening stops and the
* endOfSpeech signals about that. Does nothing if recognition is active.
*
* @timeout - timeout in milliseconds to listen.
*
* @return true if recognition was actually started
*/
public boolean startListening(int timeout) {
if (null != recognizerThread)
return false;
recognizerThread = new RecognizerThread(timeout);
recognizerThread.start();
return true;
}
private boolean stopRecognizerThread() {
if (null == recognizerThread)
return false;
try {
recognizerThread.interrupt();
recognizerThread.join();
} catch (InterruptedException e) {
// Restore the interrupted status.
Thread.currentThread().interrupt();
}
recognizerThread = null;
return true;
}
/**
* Stops recognition. All listeners should receive final result if there is
* any. Does nothing if recognition is not active.
*
* @return true if recognition was actually stopped
*/
public boolean stop() {
boolean result = stopRecognizerThread();
if (result) {
mainHandler.post(new ResultEvent(recognizer.Result(), true));
}
return result;
}
/**
* Cancels recognition. Listeners do not receive final result. Does nothing
* if recognition is not active.
*
* @return true if recognition was actually canceled
*/
public boolean cancel() {
boolean result = stopRecognizerThread();
recognizer.Result(); // Reset recognizer state
return result;
}
/**
* Shutdown the recognizer and release the recorder
*/
public void shutdown() {
recorder.release();
}
private final class RecognizerThread extends Thread {
private int remainingSamples;
private int timeoutSamples;
private final static int NO_TIMEOUT = -1;
public RecognizerThread(int timeout) {
if (timeout != NO_TIMEOUT)
this.timeoutSamples = timeout * sampleRate / 1000;
else
this.timeoutSamples = NO_TIMEOUT;
this.remainingSamples = this.timeoutSamples;
}
public RecognizerThread() {
this(NO_TIMEOUT);
}
@Override
public void run() {
recorder.startRecording();
if (recorder.getRecordingState() == AudioRecord.RECORDSTATE_STOPPED) {
recorder.stop();
IOException ioe = new IOException(
"Failed to start recording. Microphone might be already in use.");
mainHandler.post(new OnErrorEvent(ioe));
return;
}
short[] buffer = new short[bufferSize];
while (!interrupted()
&& ((timeoutSamples == NO_TIMEOUT) || (remainingSamples > 0))) {
int nread = recorder.read(buffer, 0, buffer.length);
if (nread < 0) {
throw new RuntimeException("error reading audio buffer");
} else {
boolean isFinal = recognizer.AcceptWaveform(buffer, nread);
if (isFinal) {
mainHandler.post(new ResultEvent(recognizer.Result(), true));
} else {
mainHandler.post(new ResultEvent(recognizer.PartialResult(), false));
}
}
if (timeoutSamples != NO_TIMEOUT) {
remainingSamples = remainingSamples - nread;
}
}
recorder.stop();
// Remove all pending notifications.
mainHandler.removeCallbacksAndMessages(null);
// If we met timeout signal that speech ended
if (timeoutSamples != NO_TIMEOUT && remainingSamples <= 0) {
mainHandler.post(new TimeoutEvent());
}
}
}
private abstract class RecognitionEvent implements Runnable {
public void run() {
RecognitionListener[] emptyArray = new RecognitionListener[0];
for (RecognitionListener listener : listeners.toArray(emptyArray))
execute(listener);
}
protected abstract void execute(RecognitionListener listener);
}
private class ResultEvent extends RecognitionEvent {
protected final String hypothesis;
private final boolean finalResult;
ResultEvent(String hypothesis, boolean finalResult) {
this.hypothesis = hypothesis;
this.finalResult = finalResult;
}
@Override
protected void execute(RecognitionListener listener) {
if (finalResult)
listener.onResult(hypothesis);
else
listener.onPartialResult(hypothesis);
}
}
private class OnErrorEvent extends RecognitionEvent {
private final Exception exception;
OnErrorEvent(Exception exception) {
this.exception = exception;
}
@Override
protected void execute(RecognitionListener listener) {
listener.onError(exception);
}
}
private class TimeoutEvent extends RecognitionEvent {
@Override
protected void execute(RecognitionListener listener) {
listener.onTimeout();
}
}
}
+16
View File
@@ -0,0 +1,16 @@
CFLAGS=-I../../src
LDFLAGS=-L../../src -lvosk -ldl -lpthread -Wl,-rpath,../../src
all: test_vosk test_vosk_multi
test_vosk: test_vosk.o
gcc $^ -o $@ $(LDFLAGS)
test_vosk_multi: test_vosk_multi.o
gcc $^ -o $@ $(LDFLAGS) -lpthread
%.o: %.c
gcc $(CFLAGS) -c -o $@ $<
clean:
rm -f *.o *.a test_vosk test_vosk_multi
+37
View File
@@ -0,0 +1,37 @@
#include <vosk_api.h>
#include <stdio.h>
#include <unistd.h>
int main() {
FILE *wavin;
char buf[3200];
int nread, final;
VoskModel *model = vosk_model_new("vosk-model-small-ru");
VoskRecognizer *recognizer = vosk_recognizer_new(model, 16000.0);
wavin = fopen("test.wav", "rb");
fseek(wavin, 44, SEEK_SET);
while (!feof(wavin)) {
nread = fread(buf, 1, sizeof(buf), wavin);
vosk_recognizer_accept_waveform(recognizer, buf, nread);
while (vosk_recognizer_get_num_pending_results(recognizer) > 0)
sleep(0.05);
while (!vosk_recognizer_results_empty(recognizer)) {
printf("%s\n", vosk_recognizer_result_front(recognizer));
vosk_recognizer_result_pop(recognizer);
}
}
vosk_recognizer_flush(recognizer);
while (vosk_recognizer_get_num_pending_results(recognizer) > 0)
sleep(0.05);
while (!vosk_recognizer_results_empty(recognizer)) {
printf("%s\n", vosk_recognizer_result_front(recognizer));
vosk_recognizer_result_pop(recognizer);
}
vosk_recognizer_free(recognizer);
vosk_model_free(model);
fclose(wavin);
return 0;
}
+82
View File
@@ -0,0 +1,82 @@
#include <vosk_api.h>
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <time.h>
#include <stdlib.h>
#include <string.h>
#define NUM_THREADS 10
#define NUM_FILES_PER_THREAD 3
#define NUM_FILES 400
#define MAX_FILE 100
VoskModel *model;
char infiles[NUM_FILES][MAX_FILE];
pthread_t thread_ids[NUM_THREADS];
static void *worker(void *data) {
int j;
for (j = 0; j < NUM_FILES_PER_THREAD; j++) {
FILE *wavin;
char buf[4000];
int nread;
VoskRecognizer *recognizer = vosk_recognizer_new(model, 16000.0);
int foffset = rand() % NUM_FILES;
wavin = fopen(infiles[foffset], "rb");
fseek(wavin, 44, SEEK_SET);
while (!feof(wavin)) {
nread = fread(buf, 1, sizeof(buf), wavin);
vosk_recognizer_accept_waveform(recognizer, buf, nread);
while (vosk_recognizer_get_num_pending_results(recognizer) > 0)
sleep(0.05);
while (!vosk_recognizer_results_empty(recognizer)) {
printf("%s\n", vosk_recognizer_result_front(recognizer));
vosk_recognizer_result_pop(recognizer);
}
}
vosk_recognizer_flush(recognizer);
while (vosk_recognizer_get_num_pending_results(recognizer) > 0)
sleep(0.05);
while (!vosk_recognizer_results_empty(recognizer)) {
printf("%s\n", vosk_recognizer_result_front(recognizer));
vosk_recognizer_result_pop(recognizer);
}
vosk_recognizer_free(recognizer);
fclose(wavin);
}
}
void read_file_list() {
FILE* infile;
infile = fopen("file.list", "r");
int i = 0;
while(i < NUM_FILES, fgets(infiles[i], MAX_FILE, infile)) {
infiles[i][strlen(infiles[i]) - 1] = 0;
printf("%s\n", infiles[i]);
i++;
}
fclose(infile);
}
int main() {
int i;
srand(0);
read_file_list();
model = vosk_model_new("vosk-model-small-ru");
for (i = 0; i < NUM_THREADS; i++) {
pthread_create(&thread_ids[i], NULL, worker, NULL);
}
for (i = 0; i < NUM_THREADS; i++) {
pthread_join(thread_ids[i], NULL);
}
vosk_model_free(model);
return 0;
}
+7
View File
@@ -0,0 +1,7 @@
Java bindings for Vosk API using jnr-ffi
See demo project for details, build it with Gradle.
Download model and unpack as "model" folder in the demo project.
Make sure you are using recent JDK and Gradle.
+71
View File
@@ -0,0 +1,71 @@
buildscript {
repositories {
mavenCentral()
}
}
plugins {
id 'java-library'
id 'maven-publish'
id 'com.vanniktech.maven.publish' version '0.18.0'
}
repositories {
mavenCentral()
}
archivesBaseName = 'vosk'
group = 'com.alphacephei'
version = '0.4.0'
mavenPublish {
group = 'com.alphacephei'
version = version
sonatypeHost = 's01'
}
dependencies {
api group: 'net.java.dev.jna', name: 'jna', version: '5.13.0'
testImplementation 'junit:junit:4.13'
}
publishing {
publications {
mavenJava(MavenPublication) {
artifactId = 'vosk'
from components.java
pom {
name = 'Vosk'
description = 'Speech recognition library'
url = 'http://www.alphacephei.com.com/vosk/'
licenses {
license {
name = 'The Apache License, Version 2.0'
url = 'http://www.apache.org/licenses/LICENSE-2.0.txt'
}
}
developers {
developer {
id = 'alphacephei'
name = 'Alpha Cephei Inc'
email = 'contact@alphacephei.com'
}
}
scm {
connection = 'scm:git:git://github.com/alphacep/vosk-api.git'
url = 'https://github.com/alphacep/vosk-api/'
}
}
}
}
}
test {
dependsOn cleanTest
testLogging.showStandardStreams = true
}
java {
withSourcesJar()
withJavadocJar()
}
@@ -0,0 +1,86 @@
package org.vosk;
import com.sun.jna.Native;
import com.sun.jna.Platform;
import com.sun.jna.Pointer;
import java.io.File;
import java.io.InputStream;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
public class LibVosk {
private static void unpackDll(File targetDir, String lib) throws IOException {
try (InputStream source = LibVosk.class.getResourceAsStream("/win32-x86-64/" + lib + ".dll")) {
Files.copy(source, new File(targetDir, lib + ".dll").toPath(), StandardCopyOption.REPLACE_EXISTING);
}
}
static {
if (Platform.isWindows()) {
// We have to unpack dependencies
try {
// To get a tmp folder we unpack small library and mark it for deletion
File tmpFile = Native.extractFromResourcePath("/win32-x86-64/empty", LibVosk.class.getClassLoader());
File tmpDir = tmpFile.getParentFile();
new File(tmpDir, tmpFile.getName() + ".x").createNewFile();
// Now unpack dependencies
unpackDll(tmpDir, "libwinpthread-1");
unpackDll(tmpDir, "libgcc_s_seh-1");
unpackDll(tmpDir, "libstdc++-6");
} catch (IOException e) {
// Nothing for now, it will fail on next step
} finally {
Native.register(LibVosk.class, "libvosk");
}
} else {
Native.register(LibVosk.class, "vosk");
}
}
public static native void vosk_set_log_level(int level);
public static native Pointer vosk_model_new(String path);
public static native void vosk_model_free(Pointer model);
public static native Pointer vosk_recognizer_new(Model model, float sample_rate);
public static native void vosk_recognizer_accept_waveform(Pointer recognizer, byte[] data, int len);
public static native void vosk_recognizer_accept_waveform_s(Pointer recognizer, short[] data, int len);
public static native void vosk_recognizer_accept_waveform_f(Pointer recognizer, float[] data, int len);
public static native void vosk_recognizer_flush(Pointer recognizer);
public static native String vosk_recognizer_result_front(Pointer recognizer);
public static native void vosk_recognizer_result_pop(Pointer recognizer);
public static native int vosk_recognizer_get_num_pending_results(Pointer recognizer);
public static native int vosk_recognizer_get_num_results(Pointer recognizer);
public static native int vosk_recognizer_results_empty(Pointer recognizer);
public static native void vosk_recognizer_reset(Pointer recognizer);
public static native void vosk_recognizer_free(Pointer recognizer);
/**
* Set log level for Kaldi messages.
*
* @param loglevel the level
* 0 - default value to print info and error messages but no debug
* less than 0 - don't print info messages
* greater than 0 - more verbose mode
*/
public static void setLogLevel(LogLevel loglevel) {
vosk_set_log_level(loglevel.getValue());
}
}
@@ -0,0 +1,17 @@
package org.vosk;
public enum LogLevel {
WARNINGS(-1), // Print warning and errors
INFO(0), // Print info, along with warning and error messages, but no debug
DEBUG(1); // Print debug info
private final int value;
LogLevel(int value) {
this.value = value;
}
public int getValue() {
return this.value;
}
}
@@ -0,0 +1,22 @@
package org.vosk;
import java.io.IOException;
import com.sun.jna.PointerType;
public class Model extends PointerType implements AutoCloseable {
public Model() {
}
public Model(String path) throws IOException {
super(LibVosk.vosk_model_new(path));
if (getPointer() == null) {
throw new IOException("Failed to create a model");
}
}
@Override
public void close() {
LibVosk.vosk_model_free(this.getPointer());
}
}
@@ -0,0 +1,113 @@
package org.vosk;
import com.sun.jna.PointerType;
import java.io.IOException;
public class Recognizer extends PointerType implements AutoCloseable {
/**
* Creates the recognizer object.
*
* The recognizers process the speech and return text using shared model data
* @param model VoskModel containing static data for recognizer. Model can be
* shared across recognizers, even running in different threads.
* @param sampleRate The sample rate of the audio you are going to feed into the recognizer.
* Make sure this rate matches the audio content, it is a common
* issue causing accuracy problems.
* @throws IOException if the recognizer could not be created
*/
public Recognizer(Model model, float sampleRate) throws IOException {
super(LibVosk.vosk_recognizer_new(model, sampleRate));
if (getPointer() == null) {
throw new IOException("Failed to create a recognizer");
}
}
/**
* Accept and process new chunk of voice data.
*
* @param data - audio data in PCM 16-bit mono format
* @param len - length of the audio data
*/
public void acceptWaveForm(byte[] data, int len) {
LibVosk.vosk_recognizer_accept_waveform(this.getPointer(), data, len);
}
public void acceptWaveForm(short[] data, int len) {
LibVosk.vosk_recognizer_accept_waveform_s(this.getPointer(), data, len);
}
public void acceptWaveForm(float[] data, int len) {
LibVosk.vosk_recognizer_accept_waveform_f(this.getPointer(), data, len);
}
public void flush() {
LibVosk.vosk_recognizer_flush(this.getPointer());
}
/**
* Returns speech recognition result
*
* @return the result in JSON format which contains decoded line, decoded
* words, times in seconds and confidences. You can parse this result
* with any json parser
*
* <pre>
* {
* "text" : "what zero zero zero one"
* }
* </pre>
*
* If alternatives enabled it returns result with alternatives, see also #setMaxAlternatives().
*
* If word times enabled returns word time, see also #setWordTimes().
*/
public String getResult() {
return LibVosk.vosk_recognizer_result_front(this.getPointer());
}
/**
* Removes the latest result from the result queue
*/
public void popResult() {
LibVosk.vosk_recognizer_result_pop(this.getPointer());
}
/**
* Counts pending results
*/
public int getNumPendingResults() {
return LibVosk.vosk_recognizer_get_num_pending_results(this.getPointer());
}
/**
* Counts pending results
*/
public int getNumResults() {
return LibVosk.vosk_recognizer_get_num_results(this.getPointer());
}
/**
* Check if output queue is empty
*/
public boolean getResultsEmpty() {
return (LibVosk.vosk_recognizer_results_empty(this.getPointer()) == 1);
}
/**
* Resets the recognizer.
* Resets current results so the recognition can continue from scratch.
*/
public void reset() {
LibVosk.vosk_recognizer_reset(this.getPointer());
}
/**
* Releases recognizer object.
* Underlying model is also unreferenced and if needed, released.
*/
@Override
public void close() {
LibVosk.vosk_recognizer_free(this.getPointer());
}
}
@@ -0,0 +1,86 @@
package org.vosk.test;
import java.io.FileInputStream;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import org.junit.Test;
import org.junit.Assert;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.UnsupportedAudioFileException;
import org.vosk.LogLevel;
import org.vosk.Recognizer;
import org.vosk.LibVosk;
import org.vosk.Model;
public class DecoderTest {
@Test
public void decoderTest() throws IOException, UnsupportedAudioFileException, InterruptedException {
LibVosk.setLogLevel(LogLevel.DEBUG);
try (Model model = new Model("../../python/example/vosk-model-small-ru");
InputStream ais = AudioSystem.getAudioInputStream(new BufferedInputStream(new FileInputStream("../../python/example/test-ru.wav")));
Recognizer recognizer = new Recognizer(model, 16000)) {
int nbytes;
byte[] b = new byte[4000];
while ((nbytes = ais.read(b)) >= 0) {
recognizer.acceptWaveForm(b, nbytes);
while (recognizer.getNumPendingResults() > 0) {
Thread.sleep(50);
}
while (!recognizer.getResultsEmpty()) {
System.out.println(recognizer.getResult());
recognizer.popResult();
}
}
recognizer.flush();
while (recognizer.getNumPendingResults() > 0) {
Thread.sleep(50);
}
while (!recognizer.getResultsEmpty()) {
System.out.println(recognizer.getResult());
recognizer.popResult();
}
}
Assert.assertTrue(true);
}
@Test
public void decoderTestBig() throws IOException, UnsupportedAudioFileException, InterruptedException {
LibVosk.setLogLevel(LogLevel.DEBUG);
try (Model model = new Model("../../python/example/vosk-model-small-ru");
InputStream ais = AudioSystem.getAudioInputStream(new BufferedInputStream(new FileInputStream("../../python/example/test-ru.wav")));
Recognizer recognizer = new Recognizer(model, 16000)) {
int nbytes;
byte[] b = new byte[400000];
while ((nbytes = ais.read(b)) >= 0) {
recognizer.acceptWaveForm(b, nbytes);
while (recognizer.getNumPendingResults() > 0) {
Thread.sleep(50);
}
while (!recognizer.getResultsEmpty()) {
System.out.println(recognizer.getResult());
recognizer.popResult();
}
}
recognizer.flush();
while (recognizer.getNumPendingResults() > 0) {
Thread.sleep(50);
}
while (!recognizer.getResultsEmpty()) {
System.out.println(recognizer.getResult());
recognizer.popResult();
}
}
Assert.assertTrue(true);
}
}
+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
+25
View File
@@ -0,0 +1,25 @@
This is a Python module for Vosk.
Vosk is an offline open source speech recognition toolkit. It enables
speech recognition for 20+ languages and dialects - English, Indian
English, German, French, Spanish, Portuguese, Chinese, Russian, Turkish,
Vietnamese, Italian, Dutch, Catalan, Arabic, Greek, Farsi, Filipino,
Ukrainian, Kazakh, Swedish, Japanese, Esperanto, Hindi, Czech, Polish.
More to come.
Vosk models are small (50 Mb) but provide continuous large vocabulary
transcription, zero-latency response with streaming API, reconfigurable
vocabulary and speaker identification.
Vosk supplies speech recognition for chatbots, smart home appliances,
virtual assistants. It can also create subtitles for movies,
transcription for lectures and interviews.
Vosk scales from small devices like Raspberry Pi or Android smartphone to
big clusters.
# Documentation
For installation instructions, examples and documentation visit [Vosk
Website](https://alphacephei.com/vosk). See also our project on
[Github](https://github.com/alphacep/vosk-api).
+48
View File
@@ -0,0 +1,48 @@
#!/usr/bin/env python3
import wave
import sys
import time
from vosk import Model, KaldiRecognizer, SetLogLevel
# You can set log level to -1 to disable debug messages
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("vosk-model-ru-0.53-private-0.1")
model = Model("vosk-model-small-ru")
rec = KaldiRecognizer(model, wf.getframerate())
while True:
data = wf.readframes(4000)
if len(data) == 0:
break
# Feed into waveform
rec.AcceptWaveform(data)
# Wait for processing
while rec.GetNumPendingResults() > 0:
time.sleep(0.05)
# Retrieve the results
while not rec.ResultsEmpty():
print (rec.Result())
rec.Pop()
rec.Flush()
# Wait for processing
while rec.GetNumPendingResults() > 0:
time.sleep(0.05)
# Retrieve the results
while not rec.ResultsEmpty():
print (rec.Result())
rec.Pop()
+62
View File
@@ -0,0 +1,62 @@
#!/usr/bin/env python3
import wave
import json
import sys
import os
import time
from timeit import default_timer as timer
from multiprocessing.dummy import Pool
from vosk import Model, KaldiRecognizer
#model = Model("vosk-model-ru-0.54-private-0.1")
model = Model("vosk-model-ru-0.62")
#model = Model("vosk-model-small-ru")
def recognize(line):
fn = line.strip()
wf = wave.open(fn, "rb")
rec = KaldiRecognizer(model, wf.getframerate())
results = []
while True:
data = wf.readframes(4000)
if len(data) == 0:
break
rec.AcceptWaveform(data)
while rec.GetNumPendingResults() > 0:
time.sleep(0.05)
while not rec.ResultsEmpty():
jres = json.loads(rec.Result())
if 'text' in jres:
print (jres)
results.append(jres['text'])
rec.Pop()
rec.Flush()
while rec.GetNumPendingResults() > 0:
time.sleep(0.05)
while not rec.ResultsEmpty():
jres = json.loads(rec.Result())
if 'text' in jres:
print (jres)
results.append(jres['text'])
rec.Pop()
owf = open(fn.replace(".wav", ".hyp"), "w")
owf.write("\n".join(results))
def main():
start = timer()
p = Pool(20)
p.map(recognize, open(sys.argv[1]).readlines())
allsize = sum([os.path.getsize(f.strip()) for f in open(sys.argv[1]).readlines()]) / 8000.0 / 2
dur = timer() - start
print (f"Recognized {allsize:.2f} seconds in {dur} seconds speed {dur / allsize:.3f} x RT")
main()
+30
View File
@@ -0,0 +1,30 @@
#!/usr/bin/env python3
import wave
import sys
import time
from vosk import Model, KaldiRecognizer, SetLogLevel
# You can set log level to -1 to disable debug messages
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("vosk-model-ru-0.53-private-0.1")
model = Model("vosk-model-small-ru")
rec = KaldiRecognizer(model, wf.getframerate())
data = wf.readframes(min(wf.getnframes(), 320000))
rec.AcceptWaveform(data)
rec.Flush()
while rec.GetNumPendingResults() > 0:
time.sleep(0.05)
while not rec.ResultsEmpty():
print (rec.Result())
rec.Pop()
+75
View File
@@ -0,0 +1,75 @@
import os
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_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*.*")):
print ("Adding library", lib)
shutil.copy(lib, "vosk")
# Create OS-dependent, but Python-independent wheels.
try:
from wheel.bdist_wheel import bdist_wheel
except ImportError:
cmdclass = {}
else:
class bdist_wheel_tag_name(bdist_wheel):
def get_tag(self):
abi = 'none'
if system == 'Darwin':
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 machine == 'aarch64' and architecture == '64bit':
oses = 'manylinux2014_aarch64'
elif system == 'Linux':
oses = 'linux_' + machine
else:
raise TypeError("Unknown build environment")
return 'py3', abi, oses
cmdclass = {'bdist_wheel': bdist_wheel_tag_name}
with open("README.md", "rb") as fh:
long_description = fh.read().decode("utf-8")
setuptools.setup(
name="vosk",
version="0.4.0",
author="Alpha Cephei Inc",
author_email="contact@alphacephei.com",
description="Offline open source speech recognition API based on Kaldi and Vosk",
long_description=long_description,
long_description_content_type="text/markdown",
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',
'License :: OSI Approved :: Apache Software License',
'Operating System :: Microsoft :: Windows',
'Operating System :: POSIX :: Linux',
'Operating System :: MacOS :: MacOS X',
'Topic :: Software Development :: Libraries :: Python Modules'
],
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', 'requests', 'tqdm', 'srt', 'websockets'],
install_requires=['cffi>=1.0', 'requests', 'tqdm', 'srt', 'websockets'],
cffi_modules=['vosk_builder.py:ffibuilder'],
)
+247
View File
@@ -0,0 +1,247 @@
import os
import sys
import srt
import datetime
import json
import enum
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":
# We want to load dependencies too
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":
return _ffi.dlopen(os.path.join(dlldir, "libvosk.so"))
elif sys.platform == "darwin":
return _ffi.dlopen(os.path.join(dlldir, "libvosk.dyld"))
else:
raise TypeError("Unsupported platform")
_c = open_dll()
def list_models():
response = requests.get(MODEL_LIST_URL, timeout=10)
for model in response.json():
print(model["name"])
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):
if _c is not None:
_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"))
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"))
if self._handle == _ffi.NULL:
raise Exception("Failed to create a speaker model")
def __del__(self):
_c.vosk_spk_model_free(self._handle)
class EndpointerMode(enum.Enum):
DEFAULT = 0
SHORT = 1
LONG = 2
VERY_LONG = 3
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 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)
def SetMaxAlternatives(self, max_alternatives):
_c.vosk_recognizer_set_max_alternatives(self._handle, max_alternatives)
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 SetEndpointerMode(self, mode):
_c.vosk_recognizer_set_endpointer_mode(self._handle, mode.value)
def SetEndpointerDelays(self, t_start_max, t_end, t_max):
_c.vosk_recognizer_set_endpointer_delays(self._handle, t_start_max, t_end, t_max)
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):
_c.vosk_recognizer_accept_waveform(self._handle, data, len(data))
def Flush(self):
return _c.vosk_recognizer_flush(self._handle)
def Result(self):
return _ffi.string(_c.vosk_recognizer_result_front(self._handle)).decode("utf-8")
def Pop(self):
return _c.vosk_recognizer_result_pop(self._handle)
def GetNumPendingResults(self):
return _c.vosk_recognizer_get_num_pending_results(self._handle)
def GetNumResults(self):
return _c.vosk_recognizer_get_num_results(self._handle)
def ResultsEmpty(self):
return _c.vosk_recognizer_results_empty(self._handle)
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
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", type=str,
help="use server for recognition. For example ws://localhost:2700")
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()
@@ -0,0 +1,201 @@
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"] != "":
monologues["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)
await proc.wait()
# Bad input, continue
if tot_samples == 0:
self.queue.task_done()
continue
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)
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)
if tot_samples == 0:
return
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))
+14
View File
@@ -0,0 +1,14 @@
#!/usr/bin/env python3
import os
from cffi import FFI
vosk_root=os.environ.get("VOSK_SOURCE", "../..")
cpp_command = "cpp " + vosk_root + "/src/vosk_api.h"
ffibuilder = FFI()
ffibuilder.set_source("vosk.vosk_cffi", None)
ffibuilder.cdef(os.popen(cpp_command).read())
if __name__ == '__main__':
ffibuilder.compile(verbose=True)
-54
View File
@@ -1,54 +0,0 @@
KALDI_ROOT ?= $(HOME)/kaldi
CFLAGS := -std=c++11 -g -O2 -DPIC -fPIC -Wno-unused-function
CPPFLAGS := -I$(KALDI_ROOT)/src -I$(KALDI_ROOT)/tools/openfst/include -I../src -DFST_NO_DYNAMIC_LINKING
KALDI_LIBS = \
${KALDI_ROOT}/src/online2/kaldi-online2.a \
${KALDI_ROOT}/src/decoder/kaldi-decoder.a \
${KALDI_ROOT}/src/ivector/kaldi-ivector.a \
${KALDI_ROOT}/src/gmm/kaldi-gmm.a \
${KALDI_ROOT}/src/nnet3/kaldi-nnet3.a \
${KALDI_ROOT}/src/tree/kaldi-tree.a \
${KALDI_ROOT}/src/feat/kaldi-feat.a \
${KALDI_ROOT}/src/lat/kaldi-lat.a \
${KALDI_ROOT}/src/lm/kaldi-lm.a \
${KALDI_ROOT}/src/hmm/kaldi-hmm.a \
${KALDI_ROOT}/src/transform/kaldi-transform.a \
${KALDI_ROOT}/src/cudamatrix/kaldi-cudamatrix.a \
${KALDI_ROOT}/src/matrix/kaldi-matrix.a \
${KALDI_ROOT}/src/fstext/kaldi-fstext.a \
${KALDI_ROOT}/src/util/kaldi-util.a \
${KALDI_ROOT}/src/base/kaldi-base.a \
${KALDI_ROOT}/tools/openfst/lib/libfst.a \
${KALDI_ROOT}/tools/openfst/lib/libfstngram.a \
${KALDI_ROOT}/tools/OpenBLAS/libopenblas.a \
-lgfortran -lstdc++
all: test.exe
test.exe: libkaldiwrap.so test.cs
mcs test.cs gen/*.cs
VOSK_SOURCES = \
vosk_wrap.c \
../src/kaldi_recognizer.cc \
../src/kaldi_recognizer.h \
../src/model.cc \
../src/model.h \
../src/spk_model.cc \
../src/spk_model.h \
../src/vosk_api.cc \
../src/vosk_api.h
libkaldiwrap.so: $(VOSK_SOURCES)
$(CXX) -fpermissive $(CFLAGS) $(CPPFLAGS) -shared -o $@ $(VOSK_SOURCES) $(KALDI_LIBS)
vosk_wrap.c: ../src/vosk.i
swig -csharp -DSWIG_CSHARP_NO_EXCEPTION_HELPER -dllimport "libkaldiwrap" \
-namespace "Kaldi" -outdir gen -o vosk_wrap.c ../src/vosk.i
run: test.exe
mono test.exe
clean:
$(RM) *.so vosk_wrap.c *.o gen/*.cs test.exe
-47
View File
@@ -1,47 +0,0 @@
using System;
using System.IO;
using Kaldi;
public class Test
{
public static void Main()
{
Vosk.SetLogLevel(0);
Model model = new Model("model");
KaldiRecognizer rec = new KaldiRecognizer(model, 16000.0f);
using(Stream source = File.OpenRead("test.wav")) {
byte[] buffer = new byte[4096];
int bytesRead;
while((bytesRead = source.Read(buffer, 0, buffer.Length)) > 0) {
if (rec.AcceptWaveform(buffer, bytesRead)) {
Console.WriteLine(rec.Result());
} else {
Console.WriteLine(rec.PartialResult());
}
}
}
Console.WriteLine(rec.FinalResult());
rec = new KaldiRecognizer(model, 16000.0f);
using(Stream source = File.OpenRead("test.wav")) {
byte[] buffer = new byte[4096];
int bytesRead;
while((bytesRead = source.Read(buffer, 0, buffer.Length)) > 0) {
float[] fbuffer = new float[bytesRead / 2];
for (int i = 0, n = 0; i < fbuffer.Length; i++, n+=2) {
fbuffer[i] = (short)(buffer[n] | buffer[n+1] << 8);
}
if (rec.AcceptWaveform(fbuffer, fbuffer.Length)) {
Console.WriteLine(rec.Result());
GC.Collect();
} else {
Console.WriteLine(rec.PartialResult());
}
}
}
Console.WriteLine(rec.FinalResult());
}
}
-21
View File
@@ -1,21 +0,0 @@
## Accuracy issues
Accuracy of modern systems is still unstable, that means sometimes you can have a very good accuracy and sometimes it could be bad.
It is hard to make a system that will work good. And there could be many reasons for that:
* Audio has very bad quality
* Vocabulary of the system doesn't match (yes, we still use fixed vocabulary)
* Audio conditions like accent were not really the ones that were used in training
* Some unpredictable audio issues like frame drop or frame coding bugs
* Software bugs
It is hard to guess what is going on under the hood without getting your hands dirty. For that reason in case of any accuracy
issues you must provide the following for analysis:
* Who are you, where are you from and why are you doing that. We don't like dealing with anonymous
* The complete and exact description of the system you want to build - what is it going to do, what do you want to build
* The precise description of hardware you are trying to run the system on
* The detailed list of software versions you are using
* Audio samples to demonstrate the problem together with the reference transcription for those samples
Remember, the more information you provide the faster you get a solution.
-42
View File
@@ -1,42 +0,0 @@
## Updating the language model
The Kaldi model used in Vosk is compiled from 3 data sources:
* dictionary
* acoustic model
* language model
You can rebuild all three with different level of effort, but sometimes you just
need to adjust the probability of the words to improve the recognition. For
that it is enough to recompile the language model from the text. To do that
1) Take a text that reflects the speech you want to recognize
2) Remove punctuation, convert everything to the lowercase, you can do it with a python script
3) Build openfst and opengrm inside kaldi
```
export KALDI_ROOT=`pwd`/kaldi
git clone https://github.com/kaldi-asr/kaldi
cd kaldi/tools
make
# install all required dependencies and repeat `make` if needed
extras/install_opengrm.sh
```
4) Now lets build a grammar
```
export PATH=$KALDI_ROOT/tools/openfst/bin:$PATH
export LD_LIBRARY_PATH=$KALDI_ROOT/tools/openfst/lib/fst
cd model
fstsymbols --save_osymbols=words.txt Gr.fst > /dev/null
farcompilestrings --fst_type=compact --symbols=words.txt --keep_symbols text.txt | \
ngramcount | ngrammake | \
fstconvert --fst_type=ngram > Gr.fst
```
Use created Gr.fst instead of standard one in your model.
For more details see OpenGRM documentation http://www.opengrm.org/twiki/bin/view/GRM/NGramLibrary
You can not introduce new words this way, that is something we will cover later.
-81
View File
@@ -1,81 +0,0 @@
# Models
This is the list of models compatible with Vosk-API.
To add a new model here create an issue on Github.
| Model | Size | Accuracy | Notes |
|-------------------------------------------------------------------------------------------------------------------------|-------|------------|----------------------------------------------------------------------------------------------|
| **English** | | | |
| [vosk-model-en-us-aspire-0.2](http://alphacephei.com/kaldi/models/vosk-model-en-us-aspire-0.2.zip) | 1.4G | TBD | Trained on Fisher + more or less recent LM. Should be pretty good for generic US English transcription |
| [vosk-model-small-en-us-0.3](http://alphacephei.com/kaldi/models/vosk-model-small-en-us-0.3.zip) | 36M | TBD | Lightweight wideband model for Android and RPi |
| **Chinese** | | | |
| [vosk-model-cn-0.1.zip](http://alphacephei.com/kaldi/models/vosk-model-cn-0.1.zip) | 195M | TBD | Big narrowband Chinese model for server processing |
| [vosk-model-small-cn-0.3](http://alphacephei.com/kaldi/models/vosk-model-small-cn-0.3.zip) | 32M | TBD | Lightweight wideband model for Android and RPi |
| **Russian** | | | |
| [vosk-model-ru-0.10.zip](http://alphacephei.com/kaldi/models/vosk-model-ru-0.10.zip) | 2.5G | TBD | Big narrowband Russian model for server processing |
| [vosk-model-small-ru-0.4](http://alphacephei.com/kaldi/models/vosk-model-small-ru-0.4.zip) | 39M | TBD | Lightweight wideband model for Android and RPi |
| **French** | | | |
| [vosk-model-small-fr-pguyot-0.3](http://alphacephei.com/kaldi/models/vosk-model-small-fr-pguyot-0.3.zip) | 39M | TBD | Lightweight wideband model for Android and RPi trained by [Paul Guyot](https://github.com/pguyot/zamia-speech/releases) |
| **German** | | | |
| [tuda-de](http://ltdata1.informatik.uni-hamburg.de/kaldi_tuda_de/de_400k_nnet3chain_tdnn1f_2048_sp_bi.tar.bz2) | 566M | TBD | Wideband server model from [tuda-de](https://github.com/uhh-lt/vosk-model-tuda-de) |
| [vosk-model-small-de-zamia-0.3](http://alphacephei.com/kaldi/models/vosk-model-small-de-zamia-0.3.zip) | 49M | TBD | Lightweight wideband model for Android and RPi |
| **Spanish** | | | |
| [vosk-model-small-es-0.3](http://alphacephei.com/kaldi/models/vosk-model-small-es-0.3.zip) | 33M | TBD | Lightweight wideband model for Android and RPi |
| **Portuguese** | | | |
| [vosk-model-small-pt-0.3](http://alphacephei.com/kaldi/models/vosk-model-small-pt-0.3.zip) | 31M | TBD | Lightweight wideband model for Android and RPi |
| **Greek** | | | |
| [vosk-model-el-gr-0.6.zip](http://alphacephei.com/kaldi/models/vosk-model-el-gr-0.6.zip) | 1.1G | TBD | Big narrowband Greek model for server processing, not extremely accurate though |
| **Turkish** | | | |
| [vosk-model-small-tr-0.3](http://alphacephei.com/kaldi/models/vosk-model-small-tr-0.3.zip) | 35M | TBD | Lightweight wideband model for Android and RPi |
| **Vietnamese** | | | |
| [vosk-model-small-vn-0.3](http://alphacephei.com/kaldi/models/vosk-model-small-vn-0.3.zip) | 32M | TBD | Lightweight wideband model for Android and RPi |
| **Speaker identification model** | | | |
| [vosk-model-spk-0.3](http://alphacephei.com/kaldi/models/vosk-model-spk-0.3.zip) | 13M | TBD | Model for speaker identification, should work for all languages |
## Other models
Other places where you can check for models which might be compatible:
* http://kaldi-asr.org/models.html - variety of models from Kaldi - librispeech, aspire, chinese models
* https://github.com/daanzu/kaldi-active-grammar/blob/master/docs/models.md - Big dictation models
* http://zamia-speech.org/asr/ - German and English model from Zamia
* https://github.com/pguyot/zamia-speech/releases - French models for Zamia
* https://github.com/opensource-spraakherkenning-nl/Kaldi_NL - Dutch model
* https://montreal-forced-aligner.readthedocs.io/en/latest/pretrained_models.html (GMM models, not compatible but might be still useful)
* https://github.com/goodatlas/zeroth - Korean Kaldi (just a recipe and data to train)
* https://github.com/undertheseanlp/automatic_speech_recognition - Vietnamese Kaldi project
## Training your own model
You can train your model with Kaldi toolkit. The training is pretty standard - you need tdnn nnet3 model with ivectors. You can
check mini_librispeech recipe for details. Some notes on training:
* For smaller mobile models watch number of parameters
* Train the model without pitch. It might be helpful for small amount of data, but for large database it doesn't give the advantage
but complicates the processing and increases response time.
* Train ivector of dim 30 instead of standard 100 to save memory of mobile models.
## Model structure
Once you trained the model arrange the files according to the following layout (see en-us-aspire for details):
* `am/final.mdl` - acoustic model
* `conf/mfcc.conf` - mfcc config file. Make sure you take mfcc_hires.conf version if you are using hires model (most external ones)
* `conf/model.conf` - provide default decoding beams and silence phones. you have to create this file yourself, it is not present in kaldi model
* `ivector/final.dubm` - take ivector files from ivector extractor
* `ivector/final.ie`
* `ivector/final.mat`
* `ivector/splice.conf`
* `ivector/global_cmvn.stats`
* `ivector/online_cmvn.conf`
* `graph/phones/word_boundary.int` - from the graph
* `graph/HCLG.fst` - this is the decoding graph, if you are not using lookahead
* `graph/HCLr.fst` - use Gr.fst and HCLr.fst instead of one big HCLG.fst if you want to run rescoring
* `graph/Gr.fst`
* `graph/phones.txt` - from the graph
* `graph/words.txt` - from the graph
* `rescore/G.carpa` - carpa rescoring is optional but helpful in big models. Usually located inside data/lang_test_rescore
* `rescore/G.fst` - also optional if you want to use rescoring
-3
View File
@@ -1,3 +0,0 @@
This is a baseline for the vosk-api iOS demo. It requires a build of a
Vosk-API library, mail contact@alphacephei.com for the details.
-477
View File
@@ -1,477 +0,0 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 46;
objects = {
/* Begin PBXBuildFile section */
92375222240C550B00DD6076 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 92375221240C550B00DD6076 /* AppDelegate.swift */; };
92375224240C550B00DD6076 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 92375223240C550B00DD6076 /* ViewController.swift */; };
92375227240C550B00DD6076 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 92375225240C550B00DD6076 /* Main.storyboard */; };
92375229240C550B00DD6076 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 92375228240C550B00DD6076 /* Assets.xcassets */; };
9237522C240C550B00DD6076 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 9237522A240C550B00DD6076 /* LaunchScreen.storyboard */; };
92375234240C558900DD6076 /* Vosk.swift in Sources */ = {isa = PBXBuildFile; fileRef = 92375233240C558900DD6076 /* Vosk.swift */; };
9237523C240C642000DD6076 /* libkaldiwrap.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 9237523A240C642000DD6076 /* libkaldiwrap.a */; };
92375244240C6DAF00DD6076 /* Accelerate.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 92375243240C6DAF00DD6076 /* Accelerate.framework */; };
92375246240C6DC900DD6076 /* libstdc++.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 92375245240C6DC900DD6076 /* libstdc++.tbd */; };
92375266240C6EFE00DD6076 /* disambig_tid.int in CopyFiles */ = {isa = PBXBuildFile; fileRef = 92375248240C6E3D00DD6076 /* disambig_tid.int */; };
92375267240C6EFE00DD6076 /* final.mdl in CopyFiles */ = {isa = PBXBuildFile; fileRef = 92375249240C6E3D00DD6076 /* final.mdl */; };
92375268240C6EFE00DD6076 /* Gr.fst in CopyFiles */ = {isa = PBXBuildFile; fileRef = 9237524A240C6E3D00DD6076 /* Gr.fst */; };
92375269240C6EFE00DD6076 /* HCLr.fst in CopyFiles */ = {isa = PBXBuildFile; fileRef = 9237524B240C6E3D00DD6076 /* HCLr.fst */; };
9237526A240C6EFE00DD6076 /* mfcc.conf in CopyFiles */ = {isa = PBXBuildFile; fileRef = 92375253240C6E3D00DD6076 /* mfcc.conf */; };
9237526B240C6EFE00DD6076 /* word_boundary.int in CopyFiles */ = {isa = PBXBuildFile; fileRef = 92375254240C6E3D00DD6076 /* word_boundary.int */; };
9237526C240C6EFE00DD6076 /* words.txt in CopyFiles */ = {isa = PBXBuildFile; fileRef = 92375255240C6E3D00DD6076 /* words.txt */; };
9237526E240C6F1500DD6076 /* final.dubm in CopyFiles */ = {isa = PBXBuildFile; fileRef = 9237524D240C6E3D00DD6076 /* final.dubm */; };
9237526F240C6F1500DD6076 /* final.ie in CopyFiles */ = {isa = PBXBuildFile; fileRef = 9237524E240C6E3D00DD6076 /* final.ie */; };
92375270240C6F1500DD6076 /* final.mat in CopyFiles */ = {isa = PBXBuildFile; fileRef = 9237524F240C6E3D00DD6076 /* final.mat */; };
92375271240C6F1500DD6076 /* global_cmvn.stats in CopyFiles */ = {isa = PBXBuildFile; fileRef = 92375250240C6E3D00DD6076 /* global_cmvn.stats */; };
92375272240C6F1500DD6076 /* online_cmvn.conf in CopyFiles */ = {isa = PBXBuildFile; fileRef = 92375251240C6E3D00DD6076 /* online_cmvn.conf */; };
92375273240C6F1500DD6076 /* splice.conf in CopyFiles */ = {isa = PBXBuildFile; fileRef = 92375252240C6E3D00DD6076 /* splice.conf */; };
92375274240C6F1E00DD6076 /* 10001-90210-01803.wav in Resources */ = {isa = PBXBuildFile; fileRef = 92375256240C6E3D00DD6076 /* 10001-90210-01803.wav */; };
/* End PBXBuildFile section */
/* Begin PBXCopyFilesBuildPhase section */
92375265240C6ECF00DD6076 /* CopyFiles */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = "model-en";
dstSubfolderSpec = 7;
files = (
92375266240C6EFE00DD6076 /* disambig_tid.int in CopyFiles */,
92375267240C6EFE00DD6076 /* final.mdl in CopyFiles */,
92375268240C6EFE00DD6076 /* Gr.fst in CopyFiles */,
92375269240C6EFE00DD6076 /* HCLr.fst in CopyFiles */,
9237526A240C6EFE00DD6076 /* mfcc.conf in CopyFiles */,
9237526B240C6EFE00DD6076 /* word_boundary.int in CopyFiles */,
9237526C240C6EFE00DD6076 /* words.txt in CopyFiles */,
);
runOnlyForDeploymentPostprocessing = 0;
};
9237526D240C6F0400DD6076 /* CopyFiles */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = "model-en/ivector";
dstSubfolderSpec = 7;
files = (
9237526E240C6F1500DD6076 /* final.dubm in CopyFiles */,
9237526F240C6F1500DD6076 /* final.ie in CopyFiles */,
92375270240C6F1500DD6076 /* final.mat in CopyFiles */,
92375271240C6F1500DD6076 /* global_cmvn.stats in CopyFiles */,
92375272240C6F1500DD6076 /* online_cmvn.conf in CopyFiles */,
92375273240C6F1500DD6076 /* splice.conf in CopyFiles */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
9237521E240C550B00DD6076 /* VoskApiTest.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = VoskApiTest.app; sourceTree = BUILT_PRODUCTS_DIR; };
92375221240C550B00DD6076 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
92375223240C550B00DD6076 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = "<group>"; };
92375226240C550B00DD6076 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
92375228240C550B00DD6076 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
9237522B240C550B00DD6076 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
9237522D240C550B00DD6076 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
92375233240C558900DD6076 /* Vosk.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Vosk.swift; sourceTree = "<group>"; };
9237523A240C642000DD6076 /* libkaldiwrap.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libkaldiwrap.a; sourceTree = "<group>"; };
92375243240C6DAF00DD6076 /* Accelerate.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Accelerate.framework; path = System/Library/Frameworks/Accelerate.framework; sourceTree = SDKROOT; };
92375245240C6DC900DD6076 /* libstdc++.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = "libstdc++.tbd"; path = "usr/lib/libstdc++.tbd"; sourceTree = SDKROOT; };
92375248240C6E3D00DD6076 /* disambig_tid.int */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = disambig_tid.int; sourceTree = "<group>"; };
92375249240C6E3D00DD6076 /* final.mdl */ = {isa = PBXFileReference; lastKnownFileType = file; path = final.mdl; sourceTree = "<group>"; };
9237524A240C6E3D00DD6076 /* Gr.fst */ = {isa = PBXFileReference; lastKnownFileType = file; path = Gr.fst; sourceTree = "<group>"; };
9237524B240C6E3D00DD6076 /* HCLr.fst */ = {isa = PBXFileReference; lastKnownFileType = file; path = HCLr.fst; sourceTree = "<group>"; };
9237524D240C6E3D00DD6076 /* final.dubm */ = {isa = PBXFileReference; lastKnownFileType = file; path = final.dubm; sourceTree = "<group>"; };
9237524E240C6E3D00DD6076 /* final.ie */ = {isa = PBXFileReference; lastKnownFileType = file; path = final.ie; sourceTree = "<group>"; };
9237524F240C6E3D00DD6076 /* final.mat */ = {isa = PBXFileReference; lastKnownFileType = file; path = final.mat; sourceTree = "<group>"; };
92375250240C6E3D00DD6076 /* global_cmvn.stats */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = global_cmvn.stats; sourceTree = "<group>"; };
92375251240C6E3D00DD6076 /* online_cmvn.conf */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = online_cmvn.conf; sourceTree = "<group>"; };
92375252240C6E3D00DD6076 /* splice.conf */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = splice.conf; sourceTree = "<group>"; };
92375253240C6E3D00DD6076 /* mfcc.conf */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = mfcc.conf; sourceTree = "<group>"; };
92375254240C6E3D00DD6076 /* word_boundary.int */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = word_boundary.int; sourceTree = "<group>"; };
92375255240C6E3D00DD6076 /* words.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = words.txt; sourceTree = "<group>"; };
92375256240C6E3D00DD6076 /* 10001-90210-01803.wav */ = {isa = PBXFileReference; lastKnownFileType = audio.wav; path = "10001-90210-01803.wav"; sourceTree = "<group>"; };
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>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
9237521B240C550B00DD6076 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
92375246240C6DC900DD6076 /* libstdc++.tbd in Frameworks */,
92375244240C6DAF00DD6076 /* Accelerate.framework in Frameworks */,
9237523C240C642000DD6076 /* libkaldiwrap.a in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
92375215240C550A00DD6076 = {
isa = PBXGroup;
children = (
92375239240C642000DD6076 /* Vosk */,
92375220240C550B00DD6076 /* VoskApiTest */,
9237521F240C550B00DD6076 /* Products */,
92375242240C6DAF00DD6076 /* Frameworks */,
);
sourceTree = "<group>";
};
9237521F240C550B00DD6076 /* Products */ = {
isa = PBXGroup;
children = (
9237521E240C550B00DD6076 /* VoskApiTest.app */,
);
name = Products;
sourceTree = "<group>";
};
92375220240C550B00DD6076 /* VoskApiTest */ = {
isa = PBXGroup;
children = (
92AA22AE244CDD5200DA464B /* bridging.h */,
92375221240C550B00DD6076 /* AppDelegate.swift */,
92375223240C550B00DD6076 /* ViewController.swift */,
92375225240C550B00DD6076 /* Main.storyboard */,
92375228240C550B00DD6076 /* Assets.xcassets */,
9237522A240C550B00DD6076 /* LaunchScreen.storyboard */,
9237522D240C550B00DD6076 /* Info.plist */,
92375233240C558900DD6076 /* Vosk.swift */,
);
path = VoskApiTest;
sourceTree = "<group>";
};
92375239240C642000DD6076 /* Vosk */ = {
isa = PBXGroup;
children = (
92375247240C6E3D00DD6076 /* model-android */,
92375256240C6E3D00DD6076 /* 10001-90210-01803.wav */,
92AA22AD244CDD1200DA464B /* vosk_api.h */,
9237523A240C642000DD6076 /* libkaldiwrap.a */,
);
name = Vosk;
path = VoskApiTest/Vosk;
sourceTree = "<group>";
};
92375242240C6DAF00DD6076 /* Frameworks */ = {
isa = PBXGroup;
children = (
92375245240C6DC900DD6076 /* libstdc++.tbd */,
92375243240C6DAF00DD6076 /* Accelerate.framework */,
);
name = Frameworks;
sourceTree = "<group>";
};
92375247240C6E3D00DD6076 /* model-android */ = {
isa = PBXGroup;
children = (
92375248240C6E3D00DD6076 /* disambig_tid.int */,
92375249240C6E3D00DD6076 /* final.mdl */,
9237524A240C6E3D00DD6076 /* Gr.fst */,
9237524B240C6E3D00DD6076 /* HCLr.fst */,
9237524C240C6E3D00DD6076 /* ivector */,
92375253240C6E3D00DD6076 /* mfcc.conf */,
92375254240C6E3D00DD6076 /* word_boundary.int */,
92375255240C6E3D00DD6076 /* words.txt */,
);
path = "model-android";
sourceTree = "<group>";
};
9237524C240C6E3D00DD6076 /* ivector */ = {
isa = PBXGroup;
children = (
9237524D240C6E3D00DD6076 /* final.dubm */,
9237524E240C6E3D00DD6076 /* final.ie */,
9237524F240C6E3D00DD6076 /* final.mat */,
92375250240C6E3D00DD6076 /* global_cmvn.stats */,
92375251240C6E3D00DD6076 /* online_cmvn.conf */,
92375252240C6E3D00DD6076 /* splice.conf */,
);
path = ivector;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
9237521D240C550B00DD6076 /* VoskApiTest */ = {
isa = PBXNativeTarget;
buildConfigurationList = 92375230240C550B00DD6076 /* Build configuration list for PBXNativeTarget "VoskApiTest" */;
buildPhases = (
9237521A240C550B00DD6076 /* Sources */,
9237521B240C550B00DD6076 /* Frameworks */,
9237521C240C550B00DD6076 /* Resources */,
92375265240C6ECF00DD6076 /* CopyFiles */,
9237526D240C6F0400DD6076 /* CopyFiles */,
);
buildRules = (
);
dependencies = (
);
name = VoskApiTest;
productName = VoskApiTest;
productReference = 9237521E240C550B00DD6076 /* VoskApiTest.app */;
productType = "com.apple.product-type.application";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
92375216240C550A00DD6076 /* Project object */ = {
isa = PBXProject;
attributes = {
LastSwiftUpdateCheck = 0830;
LastUpgradeCheck = 0920;
ORGANIZATIONNAME = "Alpha Cephei";
TargetAttributes = {
9237521D240C550B00DD6076 = {
CreatedOnToolsVersion = 8.3.2;
LastSwiftMigration = 0920;
ProvisioningStyle = Automatic;
};
};
};
buildConfigurationList = 92375219240C550A00DD6076 /* Build configuration list for PBXProject "VoskApiTest" */;
compatibilityVersion = "Xcode 3.2";
developmentRegion = English;
hasScannedForEncodings = 0;
knownRegions = (
en,
Base,
);
mainGroup = 92375215240C550A00DD6076;
productRefGroup = 9237521F240C550B00DD6076 /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
9237521D240C550B00DD6076 /* VoskApiTest */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
9237521C240C550B00DD6076 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
92375274240C6F1E00DD6076 /* 10001-90210-01803.wav in Resources */,
9237522C240C550B00DD6076 /* LaunchScreen.storyboard in Resources */,
92375229240C550B00DD6076 /* Assets.xcassets in Resources */,
92375227240C550B00DD6076 /* Main.storyboard in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
9237521A240C550B00DD6076 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
92375224240C550B00DD6076 /* ViewController.swift in Sources */,
92375222240C550B00DD6076 /* AppDelegate.swift in Sources */,
92375234240C558900DD6076 /* Vosk.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXVariantGroup section */
92375225240C550B00DD6076 /* Main.storyboard */ = {
isa = PBXVariantGroup;
children = (
92375226240C550B00DD6076 /* Base */,
);
name = Main.storyboard;
sourceTree = "<group>";
};
9237522A240C550B00DD6076 /* LaunchScreen.storyboard */ = {
isa = PBXVariantGroup;
children = (
9237522B240C550B00DD6076 /* Base */,
);
name = LaunchScreen.storyboard;
sourceTree = "<group>";
};
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
9237522E240C550B00DD6076 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
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;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 10.3;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
SWIFT_OBJC_BRIDGING_HEADER = bridging.h;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_SWIFT3_OBJC_INFERENCE = Default;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
};
9237522F240C550B00DD6076 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
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;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 10.3;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SWIFT_OBJC_BRIDGING_HEADER = bridging.h;
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
SWIFT_SWIFT3_OBJC_INFERENCE = Default;
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
};
name = Release;
};
92375231240C550B00DD6076 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = VoskApiTest/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/VoskApiTest/Vosk",
);
PRODUCT_BUNDLE_IDENTIFIER = com.alphacephei.VoskApiTest;
PRODUCT_NAME = "$(TARGET_NAME)";
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;
};
name = Debug;
};
92375232240C550B00DD6076 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = VoskApiTest/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/VoskApiTest/Vosk",
);
PRODUCT_BUNDLE_IDENTIFIER = com.alphacephei.VoskApiTest;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_INSTALL_OBJC_HEADER = YES;
SWIFT_OBJC_BRIDGING_HEADER = VoskApiTest/bridging.h;
SWIFT_SWIFT3_OBJC_INFERENCE = Default;
SWIFT_VERSION = 4.0;
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
92375219240C550A00DD6076 /* Build configuration list for PBXProject "VoskApiTest" */ = {
isa = XCConfigurationList;
buildConfigurations = (
9237522E240C550B00DD6076 /* Debug */,
9237522F240C550B00DD6076 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
92375230240C550B00DD6076 /* Build configuration list for PBXNativeTarget "VoskApiTest" */ = {
isa = XCConfigurationList;
buildConfigurations = (
92375231240C550B00DD6076 /* Debug */,
92375232240C550B00DD6076 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 92375216240C550A00DD6076 /* Project object */;
}
@@ -1,7 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "self:VoskApiTest.xcodeproj">
</FileRef>
</Workspace>
-46
View File
@@ -1,46 +0,0 @@
//
// AppDelegate.swift
// VoskApiTest
//
// Created by Niсkolay Shmyrev on 01.03.20.
// Copyright © 2020 Alpha Cephei. All rights reserved.
//
import UIKit
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
return true
}
func applicationWillResignActive(_ application: UIApplication) {
// Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
// Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.
}
func applicationDidEnterBackground(_ application: UIApplication) {
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
}
func applicationWillEnterForeground(_ application: UIApplication) {
// Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
}
func applicationDidBecomeActive(_ application: UIApplication) {
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
}
func applicationWillTerminate(_ application: UIApplication) {
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
}
}
@@ -1,98 +0,0 @@
{
"images" : [
{
"idiom" : "iphone",
"size" : "20x20",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "20x20",
"scale" : "3x"
},
{
"idiom" : "iphone",
"size" : "29x29",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "29x29",
"scale" : "3x"
},
{
"idiom" : "iphone",
"size" : "40x40",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "40x40",
"scale" : "3x"
},
{
"idiom" : "iphone",
"size" : "60x60",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "60x60",
"scale" : "3x"
},
{
"idiom" : "ipad",
"size" : "20x20",
"scale" : "1x"
},
{
"idiom" : "ipad",
"size" : "20x20",
"scale" : "2x"
},
{
"idiom" : "ipad",
"size" : "29x29",
"scale" : "1x"
},
{
"idiom" : "ipad",
"size" : "29x29",
"scale" : "2x"
},
{
"idiom" : "ipad",
"size" : "40x40",
"scale" : "1x"
},
{
"idiom" : "ipad",
"size" : "40x40",
"scale" : "2x"
},
{
"idiom" : "ipad",
"size" : "76x76",
"scale" : "1x"
},
{
"idiom" : "ipad",
"size" : "76x76",
"scale" : "2x"
},
{
"idiom" : "ipad",
"size" : "83.5x83.5",
"scale" : "2x"
},
{
"idiom" : "ios-marketing",
"size" : "1024x1024",
"scale" : "1x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
@@ -1,27 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="11134" systemVersion="15F34" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="11106"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="EHf-IW-A2E">
<objects>
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="Llm-lL-Icb"/>
<viewControllerLayoutGuide type="bottom" id="xb3-aO-Qok"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="53" y="375"/>
</scene>
</scenes>
</document>
@@ -1,32 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13771" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="BYZ-38-t0r">
<device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13772"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="tne-QT-ifu">
<objects>
<viewController id="BYZ-38-t0r" customClass="ViewController" customModule="VoskApiTest" customModuleProvider="target" sceneMemberID="viewController">
<textView key="view" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" editable="NO" textAlignment="natural" id="CtX-mx-X98">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
<textInputTraits key="textInputTraits" autocapitalizationType="sentences"/>
</textView>
<connections>
<outlet property="mainText" destination="CtX-mx-X98" id="oJy-5J-NKp"/>
</connections>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="32.799999999999997" y="32.833583208395808"/>
</scene>
</scenes>
</document>
-45
View File
@@ -1,45 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIMainStoryboardFile</key>
<string>Main</string>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>armv7</string>
</array>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
</dict>
</plist>
-34
View File
@@ -1,34 +0,0 @@
//
// ViewController.swift
// VoskApiTest
//
// Created by Niсkolay Shmyrev on 01.03.20.
// Copyright © 2020 Alpha Cephei. All rights reserved.
//
import UIKit
class ViewController: UIViewController {
@IBOutlet var mainText: UITextView!
override func viewDidLoad() {
super.viewDidLoad()
DispatchQueue.global(qos: .userInitiated).async {
DispatchQueue.main.async {
self.mainText.text = "Processing file..."
}
let vosk = Vosk()
let res = vosk.recognizeFile()
DispatchQueue.main.async {
self.mainText.text = res
}
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
-37
View File
@@ -1,37 +0,0 @@
//
// Vosk.swift
// VoskApiTest
//
// Created by Niсkolay Shmyrev on 01.03.20.
// Copyright © 2020 Alpha Cephei. All rights reserved.
//
import Foundation
public final class Vosk {
func recognizeFile() -> String {
var sres = ""
if let resourcePath = Bundle.main.resourcePath {
let modelPath = resourcePath + "/model-en"
let model = vosk_model_new(modelPath);
let recognizer = vosk_recognizer_new(model, 16000.0)
let audioFile = URL(fileURLWithPath: resourcePath + "/10001-90210-01803.wav")
if let data = try? Data(contentsOf: audioFile) {
let _ = data.withUnsafeBytes {
vosk_recognizer_accept_waveform(recognizer, $0, Int32(data.count))
}
let res = vosk_recognizer_final_result(recognizer);
sres = String(validatingUTF8: res!)!;
print(sres);
}
vosk_recognizer_free(recognizer)
vosk_model_free(model)
}
return sres
}
}
-48
View File
@@ -1,48 +0,0 @@
// Copyright 2020 Alpha Cephei Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// 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_API_H_
#define _VOSK_API_H_
#ifdef __cplusplus
extern "C" {
#endif
typedef struct VoskModel VoskModel;
typedef struct VoskSpkModel VoskSpkModel;
typedef struct VoskRecognizer VoskRecognizer;
VoskModel *vosk_model_new(const char *model_path);
void vosk_model_free(VoskModel *model);
VoskSpkModel *vosk_spk_model_new(const char *model_path);
void vosk_spk_model_free(VoskSpkModel *model);
VoskRecognizer *vosk_recognizer_new(VoskModel *model, float sample_rate);
VoskRecognizer *vosk_recognizer_new_spk(VoskModel *model, VoskSpkModel *spk_model, float sample_rate);
VoskRecognizer *vosk_recognizer_new_grm(VoskModel *model, float sample_rate, const char *grammar);
int vosk_recognizer_accept_waveform(VoskRecognizer *recognizer, const char *data, int length);
int vosk_recognizer_accept_waveform_s(VoskRecognizer *recognizer, const short *data, int length);
int vosk_recognizer_accept_waveform_f(VoskRecognizer *recognizer, const float *data, int length);
const char *vosk_recognizer_result(VoskRecognizer *recognizer);
const char *vosk_recognizer_partial_result(VoskRecognizer *recognizer);
const char *vosk_recognizer_final_result(VoskRecognizer *recognizer);
void vosk_recognizer_free(VoskRecognizer *recognizer);
#ifdef __cplusplus
}
#endif
#endif /* _VOSK_API_H_ */
-1
View File
@@ -1 +0,0 @@
#import "Vosk/vosk_api.h"
-65
View File
@@ -1,65 +0,0 @@
KALDI_ROOT ?= $(HOME)/kaldi
CFLAGS := -g -O2 -DPIC -fPIC -Wno-unused-function
CPPFLAGS := -I$(JAVA_HOME)/include -I$(JAVA_HOME)/include/linux -I$(KALDI_ROOT)/src -I$(KALDI_ROOT)/tools/openfst/include -I../src
KALDI_LIBS = \
${KALDI_ROOT}/src/online2/kaldi-online2.a \
${KALDI_ROOT}/src/decoder/kaldi-decoder.a \
${KALDI_ROOT}/src/ivector/kaldi-ivector.a \
${KALDI_ROOT}/src/gmm/kaldi-gmm.a \
${KALDI_ROOT}/src/nnet3/kaldi-nnet3.a \
${KALDI_ROOT}/src/tree/kaldi-tree.a \
${KALDI_ROOT}/src/feat/kaldi-feat.a \
${KALDI_ROOT}/src/lat/kaldi-lat.a \
${KALDI_ROOT}/src/lm/kaldi-lm.a \
${KALDI_ROOT}/src/hmm/kaldi-hmm.a \
${KALDI_ROOT}/src/transform/kaldi-transform.a \
${KALDI_ROOT}/src/cudamatrix/kaldi-cudamatrix.a \
${KALDI_ROOT}/src/matrix/kaldi-matrix.a \
${KALDI_ROOT}/src/fstext/kaldi-fstext.a \
${KALDI_ROOT}/src/util/kaldi-util.a \
${KALDI_ROOT}/src/base/kaldi-base.a \
${KALDI_ROOT}/tools/openfst/lib/libfst.a \
${KALDI_ROOT}/tools/openfst/lib/libfstngram.a \
${KALDI_ROOT}/tools/OpenBLAS/libopenblas.a \
-lgfortran
all: libvosk_jni.so
VOSK_SOURCES = \
vosk_wrap.cc \
../src/kaldi_recognizer.cc \
../src/kaldi_recognizer.h \
../src/model.cc \
../src/model.h \
../src/spk_model.cc \
../src/spk_model.h \
../src/vosk_api.cc \
../src/vosk_api.h
libvosk_jni.so: $(VOSK_SOURCES)
$(CXX) -shared -o $@ $(CPPFLAGS) $(CFLAGS) $(VOSK_SOURCES) $(KALDI_LIBS)
vosk_wrap.cc: ../src/vosk.i
mkdir -p org/kaldi
swig -c++ -I../src \
-java -package org.kaldi \
-outdir org/kaldi -o $@ $<
clean:
$(RM) *.so *_wrap.cc *_wrap.o test/*.class
$(RM) -r org model-en
model:
wget https://alphacephei.com/kaldi/models/vosk-model-small-en-us-0.3.zip
unzip vosk-model-small-en-us-0.3.zip && rm vosk-model-small-en-us-0.3.zip
mv vosk-model-small-en-us-0.3 model
model-spk:
wget https://alphacephei.com/kaldi/models/vosk-model-spk-0.3.zip
unzip vosk-model-spk-0.3.zip && rm vosk-model-spk-0.3.zip
mv vosk-model-spk-0.3 model-spk
run: model model-spk
javac test/*.java org/kaldi/*.java
java -Djava.library.path=. -cp . test.DecoderTest
-19
View File
@@ -1,19 +0,0 @@
Java API sample
Doesn't work on Windows or Mac yet, help to prepare the packaged jars is welcome.
For now to try it:
On Linux you can do
1. Build recent kaldi
1. `git clone https://github.com/alphacep/vosk-api`
1. `cd vosk-api/java`
1. `export KALDI_ROOT=<KALDI_ROOT>`
1. `export JAVA_HOME=<JAVA_HOME>`
1. `make`
1. `make run`
For details of the code you can check:
https://github.com/alphacep/vosk-api/blob/master/java/test/DecoderTest.java
-40
View File
@@ -1,40 +0,0 @@
package test;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.URL;
import java.nio.*;
import org.kaldi.KaldiRecognizer;
import org.kaldi.Model;
import org.kaldi.SpkModel;
import org.kaldi.Vosk;
public class DecoderTest {
static {
System.loadLibrary("vosk_jni");
}
public static void main(String args[]) throws IOException {
Vosk.SetLogLevel(-10);
FileInputStream ais = new FileInputStream(new File("../python/example/test.wav"));
Model model = new Model("model");
SpkModel spkModel = new SpkModel("model-spk");
KaldiRecognizer rec = new KaldiRecognizer(model, spkModel, 16000.0f);
int nbytes;
byte[] b = new byte[4096];
while ((nbytes = ais.read(b)) >= 0) {
if (rec.AcceptWaveform(b)) {
System.out.println(rec.Result());
} else {
System.out.println(rec.PartialResult());
}
}
System.out.println(rec.FinalResult());
}
}
-3
View File
@@ -1,3 +0,0 @@
exports.printMsg = function() {
console.log("This is a message from the Vosk package");
}
-18
View File
@@ -1,18 +0,0 @@
{
"name": "vosk",
"version": "0.1.0",
"description": "Node binding for continuous voice recoginition through pocketsphinx.",
"repository": {
"type": "git",
"url": "git://github.com/alphacep/vosk-api.git"
},
"main": "index.js",
"keywords": [
"speech",
"speech recognition",
"voice"
],
"author": "Alpha Cephei Inc.",
"license": "Apache 2.0",
"engines": { "node" : ">= 12.x.x" }
}
-51
View File
@@ -1,51 +0,0 @@
cmake_minimum_required(VERSION 3.12.0)
project(vosk)
set(TOP_SRCDIR "${CMAKE_SOURCE_DIR}/..")
if("x$ENV{WHEEL_FLAGS}" STREQUAL "x")
find_package (Python3 COMPONENTS Interpreter Development)
else()
# docker case
set(Python3_INCLUDE_DIRS "")
set(TOP_SRCDIR "/io")
endif()
set(KALDI_ROOT "$ENV{KALDI_ROOT}")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DFST_NO_DYNAMIC_LINKING")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} $ENV{WHEEL_FLAGS}")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${CMAKE_C_FLAGS} -std=c++11")
include_directories("${TOP_SRCDIR}/src" "${KALDI_ROOT}/src" "${KALDI_ROOT}/tools/openfst/include" ${Python3_INCLUDE_DIRS})
find_package(SWIG REQUIRED)
include(${SWIG_USE_FILE})
swig_add_library(vosk TYPE SHARED LANGUAGE Python OUTPUT_DIR "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}" OUTFILE_DIR "."
SOURCES "${TOP_SRCDIR}/src/kaldi_recognizer.cc"
"${TOP_SRCDIR}/src/spk_model.cc"
"${TOP_SRCDIR}/src/model.cc"
"${TOP_SRCDIR}/src/vosk_api.cc"
"${TOP_SRCDIR}/src/vosk.i")
swig_link_libraries(vosk
${KALDI_ROOT}/src/online2/kaldi-online2.a
${KALDI_ROOT}/src/decoder/kaldi-decoder.a
${KALDI_ROOT}/src/ivector/kaldi-ivector.a
${KALDI_ROOT}/src/gmm/kaldi-gmm.a
${KALDI_ROOT}/src/nnet3/kaldi-nnet3.a
${KALDI_ROOT}/src/tree/kaldi-tree.a
${KALDI_ROOT}/src/feat/kaldi-feat.a
${KALDI_ROOT}/src/lat/kaldi-lat.a
${KALDI_ROOT}/src/lm/kaldi-lm.a
${KALDI_ROOT}/src/hmm/kaldi-hmm.a
${KALDI_ROOT}/src/transform/kaldi-transform.a
${KALDI_ROOT}/src/cudamatrix/kaldi-cudamatrix.a
${KALDI_ROOT}/src/matrix/kaldi-matrix.a
${KALDI_ROOT}/src/fstext/kaldi-fstext.a
${KALDI_ROOT}/src/util/kaldi-util.a
${KALDI_ROOT}/src/base/kaldi-base.a
${KALDI_ROOT}/tools/openfst/lib/libfst.a
${KALDI_ROOT}/tools/openfst/lib/libfstngram.a
${KALDI_ROOT}/tools/OpenBLAS/libopenblas.a
-lgfortran -lstdc++)
set_target_properties(_vosk PROPERTIES LINK_FLAGS_RELEASE -s)
-3
View File
@@ -1,3 +0,0 @@
Python module for vosk-api
See for details https://github.com/alphacep/vosk-api
-76
View File
@@ -1,76 +0,0 @@
# From https://github.com/raydouglass/cmake_setuptools
import os
import subprocess
import shutil
import sys
from setuptools import Extension
from setuptools.command.build_ext import build_ext
from setuptools.command.build_py import build_py
CMAKE_EXE = os.environ.get('CMAKE_EXE', shutil.which('cmake'))
def check_for_cmake():
if not CMAKE_EXE:
print('cmake executable not found. '
'Set CMAKE_EXE environment or update your path')
sys.exit(1)
class CMakeExtension(Extension):
"""
setuptools.Extension for cmake
"""
def __init__(self, name, pkg_name, sourcedir=''):
check_for_cmake()
Extension.__init__(self, name, sources=[])
self.sourcedir = os.path.abspath(sourcedir)
self.pkg_name = pkg_name
class CMakeBuildExt(build_ext):
"""
setuptools build_exit which builds using cmake & make
You can add cmake args with the CMAKE_COMMON_VARIABLES environment variable
"""
def build_extension(self, ext):
check_for_cmake()
if isinstance(ext, CMakeExtension):
output_dir = os.path.abspath(
os.path.dirname(self.get_ext_fullpath(ext.pkg_name + "/" + ext.name)))
build_type = 'Debug' if self.debug else 'Release'
cmake_args = [CMAKE_EXE,
ext.sourcedir,
'-Wno-dev',
'-DCMAKE_LIBRARY_OUTPUT_DIRECTORY=' + output_dir,
'-DCMAKE_BUILD_TYPE=' + build_type]
cmake_args.extend(
[x for x in
os.environ.get('CMAKE_COMMON_VARIABLES', '').split(' ')
if x])
env = os.environ.copy()
if not os.path.exists(self.build_temp):
os.makedirs(self.build_temp)
subprocess.check_call(cmake_args,
cwd=self.build_temp,
env=env)
subprocess.check_call(['make', 'VERBOSE=1', ext.name],
cwd=self.build_temp,
env=env)
print()
else:
super().build_extension(ext)
class CMakeBuildExtFirst(build_py):
def run(self):
self.run_command("build_ext")
return super().run()
__all__ = ['CMakeBuildExt', 'CMakeExtension', 'CMakeBuildExtFirst']
-11
View File
@@ -1,11 +0,0 @@
#!/usr/bin/python3
from vosk import Model, KaldiRecognizer
import sys
import json
model = Model("model")
rec = KaldiRecognizer(model, 8000)
res = json.loads(rec.FinalResult())
print (res)
-28
View File
@@ -1,28 +0,0 @@
#!/usr/bin/python3
from vosk import Model, KaldiRecognizer
import os
if not os.path.exists("model"):
print ("Please download the model from https://github.com/alphacep/vosk-api/blob/master/doc/models.md and unpack as 'model' in the current folder.")
exit (1)
import pyaudio
p = pyaudio.PyAudio()
stream = p.open(format=pyaudio.paInt16, channels=1, rate=16000, input=True, frames_per_buffer=8000)
stream.start_stream()
model = Model("model")
rec = KaldiRecognizer(model, 16000)
while True:
data = stream.read(4000)
if len(data) == 0:
break
if rec.AcceptWaveform(data):
print(rec.Result())
else:
print(rec.PartialResult())
print(rec.FinalResult())
-31
View File
@@ -1,31 +0,0 @@
#!/usr/bin/python3
from vosk import Model, KaldiRecognizer, SetLogLevel
import sys
import os
import wave
SetLogLevel(0)
if not os.path.exists("model"):
print ("Please download the model from https://github.com/alphacep/vosk-api/blob/master/doc/models.md 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)
model = Model("model")
rec = KaldiRecognizer(model, wf.getframerate())
while True:
data = wf.readframes(4000)
if len(data) == 0:
break
if rec.AcceptWaveform(data):
print(rec.Result())
else:
print(rec.PartialResult())
print(rec.FinalResult())
-52
View File
@@ -1,52 +0,0 @@
#!/usr/bin/python3
from vosk import Model, KaldiRecognizer, SpkModel
import sys
import wave
import json
import os
import numpy as np
model_path = "model"
spk_model_path = "model-spk"
if not os.path.exists(model_path):
print ("Please download the model from https://github.com/alphacep/vosk-api/blob/master/doc/models.md and unpack as {} in the current folder.".format(model_path))
exit (1)
if not os.path.exists(spk_model_path):
print ("Please download the speaker model from https://github.com/alphacep/vosk-api/blob/master/doc/models.md and unpack as {} in the current folder.".format(spk_model_path))
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)
# Large vocabulary free form recognition
model = Model(model_path)
spk_model = SpkModel(spk_model_path)
rec = KaldiRecognizer(model, spk_model, wf.getframerate())
# 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 = [5.64308, 4.23898, 1.119433, -0.810904, 2.115443, 2.328436, 6.135152, 1.348195, 2.60771, 1.020717, 4.324225, -0.873012, 6.123375, 4.903791, 0.064803, 4.66212, 3.502724, 2.535861, 5.452417, 7.081769, -0.823969, -5.167974, 8.568919, 4.159035, 5.314441, 3.688272, 5.730379, 4.463213, 7.227232, 3.538961, 3.316218, 1.269628, -1.902378, 3.512679, -1.947611, -1.520158, 3.80928, -2.721601, 5.359588, 2.942463, -7.474174, 3.788054, 0.303426, 4.951366, 1.72281, -1.867125, -3.574615, 3.622509, 4.803109, 2.829714, 1.528521, 6.408293, 0.820131, 5.066522, 2.836125, 2.867029, 3.725267, 0.505927, 1.462984, 5.001863, -3.838309, -2.45902, 3.992581, 4.451616, 2.865211, -1.148313, 4.996399, -3.473454, 2.876967, 3.940124, 7.553079, 0.373356, 1.396561, 2.686691, 2.094895, 0.913796, -0.286909, 3.540179, 4.904687, 0.84554, 7.585956, 1.017081, 0.168355, 6.672327, 4.092033, -4.240158, -2.017081, -0.813043, 6.468298, 4.115041, 2.231936, 2.370055, 4.972295, 5.58382, 6.022872, 2.706988, 5.248096, -1.918003, 8.259204, -0.900911, 1.961962, 2.349709, 3.290093, 3.344172, 3.307027, 4.203372, -0.315103, 5.61919, -3.229496, 3.777309, 4.328595, 1.461014, 2.622894, 0.315525, 5.447259, 5.407609, 5.339016, 1.604555, 5.359932, 0.090242, 0.535306, 4.724705, 4.692502, 0.5783, -5.436688, -4.915511, 1.959807, 2.825248]
def cosine_dist(x, y):
nx = np.array(x)
ny = np.array(y)
return 1 - np.dot(nx, ny) / np.linalg.norm(nx) / np.linalg.norm(ny)
while True:
data = wf.readframes(4000)
if len(data) == 0:
break
if rec.AcceptWaveform(data):
res = json.loads(rec.Result())
print ("Text:", res['text'])
print ("Speaker distance:", cosine_dist(spk_sig, res['spk']))
res = json.loads(rec.FinalResult())
print ("Text:", res['text'])
print ("Speaker distance:", cosine_dist(spk_sig, res['spk']))
-33
View File
@@ -1,33 +0,0 @@
#!/usr/bin/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://github.com/alphacep/vosk-api/blob/master/doc/models.md and unpack as 'model' in the current folder.")
exit (1)
model = Model("model")
# Large vocabulary free form recognition
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
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'])
-30
View File
@@ -1,30 +0,0 @@
#!/usr/bin/python3
from vosk import Model, KaldiRecognizer
import sys
import os
import wave
if not os.path.exists("model"):
print ("Please download the model from https://github.com/alphacep/vosk-api/blob/master/doc/models.md 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)
model = Model("model")
# You can also specify the possible word list
rec = KaldiRecognizer(model, wf.getframerate(), "zero oh one two three four five six seven eight nine")
while True:
data = wf.readframes(4000)
if len(data) == 0:
break
if rec.AcceptWaveform(data):
print(rec.Result())
else:
print(rec.PartialResult())
print(rec.FinalResult())
-29
View File
@@ -1,29 +0,0 @@
import os
import setuptools
from cmake import *
with open("README.md", "r") as fh:
long_description = fh.read()
setuptools.setup(
name="vosk", # Replace with your own username
version="0.3.7",
author="Alpha Cephei Inc",
author_email="contact@alphacephei.com",
description="API for Kaldi and Vosk",
long_description=long_description,
long_description_content_type="text/markdown",
url="https://github.com/alphacep/vosk-api",
packages=setuptools.find_packages(),
ext_modules=[CMakeExtension('_vosk', 'vosk')],
cmdclass={'build_ext': CMakeBuildExt, 'build_py' : CMakeBuildExtFirst},
classifiers=[
'Programming Language :: Python :: 3',
'License :: OSI Approved :: Apache Software License',
'Operating System :: Microsoft :: Windows',
'Operating System :: POSIX :: Linux',
'Operating System :: MacOS :: MacOS X',
'Topic :: Software Development :: Libraries :: Python Modules'
],
python_requires='>=3.4',
)
-27
View File
@@ -1,27 +0,0 @@
#!/usr/bin/python3
from vosk import Model, KaldiRecognizer
import sys
import os
import wave
import json
model = Model("model")
for line in open(sys.argv[1]):
uid, fn = line.split()
wf = wave.open(fn, "rb")
rec = KaldiRecognizer(model, wf.getframerate())
text = ""
while True:
data = wf.readframes(1000)
if len(data) == 0:
break
if rec.AcceptWaveform(data):
jres = json.loads(rec.Result())
text = text + " " + jres['text']
jres = json.loads(rec.FinalResult())
text = text + " " + jres['text']
print (uid + text)
-2
View File
@@ -1,2 +0,0 @@
from .vosk import KaldiRecognizer, Model, SpkModel, SetLogLevel
+116
View File
@@ -0,0 +1,116 @@
# ONNX
ONNX_ROOT=$(abspath ../../onnxruntime-linux-x64-gpu-1.22.0)
# Compiler
CXX?=g++
EXT?=so
# Extra
EXTRA_CFLAGS?=
EXTRA_LDFLAGS?=
OUTDIR?=.
VOSK_SOURCES= \
circular-buffer.cc \
context-graph.cc \
feature-fbank.cc \
feature-functions.cc \
feature-extractor.cc \
feature-window.cc \
fftsg.cc \
file-utils.cc \
hypothesis.cc \
log.cc \
mel-computations.cc \
offline-lm.cc \
offline-lm-config.cc \
offline-model-config.cc \
offline-recognizer.cc \
offline-recognizer-impl.cc \
offline-rnn-lm.cc \
offline-stream.cc \
offline-transducer-greedy-search-decoder.cc \
offline-transducer-model.cc \
offline-transducer-model-config.cc \
offline-transducer-modified-beam-search-decoder.cc \
online-feature.cc \
onnx-utils.cc \
packed-sequence.cc \
pad-sequence.cc \
parse-options.cc \
provider.cc \
resample.cc \
rfft.cc \
session.cc \
silero-vad-model.cc \
silero-vad-model-config.cc \
slice.cc \
symbol-table.cc \
text-utils.cc \
transpose.cc \
utils.cc \
vad-model.cc \
vad-model-config.cc \
voice-activity-detector.cc \
vosk_api.cc
VOSK_HEADERS= \
circular-buffer.h \
context-graph.h \
feature-fbank.h \
feature-functions.h \
feature-extractor.h \
feature-window.h \
file-utils.h \
hypothesis.h \
log.h \
macros.h \
math.h \
mel-computations.h \
offline-lm-config.h \
offline-lm.h \
offline-model-config.h \
offline-recognizer.h \
offline-recognizer-impl.h \
offline-recognizer-transducer-impl.h \
offline-rnn-lm.h \
offline-stream.h \
offline-transducer-decoder.h \
offline-transducer-greedy-search-decoder.h \
offline-transducer-model-config.h \
offline-transducer-model.h \
offline-transducer-modified-beam-search-decoder.h \
online-feature.h \
onnx-utils.h \
packed-sequence.h \
pad-sequence.h \
parse-options.h \
provider.h \
resample.h \
rfft.h \
session.h \
silero-vad-model-config.h \
silero-vad-model.h \
slice.h \
symbol-table.h \
text-utils.h \
transpose.h \
utils.h \
vad-model-config.h \
vad-model.h \
voice-activity-detector.h \
vosk_api.h
CFLAGS=-g -O3 -mavx2 -fPIC -std=c++17 \
-I$(ONNX_ROOT)/include $(EXTRA_CFLAGS)
LDFLAGS=-Wl,-rpath=$(ONNX_ROOT)/lib -L$(ONNX_ROOT)/lib -lonnxruntime
all: $(OUTDIR)/libvosk.$(EXT)
$(OUTDIR)/libvosk.$(EXT): $(VOSK_SOURCES:%.cc=$(OUTDIR)/%.o)
$(CXX) --shared -o $@ $^ $(LDFLAGS) $(EXTRA_LDFLAGS)
$(OUTDIR)/%.o: %.cc $(VOSK_HEADERS)
$(CXX) -c -o $@ $(CFLAGS) $<
clean:
rm -f *.o *.so *.dll
+166
View File
@@ -0,0 +1,166 @@
// circular-buffer.cc
//
// Copyright (c) 2023 Xiaomi Corporation
#include "circular-buffer.h"
#include <algorithm>
#include "macros.h"
namespace sherpa_onnx {
CircularBuffer::CircularBuffer(int32_t capacity) {
if (capacity <= 0) {
SHERPA_ONNX_LOGE("Please specify a positive capacity. Given: %d\n",
capacity);
exit(-1);
}
buffer_.resize(capacity);
}
void CircularBuffer::Resize(int32_t new_capacity) {
int32_t capacity = buffer_.size();
if (new_capacity <= capacity) {
SHERPA_ONNX_LOGE("new_capacity (%d) <= original capacity (%d). Skip it.",
new_capacity, capacity);
return;
}
int32_t size = Size();
if (size == 0) {
buffer_.resize(new_capacity);
return;
}
std::vector<float> new_buffer(new_capacity);
int32_t start = head_ % capacity;
int32_t dest = head_ % new_capacity;
if (start + size <= capacity) {
if (dest + size <= new_capacity) {
std::copy(buffer_.begin() + start, buffer_.begin() + start + size,
new_buffer.begin() + dest);
} else {
int32_t part1_size = new_capacity - dest;
// copy [start, start+part1_size] to new_buffer
std::copy(buffer_.begin() + start, buffer_.begin() + start + part1_size,
new_buffer.begin() + dest);
// copy [start+part1_size, start+size] to new_buffer
std::copy(buffer_.begin() + start + part1_size,
buffer_.begin() + start + size, new_buffer.begin());
}
} else {
int32_t part1_size = capacity - start;
int32_t part2_size = size - part1_size;
// copy [start, start+part1_size] to new_buffer
if (dest + part1_size <= new_capacity) {
std::copy(buffer_.begin() + start, buffer_.begin() + start + part1_size,
new_buffer.begin() + dest);
} else {
int32_t first_part = new_capacity - dest;
int32_t second_part = part1_size - first_part;
std::copy(buffer_.begin() + start, buffer_.begin() + start + first_part,
new_buffer.begin() + dest);
std::copy(buffer_.begin() + start + first_part,
buffer_.begin() + start + part1_size, new_buffer.begin());
}
int32_t new_dest = (dest + part1_size) % new_capacity;
if (new_dest + part2_size <= new_capacity) {
std::copy(buffer_.begin(), buffer_.begin() + part2_size,
new_buffer.begin() + new_dest);
} else {
int32_t first_part = new_capacity - new_dest;
std::copy(buffer_.begin(), buffer_.begin() + first_part,
new_buffer.begin() + new_dest);
std::copy(buffer_.begin() + first_part, buffer_.begin() + part2_size,
new_buffer.begin());
}
}
buffer_.swap(new_buffer);
}
void CircularBuffer::Push(const float *p, int32_t n) {
int32_t capacity = buffer_.size();
int32_t size = Size();
if (n + size > capacity) {
int32_t new_capacity = std::max(capacity * 2, n + size);
SHERPA_ONNX_LOGE(
"Overflow! n: %d, size: %d, n+size: %d, capacity: %d. Increase "
"capacity to: %d",
n, size, n + size, capacity, new_capacity);
Resize(new_capacity);
}
int32_t start = tail_ % capacity;
tail_ += n;
if (start + n < capacity) {
std::copy(p, p + n, buffer_.begin() + start);
return;
}
int32_t part1_size = capacity - start;
std::copy(p, p + part1_size, buffer_.begin() + start);
std::copy(p + part1_size, p + n, buffer_.begin());
}
std::vector<float> CircularBuffer::Get(int32_t start_index, int32_t n) const {
if (start_index < head_ || start_index >= tail_) {
SHERPA_ONNX_LOGE("Invalid start_index: %d. head_: %d, tail_: %d",
start_index, head_, tail_);
return {};
}
int32_t size = Size();
if (n < 0 || n > size) {
SHERPA_ONNX_LOGE("Invalid n: %d. size: %d", n, size);
return {};
}
int32_t capacity = buffer_.size();
if (start_index - head_ + n > size) {
SHERPA_ONNX_LOGE("Invalid start_index: %d and n: %d. head_: %d, size: %d",
start_index, n, head_, size);
return {};
}
int32_t start = start_index % capacity;
if (start + n < capacity) {
return {buffer_.begin() + start, buffer_.begin() + start + n};
}
std::vector<float> ans(n);
std::copy(buffer_.begin() + start, buffer_.end(), ans.begin());
int32_t part1_size = capacity - start;
int32_t part2_size = n - part1_size;
std::copy(buffer_.begin(), buffer_.begin() + part2_size,
ans.begin() + part1_size);
return ans;
}
void CircularBuffer::Pop(int32_t n) {
int32_t size = Size();
if (n < 0 || n > size) {
SHERPA_ONNX_LOGE("Invalid n: %d. size: %d", n, size);
return;
}
head_ += n;
}
} // namespace sherpa_onnx
+61
View File
@@ -0,0 +1,61 @@
// circular-buffer.h
//
// Copyright (c) 2023 Xiaomi Corporation
#ifndef SHERPA_ONNX_CSRC_CIRCULAR_BUFFER_H_
#define SHERPA_ONNX_CSRC_CIRCULAR_BUFFER_H_
#include <cstdint>
#include <vector>
namespace sherpa_onnx {
class CircularBuffer {
public:
// Capacity of this buffer. Should be large enough.
// If it is full, we just print a message and exit the program.
explicit CircularBuffer(int32_t capacity);
// Push an array
//
// @param p Pointer to the start address of the array
// @param n Number of elements in the array
//
// Note: If n + Size() > capacity, we print an error message and exit.
void Push(const float *p, int32_t n);
// @param start_index Should in the range [head_, tail_)
// @param n Number of elements to get
// @return Return a vector of size n containing the requested elements
std::vector<float> Get(int32_t start_index, int32_t n) const;
// Remove n elements from the buffer
//
// @param n Should be in the range [0, size_]
void Pop(int32_t n);
// Number of elements in the buffer.
int32_t Size() const { return tail_ - head_; }
// Current position of the head
int32_t Head() const { return head_; }
// Current position of the tail
int32_t Tail() const { return tail_; }
void Reset() {
head_ = 0;
tail_ = 0;
}
void Resize(int32_t new_capacity);
private:
std::vector<float> buffer_;
int32_t head_ = 0; // linear index; always increasing; never wraps around
int32_t tail_ = 0; // linear index, always increasing; never wraps around.
};
} // namespace sherpa_onnx
#endif // SHERPA_ONNX_CSRC_CIRCULAR_BUFFER_H_
+96
View File
@@ -0,0 +1,96 @@
// sherpa-onnx/csrc/context-graph.cc
//
// Copyright (c) 2023 Xiaomi Corporation
#include "context-graph.h"
#include <cassert>
#include <queue>
#include <utility>
namespace sherpa_onnx {
void ContextGraph::Build(
const std::vector<std::vector<int32_t>> &token_ids) const {
for (int32_t i = 0; i < token_ids.size(); ++i) {
auto node = root_.get();
for (int32_t j = 0; j < token_ids[i].size(); ++j) {
int32_t token = token_ids[i][j];
if (0 == node->next.count(token)) {
bool is_end = j == token_ids[i].size() - 1;
node->next[token] = std::make_unique<ContextState>(
token, context_score_, node->node_score + context_score_,
is_end ? node->node_score + context_score_ : 0, is_end);
}
node = node->next[token].get();
}
}
FillFailOutput();
}
std::pair<float, const ContextState *> ContextGraph::ForwardOneStep(
const ContextState *state, int32_t token) const {
const ContextState *node;
float score;
if (1 == state->next.count(token)) {
node = state->next.at(token).get();
score = node->token_score;
} else {
node = state->fail;
while (0 == node->next.count(token)) {
node = node->fail;
if (-1 == node->token) break; // root
}
if (1 == node->next.count(token)) {
node = node->next.at(token).get();
}
score = node->node_score - state->node_score;
}
SHERPA_ONNX_CHECK(nullptr != node);
return std::make_pair(score + node->output_score, node);
}
std::pair<float, const ContextState *> ContextGraph::Finalize(
const ContextState *state) const {
float score = -state->node_score;
return std::make_pair(score, root_.get());
}
void ContextGraph::FillFailOutput() const {
std::queue<const ContextState *> node_queue;
for (auto &kv : root_->next) {
kv.second->fail = root_.get();
node_queue.push(kv.second.get());
}
while (!node_queue.empty()) {
auto current_node = node_queue.front();
node_queue.pop();
for (auto &kv : current_node->next) {
auto fail = current_node->fail;
if (1 == fail->next.count(kv.first)) {
fail = fail->next.at(kv.first).get();
} else {
fail = fail->fail;
while (0 == fail->next.count(kv.first)) {
fail = fail->fail;
if (-1 == fail->token) break;
}
if (1 == fail->next.count(kv.first))
fail = fail->next.at(kv.first).get();
}
kv.second->fail = fail;
// fill the output arc
auto output = fail;
while (!output->is_end) {
output = output->fail;
if (-1 == output->token) {
output = nullptr;
break;
}
}
kv.second->output = output;
kv.second->output_score += output == nullptr ? 0 : output->output_score;
node_queue.push(kv.second.get());
}
}
}
} // namespace sherpa_onnx
+66
View File
@@ -0,0 +1,66 @@
// sherpa-onnx/csrc/context-graph.h
//
// Copyright (c) 2023 Xiaomi Corporation
#ifndef SHERPA_ONNX_CSRC_CONTEXT_GRAPH_H_
#define SHERPA_ONNX_CSRC_CONTEXT_GRAPH_H_
#include <memory>
#include <unordered_map>
#include <utility>
#include <vector>
#include "log.h"
namespace sherpa_onnx {
class ContextGraph;
using ContextGraphPtr = std::shared_ptr<ContextGraph>;
struct ContextState {
int32_t token;
float token_score;
float node_score;
float output_score;
bool is_end;
std::unordered_map<int32_t, std::unique_ptr<ContextState>> next;
const ContextState *fail = nullptr;
const ContextState *output = nullptr;
ContextState() = default;
ContextState(int32_t token, float token_score, float node_score,
float output_score, bool is_end)
: token(token),
token_score(token_score),
node_score(node_score),
output_score(output_score),
is_end(is_end) {}
};
class ContextGraph {
public:
ContextGraph() = default;
ContextGraph(const std::vector<std::vector<int32_t>> &token_ids,
float context_score)
: context_score_(context_score) {
root_ = std::make_unique<ContextState>(-1, 0, 0, 0, false);
root_->fail = root_.get();
Build(token_ids);
}
std::pair<float, const ContextState *> ForwardOneStep(
const ContextState *state, int32_t token_id) const;
std::pair<float, const ContextState *> Finalize(
const ContextState *state) const;
const ContextState *Root() const { return root_.get(); }
private:
float context_score_;
std::unique_ptr<ContextState> root_;
void Build(const std::vector<std::vector<int32_t>> &token_ids) const;
void FillFailOutput() const;
};
} // namespace sherpa_onnx
#endif // SHERPA_ONNX_CSRC_CONTEXT_GRAPH_H_
+204
View File
@@ -0,0 +1,204 @@
// features.cc
//
// Copyright (c) 2023 Xiaomi Corporation
#include "feature-extractor.h"
#include <algorithm>
#include <memory>
#include <mutex> // NOLINT
#include <sstream>
#include <vector>
#include <iostream>
#include "online-feature.h"
#include "macros.h"
#include "resample.h"
namespace sherpa_onnx {
void FeatureExtractorConfig::Register(ParseOptions *po) {
po->Register("sample-rate", &sampling_rate,
"Sampling rate of the input waveform. "
"Note: You can have a different "
"sample rate for the input waveform. We will do resampling "
"inside the feature extractor");
po->Register("feat-dim", &feature_dim,
"Feature dimension. Must match the one expected by the model.");
}
std::string FeatureExtractorConfig::ToString() const {
std::ostringstream os;
os << "FeatureExtractorConfig(";
os << "sampling_rate=" << sampling_rate << ", ";
os << "feature_dim=" << feature_dim << ")";
return os.str();
}
class FeatureExtractor::Impl {
public:
explicit Impl(const FeatureExtractorConfig &config) : config_(config) {
opts_.frame_opts.dither = 1;
opts_.frame_opts.snip_edges = false;
opts_.frame_opts.samp_freq = config.sampling_rate;
opts_.mel_opts.num_bins = config.feature_dim;
opts_.mel_opts.high_freq = -400;
fbank_ = std::make_unique<knf::OnlineFbank>(opts_);
}
void AcceptWaveform(int32_t sampling_rate, const float *waveform, int32_t n) {
if (config_.normalize_samples) {
AcceptWaveformImpl(sampling_rate, waveform, n);
} else {
std::vector<float> buf(n);
for (int32_t i = 0; i != n; ++i) {
buf[i] = waveform[i] * 32768;
}
AcceptWaveformImpl(sampling_rate, buf.data(), n);
}
}
void AcceptWaveformImpl(int32_t sampling_rate, const float *waveform,
int32_t n) {
std::lock_guard<std::mutex> lock(mutex_);
if (resampler_) {
if (sampling_rate != resampler_->GetInputSamplingRate()) {
SHERPA_ONNX_LOGE(
"You changed the input sampling rate!! Expected: %d, given: "
"%d",
resampler_->GetInputSamplingRate(), sampling_rate);
exit(-1);
}
std::vector<float> samples;
resampler_->Resample(waveform, n, false, &samples);
fbank_->AcceptWaveform(opts_.frame_opts.samp_freq, samples.data(),
samples.size());
return;
}
if (sampling_rate != opts_.frame_opts.samp_freq) {
SHERPA_ONNX_LOGE(
"Creating a resampler:\n"
" in_sample_rate: %d\n"
" output_sample_rate: %d\n",
sampling_rate, static_cast<int32_t>(opts_.frame_opts.samp_freq));
float min_freq =
std::min<int32_t>(sampling_rate, opts_.frame_opts.samp_freq);
float lowpass_cutoff = 0.99 * 0.5 * min_freq;
int32_t lowpass_filter_width = 6;
resampler_ = std::make_unique<LinearResample>(
sampling_rate, opts_.frame_opts.samp_freq, lowpass_cutoff,
lowpass_filter_width);
std::vector<float> samples;
resampler_->Resample(waveform, n, false, &samples);
fbank_->AcceptWaveform(opts_.frame_opts.samp_freq, samples.data(),
samples.size());
return;
}
fbank_->AcceptWaveform(sampling_rate, waveform, n);
}
void InputFinished() const {
std::lock_guard<std::mutex> lock(mutex_);
fbank_->InputFinished();
}
int32_t NumFramesReady() const {
std::lock_guard<std::mutex> lock(mutex_);
return fbank_->NumFramesReady();
}
bool IsLastFrame(int32_t frame) const {
std::lock_guard<std::mutex> lock(mutex_);
return fbank_->IsLastFrame(frame);
}
std::vector<float> GetFrames(int32_t frame_index, int32_t n) {
std::lock_guard<std::mutex> lock(mutex_);
if (frame_index + n > fbank_->NumFramesReady()) {
SHERPA_ONNX_LOGE("%d + %d > %d\n", frame_index, n,
fbank_->NumFramesReady());
exit(-1);
}
int32_t discard_num = frame_index - last_frame_index_;
if (discard_num < 0) {
SHERPA_ONNX_LOGE("last_frame_index_: %d, frame_index_: %d",
last_frame_index_, frame_index);
exit(-1);
}
fbank_->Pop(discard_num);
int32_t feature_dim = fbank_->Dim();
std::vector<float> features(feature_dim * n);
float *p = features.data();
for (int32_t i = 0; i != n; ++i) {
const float *f = fbank_->GetFrame(i + frame_index);
// std::cout << "Frame " << i;
// for (int j = 0; j < feature_dim; j++) {
// std::cout << " " << f[j];
// }
// std::cout << std::endl;
std::copy(f, f + feature_dim, p);
p += feature_dim;
}
last_frame_index_ = frame_index;
return features;
}
int32_t FeatureDim() const { return opts_.mel_opts.num_bins; }
private:
std::unique_ptr<knf::OnlineFbank> fbank_;
knf::FbankOptions opts_;
FeatureExtractorConfig config_;
mutable std::mutex mutex_;
std::unique_ptr<LinearResample> resampler_;
int32_t last_frame_index_ = 0;
};
FeatureExtractor::FeatureExtractor(const FeatureExtractorConfig &config /*={}*/)
: impl_(std::make_unique<Impl>(config)) {}
FeatureExtractor::~FeatureExtractor() = default;
void FeatureExtractor::AcceptWaveform(int32_t sampling_rate,
const float *waveform, int32_t n) const {
impl_->AcceptWaveform(sampling_rate, waveform, n);
}
void FeatureExtractor::InputFinished() const { impl_->InputFinished(); }
int32_t FeatureExtractor::NumFramesReady() const {
return impl_->NumFramesReady();
}
bool FeatureExtractor::IsLastFrame(int32_t frame) const {
return impl_->IsLastFrame(frame);
}
std::vector<float> FeatureExtractor::GetFrames(int32_t frame_index,
int32_t n) const {
return impl_->GetFrames(frame_index, n);
}
int32_t FeatureExtractor::FeatureDim() const { return impl_->FeatureDim(); }
} // namespace sherpa_onnx
+86
View File
@@ -0,0 +1,86 @@
// features.h
//
// Copyright (c) 2023 Xiaomi Corporation
#ifndef SHERPA_ONNX_CSRC_FEATURES_H_
#define SHERPA_ONNX_CSRC_FEATURES_H_
#include <memory>
#include <string>
#include <vector>
#include "parse-options.h"
namespace sherpa_onnx {
struct FeatureExtractorConfig {
// Sampling rate used by the feature extractor. If it is different from
// the sampling rate of the input waveform, we will do resampling inside.
int32_t sampling_rate = 16000;
// Feature dimension
int32_t feature_dim = 80;
// Set internally by some models, e.g., paraformer sets it to false.
// This parameter is not exposed to users from the commandline
// If true, the feature extractor expects inputs to be normalized to
// the range [-1, 1].
// If false, we will multiply the inputs by 32768
bool normalize_samples = true;
std::string ToString() const;
void Register(ParseOptions *po);
};
class FeatureExtractor {
public:
explicit FeatureExtractor(const FeatureExtractorConfig &config = {});
~FeatureExtractor();
/**
@param sampling_rate The sampling_rate of the input waveform. If it does
not equal to config.sampling_rate, we will do
resampling inside.
@param waveform Pointer to a 1-D array of size n. It must be normalized to
the range [-1, 1].
@param n Number of entries in waveform
*/
void AcceptWaveform(int32_t sampling_rate, const float *waveform,
int32_t n) const;
/**
* InputFinished() tells the class you won't be providing any
* more waveform. This will help flush out the last frame or two
* of features, in the case where snip-edges == false; it also
* affects the return value of IsLastFrame().
*/
void InputFinished() const;
int32_t NumFramesReady() const;
/** Note: IsLastFrame() will only ever return true if you have called
* InputFinished() (and this frame is the last frame).
*/
bool IsLastFrame(int32_t frame) const;
/** Get n frames starting from the given frame index.
*
* @param frame_index The starting frame index
* @param n Number of frames to get.
* @return Return a 2-D tensor of shape (n, feature_dim).
* which is flattened into a 1-D vector (flattened in in row major)
*/
std::vector<float> GetFrames(int32_t frame_index, int32_t n) const;
/// Return feature dim of this extractor
int32_t FeatureDim() const;
private:
class Impl;
std::unique_ptr<Impl> impl_;
};
} // namespace sherpa_onnx
#endif // SHERPA_ONNX_CSRC_FEATURES_H_
+120
View File
@@ -0,0 +1,120 @@
/**
* Copyright (c) 2022 Xiaomi Corporation (authors: Fangjun Kuang)
*
* See LICENSE for clarification regarding multiple authors
*
* 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.
*/
// This file is copied/modified from kaldi/src/feat/feature-fbank.cc
//
#include "feature-fbank.h"
#include <algorithm>
#include <cmath>
#include <limits>
#include <vector>
#include "feature-functions.h"
namespace knf {
static void Sqrt(float *in_out, int32_t n) {
for (int32_t i = 0; i != n; ++i) {
in_out[i] = std::sqrt(in_out[i]);
}
}
std::ostream &operator<<(std::ostream &os, const FbankOptions &opts) {
os << opts.ToString();
return os;
}
FbankComputer::FbankComputer(const FbankOptions &opts)
: opts_(opts), rfft_(opts.frame_opts.PaddedWindowSize()) {
if (opts.energy_floor > 0.0f) {
log_energy_floor_ = logf(opts.energy_floor);
}
// We'll definitely need the filterbanks info for VTLN warping factor 1.0.
// [note: this call caches it.]
GetMelBanks(1.0f);
}
FbankComputer::~FbankComputer() {
for (auto iter = mel_banks_.begin(); iter != mel_banks_.end(); ++iter)
delete iter->second;
}
const MelBanks *FbankComputer::GetMelBanks(float vtln_warp) {
MelBanks *this_mel_banks = nullptr;
// std::map<float, MelBanks *>::iterator iter = mel_banks_.find(vtln_warp);
auto iter = mel_banks_.find(vtln_warp);
if (iter == mel_banks_.end()) {
this_mel_banks = new MelBanks(opts_.mel_opts, opts_.frame_opts, vtln_warp);
mel_banks_[vtln_warp] = this_mel_banks;
} else {
this_mel_banks = iter->second;
}
return this_mel_banks;
}
void FbankComputer::Compute(float signal_raw_log_energy, float vtln_warp,
std::vector<float> *signal_frame, float *feature) {
const MelBanks &mel_banks = *(GetMelBanks(vtln_warp));
SHERPA_ONNX_CHECK_EQ(signal_frame->size(), opts_.frame_opts.PaddedWindowSize());
// Compute energy after window function (not the raw one).
if (opts_.use_energy && !opts_.raw_energy) {
signal_raw_log_energy = std::log(
std::max<float>(InnerProduct(signal_frame->data(), signal_frame->data(),
signal_frame->size()),
std::numeric_limits<float>::epsilon()));
}
rfft_.Compute(signal_frame->data()); // signal_frame is modified in-place
ComputePowerSpectrum(signal_frame);
// Use magnitude instead of power if requested.
if (!opts_.use_power) {
Sqrt(signal_frame->data(), signal_frame->size() / 2 + 1);
}
int32_t mel_offset = ((opts_.use_energy && !opts_.htk_compat) ? 1 : 0);
// Its length is opts_.mel_opts.num_bins
float *mel_energies = feature + mel_offset;
// Sum with mel filter banks over the power spectrum
mel_banks.Compute(signal_frame->data(), mel_energies);
if (opts_.use_log_fbank) {
// Avoid log of zero (which should be prevented anyway by dithering).
for (int32_t i = 0; i != opts_.mel_opts.num_bins; ++i) {
auto t = std::max(mel_energies[i], std::numeric_limits<float>::epsilon());
mel_energies[i] = std::log(t);
}
}
// Copy energy as first value (or the last, if htk_compat == true).
if (opts_.use_energy) {
if (opts_.energy_floor > 0.0 && signal_raw_log_energy < log_energy_floor_) {
signal_raw_log_energy = log_energy_floor_;
}
int32_t energy_index = opts_.htk_compat ? opts_.mel_opts.num_bins : 0;
feature[energy_index] = signal_raw_log_energy;
}
}
} // namespace knf
+134
View File
@@ -0,0 +1,134 @@
/**
* Copyright (c) 2022 Xiaomi Corporation (authors: Fangjun Kuang)
*
* See LICENSE for clarification regarding multiple authors
*
* 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.
*/
// This file is copied/modified from kaldi/src/feat/feature-fbank.h
#ifndef KALDI_NATIVE_FBANK_CSRC_FEATURE_FBANK_H_
#define KALDI_NATIVE_FBANK_CSRC_FEATURE_FBANK_H_
#include <map>
#include <string>
#include <vector>
#include "feature-window.h"
#include "mel-computations.h"
#include "rfft.h"
namespace knf {
struct FbankOptions {
FrameExtractionOptions frame_opts;
MelBanksOptions mel_opts;
// append an extra dimension with energy to the filter banks
bool use_energy = false;
float energy_floor = 0.0f; // active iff use_energy==true
// If true, compute log_energy before preemphasis and windowing
// If false, compute log_energy after preemphasis ans windowing
bool raw_energy = true; // active iff use_energy==true
// If true, put energy last (if using energy)
// If false, put energy first
bool htk_compat = false; // active iff use_energy==true
// if true (default), produce log-filterbank, else linear
bool use_log_fbank = true;
// if true (default), use power in filterbank
// analysis, else magnitude.
bool use_power = true;
FbankOptions() { mel_opts.num_bins = 23; }
std::string ToString() const {
std::ostringstream os;
os << "frame_opts: \n";
os << frame_opts << "\n";
os << "\n";
os << "mel_opts: \n";
os << mel_opts << "\n";
os << "use_energy: " << use_energy << "\n";
os << "energy_floor: " << energy_floor << "\n";
os << "raw_energy: " << raw_energy << "\n";
os << "htk_compat: " << htk_compat << "\n";
os << "use_log_fbank: " << use_log_fbank << "\n";
os << "use_power: " << use_power << "\n";
return os.str();
}
};
std::ostream &operator<<(std::ostream &os, const FbankOptions &opts);
class FbankComputer {
public:
using Options = FbankOptions;
explicit FbankComputer(const FbankOptions &opts);
~FbankComputer();
int32_t Dim() const {
return opts_.mel_opts.num_bins + (opts_.use_energy ? 1 : 0);
}
// if true, compute log_energy_pre_window but after dithering and dc removal
bool NeedRawLogEnergy() const { return opts_.use_energy && opts_.raw_energy; }
const FrameExtractionOptions &GetFrameOptions() const {
return opts_.frame_opts;
}
const FbankOptions &GetOptions() const { return opts_; }
/**
Function that computes one frame of features from
one frame of signal.
@param [in] signal_raw_log_energy The log-energy of the frame of the signal
prior to windowing and pre-emphasis, or
log(numeric_limits<float>::min()), whichever is greater. Must be
ignored by this function if this class returns false from
this->NeedsRawLogEnergy().
@param [in] vtln_warp The VTLN warping factor that the user wants
to be applied when computing features for this utterance. Will
normally be 1.0, meaning no warping is to be done. The value will
be ignored for feature types that don't support VLTN, such as
spectrogram features.
@param [in] signal_frame One frame of the signal,
as extracted using the function ExtractWindow() using the options
returned by this->GetFrameOptions(). The function will use the
vector as a workspace, which is why it's a non-const pointer.
@param [out] feature Pointer to a vector of size this->Dim(), to which
the computed feature will be written. It should be pre-allocated.
*/
void Compute(float signal_raw_log_energy, float vtln_warp,
std::vector<float> *signal_frame, float *feature);
private:
const MelBanks *GetMelBanks(float vtln_warp);
FbankOptions opts_;
float log_energy_floor_;
std::map<float, MelBanks *> mel_banks_; // float is VTLN coefficient.
Rfft rfft_;
};
} // namespace knf
#endif // KALDI_NATIVE_FBANK_CSRC_FEATURE_FBANK_H_
+49
View File
@@ -0,0 +1,49 @@
/**
* Copyright (c) 2022 Xiaomi Corporation (authors: Fangjun Kuang)
*
* See LICENSE for clarification regarding multiple authors
*
* 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.
*/
// This file is copied/modified from kaldi/src/feat/feature-functions.cc
#include "feature-functions.h"
#include <cstdint>
#include <vector>
namespace knf {
void ComputePowerSpectrum(std::vector<float> *complex_fft) {
int32_t dim = complex_fft->size();
// now we have in complex_fft, first half of complex spectrum
// it's stored as [real0, realN/2, real1, im1, real2, im2, ...]
float *p = complex_fft->data();
int32_t half_dim = dim / 2;
float first_energy = p[0] * p[0];
float last_energy = p[1] * p[1]; // handle this special case
for (int32_t i = 1; i < half_dim; ++i) {
float real = p[i * 2];
float im = p[i * 2 + 1];
p[i] = real * real + im * im;
}
p[0] = first_energy;
p[half_dim] = last_energy; // Will actually never be used, and anyway
// if the signal has been bandlimited sensibly this should be zero.
}
} // namespace knf
+38
View File
@@ -0,0 +1,38 @@
/**
* Copyright (c) 2022 Xiaomi Corporation (authors: Fangjun Kuang)
*
* See LICENSE for clarification regarding multiple authors
*
* 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.
*/
// This file is copied/modified from kaldi/src/feat/feature-functions.h
#ifndef KALDI_NATIVE_FBANK_CSRC_FEATURE_FUNCTIONS_H_
#define KALDI_NATIVE_FBANK_CSRC_FEATURE_FUNCTIONS_H_
#include <vector>
namespace knf {
// ComputePowerSpectrum converts a complex FFT (as produced by the FFT
// functions in csrc/rfft.h), and converts it into
// a power spectrum. If the complex FFT is a vector of size n (representing
// half of the complex FFT of a real signal of size n, as described there),
// this function computes in the first (n/2) + 1 elements of it, the
// energies of the fft bins from zero to the Nyquist frequency. Contents of the
// remaining (n/2) - 1 elements are undefined at output.
void ComputePowerSpectrum(std::vector<float> *complex_fft);
} // namespace knf
#endif // KALDI_NATIVE_FBANK_CSRC_FEATURE_FUNCTIONS_H_
+258
View File
@@ -0,0 +1,258 @@
// feature-window.cc
//
// Copyright (c) 2022 Xiaomi Corporation (authors: Fangjun Kuang)
// This file is copied/modified from kaldi/src/feat/feature-window.cc
#include "feature-window.h"
#include <algorithm>
#include <cmath>
#include <limits>
#include <vector>
#include <random>
#ifndef M_2PI
#define M_2PI 6.283185307179586476925286766559005
#endif
namespace knf {
std::ostream &operator<<(std::ostream &os, const FrameExtractionOptions &opts) {
os << opts.ToString();
return os;
}
FeatureWindowFunction::FeatureWindowFunction(const FrameExtractionOptions &opts)
: window_(opts.WindowSize()) {
int32_t frame_length = opts.WindowSize();
SHERPA_ONNX_CHECK_GT(frame_length, 0);
float *window_data = window_.data();
double a = M_2PI / (frame_length - 1);
if (opts.window_type == "hann") {
// see https://pytorch.org/docs/stable/generated/torch.hann_window.html
// We assume periodic is true
a = M_2PI / frame_length;
}
for (int32_t i = 0; i < frame_length; i++) {
double i_fl = static_cast<double>(i);
if (opts.window_type == "hanning") {
window_data[i] = 0.5 - 0.5 * cos(a * i_fl);
} else if (opts.window_type == "sine") {
// when you are checking ws wikipedia, please
// note that 0.5 * a = M_PI/(frame_length-1)
window_data[i] = sin(0.5 * a * i_fl);
} else if (opts.window_type == "hamming") {
window_data[i] = 0.54 - 0.46 * cos(a * i_fl);
} else if (opts.window_type == "hann") {
window_data[i] = 0.50 - 0.50 * cos(a * i_fl);
} else if (opts.window_type == "povey") {
// like hamming but goes to zero at edges.
window_data[i] = pow(0.5 - 0.5 * cos(a * i_fl), 0.85);
} else if (opts.window_type == "rectangular") {
window_data[i] = 1.0;
} else if (opts.window_type == "blackman") {
window_data[i] = opts.blackman_coeff - 0.5 * cos(a * i_fl) +
(0.5 - opts.blackman_coeff) * cos(2 * a * i_fl);
} else {
SHERPA_ONNX_LOG(FATAL) << "Invalid window type " << opts.window_type;
}
}
}
void FeatureWindowFunction::Apply(float *wave) const {
int32_t window_size = window_.size();
const float *p = window_.data();
for (int32_t k = 0; k != window_size; ++k) {
wave[k] *= p[k];
}
}
int64_t FirstSampleOfFrame(int32_t frame, const FrameExtractionOptions &opts) {
int64_t frame_shift = opts.WindowShift();
if (opts.snip_edges) {
return frame * frame_shift;
} else {
int64_t midpoint_of_frame = frame_shift * frame + frame_shift / 2,
beginning_of_frame = midpoint_of_frame - opts.WindowSize() / 2;
return beginning_of_frame;
}
}
int32_t NumFrames(int64_t num_samples, const FrameExtractionOptions &opts,
bool flush /*= true*/) {
int64_t frame_shift = opts.WindowShift();
int64_t frame_length = opts.WindowSize();
if (opts.snip_edges) {
// with --snip-edges=true (the default), we use a HTK-like approach to
// determining the number of frames-- all frames have to fit completely into
// the waveform, and the first frame begins at sample zero.
if (num_samples < frame_length)
return 0;
else
return (1 + ((num_samples - frame_length) / frame_shift));
// You can understand the expression above as follows: 'num_samples -
// frame_length' is how much room we have to shift the frame within the
// waveform; 'frame_shift' is how much we shift it each time; and the ratio
// is how many times we can shift it (integer arithmetic rounds down).
} else {
// if --snip-edges=false, the number of frames is determined by rounding the
// (file-length / frame-shift) to the nearest integer. The point of this
// formula is to make the number of frames an obvious and predictable
// function of the frame shift and signal length, which makes many
// segmentation-related questions simpler.
//
// Because integer division in C++ rounds toward zero, we add (half the
// frame-shift minus epsilon) before dividing, to have the effect of
// rounding towards the closest integer.
int32_t num_frames = (num_samples + (frame_shift / 2)) / frame_shift;
if (flush) return num_frames;
// note: 'end' always means the last plus one, i.e. one past the last.
int64_t end_sample_of_last_frame =
FirstSampleOfFrame(num_frames - 1, opts) + frame_length;
// the following code is optimized more for clarity than efficiency.
// If flush == false, we can't output frames that extend past the end
// of the signal.
while (num_frames > 0 && end_sample_of_last_frame > num_samples) {
num_frames--;
end_sample_of_last_frame -= frame_shift;
}
return num_frames;
}
}
void ExtractWindow(int64_t sample_offset, const std::vector<float> &wave,
int32_t f, const FrameExtractionOptions &opts,
const FeatureWindowFunction &window_function,
std::vector<float> *window,
float *log_energy_pre_window /*= nullptr*/) {
SHERPA_ONNX_CHECK(sample_offset >= 0 && wave.size() != 0);
int32_t frame_length = opts.WindowSize();
int32_t frame_length_padded = opts.PaddedWindowSize();
int64_t num_samples = sample_offset + wave.size();
int64_t start_sample = FirstSampleOfFrame(f, opts);
int64_t end_sample = start_sample + frame_length;
if (opts.snip_edges) {
SHERPA_ONNX_CHECK(start_sample >= sample_offset && end_sample <= num_samples);
} else {
SHERPA_ONNX_CHECK(sample_offset == 0 || start_sample >= sample_offset);
}
if (window->size() != frame_length_padded) {
window->resize(frame_length_padded);
}
// wave_start and wave_end are start and end indexes into 'wave', for the
// piece of wave that we're trying to extract.
int32_t wave_start = int32_t(start_sample - sample_offset);
int32_t wave_end = wave_start + frame_length;
if (wave_start >= 0 && wave_end <= wave.size()) {
// the normal case-- no edge effects to consider.
std::copy(wave.begin() + wave_start,
wave.begin() + wave_start + frame_length, window->data());
} else {
// Deal with any end effects by reflection, if needed. This code will only
// be reached for about two frames per utterance, so we don't concern
// ourselves excessively with efficiency.
int32_t wave_dim = wave.size();
for (int32_t s = 0; s < frame_length; ++s) {
int32_t s_in_wave = s + wave_start;
while (s_in_wave < 0 || s_in_wave >= wave_dim) {
// reflect around the beginning or end of the wave.
// e.g. -1 -> 0, -2 -> 1.
// dim -> dim - 1, dim + 1 -> dim - 2.
// the code supports repeated reflections, although this
// would only be needed in pathological cases.
if (s_in_wave < 0)
s_in_wave = -s_in_wave - 1;
else
s_in_wave = 2 * wave_dim - 1 - s_in_wave;
}
(*window)[s] = wave[s_in_wave];
}
}
ProcessWindow(opts, window_function, window->data(), log_energy_pre_window);
}
static void RemoveDcOffset(float *d, int32_t n) {
float sum = 0;
for (int32_t i = 0; i != n; ++i) {
sum += d[i];
}
float mean = sum / n;
for (int32_t i = 0; i != n; ++i) {
d[i] -= mean;
}
}
float InnerProduct(const float *a, const float *b, int32_t n) {
float sum = 0;
for (int32_t i = 0; i != n; ++i) {
sum += a[i] * b[i];
}
return sum;
}
static std::default_random_engine de(1234); //seed
void Dither(float *waveform, int32_t n, float dither_value) {
if (dither_value == 0.0)
return;
std::normal_distribution<float> nd(0, 1.0 / 32768.0); //mean followed by stdiv
for (int i = 0; i < n; i++)
waveform[i] += nd(de) * dither_value;
}
static void Preemphasize(float *d, int32_t n, float preemph_coeff) {
if (preemph_coeff == 0.0) {
return;
}
SHERPA_ONNX_CHECK(preemph_coeff >= 0.0 && preemph_coeff <= 1.0);
for (int32_t i = n - 1; i > 0; --i) {
d[i] -= preemph_coeff * d[i - 1];
}
d[0] -= preemph_coeff * d[0];
}
void ProcessWindow(const FrameExtractionOptions &opts,
const FeatureWindowFunction &window_function, float *window,
float *log_energy_pre_window /*= nullptr*/) {
int32_t frame_length = opts.WindowSize();
if (opts.dither != 0.0) {
Dither(window, frame_length, opts.dither);
}
if (opts.remove_dc_offset) {
RemoveDcOffset(window, frame_length);
}
if (log_energy_pre_window != NULL) {
float energy = std::max<float>(InnerProduct(window, window, frame_length),
std::numeric_limits<float>::epsilon());
*log_energy_pre_window = std::log(energy);
}
if (opts.preemph_coeff != 0.0) {
Preemphasize(window, frame_length, opts.preemph_coeff);
}
window_function.Apply(window);
}
} // namespace knf
+174
View File
@@ -0,0 +1,174 @@
// feature-window.h
//
// Copyright (c) 2022 Xiaomi Corporation (authors: Fangjun Kuang)
// This file is copied/modified from kaldi/src/feat/feature-window.h
#ifndef KALDI_NATIVE_FBANK_CSRC_FEATURE_WINDOW_H_
#define KALDI_NATIVE_FBANK_CSRC_FEATURE_WINDOW_H_
#include <sstream>
#include <string>
#include <vector>
#include "log.h"
namespace knf {
inline int32_t RoundUpToNearestPowerOfTwo(int32_t n) {
// copied from kaldi/src/base/kaldi-math.cc
SHERPA_ONNX_CHECK_GT(n, 0);
n--;
n |= n >> 1;
n |= n >> 2;
n |= n >> 4;
n |= n >> 8;
n |= n >> 16;
return n + 1;
}
struct FrameExtractionOptions {
float samp_freq = 16000;
float frame_shift_ms = 10.0f; // in milliseconds.
float frame_length_ms = 25.0f; // in milliseconds.
float dither = 1.0f; // Amount of dithering, 0.0 means no dither.
float preemph_coeff = 0.97f; // Preemphasis coefficient.
bool remove_dc_offset = true; // Subtract mean of wave before FFT.
std::string window_type = "povey"; // e.g. Hamming window
// May be "hamming", "rectangular", "povey", "hanning", "hann", "sine",
// "blackman".
// "povey" is a window I made to be similar to Hamming but to go to zero at
// the edges, it's pow((0.5 - 0.5*cos(n/N*2*pi)), 0.85) I just don't think the
// Hamming window makes sense as a windowing function.
bool round_to_power_of_two = true;
float blackman_coeff = 0.42f;
bool snip_edges = true;
// bool allow_downsample = false;
// bool allow_upsample = false;
int32_t WindowShift() const {
return static_cast<int32_t>(samp_freq * 0.001f * frame_shift_ms);
}
int32_t WindowSize() const {
return static_cast<int32_t>(samp_freq * 0.001f * frame_length_ms);
}
int32_t PaddedWindowSize() const {
return (round_to_power_of_two ? RoundUpToNearestPowerOfTwo(WindowSize())
: WindowSize());
}
std::string ToString() const {
std::ostringstream os;
#define KNF_PRINT(x) os << #x << ": " << x << "\n"
KNF_PRINT(samp_freq);
KNF_PRINT(frame_shift_ms);
KNF_PRINT(frame_length_ms);
KNF_PRINT(dither);
KNF_PRINT(preemph_coeff);
KNF_PRINT(remove_dc_offset);
KNF_PRINT(window_type);
KNF_PRINT(round_to_power_of_two);
KNF_PRINT(blackman_coeff);
KNF_PRINT(snip_edges);
// KNF_PRINT(allow_downsample);
// KNF_PRINT(allow_upsample);
#undef KNF_PRINT
return os.str();
}
};
std::ostream &operator<<(std::ostream &os, const FrameExtractionOptions &opts);
class FeatureWindowFunction {
public:
FeatureWindowFunction() = default;
explicit FeatureWindowFunction(const FrameExtractionOptions &opts);
/**
* @param wave Pointer to a 1-D array of shape [window_size].
* It is modified in-place: wave[i] = wave[i] * window_[i].
* @param
*/
void Apply(float *wave) const;
const std::vector<float> &GetWindow() const { return window_; }
private:
std::vector<float> window_; // of size opts.WindowSize()
};
int64_t FirstSampleOfFrame(int32_t frame, const FrameExtractionOptions &opts);
/**
This function returns the number of frames that we can extract from a wave
file with the given number of samples in it (assumed to have the same
sampling rate as specified in 'opts').
@param [in] num_samples The number of samples in the wave file.
@param [in] opts The frame-extraction options class
@param [in] flush True if we are asserting that this number of samples
is 'all there is', false if we expecting more data to possibly come in. This
only makes a difference to the answer
if opts.snip_edges== false. For offline feature extraction you always want
flush == true. In an online-decoding context, once you know (or decide) that
no more data is coming in, you'd call it with flush == true at the end to
flush out any remaining data.
*/
int32_t NumFrames(int64_t num_samples, const FrameExtractionOptions &opts,
bool flush = true);
/*
ExtractWindow() extracts a windowed frame of waveform (possibly with a
power-of-two, padded size, depending on the config), including all the
processing done by ProcessWindow().
@param [in] sample_offset If 'wave' is not the entire waveform, but
part of it to the left has been discarded, then the
number of samples prior to 'wave' that we have
already discarded. Set this to zero if you are
processing the entire waveform in one piece, or
if you get 'no matching function' compilation
errors when updating the code.
@param [in] wave The waveform
@param [in] f The frame index to be extracted, with
0 <= f < NumFrames(sample_offset + wave.Dim(), opts, true)
@param [in] opts The options class to be used
@param [in] window_function The windowing function, as derived from the
options class.
@param [out] window The windowed, possibly-padded waveform to be
extracted. Will be resized as needed.
@param [out] log_energy_pre_window If non-NULL, the log-energy of
the signal prior to pre-emphasis and multiplying by
the windowing function will be written to here.
*/
void ExtractWindow(int64_t sample_offset, const std::vector<float> &wave,
int32_t f, const FrameExtractionOptions &opts,
const FeatureWindowFunction &window_function,
std::vector<float> *window,
float *log_energy_pre_window = nullptr);
/**
This function does all the windowing steps after actually
extracting the windowed signal: depending on the
configuration, it does dithering, dc offset removal,
preemphasis, and multiplication by the windowing function.
@param [in] opts The options class to be used
@param [in] window_function The windowing function-- should have
been initialized using 'opts'.
@param [in,out] window A vector of size opts.WindowSize(). Note:
it will typically be a sub-vector of a larger vector of size
opts.PaddedWindowSize(), with the remaining samples zero,
as the FFT code is more efficient if it operates on data with
power-of-two size.
@param [out] log_energy_pre_window If non-NULL, then after dithering and
DC offset removal, this function will write to this pointer the log of
the total energy (i.e. sum-squared) of the frame.
*/
void ProcessWindow(const FrameExtractionOptions &opts,
const FeatureWindowFunction &window_function, float *window,
float *log_energy_pre_window = nullptr);
// Compute the inner product of two vectors
float InnerProduct(const float *a, const float *b, int32_t n);
} // namespace knf
#endif // KALDI_NATIVE_FBANK_CSRC_FEATURE_WINDOW_H_
+2883
View File
File diff suppressed because it is too large Load Diff
+24
View File
@@ -0,0 +1,24 @@
// file-utils.cc
//
// Copyright (c) 2022-2023 Xiaomi Corporation
#include "file-utils.h"
#include <fstream>
#include <string>
#include "log.h"
namespace sherpa_onnx {
bool FileExists(const std::string &filename) {
return std::ifstream(filename).good();
}
void AssertFileExists(const std::string &filename) {
if (!FileExists(filename)) {
SHERPA_ONNX_LOG(FATAL) << filename << " does not exist!";
}
}
} // namespace sherpa_onnx
+28
View File
@@ -0,0 +1,28 @@
// file-utils.h
//
// Copyright (c) 2022-2023 Xiaomi Corporation
#ifndef SHERPA_ONNX_CSRC_FILE_UTILS_H_
#define SHERPA_ONNX_CSRC_FILE_UTILS_H_
#include <fstream>
#include <string>
namespace sherpa_onnx {
/** Check whether a given path is a file or not
*
* @param filename Path to check.
* @return Return true if the given path is a file; return false otherwise.
*/
bool FileExists(const std::string &filename);
/** Abort if the file does not exist.
*
* @param filename The file to check.
*/
void AssertFileExists(const std::string &filename);
} // namespace sherpa_onnx
#endif // SHERPA_ONNX_CSRC_FILE_UTILS_H_
+81
View File
@@ -0,0 +1,81 @@
/**
* Copyright (c) 2023 Xiaomi Corporation
* Copyright (c) 2023 Pingfeng Luo
*/
#include "hypothesis.h"
#include <algorithm>
#include <utility>
namespace sherpa_onnx {
void Hypotheses::Add(Hypothesis hyp) {
auto key = hyp.Key();
auto it = hyps_dict_.find(key);
if (it == hyps_dict_.end()) {
hyps_dict_[key] = std::move(hyp);
} else {
it->second.log_prob = LogAdd<double>()(it->second.log_prob, hyp.log_prob);
}
}
Hypothesis Hypotheses::GetMostProbable(bool length_norm) const {
if (length_norm == false) {
return std::max_element(hyps_dict_.begin(), hyps_dict_.end(),
[](const auto &left, auto &right) -> bool {
return left.second.TotalLogProb() <
right.second.TotalLogProb();
})
->second;
} else {
// for length_norm is true
return std::max_element(
hyps_dict_.begin(), hyps_dict_.end(),
[](const auto &left, const auto &right) -> bool {
return left.second.TotalLogProb() / left.second.ys.size() <
right.second.TotalLogProb() / right.second.ys.size();
})
->second;
}
}
std::vector<Hypothesis> Hypotheses::GetTopK(int32_t k, bool length_norm) const {
k = std::max(k, 1);
k = std::min(k, Size());
std::vector<Hypothesis> all_hyps = Vec();
if (length_norm == false) {
std::partial_sort(all_hyps.begin(), all_hyps.begin() + k, all_hyps.end(),
[](const auto &a, const auto &b) {
return a.TotalLogProb() > b.TotalLogProb();
});
} else {
// for length_norm is true
std::partial_sort(all_hyps.begin(), all_hyps.begin() + k, all_hyps.end(),
[](const auto &a, const auto &b) {
return a.TotalLogProb() / a.ys.size() >
b.TotalLogProb() / b.ys.size();
});
}
return {all_hyps.begin(), all_hyps.begin() + k};
}
const std::vector<int32_t> GetHypsRowSplits(
const std::vector<Hypotheses> &hyps) {
std::vector<int32_t> row_splits;
row_splits.reserve(hyps.size() + 1);
row_splits.push_back(0);
int32_t s = 0;
for (const auto &h : hyps) {
s += h.Size();
row_splits.push_back(s);
}
return row_splits;
}
} // namespace sherpa_onnx
+145
View File
@@ -0,0 +1,145 @@
/**
* Copyright (c) 2023 Xiaomi Corporation
* Copyright (c) 2023 Pingfeng Luo
*
*/
#ifndef SHERPA_ONNX_CSRC_HYPOTHESIS_H_
#define SHERPA_ONNX_CSRC_HYPOTHESIS_H_
#include <sstream>
#include <string>
#include <unordered_map>
#include <utility>
#include <vector>
#include "onnxruntime_cxx_api.h" // NOLINT
#include "context-graph.h"
#include "math.h"
#include "onnx-utils.h"
namespace sherpa_onnx {
struct Hypothesis {
// The predicted tokens so far. Newly predicated tokens are appended.
std::vector<int64_t> ys;
// timestamps[i] contains the frame number after subsampling
// on which ys[i] is decoded.
std::vector<int32_t> timestamps;
// The total score of ys in log space.
// It contains only acoustic scores
double log_prob = 0;
// LM log prob if any.
double lm_log_prob = 0;
// the nn lm score for next token given the current ys
CopyableOrtValue nn_lm_scores;
// the nn lm states
std::vector<CopyableOrtValue> nn_lm_states;
const ContextState *context_state;
// TODO(fangjun): Make it configurable
// the minimum of tokens in a chunk for streaming RNN LM
int32_t lm_rescore_min_chunk = 2; // a const
int32_t num_trailing_blanks = 0;
Hypothesis() = default;
Hypothesis(const std::vector<int64_t> &ys, double log_prob,
const ContextState *context_state = nullptr)
: ys(ys), log_prob(log_prob), context_state(context_state) {}
double TotalLogProb() const { return log_prob + lm_log_prob; }
// If two Hypotheses have the same `Key`, then they contain
// the same token sequence.
std::string Key() const {
// TODO(fangjun): Use a hash function?
std::ostringstream os;
std::string sep;
for (auto i : ys) {
os << sep << i;
sep = "-";
}
return os.str();
}
// For debugging
std::string ToString() const {
std::ostringstream os;
os << "(" << Key() << ", " << log_prob << ")";
return os.str();
}
};
class Hypotheses {
public:
Hypotheses() = default;
explicit Hypotheses(std::vector<Hypothesis> hyps) {
for (auto &h : hyps) {
hyps_dict_[h.Key()] = std::move(h);
}
}
explicit Hypotheses(std::unordered_map<std::string, Hypothesis> hyps_dict)
: hyps_dict_(std::move(hyps_dict)) {}
// Add hyp to this object. If it already exists, its log_prob
// is updated with the given hyp using log-sum-exp.
void Add(Hypothesis hyp);
// Get the hyp that has the largest log_prob.
// If length_norm is true, hyp's log_prob is divided by
// len(hyp.ys) before comparison.
Hypothesis GetMostProbable(bool length_norm) const;
// Get the k hyps that have the largest log_prob.
// If length_norm is true, hyp's log_prob is divided by
// len(hyp.ys) before comparison.
std::vector<Hypothesis> GetTopK(int32_t k, bool length_norm) const;
int32_t Size() const { return hyps_dict_.size(); }
std::string ToString() const {
std::ostringstream os;
for (const auto &p : hyps_dict_) {
os << p.second.ToString() << "\n";
}
return os.str();
}
const auto begin() const { return hyps_dict_.begin(); }
const auto end() const { return hyps_dict_.end(); }
auto begin() { return hyps_dict_.begin(); }
auto end() { return hyps_dict_.end(); }
void Clear() { hyps_dict_.clear(); }
private:
// Return a list of hyps contained in this object.
std::vector<Hypothesis> Vec() const {
std::vector<Hypothesis> ans;
ans.reserve(hyps_dict_.size());
for (const auto &p : hyps_dict_) {
ans.push_back(p.second);
}
return ans;
}
private:
using Map = std ::unordered_map<std::string, Hypothesis>;
Map hyps_dict_;
};
const std::vector<int32_t> GetHypsRowSplits(
const std::vector<Hypotheses> &hyps);
} // namespace sherpa_onnx
#endif // SHERPA_ONNX_CSRC_HYPOTHESIS_H_
-655
View File
@@ -1,655 +0,0 @@
// From https://github.com/nbsdx/SimpleJSON
// SimpleJSON
// Simple C++ JSON library
//
// License
// Do what the fuck you want public license
#pragma once
#include <cstdint>
#include <cmath>
#include <cctype>
#include <string>
#include <deque>
#include <map>
#include <type_traits>
#include <initializer_list>
#include <ostream>
#include <iostream>
namespace json {
using std::map;
using std::deque;
using std::string;
using std::enable_if;
using std::initializer_list;
using std::is_same;
using std::is_convertible;
using std::is_integral;
using std::is_floating_point;
namespace {
string json_escape( const string &str ) {
string output;
for( unsigned i = 0; i < str.length(); ++i )
switch( str[i] ) {
case '\"': output += "\\\""; break;
case '\\': output += "\\\\"; break;
case '\b': output += "\\b"; break;
case '\f': output += "\\f"; break;
case '\n': output += "\\n"; break;
case '\r': output += "\\r"; break;
case '\t': output += "\\t"; break;
default : output += str[i]; break;
}
return std::move( output );
}
}
class JSON
{
union BackingData {
BackingData( double d ) : Float( d ){}
BackingData( long l ) : Int( l ){}
BackingData( bool b ) : Bool( b ){}
BackingData( string s ) : String( new string( s ) ){}
BackingData() : Int( 0 ){}
deque<JSON> *List;
map<string,JSON> *Map;
string *String;
double Float;
long Int;
bool Bool;
} Internal;
public:
enum class Class {
Null,
Object,
Array,
String,
Floating,
Integral,
Boolean
};
template <typename Container>
class JSONWrapper {
Container *object;
public:
JSONWrapper( Container *val ) : object( val ) {}
JSONWrapper( std::nullptr_t ) : object( nullptr ) {}
typename Container::iterator begin() { return object ? object->begin() : typename Container::iterator(); }
typename Container::iterator end() { return object ? object->end() : typename Container::iterator(); }
typename Container::const_iterator begin() const { return object ? object->begin() : typename Container::iterator(); }
typename Container::const_iterator end() const { return object ? object->end() : typename Container::iterator(); }
};
template <typename Container>
class JSONConstWrapper {
const Container *object;
public:
JSONConstWrapper( const Container *val ) : object( val ) {}
JSONConstWrapper( std::nullptr_t ) : object( nullptr ) {}
typename Container::const_iterator begin() const { return object ? object->begin() : typename Container::const_iterator(); }
typename Container::const_iterator end() const { return object ? object->end() : typename Container::const_iterator(); }
};
JSON() : Internal(), Type( Class::Null ){}
JSON( initializer_list<JSON> list )
: JSON()
{
SetType( Class::Object );
for( auto i = list.begin(), e = list.end(); i != e; ++i, ++i )
operator[]( i->ToString() ) = *std::next( i );
}
JSON( JSON&& other )
: Internal( other.Internal )
, Type( other.Type )
{ other.Type = Class::Null; other.Internal.Map = nullptr; }
JSON& operator=( JSON&& other ) {
ClearInternal();
Internal = other.Internal;
Type = other.Type;
other.Internal.Map = nullptr;
other.Type = Class::Null;
return *this;
}
JSON( const JSON &other ) {
switch( other.Type ) {
case Class::Object:
Internal.Map =
new map<string,JSON>( other.Internal.Map->begin(),
other.Internal.Map->end() );
break;
case Class::Array:
Internal.List =
new deque<JSON>( other.Internal.List->begin(),
other.Internal.List->end() );
break;
case Class::String:
Internal.String =
new string( *other.Internal.String );
break;
default:
Internal = other.Internal;
}
Type = other.Type;
}
JSON& operator=( const JSON &other ) {
ClearInternal();
switch( other.Type ) {
case Class::Object:
Internal.Map =
new map<string,JSON>( other.Internal.Map->begin(),
other.Internal.Map->end() );
break;
case Class::Array:
Internal.List =
new deque<JSON>( other.Internal.List->begin(),
other.Internal.List->end() );
break;
case Class::String:
Internal.String =
new string( *other.Internal.String );
break;
default:
Internal = other.Internal;
}
Type = other.Type;
return *this;
}
~JSON() {
switch( Type ) {
case Class::Array:
delete Internal.List;
break;
case Class::Object:
delete Internal.Map;
break;
case Class::String:
delete Internal.String;
break;
default:;
}
}
template <typename T>
JSON( T b, typename enable_if<is_same<T,bool>::value>::type* = 0 ) : Internal( b ), Type( Class::Boolean ){}
template <typename T>
JSON( T i, typename enable_if<is_integral<T>::value && !is_same<T,bool>::value>::type* = 0 ) : Internal( (long)i ), Type( Class::Integral ){}
template <typename T>
JSON( T f, typename enable_if<is_floating_point<T>::value>::type* = 0 ) : Internal( (double)f ), Type( Class::Floating ){}
template <typename T>
JSON( T s, typename enable_if<is_convertible<T,string>::value>::type* = 0 ) : Internal( string( s ) ), Type( Class::String ){}
JSON( std::nullptr_t ) : Internal(), Type( Class::Null ){}
static JSON Make( Class type ) {
JSON ret; ret.SetType( type );
return ret;
}
static JSON Load( const string & );
template <typename T>
void append( T arg ) {
SetType( Class::Array ); Internal.List->emplace_back( arg );
}
template <typename T, typename... U>
void append( T arg, U... args ) {
append( arg ); append( args... );
}
template <typename T>
typename enable_if<is_same<T,bool>::value, JSON&>::type operator=( T b ) {
SetType( Class::Boolean ); Internal.Bool = b; return *this;
}
template <typename T>
typename enable_if<is_integral<T>::value && !is_same<T,bool>::value, JSON&>::type operator=( T i ) {
SetType( Class::Integral ); Internal.Int = i; return *this;
}
template <typename T>
typename enable_if<is_floating_point<T>::value, JSON&>::type operator=( T f ) {
SetType( Class::Floating ); Internal.Float = f; return *this;
}
template <typename T>
typename enable_if<is_convertible<T,string>::value, JSON&>::type operator=( T s ) {
SetType( Class::String ); *Internal.String = string( s ); return *this;
}
JSON& operator[]( const string &key ) {
SetType( Class::Object ); return Internal.Map->operator[]( key );
}
JSON& operator[]( unsigned index ) {
SetType( Class::Array );
if( index >= Internal.List->size() ) Internal.List->resize( index + 1 );
return Internal.List->operator[]( index );
}
JSON &at( const string &key ) {
return operator[]( key );
}
const JSON &at( const string &key ) const {
return Internal.Map->at( key );
}
JSON &at( unsigned index ) {
return operator[]( index );
}
const JSON &at( unsigned index ) const {
return Internal.List->at( index );
}
int length() const {
if( Type == Class::Array )
return Internal.List->size();
else
return -1;
}
bool hasKey( const string &key ) const {
if( Type == Class::Object )
return Internal.Map->find( key ) != Internal.Map->end();
return false;
}
int size() const {
if( Type == Class::Object )
return Internal.Map->size();
else if( Type == Class::Array )
return Internal.List->size();
else
return -1;
}
Class JSONType() const { return Type; }
/// Functions for getting primitives from the JSON object.
bool IsNull() const { return Type == Class::Null; }
string ToString() const { bool b; return std::move( ToString( b ) ); }
string ToString( bool &ok ) const {
ok = (Type == Class::String);
return ok ? std::move( json_escape( *Internal.String ) ): string("");
}
double ToFloat() const { bool b; return ToFloat( b ); }
double ToFloat( bool &ok ) const {
ok = (Type == Class::Floating);
return ok ? Internal.Float : 0.0;
}
long ToInt() const { bool b; return ToInt( b ); }
long ToInt( bool &ok ) const {
ok = (Type == Class::Integral);
return ok ? Internal.Int : 0;
}
bool ToBool() const { bool b; return ToBool( b ); }
bool ToBool( bool &ok ) const {
ok = (Type == Class::Boolean);
return ok ? Internal.Bool : false;
}
JSONWrapper<map<string,JSON>> ObjectRange() {
if( Type == Class::Object )
return JSONWrapper<map<string,JSON>>( Internal.Map );
return JSONWrapper<map<string,JSON>>( nullptr );
}
JSONWrapper<deque<JSON>> ArrayRange() {
if( Type == Class::Array )
return JSONWrapper<deque<JSON>>( Internal.List );
return JSONWrapper<deque<JSON>>( nullptr );
}
JSONConstWrapper<map<string,JSON>> ObjectRange() const {
if( Type == Class::Object )
return JSONConstWrapper<map<string,JSON>>( Internal.Map );
return JSONConstWrapper<map<string,JSON>>( nullptr );
}
JSONConstWrapper<deque<JSON>> ArrayRange() const {
if( Type == Class::Array )
return JSONConstWrapper<deque<JSON>>( Internal.List );
return JSONConstWrapper<deque<JSON>>( nullptr );
}
string dump( int depth = 1, string tab = " ") const {
string pad = "";
for( int i = 0; i < depth; ++i, pad += tab );
switch( Type ) {
case Class::Null:
return "null";
case Class::Object: {
string s = "{\n";
bool skip = true;
for( auto &p : *Internal.Map ) {
if( !skip ) s += ",\n";
s += ( pad + "\"" + p.first + "\" : " + p.second.dump( depth + 1, tab ) );
skip = false;
}
s += ( "\n" + pad.erase( 0, 2 ) + "}" ) ;
return s;
}
case Class::Array: {
string s = "[";
bool skip = true;
for( auto &p : *Internal.List ) {
if( !skip ) s += ", ";
s += p.dump( depth + 1, tab );
skip = false;
}
s += "]";
return s;
}
case Class::String:
return "\"" + json_escape( *Internal.String ) + "\"";
case Class::Floating:
return std::to_string( Internal.Float );
case Class::Integral:
return std::to_string( Internal.Int );
case Class::Boolean:
return Internal.Bool ? "true" : "false";
default:
return "";
}
return "";
}
friend std::ostream& operator<<( std::ostream&, const JSON & );
private:
void SetType( Class type ) {
if( type == Type )
return;
ClearInternal();
switch( type ) {
case Class::Null: Internal.Map = nullptr; break;
case Class::Object: Internal.Map = new map<string,JSON>(); break;
case Class::Array: Internal.List = new deque<JSON>(); break;
case Class::String: Internal.String = new string(); break;
case Class::Floating: Internal.Float = 0.0; break;
case Class::Integral: Internal.Int = 0; break;
case Class::Boolean: Internal.Bool = false; break;
}
Type = type;
}
private:
/* beware: only call if YOU know that Internal is allocated. No checks performed here.
This function should be called in a constructed JSON just before you are going to
overwrite Internal...
*/
void ClearInternal() {
switch( Type ) {
case Class::Object: delete Internal.Map; break;
case Class::Array: delete Internal.List; break;
case Class::String: delete Internal.String; break;
default:;
}
}
private:
Class Type = Class::Null;
};
JSON Array() {
return std::move( JSON::Make( JSON::Class::Array ) );
}
template <typename... T>
JSON Array( T... args ) {
JSON arr = JSON::Make( JSON::Class::Array );
arr.append( args... );
return std::move( arr );
}
JSON Object() {
return std::move( JSON::Make( JSON::Class::Object ) );
}
std::ostream& operator<<( std::ostream &os, const JSON &json ) {
os << json.dump();
return os;
}
namespace {
JSON parse_next( const string &, size_t & );
void consume_ws( const string &str, size_t &offset ) {
while( isspace( str[offset] ) ) ++offset;
}
JSON parse_object( const string &str, size_t &offset ) {
JSON Object = JSON::Make( JSON::Class::Object );
++offset;
consume_ws( str, offset );
if( str[offset] == '}' ) {
++offset; return std::move( Object );
}
while( true ) {
JSON Key = parse_next( str, offset );
consume_ws( str, offset );
if( str[offset] != ':' ) {
std::cerr << "Error: Object: Expected colon, found '" << str[offset] << "'\n";
break;
}
consume_ws( str, ++offset );
JSON Value = parse_next( str, offset );
Object[Key.ToString()] = Value;
consume_ws( str, offset );
if( str[offset] == ',' ) {
++offset; continue;
}
else if( str[offset] == '}' ) {
++offset; break;
}
else {
std::cerr << "ERROR: Object: Expected comma, found '" << str[offset] << "'\n";
break;
}
}
return std::move( Object );
}
JSON parse_array( const string &str, size_t &offset ) {
JSON Array = JSON::Make( JSON::Class::Array );
unsigned index = 0;
++offset;
consume_ws( str, offset );
if( str[offset] == ']' ) {
++offset; return std::move( Array );
}
while( true ) {
Array[index++] = parse_next( str, offset );
consume_ws( str, offset );
if( str[offset] == ',' ) {
++offset; continue;
}
else if( str[offset] == ']' ) {
++offset; break;
}
else {
std::cerr << "ERROR: Array: Expected ',' or ']', found '" << str[offset] << "'\n";
return std::move( JSON::Make( JSON::Class::Array ) );
}
}
return std::move( Array );
}
JSON parse_string( const string &str, size_t &offset ) {
JSON String;
string val;
for( char c = str[++offset]; c != '\"' ; c = str[++offset] ) {
if( c == '\\' ) {
switch( str[ ++offset ] ) {
case '\"': val += '\"'; break;
case '\\': val += '\\'; break;
case '/' : val += '/' ; break;
case 'b' : val += '\b'; break;
case 'f' : val += '\f'; break;
case 'n' : val += '\n'; break;
case 'r' : val += '\r'; break;
case 't' : val += '\t'; break;
case 'u' : {
val += "\\u" ;
for( unsigned i = 1; i <= 4; ++i ) {
c = str[offset+i];
if( (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F') )
val += c;
else {
std::cerr << "ERROR: String: Expected hex character in unicode escape, found '" << c << "'\n";
return std::move( JSON::Make( JSON::Class::String ) );
}
}
offset += 4;
} break;
default : val += '\\'; break;
}
}
else
val += c;
}
++offset;
String = val;
return std::move( String );
}
JSON parse_number( const string &str, size_t &offset ) {
JSON Number;
string val, exp_str;
char c;
bool isDouble = false;
long exp = 0;
while( true ) {
c = str[offset++];
if( (c == '-') || (c >= '0' && c <= '9') )
val += c;
else if( c == '.' ) {
val += c;
isDouble = true;
}
else
break;
}
if( c == 'E' || c == 'e' ) {
c = str[ offset++ ];
if( c == '-' ){ ++offset; exp_str += '-';}
while( true ) {
c = str[ offset++ ];
if( c >= '0' && c <= '9' )
exp_str += c;
else if( !isspace( c ) && c != ',' && c != ']' && c != '}' ) {
std::cerr << "ERROR: Number: Expected a number for exponent, found '" << c << "'\n";
return std::move( JSON::Make( JSON::Class::Null ) );
}
else
break;
}
exp = std::stol( exp_str );
}
else if( !isspace( c ) && c != ',' && c != ']' && c != '}' ) {
std::cerr << "ERROR: Number: unexpected character '" << c << "'\n";
return std::move( JSON::Make( JSON::Class::Null ) );
}
--offset;
if( isDouble )
Number = std::stod( val ) * std::pow( 10, exp );
else {
if( !exp_str.empty() )
Number = std::stol( val ) * std::pow( 10, exp );
else
Number = std::stol( val );
}
return std::move( Number );
}
JSON parse_bool( const string &str, size_t &offset ) {
JSON Bool;
if( str.substr( offset, 4 ) == "true" )
Bool = true;
else if( str.substr( offset, 5 ) == "false" )
Bool = false;
else {
std::cerr << "ERROR: Bool: Expected 'true' or 'false', found '" << str.substr( offset, 5 ) << "'\n";
return std::move( JSON::Make( JSON::Class::Null ) );
}
offset += (Bool.ToBool() ? 4 : 5);
return std::move( Bool );
}
JSON parse_null( const string &str, size_t &offset ) {
JSON Null;
if( str.substr( offset, 4 ) != "null" ) {
std::cerr << "ERROR: Null: Expected 'null', found '" << str.substr( offset, 4 ) << "'\n";
return std::move( JSON::Make( JSON::Class::Null ) );
}
offset += 4;
return std::move( Null );
}
JSON parse_next( const string &str, size_t &offset ) {
char value;
consume_ws( str, offset );
value = str[offset];
switch( value ) {
case '[' : return std::move( parse_array( str, offset ) );
case '{' : return std::move( parse_object( str, offset ) );
case '\"': return std::move( parse_string( str, offset ) );
case 't' :
case 'f' : return std::move( parse_bool( str, offset ) );
case 'n' : return std::move( parse_null( str, offset ) );
default : if( ( value <= '9' && value >= '0' ) || value == '-' )
return std::move( parse_number( str, offset ) );
}
std::cerr << "ERROR: Parse: Unknown starting character '" << value << "'\n";
return JSON();
}
}
JSON JSON::Load( const string &str ) {
size_t offset = 0;
return std::move( parse_next( str, offset ) );
}
} // End Namespace json
-404
View File
@@ -1,404 +0,0 @@
// Copyright 2019 Alpha Cephei Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "kaldi_recognizer.h"
#include "json.h"
#include "fstext/fstext-utils.h"
#include "lat/sausages.h"
using namespace fst;
using namespace kaldi::nnet3;
KaldiRecognizer::KaldiRecognizer(Model *model, float sample_frequency) : model_(model), spk_model_(0), sample_frequency_(sample_frequency) {
model_->Ref();
feature_pipeline_ = new kaldi::OnlineNnet2FeaturePipeline (model_->feature_info_);
silence_weighting_ = new kaldi::OnlineSilenceWeighting(*model_->trans_model_, model_->feature_info_.silence_weighting_config, 3);
g_fst_ = NULL;
decode_fst_ = NULL;
if (!model_->hclg_fst_) {
if (model_->hcl_fst_ && model_->g_fst_) {
decode_fst_ = LookaheadComposeFst(*model_->hcl_fst_, *model_->g_fst_, model_->disambig_);
} else {
KALDI_ERR << "Can't create decoding graph";
}
}
decoder_ = new kaldi::SingleUtteranceNnet3Decoder(model_->nnet3_decoding_config_,
*model_->trans_model_,
*model_->decodable_info_,
model_->hclg_fst_ ? *model_->hclg_fst_ : *decode_fst_,
feature_pipeline_);
frame_offset_ = 0;
input_finalized_ = false;
spk_feature_ = NULL;
InitRescoring();
}
KaldiRecognizer::KaldiRecognizer(Model *model, float sample_frequency, char const *grammar) : model_(model), spk_model_(0), sample_frequency_(sample_frequency)
{
model_->Ref();
feature_pipeline_ = new kaldi::OnlineNnet2FeaturePipeline (model_->feature_info_);
silence_weighting_ = new kaldi::OnlineSilenceWeighting(*model_->trans_model_, model_->feature_info_.silence_weighting_config, 3);
g_fst_ = new StdVectorFst();
if (model_->hcl_fst_) {
g_fst_->AddState();
g_fst_->SetStart(0);
g_fst_->AddState();
g_fst_->SetFinal(1, fst::TropicalWeight::One());
g_fst_->AddArc(1, StdArc(0, 0, fst::TropicalWeight::One(), 0));
// Create simple word loop FST
stringstream ss(grammar);
string token;
while (getline(ss, token, ' ')) {
int32 id = model_->word_syms_->Find(token);
g_fst_->AddArc(0, StdArc(id, id, fst::TropicalWeight::One(), 1));
}
ArcSort(g_fst_, ILabelCompare<StdArc>());
decode_fst_ = LookaheadComposeFst(*model_->hcl_fst_, *g_fst_, model_->disambig_);
} else {
decode_fst_ = NULL;
KALDI_ERR << "Can't create decoding graph";
}
decoder_ = new kaldi::SingleUtteranceNnet3Decoder(model_->nnet3_decoding_config_,
*model_->trans_model_,
*model_->decodable_info_,
model_->hclg_fst_ ? *model_->hclg_fst_ : *decode_fst_,
feature_pipeline_);
frame_offset_ = 0;
input_finalized_ = false;
spk_feature_ = NULL;
InitRescoring();
}
KaldiRecognizer::KaldiRecognizer(Model *model, SpkModel *spk_model, float sample_frequency) : model_(model), spk_model_(spk_model), sample_frequency_(sample_frequency) {
model_->Ref();
spk_model->Ref();
feature_pipeline_ = new kaldi::OnlineNnet2FeaturePipeline (model_->feature_info_);
silence_weighting_ = new kaldi::OnlineSilenceWeighting(*model_->trans_model_, model_->feature_info_.silence_weighting_config, 3);
decode_fst_ = NULL;
g_fst_ = NULL;
if (!model_->hclg_fst_) {
if (model_->hcl_fst_ && model_->g_fst_) {
decode_fst_ = LookaheadComposeFst(*model_->hcl_fst_, *model_->g_fst_, model_->disambig_);
} else {
KALDI_ERR << "Can't create decoding graph";
}
}
decoder_ = new kaldi::SingleUtteranceNnet3Decoder(model_->nnet3_decoding_config_,
*model_->trans_model_,
*model_->decodable_info_,
model_->hclg_fst_ ? *model_->hclg_fst_ : *decode_fst_,
feature_pipeline_);
frame_offset_ = 0;
input_finalized_ = false;
spk_feature_ = new OnlineMfcc(spk_model_->spkvector_mfcc_opts);
InitRescoring();
}
KaldiRecognizer::~KaldiRecognizer() {
delete feature_pipeline_;
delete silence_weighting_;
delete decoder_;
delete g_fst_;
delete decode_fst_;
delete spk_feature_;
delete lm_fst_;
model_->Unref();
if (spk_model_)
spk_model_->Unref();
}
void KaldiRecognizer::InitRescoring()
{
if (model_->std_lm_fst_) {
fst::CacheOptions cache_opts(true, 50000);
fst::MapFstOptions mapfst_opts(cache_opts);
fst::StdToLatticeMapper<kaldi::BaseFloat> mapper;
lm_fst_ = new fst::MapFst<fst::StdArc, kaldi::LatticeArc, fst::StdToLatticeMapper<kaldi::BaseFloat> >(*model_->std_lm_fst_, mapper, mapfst_opts);
} else {
lm_fst_ = NULL;
}
}
void KaldiRecognizer::CleanUp()
{
delete silence_weighting_;
silence_weighting_ = new kaldi::OnlineSilenceWeighting(*model_->trans_model_, model_->feature_info_.silence_weighting_config, 3);
frame_offset_ += decoder_->NumFramesDecoded();
decoder_->InitDecoding(frame_offset_);
}
void KaldiRecognizer::UpdateSilenceWeights()
{
if (silence_weighting_->Active() && feature_pipeline_->NumFramesReady() > 0 &&
feature_pipeline_->IvectorFeature() != NULL) {
vector<pair<int32, BaseFloat> > delta_weights;
silence_weighting_->ComputeCurrentTraceback(decoder_->Decoder());
silence_weighting_->GetDeltaWeights(feature_pipeline_->NumFramesReady(),
frame_offset_ * 3,
&delta_weights);
feature_pipeline_->UpdateFrameWeights(delta_weights);
}
}
bool KaldiRecognizer::AcceptWaveform(const char *data, int len)
{
Vector<BaseFloat> wave;
wave.Resize(len / 2, kUndefined);
for (int i = 0; i < len / 2; i++)
wave(i) = *(((short *)data) + i);
return AcceptWaveform(wave);
}
bool KaldiRecognizer::AcceptWaveform(const short *sdata, int len)
{
Vector<BaseFloat> wave;
wave.Resize(len, kUndefined);
for (int i = 0; i < len; i++)
wave(i) = sdata[i];
return AcceptWaveform(wave);
}
bool KaldiRecognizer::AcceptWaveform(const float *fdata, int len)
{
Vector<BaseFloat> wave;
wave.Resize(len, kUndefined);
for (int i = 0; i < len; i++)
wave(i) = fdata[i];
return AcceptWaveform(wave);
}
bool KaldiRecognizer::AcceptWaveform(Vector<BaseFloat> &wdata)
{
if (input_finalized_) {
CleanUp();
input_finalized_ = false;
}
feature_pipeline_->AcceptWaveform(sample_frequency_, wdata);
UpdateSilenceWeights();
decoder_->AdvanceDecoding();
if (spk_feature_) {
spk_feature_->AcceptWaveform(sample_frequency_, wdata);
}
if (decoder_->EndpointDetected(model_->endpoint_config_)) {
return true;
}
return false;
}
// Computes an xvector from a chunk of speech features.
static void RunNnetComputation(const MatrixBase<BaseFloat> &features,
const nnet3::Nnet &nnet, nnet3::CachingOptimizingCompiler *compiler,
Vector<BaseFloat> *xvector)
{
nnet3::ComputationRequest request;
request.need_model_derivative = false;
request.store_component_stats = false;
request.inputs.push_back(
nnet3::IoSpecification("input", 0, features.NumRows()));
nnet3::IoSpecification output_spec;
output_spec.name = "output";
output_spec.has_deriv = false;
output_spec.indexes.resize(1);
request.outputs.resize(1);
request.outputs[0].Swap(&output_spec);
shared_ptr<const nnet3::NnetComputation> computation = compiler->Compile(request);
nnet3::Nnet *nnet_to_update = NULL; // we're not doing any update.
nnet3::NnetComputer computer(nnet3::NnetComputeOptions(), *computation,
nnet, nnet_to_update);
CuMatrix<BaseFloat> input_feats_cu(features);
computer.AcceptInput("input", &input_feats_cu);
computer.Run();
CuMatrix<BaseFloat> cu_output;
computer.GetOutputDestructive("output", &cu_output);
xvector->Resize(cu_output.NumCols());
xvector->CopyFromVec(cu_output.Row(0));
}
void KaldiRecognizer::GetSpkVector(Vector<BaseFloat> &xvector)
{
int num_frames = spk_feature_->NumFramesReady() - frame_offset_ * 3;
Matrix<BaseFloat> mfcc(num_frames, spk_feature_->Dim());
for (int i = 0; i < num_frames; ++i) {
Vector<BaseFloat> feat(spk_feature_->Dim());
spk_feature_->GetFrame(i + frame_offset_ * 3, &feat);
mfcc.CopyRowFromVec(feat, i);
}
SlidingWindowCmnOptions cmvn_opts;
Matrix<BaseFloat> features(mfcc.NumRows(), mfcc.NumCols(), kUndefined);
SlidingWindowCmn(cmvn_opts, mfcc, &features);
nnet3::NnetSimpleComputationOptions opts;
nnet3::CachingOptimizingCompilerOptions compiler_config;
nnet3::CachingOptimizingCompiler compiler(spk_model_->speaker_nnet, opts.optimize_config, compiler_config);
RunNnetComputation(features, spk_model_->speaker_nnet, &compiler, &xvector);
}
const char* KaldiRecognizer::Result()
{
if (!input_finalized_) {
decoder_->FinalizeDecoding();
input_finalized_ = true;
}
if (decoder_->NumFramesDecoded() == 0) {
last_result_ = "{\"text\": \"\"}";
return last_result_.c_str();
}
kaldi::CompactLattice clat;
decoder_->GetLattice(true, &clat);
if (model_->std_lm_fst_) {
Lattice lat1;
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);
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>());
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, &clat);
}
fst::ScaleLattice(fst::LatticeScale(9.0, 10.0), &clat);
CompactLattice aligned_lat;
if (model_->winfo_) {
WordAlignLattice(clat, *model_->trans_model_, *model_->winfo_, 0, &aligned_lat);
} else {
aligned_lat = clat;
}
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();
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"] = (frame_offset_ + times[i].first) * 0.03;
word["end"] = (frame_offset_ + times[i].second) * 0.03;
word["conf"] = conf[i];
obj["result"].append(word);
if (i) {
text << " ";
}
text << model_->word_syms_->Find(words[i]);
}
obj["text"] = text.str();
if (spk_model_) {
Vector<BaseFloat> xvector;
GetSpkVector(xvector);
for (int i = 0; i < xvector.Dim(); i++) {
obj["spk"].append(xvector(i));
}
}
last_result_ = obj.dump();
return last_result_.c_str();
}
const char* KaldiRecognizer::PartialResult()
{
json::JSON res;
if (decoder_->NumFramesDecoded() == 0) {
res["partial"] = "";
last_result_ = res.dump();
return last_result_.c_str();
}
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 << " ";
}
text << model_->word_syms_->Find(words[i]);
}
res["partial"] = text.str();
last_result_ = res.dump();
return last_result_.c_str();
}
const char* KaldiRecognizer::FinalResult()
{
if (!input_finalized_) {
feature_pipeline_->InputFinished();
UpdateSilenceWeights();
decoder_->AdvanceDecoding();
decoder_->FinalizeDecoding();
input_finalized_ = true;
return Result();
} else {
last_result_ = "{\"text\": \"\"}";
return last_result_.c_str();
}
}
-68
View File
@@ -1,68 +0,0 @@
// Copyright 2019 Alpha Cephei Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#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 "nnet3/am-nnet-simple.h"
#include "nnet3/nnet-am-decodable-simple.h"
#include "nnet3/nnet-utils.h"
#include "model.h"
#include "spk_model.h"
using namespace kaldi;
class KaldiRecognizer {
public:
KaldiRecognizer(Model *model, float sample_frequency);
KaldiRecognizer(Model *model, SpkModel *spk_model, float sample_frequency);
KaldiRecognizer(Model *model, float sample_frequency, char const *grammar);
~KaldiRecognizer();
bool AcceptWaveform(const char *data, int len);
bool AcceptWaveform(const short *sdata, int len);
bool AcceptWaveform(const float *fdata, int len);
const char* Result();
const char* FinalResult();
const char* PartialResult();
private:
void InitRescoring();
void CleanUp();
void UpdateSilenceWeights();
bool AcceptWaveform(Vector<BaseFloat> &wdata);
void GetSpkVector(Vector<BaseFloat> &xvector);
Model *model_;
SingleUtteranceNnet3Decoder *decoder_;
fst::LookaheadFst<fst::StdArc, int32> *decode_fst_;
fst::StdVectorFst *g_fst_; // dynamically constructed grammar
OnlineNnet2FeaturePipeline *feature_pipeline_;
OnlineSilenceWeighting *silence_weighting_;
SpkModel *spk_model_;
OnlineBaseFeature *spk_feature_;
fst::MapFst<fst::StdArc, kaldi::LatticeArc, fst::StdToLatticeMapper<kaldi::BaseFloat> > *lm_fst_;
float sample_frequency_;
int32 frame_offset_;
bool input_finalized_;
string last_result_;
};
+122
View File
@@ -0,0 +1,122 @@
// log.cc
//
// Copyright (c) 2023 Xiaomi Corporation
#include "log.h"
#ifdef SHERPA_ONNX_HAVE_EXECINFO_H
#include <execinfo.h> // To get stack trace in error messages.
#ifdef SHERPA_ONNX_HAVE_CXXABI_H
#include <cxxabi.h> // For name demangling.
// Useful to decode the stack trace, but only used if we have execinfo.h
#endif // SHERPA_ONNX_HAVE_CXXABI_H
#endif // SHERPA_ONNX_HAVE_EXECINFO_H
#include <stdlib.h>
#include <ctime>
#include <iomanip>
#include <string>
namespace sherpa_onnx {
std::string GetDateTimeStr() {
std::ostringstream os;
std::time_t t = std::time(nullptr);
std::tm tm = *std::localtime(&t);
os << std::put_time(&tm, "%F %T"); // yyyy-mm-dd hh:mm:ss
return os.str();
}
static bool LocateSymbolRange(const std::string &trace_name, std::size_t *begin,
std::size_t *end) {
// Find the first '_' with leading ' ' or '('.
*begin = std::string::npos;
for (std::size_t i = 1; i < trace_name.size(); ++i) {
if (trace_name[i] != '_') {
continue;
}
if (trace_name[i - 1] == ' ' || trace_name[i - 1] == '(') {
*begin = i;
break;
}
}
if (*begin == std::string::npos) {
return false;
}
*end = trace_name.find_first_of(" +", *begin);
return *end != std::string::npos;
}
#ifdef SHERPA_ONNX_HAVE_EXECINFO_H
static std::string Demangle(const std::string &trace_name) {
#ifndef SHERPA_ONNX_HAVE_CXXABI_H
return trace_name;
#else // SHERPA_ONNX_HAVE_CXXABI_H
// Try demangle the symbol. We are trying to support the following formats
// produced by different platforms:
//
// Linux:
// ./kaldi-error-test(_ZN5kaldi13UnitTestErrorEv+0xb) [0x804965d]
//
// Mac:
// 0 server 0x000000010f67614d _ZNK5kaldi13MessageLogger10LogMessageEv + 813
//
// We want to extract the name e.g., '_ZN5kaldi13UnitTestErrorEv' and
// demangle it info a readable name like kaldi::UnitTextError.
std::size_t begin, end;
if (!LocateSymbolRange(trace_name, &begin, &end)) {
return trace_name;
}
std::string symbol = trace_name.substr(begin, end - begin);
int status;
char *demangled_name = abi::__cxa_demangle(symbol.c_str(), 0, 0, &status);
if (status == 0 && demangled_name != nullptr) {
symbol = demangled_name;
free(demangled_name);
}
return trace_name.substr(0, begin) + symbol +
trace_name.substr(end, std::string::npos);
#endif // SHERPA_ONNX_HAVE_CXXABI_H
}
#endif // SHERPA_ONNX_HAVE_EXECINFO_H
std::string GetStackTrace() {
std::string ans;
#ifdef SHERPA_ONNX_HAVE_EXECINFO_H
constexpr const std::size_t kMaxTraceSize = 50;
constexpr const std::size_t kMaxTracePrint = 50; // Must be even.
// Buffer for the trace.
void *trace[kMaxTraceSize];
// Get the trace.
std::size_t size = backtrace(trace, kMaxTraceSize);
// Get the trace symbols.
char **trace_symbol = backtrace_symbols(trace, size);
if (trace_symbol == nullptr) return ans;
// Compose a human-readable backtrace string.
ans += "[ Stack-Trace: ]\n";
if (size <= kMaxTracePrint) {
for (std::size_t i = 0; i < size; ++i) {
ans += Demangle(trace_symbol[i]) + "\n";
}
} else { // Print out first+last (e.g.) 5.
for (std::size_t i = 0; i < kMaxTracePrint / 2; ++i) {
ans += Demangle(trace_symbol[i]) + "\n";
}
ans += ".\n.\n.\n";
for (std::size_t i = size - kMaxTracePrint / 2; i < size; ++i) {
ans += Demangle(trace_symbol[i]) + "\n";
}
if (size == kMaxTraceSize)
ans += ".\n.\n.\n"; // Stack was too long, probably a bug.
}
// We must free the array of pointers allocated by backtrace_symbols(),
// but not the strings themselves.
free(trace_symbol);
#endif // SHERPA_ONNX_HAVE_EXECINFO_H
return ans;
}
} // namespace sherpa_onnx
+378
View File
@@ -0,0 +1,378 @@
// log.h
//
// Copyright (c) 2023 Xiaomi Corporation
#ifndef SHERPA_ONNX_CSRC_LOG_H_
#define SHERPA_ONNX_CSRC_LOG_H_
#include <stdio.h>
#include <mutex> // NOLINT
#include <sstream>
#include <string>
namespace sherpa_onnx {
#if SHERPA_ONNX_ENABLE_CHECK
#if defined(NDEBUG)
constexpr bool kDisableDebug = true;
#else
constexpr bool kDisableDebug = false;
#endif
enum class LogLevel {
kTrace = 0,
kDebug = 1,
kInfo = 2,
kWarning = 3,
kError = 4,
kFatal = 5, // print message and abort the program
};
// They are used in SHERPA_ONNX_LOG(xxx), so their names
// do not follow the google c++ code style
//
// You can use them in the following way:
//
// SHERPA_ONNX_LOG(TRACE) << "some message";
// SHERPA_ONNX_LOG(DEBUG) << "some message";
#ifndef _MSC_VER
constexpr LogLevel TRACE = LogLevel::kTrace;
constexpr LogLevel DEBUG = LogLevel::kDebug;
constexpr LogLevel INFO = LogLevel::kInfo;
constexpr LogLevel WARNING = LogLevel::kWarning;
constexpr LogLevel ERROR = LogLevel::kError;
constexpr LogLevel FATAL = LogLevel::kFatal;
#else
#define TRACE LogLevel::kTrace
#define DEBUG LogLevel::kDebug
#define INFO LogLevel::kInfo
#define WARNING LogLevel::kWarning
#define ERROR LogLevel::kError
#define FATAL LogLevel::kFatal
#endif
std::string GetStackTrace();
/* Return the current log level.
If the current log level is TRACE, then all logged messages are printed out.
If the current log level is DEBUG, log messages with "TRACE" level are not
shown and all other levels are printed out.
Similarly, if the current log level is INFO, log message with "TRACE" and
"DEBUG" are not shown and all other levels are printed out.
If it is FATAL, then only FATAL messages are shown.
*/
inline LogLevel GetCurrentLogLevel() {
static LogLevel log_level = INFO;
static std::once_flag init_flag;
std::call_once(init_flag, []() {
const char *env_log_level = std::getenv("SHERPA_ONNX_LOG_LEVEL");
if (env_log_level == nullptr) return;
std::string s = env_log_level;
if (s == "TRACE")
log_level = TRACE;
else if (s == "DEBUG")
log_level = DEBUG;
else if (s == "INFO")
log_level = INFO;
else if (s == "WARNING")
log_level = WARNING;
else if (s == "ERROR")
log_level = ERROR;
else if (s == "FATAL")
log_level = FATAL;
else
fprintf(stderr,
"Unknown SHERPA_ONNX_LOG_LEVEL: %s"
"\nSupported values are: "
"TRACE, DEBUG, INFO, WARNING, ERROR, FATAL",
s.c_str());
});
return log_level;
}
inline bool EnableAbort() {
static std::once_flag init_flag;
static bool enable_abort = false;
std::call_once(init_flag, []() {
enable_abort = (std::getenv("SHERPA_ONNX_ABORT") != nullptr);
});
return enable_abort;
}
class Logger {
public:
Logger(const char *filename, const char *func_name, uint32_t line_num,
LogLevel level)
: filename_(filename),
func_name_(func_name),
line_num_(line_num),
level_(level) {
cur_level_ = GetCurrentLogLevel();
switch (level) {
case TRACE:
if (cur_level_ <= TRACE) fprintf(stderr, "[T] ");
break;
case DEBUG:
if (cur_level_ <= DEBUG) fprintf(stderr, "[D] ");
break;
case INFO:
if (cur_level_ <= INFO) fprintf(stderr, "[I] ");
break;
case WARNING:
if (cur_level_ <= WARNING) fprintf(stderr, "[W] ");
break;
case ERROR:
if (cur_level_ <= ERROR) fprintf(stderr, "[E] ");
break;
case FATAL:
if (cur_level_ <= FATAL) fprintf(stderr, "[F] ");
break;
}
if (cur_level_ <= level_) {
fprintf(stderr, "%s:%u:%s ", filename, line_num, func_name);
}
}
~Logger() noexcept(false) {
static constexpr const char *kErrMsg = R"(
Some bad things happened. Please read the above error messages and stack
trace. If you are using Python, the following command may be helpful:
gdb --args python /path/to/your/code.py
(You can use `gdb` to debug the code. Please consider compiling
a debug version of sherpa_onnx.).
If you are unable to fix it, please open an issue at:
https://github.com/csukuangfj/kaldi-native-fbank/issues/new
)";
if (level_ == FATAL) {
fprintf(stderr, "\n");
std::string stack_trace = GetStackTrace();
if (!stack_trace.empty()) {
fprintf(stderr, "\n\n%s\n", stack_trace.c_str());
}
fflush(nullptr);
#ifndef __ANDROID_API__
if (EnableAbort()) {
// NOTE: abort() will terminate the program immediately without
// printing the Python stack backtrace.
abort();
}
throw std::runtime_error(kErrMsg);
#else
abort();
#endif
}
}
const Logger &operator<<(bool b) const {
if (cur_level_ <= level_) {
fprintf(stderr, b ? "true" : "false");
}
return *this;
}
const Logger &operator<<(int8_t i) const {
if (cur_level_ <= level_) fprintf(stderr, "%d", i);
return *this;
}
const Logger &operator<<(const char *s) const {
if (cur_level_ <= level_) fprintf(stderr, "%s", s);
return *this;
}
const Logger &operator<<(int32_t i) const {
if (cur_level_ <= level_) fprintf(stderr, "%d", i);
return *this;
}
const Logger &operator<<(uint32_t i) const {
if (cur_level_ <= level_) fprintf(stderr, "%u", i);
return *this;
}
const Logger &operator<<(uint64_t i) const {
if (cur_level_ <= level_)
fprintf(stderr, "%llu", (long long unsigned int)i); // NOLINT
return *this;
}
const Logger &operator<<(int64_t i) const {
if (cur_level_ <= level_)
fprintf(stderr, "%lli", (long long int)i); // NOLINT
return *this;
}
const Logger &operator<<(float f) const {
if (cur_level_ <= level_) fprintf(stderr, "%f", f);
return *this;
}
const Logger &operator<<(double d) const {
if (cur_level_ <= level_) fprintf(stderr, "%f", d);
return *this;
}
template <typename T>
const Logger &operator<<(const T &t) const {
// require T overloads operator<<
std::ostringstream os;
os << t;
return *this << os.str().c_str();
}
// specialization to fix compile error: `stringstream << nullptr` is ambiguous
const Logger &operator<<(const std::nullptr_t &null) const {
if (cur_level_ <= level_) *this << "(null)";
return *this;
}
private:
const char *filename_;
const char *func_name_;
uint32_t line_num_;
LogLevel level_;
LogLevel cur_level_;
};
#endif // SHERPA_ONNX_ENABLE_CHECK
class Voidifier {
public:
#if SHERPA_ONNX_ENABLE_CHECK
void operator&(const Logger &) const {}
#endif
};
#if !defined(SHERPA_ONNX_ENABLE_CHECK)
template <typename T>
const Voidifier &operator<<(const Voidifier &v, T &&) {
return v;
}
#endif
} // namespace sherpa_onnx
#define SHERPA_ONNX_STATIC_ASSERT(x) static_assert(x, "")
#ifdef SHERPA_ONNX_ENABLE_CHECK
#if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__) || \
defined(__PRETTY_FUNCTION__)
// for clang and GCC
#define SHERPA_ONNX_FUNC __PRETTY_FUNCTION__
#else
// for other compilers
#define SHERPA_ONNX_FUNC __func__
#endif
#define SHERPA_ONNX_CHECK(x) \
(x) ? (void)0 \
: ::sherpa_onnx::Voidifier() & \
::sherpa_onnx::Logger(__FILE__, SHERPA_ONNX_FUNC, __LINE__, \
::sherpa_onnx::FATAL) \
<< "Check failed: " << #x << " "
// WARNING: x and y may be evaluated multiple times, but this happens only
// when the check fails. Since the program aborts if it fails, we don't think
// the extra evaluation of x and y matters.
//
// CAUTION: we recommend the following use case:
//
// auto x = Foo();
// auto y = Bar();
// SHERPA_ONNX_CHECK_EQ(x, y) << "Some message";
//
// And please avoid
//
// SHERPA_ONNX_CHECK_EQ(Foo(), Bar());
//
// if `Foo()` or `Bar()` causes some side effects, e.g., changing some
// local static variables or global variables.
#define _SHERPA_ONNX_CHECK_OP(x, y, op) \
((x)op(y)) ? (void)0 \
: ::sherpa_onnx::Voidifier() & \
::sherpa_onnx::Logger(__FILE__, SHERPA_ONNX_FUNC, __LINE__, \
::sherpa_onnx::FATAL) \
<< "Check failed: " << #x << " " << #op << " " << #y \
<< " (" << (x) << " vs. " << (y) << ") "
#define SHERPA_ONNX_CHECK_EQ(x, y) _SHERPA_ONNX_CHECK_OP(x, y, ==)
#define SHERPA_ONNX_CHECK_NE(x, y) _SHERPA_ONNX_CHECK_OP(x, y, !=)
#define SHERPA_ONNX_CHECK_LT(x, y) _SHERPA_ONNX_CHECK_OP(x, y, <)
#define SHERPA_ONNX_CHECK_LE(x, y) _SHERPA_ONNX_CHECK_OP(x, y, <=)
#define SHERPA_ONNX_CHECK_GT(x, y) _SHERPA_ONNX_CHECK_OP(x, y, >)
#define SHERPA_ONNX_CHECK_GE(x, y) _SHERPA_ONNX_CHECK_OP(x, y, >=)
#define SHERPA_ONNX_LOG(x) \
::sherpa_onnx::Logger(__FILE__, SHERPA_ONNX_FUNC, __LINE__, ::sherpa_onnx::x)
// ------------------------------------------------------------
// For debug check
// ------------------------------------------------------------
// If you define the macro "-D NDEBUG" while compiling kaldi-native-fbank,
// the following macros are in fact empty and does nothing.
#define SHERPA_ONNX_DCHECK(x) \
::sherpa_onnx::kDisableDebug ? (void)0 : SHERPA_ONNX_CHECK(x)
#define SHERPA_ONNX_DCHECK_EQ(x, y) \
::sherpa_onnx::kDisableDebug ? (void)0 : SHERPA_ONNX_CHECK_EQ(x, y)
#define SHERPA_ONNX_DCHECK_NE(x, y) \
::sherpa_onnx::kDisableDebug ? (void)0 : SHERPA_ONNX_CHECK_NE(x, y)
#define SHERPA_ONNX_DCHECK_LT(x, y) \
::sherpa_onnx::kDisableDebug ? (void)0 : SHERPA_ONNX_CHECK_LT(x, y)
#define SHERPA_ONNX_DCHECK_LE(x, y) \
::sherpa_onnx::kDisableDebug ? (void)0 : SHERPA_ONNX_CHECK_LE(x, y)
#define SHERPA_ONNX_DCHECK_GT(x, y) \
::sherpa_onnx::kDisableDebug ? (void)0 : SHERPA_ONNX_CHECK_GT(x, y)
#define SHERPA_ONNX_DCHECK_GE(x, y) \
::sherpa_onnx::kDisableDebug ? (void)0 : SHERPA_ONNX_CHECK_GE(x, y)
#define SHERPA_ONNX_DLOG(x) \
::sherpa_onnx::kDisableDebug \
? (void)0 \
: ::sherpa_onnx::Voidifier() & SHERPA_ONNX_LOG(x)
#else
#define SHERPA_ONNX_CHECK(x) ::sherpa_onnx::Voidifier()
#define SHERPA_ONNX_LOG(x) ::sherpa_onnx::Voidifier()
#define SHERPA_ONNX_CHECK_EQ(x, y) ::sherpa_onnx::Voidifier()
#define SHERPA_ONNX_CHECK_NE(x, y) ::sherpa_onnx::Voidifier()
#define SHERPA_ONNX_CHECK_LT(x, y) ::sherpa_onnx::Voidifier()
#define SHERPA_ONNX_CHECK_LE(x, y) ::sherpa_onnx::Voidifier()
#define SHERPA_ONNX_CHECK_GT(x, y) ::sherpa_onnx::Voidifier()
#define SHERPA_ONNX_CHECK_GE(x, y) ::sherpa_onnx::Voidifier()
#define SHERPA_ONNX_DCHECK(x) ::sherpa_onnx::Voidifier()
#define SHERPA_ONNX_DLOG(x) ::sherpa_onnx::Voidifier()
#define SHERPA_ONNX_DCHECK_EQ(x, y) ::sherpa_onnx::Voidifier()
#define SHERPA_ONNX_DCHECK_NE(x, y) ::sherpa_onnx::Voidifier()
#define SHERPA_ONNX_DCHECK_LT(x, y) ::sherpa_onnx::Voidifier()
#define SHERPA_ONNX_DCHECK_LE(x, y) ::sherpa_onnx::Voidifier()
#define SHERPA_ONNX_DCHECK_GT(x, y) ::sherpa_onnx::Voidifier()
#define SHERPA_ONNX_DCHECK_GE(x, y) ::sherpa_onnx::Voidifier()
#endif // SHERPA_ONNX_CHECK_NE
#endif // SHERPA_ONNX_CSRC_LOG_H_
+115
View File
@@ -0,0 +1,115 @@
// macros.h
//
// Copyright 2023 Xiaomi Corporation
#ifndef SHERPA_ONNX_CSRC_MACROS_H_
#define SHERPA_ONNX_CSRC_MACROS_H_
#include <stdio.h>
#if __ANDROID_API__ >= 8
#include "android/log.h"
#define SHERPA_ONNX_LOGE(...) \
do { \
fprintf(stderr, "%s:%s:%d ", __FILE__, __func__, \
static_cast<int>(__LINE__)); \
fprintf(stderr, ##__VA_ARGS__); \
fprintf(stderr, "\n"); \
__android_log_print(ANDROID_LOG_WARN, "sherpa-onnx", ##__VA_ARGS__); \
} while (0)
#else
#define SHERPA_ONNX_LOGE(...) \
do { \
fprintf(stderr, "%s:%s:%d ", __FILE__, __func__, \
static_cast<int>(__LINE__)); \
fprintf(stderr, ##__VA_ARGS__); \
fprintf(stderr, "\n"); \
} while (0)
#endif
// Read an integer
#define SHERPA_ONNX_READ_META_DATA(dst, src_key) \
do { \
auto value = \
meta_data.LookupCustomMetadataMapAllocated(src_key, allocator); \
if (!value) { \
SHERPA_ONNX_LOGE("%s does not exist in the metadata", src_key); \
exit(-1); \
} \
\
dst = atoi(value.get()); \
if (dst < 0) { \
SHERPA_ONNX_LOGE("Invalid value %d for %s", dst, src_key); \
exit(-1); \
} \
} while (0)
// read a vector of integers
#define SHERPA_ONNX_READ_META_DATA_VEC(dst, src_key) \
do { \
auto value = \
meta_data.LookupCustomMetadataMapAllocated(src_key, allocator); \
if (!value) { \
SHERPA_ONNX_LOGE("%s does not exist in the metadata", src_key); \
exit(-1); \
} \
\
bool ret = SplitStringToIntegers(value.get(), ",", true, &dst); \
if (!ret) { \
SHERPA_ONNX_LOGE("Invalid value %s for %s", value.get(), src_key); \
exit(-1); \
} \
} while (0)
// read a vector of floats
#define SHERPA_ONNX_READ_META_DATA_VEC_FLOAT(dst, src_key) \
do { \
auto value = \
meta_data.LookupCustomMetadataMapAllocated(src_key, allocator); \
if (!value) { \
SHERPA_ONNX_LOGE("%s does not exist in the metadata", src_key); \
exit(-1); \
} \
\
bool ret = SplitStringToFloats(value.get(), ",", true, &dst); \
if (!ret) { \
SHERPA_ONNX_LOGE("Invalid value %s for %s", value.get(), src_key); \
exit(-1); \
} \
} while (0)
// read a vector of strings
#define SHERPA_ONNX_READ_META_DATA_VEC_STRING(dst, src_key) \
do { \
auto value = \
meta_data.LookupCustomMetadataMapAllocated(src_key, allocator); \
if (!value) { \
SHERPA_ONNX_LOGE("%s does not exist in the metadata", src_key); \
exit(-1); \
} \
SplitStringToVector(value.get(), ",", false, &dst); \
\
if (dst.empty()) { \
SHERPA_ONNX_LOGE("Invalid value %s for %s. Empty vector!", value.get(), \
src_key); \
exit(-1); \
} \
} while (0)
// Read a string
#define SHERPA_ONNX_READ_META_DATA_STR(dst, src_key) \
do { \
auto value = \
meta_data.LookupCustomMetadataMapAllocated(src_key, allocator); \
if (!value) { \
SHERPA_ONNX_LOGE("%s does not exist in the metadata", src_key); \
exit(-1); \
} \
\
dst = value.get(); \
if (dst.empty()) { \
SHERPA_ONNX_LOGE("Invalid value for %s\n", src_key); \
exit(-1); \
} \
} while (0)
#endif // SHERPA_ONNX_CSRC_MACROS_H_
+115
View File
@@ -0,0 +1,115 @@
/**
* Copyright (c) 2022 Xiaomi Corporation (authors: Daniel Povey)
* Copyright (c) 2023 (Pingfeng Luo)
*
*/
// This file is copied from k2/csrc/utils.h
#ifndef SHERPA_ONNX_CSRC_MATH_H_
#define SHERPA_ONNX_CSRC_MATH_H_
#include <algorithm>
#include <cassert>
#include <cmath>
#include <numeric>
#include <vector>
namespace sherpa_onnx {
// logf(FLT_EPSILON)
#define SHERPA_ONNX_MIN_LOG_DIFF_FLOAT -15.9423847198486328125f
// log(DBL_EPSILON)
#define SHERPA_ONNX_MIN_LOG_DIFF_DOUBLE \
-36.0436533891171535515240975655615329742431640625
template <typename T>
struct LogAdd;
template <>
struct LogAdd<double> {
double operator()(double x, double y) const {
double diff;
if (x < y) {
diff = x - y;
x = y;
} else {
diff = y - x;
}
// diff is negative. x is now the larger one.
if (diff >= SHERPA_ONNX_MIN_LOG_DIFF_DOUBLE) {
double res;
res = x + log1p(exp(diff));
return res;
}
return x; // return the larger one.
}
};
template <>
struct LogAdd<float> {
float operator()(float x, float y) const {
float diff;
if (x < y) {
diff = x - y;
x = y;
} else {
diff = y - x;
}
// diff is negative. x is now the larger one.
if (diff >= SHERPA_ONNX_MIN_LOG_DIFF_DOUBLE) {
float res;
res = x + log1pf(expf(diff));
return res;
}
return x; // return the larger one.
}
};
template <class T>
void LogSoftmax(T *input, int32_t input_len) {
assert(input);
T m = *std::max_element(input, input + input_len);
T sum = 0.0;
for (int32_t i = 0; i < input_len; i++) {
sum += exp(input[i] - m);
}
T offset = m + log(sum);
for (int32_t i = 0; i < input_len; i++) {
input[i] -= offset;
}
}
template <typename T>
void LogSoftmax(T *in, int32_t w, int32_t h) {
for (int32_t i = 0; i != h; ++i) {
LogSoftmax(in, w);
in += w;
}
}
template <class T>
std::vector<int32_t> TopkIndex(const T *vec, int32_t size, int32_t topk) {
std::vector<int32_t> vec_index(size);
std::iota(vec_index.begin(), vec_index.end(), 0);
std::partial_sort(vec_index.begin(), vec_index.begin() + topk,
vec_index.end(), [vec](int32_t index_1, int32_t index_2) {
return vec[index_1] > vec[index_2];
});
int32_t k_num = std::min<int32_t>(size, topk);
std::vector<int32_t> index(vec_index.begin(), vec_index.begin() + k_num);
return index;
}
} // namespace sherpa_onnx
#endif // SHERPA_ONNX_CSRC_MATH_H_
+282
View File
@@ -0,0 +1,282 @@
/**
* Copyright (c) 2022 Xiaomi Corporation (authors: Fangjun Kuang)
*
* See LICENSE for clarification regarding multiple authors
*
* 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.
*/
// This file is copied/modified from kaldi/src/feat/mel-computations.cc
#include "mel-computations.h"
#include <algorithm>
#include <sstream>
#include <vector>
#include "feature-window.h"
namespace knf {
std::ostream &operator<<(std::ostream &os, const MelBanksOptions &opts) {
os << opts.ToString();
return os;
}
float MelBanks::VtlnWarpFreq(
float vtln_low_cutoff, // upper+lower frequency cutoffs for VTLN.
float vtln_high_cutoff,
float low_freq, // upper+lower frequency cutoffs in mel computation
float high_freq, float vtln_warp_factor, float freq) {
/// This computes a VTLN warping function that is not the same as HTK's one,
/// but has similar inputs (this function has the advantage of never producing
/// empty bins).
/// This function computes a warp function F(freq), defined between low_freq
/// and high_freq inclusive, with the following properties:
/// F(low_freq) == low_freq
/// F(high_freq) == high_freq
/// The function is continuous and piecewise linear with two inflection
/// points.
/// The lower inflection point (measured in terms of the unwarped
/// frequency) is at frequency l, determined as described below.
/// The higher inflection point is at a frequency h, determined as
/// described below.
/// If l <= f <= h, then F(f) = f/vtln_warp_factor.
/// If the higher inflection point (measured in terms of the unwarped
/// frequency) is at h, then max(h, F(h)) == vtln_high_cutoff.
/// Since (by the last point) F(h) == h/vtln_warp_factor, then
/// max(h, h/vtln_warp_factor) == vtln_high_cutoff, so
/// h = vtln_high_cutoff / max(1, 1/vtln_warp_factor).
/// = vtln_high_cutoff * min(1, vtln_warp_factor).
/// If the lower inflection point (measured in terms of the unwarped
/// frequency) is at l, then min(l, F(l)) == vtln_low_cutoff
/// This implies that l = vtln_low_cutoff / min(1, 1/vtln_warp_factor)
/// = vtln_low_cutoff * max(1, vtln_warp_factor)
if (freq < low_freq || freq > high_freq)
return freq; // in case this gets called
// for out-of-range frequencies, just return the freq.
SHERPA_ONNX_CHECK_GT(vtln_low_cutoff, low_freq);
SHERPA_ONNX_CHECK_LT(vtln_high_cutoff, high_freq);
float one = 1.0f;
float l = vtln_low_cutoff * std::max(one, vtln_warp_factor);
float h = vtln_high_cutoff * std::min(one, vtln_warp_factor);
float scale = 1.0f / vtln_warp_factor;
float Fl = scale * l; // F(l);
float Fh = scale * h; // F(h);
SHERPA_ONNX_CHECK(l > low_freq && h < high_freq);
// slope of left part of the 3-piece linear function
float scale_left = (Fl - low_freq) / (l - low_freq);
// [slope of center part is just "scale"]
// slope of right part of the 3-piece linear function
float scale_right = (high_freq - Fh) / (high_freq - h);
if (freq < l) {
return low_freq + scale_left * (freq - low_freq);
} else if (freq < h) {
return scale * freq;
} else { // freq >= h
return high_freq + scale_right * (freq - high_freq);
}
}
float MelBanks::VtlnWarpMelFreq(
float vtln_low_cutoff, // upper+lower frequency cutoffs for VTLN.
float vtln_high_cutoff,
float low_freq, // upper+lower frequency cutoffs in mel computation
float high_freq, float vtln_warp_factor, float mel_freq) {
return MelScale(VtlnWarpFreq(vtln_low_cutoff, vtln_high_cutoff, low_freq,
high_freq, vtln_warp_factor,
InverseMelScale(mel_freq)));
}
MelBanks::MelBanks(const MelBanksOptions &opts,
const FrameExtractionOptions &frame_opts,
float vtln_warp_factor)
: htk_mode_(opts.htk_mode) {
int32_t num_bins = opts.num_bins;
if (num_bins < 3) SHERPA_ONNX_LOG(FATAL) << "Must have at least 3 mel bins";
float sample_freq = frame_opts.samp_freq;
int32_t window_length_padded = frame_opts.PaddedWindowSize();
SHERPA_ONNX_CHECK_EQ(window_length_padded % 2, 0);
int32_t num_fft_bins = window_length_padded / 2;
float nyquist = 0.5f * sample_freq;
float low_freq = opts.low_freq, high_freq;
if (opts.high_freq > 0.0f)
high_freq = opts.high_freq;
else
high_freq = nyquist + opts.high_freq;
if (low_freq < 0.0f || low_freq >= nyquist || high_freq <= 0.0f ||
high_freq > nyquist || high_freq <= low_freq) {
SHERPA_ONNX_LOG(FATAL) << "Bad values in options: low-freq " << low_freq
<< " and high-freq " << high_freq << " vs. nyquist "
<< nyquist;
}
float fft_bin_width = sample_freq / window_length_padded;
// fft-bin width [think of it as Nyquist-freq / half-window-length]
float mel_low_freq = MelScale(low_freq);
float mel_high_freq = MelScale(high_freq);
debug_ = opts.debug_mel;
// divide by num_bins+1 in next line because of end-effects where the bins
// spread out to the sides.
float mel_freq_delta = (mel_high_freq - mel_low_freq) / (num_bins + 1);
float vtln_low = opts.vtln_low, vtln_high = opts.vtln_high;
if (vtln_high < 0.0f) {
vtln_high += nyquist;
}
if (vtln_warp_factor != 1.0f &&
(vtln_low < 0.0f || vtln_low <= low_freq || vtln_low >= high_freq ||
vtln_high <= 0.0f || vtln_high >= high_freq || vtln_high <= vtln_low)) {
SHERPA_ONNX_LOG(FATAL) << "Bad values in options: vtln-low " << vtln_low
<< " and vtln-high " << vtln_high << ", versus "
<< "low-freq " << low_freq << " and high-freq " << high_freq;
}
bins_.resize(num_bins);
for (int32_t bin = 0; bin < num_bins; ++bin) {
float left_mel = mel_low_freq + bin * mel_freq_delta,
center_mel = mel_low_freq + (bin + 1) * mel_freq_delta,
right_mel = mel_low_freq + (bin + 2) * mel_freq_delta;
if (vtln_warp_factor != 1.0f) {
left_mel = VtlnWarpMelFreq(vtln_low, vtln_high, low_freq, high_freq,
vtln_warp_factor, left_mel);
center_mel = VtlnWarpMelFreq(vtln_low, vtln_high, low_freq, high_freq,
vtln_warp_factor, center_mel);
right_mel = VtlnWarpMelFreq(vtln_low, vtln_high, low_freq, high_freq,
vtln_warp_factor, right_mel);
}
// this_bin will be a vector of coefficients that is only
// nonzero where this mel bin is active.
std::vector<float> this_bin(num_fft_bins);
int32_t first_index = -1, last_index = -1;
for (int32_t i = 0; i < num_fft_bins; ++i) {
float freq = (fft_bin_width * i); // Center frequency of this fft
// bin.
float mel = MelScale(freq);
if (mel > left_mel && mel < right_mel) {
float weight;
if (mel <= center_mel)
weight = (mel - left_mel) / (center_mel - left_mel);
else
weight = (right_mel - mel) / (right_mel - center_mel);
this_bin[i] = weight;
if (first_index == -1) first_index = i;
last_index = i;
}
}
SHERPA_ONNX_CHECK(first_index != -1 && last_index >= first_index &&
"You may have set num_mel_bins too large.");
bins_[bin].first = first_index;
int32_t size = last_index + 1 - first_index;
bins_[bin].second.insert(bins_[bin].second.end(),
this_bin.begin() + first_index,
this_bin.begin() + first_index + size);
// Replicate a bug in HTK, for testing purposes.
if (opts.htk_mode && bin == 0 && mel_low_freq != 0.0f) {
bins_[bin].second[0] = 0.0;
}
} // for (int32_t bin = 0; bin < num_bins; ++bin) {
if (debug_) {
std::ostringstream os;
for (size_t i = 0; i < bins_.size(); i++) {
os << "bin " << i << ", offset = " << bins_[i].first << ", vec = ";
for (auto k : bins_[i].second) os << k << ", ";
os << "\n";
}
SHERPA_ONNX_LOG(INFO) << os.str();
}
}
MelBanks::MelBanks(const float *weights, int32_t num_rows, int32_t num_cols)
: debug_(false), htk_mode_(false) {
bins_.resize(num_rows);
for (int32_t bin = 0; bin < num_rows; ++bin) {
const float *this_bin = weights + bin * num_cols;
int32_t first_index = -1, last_index = -1;
for (int32_t i = 0; i < num_cols; ++i) {
if (this_bin[i] == 0) {
continue;
}
if (first_index == -1) first_index = i;
last_index = i;
}
SHERPA_ONNX_CHECK(first_index != -1 && last_index >= first_index &&
"You have an incorrect weight matrix.");
bins_[bin].first = first_index;
int32_t size = last_index + 1 - first_index;
bins_[bin].second.insert(bins_[bin].second.end(), this_bin + first_index,
this_bin + first_index + size);
}
}
// "power_spectrum" contains fft energies.
void MelBanks::Compute(const float *power_spectrum,
float *mel_energies_out) const {
int32_t num_bins = bins_.size();
for (int32_t i = 0; i < num_bins; i++) {
int32_t offset = bins_[i].first;
const auto &v = bins_[i].second;
float energy = 0;
for (int32_t k = 0; k != v.size(); ++k) {
energy += v[k] * power_spectrum[k + offset];
}
// HTK-like flooring- for testing purposes (we prefer dither)
if (htk_mode_ && energy < 1.0) {
energy = 1.0;
}
mel_energies_out[i] = energy;
// The following assert was added due to a problem with OpenBlas that
// we had at one point (it was a bug in that library). Just to detect
// it early.
SHERPA_ONNX_CHECK_EQ(energy, energy); // check that energy is not nan
}
if (debug_) {
fprintf(stderr, "MEL BANKS:\n");
for (int32_t i = 0; i < num_bins; i++)
fprintf(stderr, " %f", mel_energies_out[i]);
fprintf(stderr, "\n");
}
}
} // namespace knf
+119
View File
@@ -0,0 +1,119 @@
/**
* Copyright (c) 2022 Xiaomi Corporation (authors: Fangjun Kuang)
*
* See LICENSE for clarification regarding multiple authors
*
* 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.
*/
// This file is copied/modified from kaldi/src/feat/mel-computations.h
#ifndef KALDI_NATIVE_FBANK_CSRC_MEL_COMPUTATIONS_H_
#define KALDI_NATIVE_FBANK_CSRC_MEL_COMPUTATIONS_H_
#include <cmath>
#include <string>
#include <utility>
#include <vector>
#include "feature-window.h"
namespace knf {
struct MelBanksOptions {
int32_t num_bins = 25; // e.g. 25; number of triangular bins
float low_freq = 20; // e.g. 20; lower frequency cutoff
// an upper frequency cutoff; 0 -> no cutoff, negative
// ->added to the Nyquist frequency to get the cutoff.
float high_freq = 0;
float vtln_low = 100; // vtln lower cutoff of warping function.
// vtln upper cutoff of warping function: if negative, added
// to the Nyquist frequency to get the cutoff.
float vtln_high = -500;
bool debug_mel = false;
// htk_mode is a "hidden" config, it does not show up on command line.
// Enables more exact compatibility with HTK, for testing purposes. Affects
// mel-energy flooring and reproduces a bug in HTK.
bool htk_mode = false;
std::string ToString() const {
std::ostringstream os;
os << "num_bins: " << num_bins << "\n";
os << "low_freq: " << low_freq << "\n";
os << "high_freq: " << high_freq << "\n";
os << "vtln_low: " << vtln_low << "\n";
os << "vtln_high: " << vtln_high << "\n";
os << "debug_mel: " << debug_mel << "\n";
os << "htk_mode: " << htk_mode << "\n";
return os.str();
}
};
std::ostream &operator<<(std::ostream &os, const MelBanksOptions &opts);
class MelBanks {
public:
static inline float InverseMelScale(float mel_freq) {
return 700.0f * (expf(mel_freq / 1127.0f) - 1.0f);
}
static inline float MelScale(float freq) {
return 1127.0f * logf(1.0f + freq / 700.0f);
}
static float VtlnWarpFreq(
float vtln_low_cutoff,
float vtln_high_cutoff, // discontinuities in warp func
float low_freq,
float high_freq, // upper+lower frequency cutoffs in
// the mel computation
float vtln_warp_factor, float freq);
static float VtlnWarpMelFreq(float vtln_low_cutoff, float vtln_high_cutoff,
float low_freq, float high_freq,
float vtln_warp_factor, float mel_freq);
// TODO(fangjun): Remove vtln_warp_factor
MelBanks(const MelBanksOptions &opts,
const FrameExtractionOptions &frame_opts, float vtln_warp_factor);
// Initialize with a 2-d weights matrix
// @param weights Pointer to the start address of the matrix
// @param num_rows It equls to number of mel bins
// @param num_cols It equals to (number of fft bins)/2+1
MelBanks(const float *weights, int32_t num_rows, int32_t num_cols);
/// Compute Mel energies (note: not log energies).
/// At input, "fft_energies" contains the FFT energies (not log).
///
/// @param fft_energies 1-D array of size num_fft_bins/2+1
/// @param mel_energies_out 1-D array of size num_mel_bins
void Compute(const float *fft_energies, float *mel_energies_out) const;
int32_t NumBins() const { return bins_.size(); }
private:
// the "bins_" vector is a vector, one for each bin, of a pair:
// (the first nonzero fft-bin), (the vector of weights).
std::vector<std::pair<int32_t, std::vector<float>>> bins_;
// TODO(fangjun): Remove debug_ and htk_mode_
bool debug_;
bool htk_mode_;
};
} // namespace knf
#endif // KALDI_NATIVE_FBANK_CSRC_MEL_COMPUTATIONS_H_
-323
View File
@@ -1,323 +0,0 @@
// Copyright 2019 Alpha Cephei Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Possible model layout:
//
// * Default kaldi model with HCLG.fst
//
// * Lookahead model with const G.fst
//
// * Lookahead model with ngram G.fst
//
// * File disambig_tid.int required only for lookadhead models
//
// * File word_boundary.int is required if we want to have precise word timing information
// otherwise we don't do any word alignment. Optionally lexicon alignment can be done
// with corresponding C++ code inside kaldi recognizer.
#include "model.h"
#include <sys/stat.h>
#include <fst/fst.h>
#include <fst/register.h>
#include <fst/matcher-fst.h>
#include <fst/extensions/ngram/ngram-fst.h>
namespace fst {
static FstRegisterer<StdOLabelLookAheadFst> OLabelLookAheadFst_StdArc_registerer;
static FstRegisterer<NGramFst<StdArc>> NGramFst_StdArc_registerer;
} // namespace fst
#ifdef __ANDROID__
#include <android/log.h>
static void KaldiLogHandler(const LogMessageEnvelope &env, const char *message)
{
int priority;
if (env.severity > GetVerboseLevel())
return;
if (env.severity > LogMessageEnvelope::kInfo) {
priority = ANDROID_LOG_VERBOSE;
} else {
switch (env.severity) {
case LogMessageEnvelope::kInfo:
priority = ANDROID_LOG_INFO;
break;
case LogMessageEnvelope::kWarning:
priority = ANDROID_LOG_WARN;
break;
case LogMessageEnvelope::kAssertFailed:
priority = ANDROID_LOG_FATAL;
break;
case LogMessageEnvelope::kError:
default: // If not the ERROR, it still an error!
priority = ANDROID_LOG_ERROR;
break;
}
}
std::stringstream full_message;
full_message << env.func << "():" << env.file << ':'
<< env.line << ") " << message;
__android_log_print(priority, "VoskAPI", "%s", full_message.str().c_str());
}
#else
static void KaldiLogHandler(const LogMessageEnvelope &env, const char *message)
{
if (env.severity > GetVerboseLevel())
return;
// Modified default Kaldi logging so we can disable LOG messages.
std::stringstream full_message;
if (env.severity > LogMessageEnvelope::kInfo) {
full_message << "VLOG[" << env.severity << "] (";
} else {
switch (env.severity) {
case LogMessageEnvelope::kInfo:
full_message << "LOG (";
break;
case LogMessageEnvelope::kWarning:
full_message << "WARNING (";
break;
case LogMessageEnvelope::kAssertFailed:
full_message << "ASSERTION_FAILED (";
break;
case LogMessageEnvelope::kError:
default: // If not the ERROR, it still an error!
full_message << "ERROR (";
break;
}
}
// Add other info from the envelope and the message text.
full_message << "VoskAPI" << ':'
<< env.func << "():" << env.file << ':'
<< env.line << ") " << message;
// Print the complete message to stderr.
full_message << "\n";
std::cerr << full_message.str();
}
#endif
Model::Model(const char *model_path) : model_path_str_(model_path) {
SetLogHandler(KaldiLogHandler);
struct stat buffer;
string am_path = model_path_str_ + "/am/final.mdl";
if (stat(am_path.c_str(), &buffer) == 0) {
ConfigureV2();
} else {
ConfigureV1();
}
ReadDataFiles();
ref_cnt_ = 1;
}
// Old model layout without model configuration file
void Model::ConfigureV1()
{
const char *extra_args[] = {
"--min-active=200",
"--max-active=3000",
"--beam=10.0",
"--lattice-beam=2.0",
"--acoustic-scale=1.0",
"--frame-subsampling-factor=3",
"--endpoint.silence-phones=1:2:3:4:5:6:7:8:9:10",
"--endpoint.rule2.min-trailing-silence=0.5",
"--endpoint.rule3.min-trailing-silence=1.0",
"--endpoint.rule4.min-trailing-silence=2.0",
"--print-args=false",
};
kaldi::ParseOptions po("");
nnet3_decoding_config_.Register(&po);
endpoint_config_.Register(&po);
decodable_opts_.Register(&po);
vector<const char*> args;
args.push_back("vosk");
args.insert(args.end(), extra_args, extra_args + sizeof(extra_args) / sizeof(extra_args[0]));
po.Read(args.size(), args.data());
feature_info_.feature_type = "mfcc";
ReadConfigFromFile(model_path_str_ + "/mfcc.conf", &feature_info_.mfcc_opts);
feature_info_.mfcc_opts.frame_opts.allow_downsample = true; // It is safe to downsample
feature_info_.silence_weighting_config.silence_weight = 1e-3;
feature_info_.silence_weighting_config.silence_phones_str = endpoint_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";
feature_info_.use_ivectors = true;
feature_info_.ivector_extractor_info.Init(ivector_extraction_opts);
nnet3_rxfilename_ = model_path_str_ + "/final.mdl";
hclg_fst_rxfilename_ = model_path_str_ + "/HCLG.fst";
hcl_fst_rxfilename_ = model_path_str_ + "/HCLr.fst";
g_fst_rxfilename_ = model_path_str_ + "/Gr.fst";
disambig_rxfilename_ = model_path_str_ + "/disambig_tid.int";
word_syms_rxfilename_ = model_path_str_ + "/words.txt";
winfo_rxfilename_ = model_path_str_ + "/word_boundary.int";
carpa_rxfilename_ = model_path_str_ + "/rescore/G.carpa";
std_fst_rxfilename_ = model_path_str_ + "/rescore/G.fst";
}
void Model::ConfigureV2()
{
kaldi::ParseOptions po("something");
nnet3_decoding_config_.Register(&po);
endpoint_config_.Register(&po);
decodable_opts_.Register(&po);
po.ReadConfigFile(model_path_str_ + "/conf/model.conf");
KALDI_LOG << "Decoding params beam=" << nnet3_decoding_config_.beam <<
" max-active=" << nnet3_decoding_config_.max_active <<
" lattice-beam=" << nnet3_decoding_config_.lattice_beam;
KALDI_LOG << "Silence phones " << endpoint_config_.silence_phones;
feature_info_.feature_type = "mfcc";
ReadConfigFromFile(model_path_str_ + "/conf/mfcc.conf", &feature_info_.mfcc_opts);
feature_info_.mfcc_opts.frame_opts.allow_downsample = true; // It is safe to downsample
feature_info_.silence_weighting_config.silence_weight = 1e-3;
feature_info_.silence_weighting_config.silence_phones_str = endpoint_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";
feature_info_.use_ivectors = true;
feature_info_.ivector_extractor_info.Init(ivector_extraction_opts);
nnet3_rxfilename_ = model_path_str_ + "/am/final.mdl";
hclg_fst_rxfilename_ = model_path_str_ + "/graph/HCLG.fst";
hcl_fst_rxfilename_ = model_path_str_ + "/graph/HCLr.fst";
g_fst_rxfilename_ = model_path_str_ + "/graph/Gr.fst";
disambig_rxfilename_ = model_path_str_ + "/graph/disambig_tid.int";
word_syms_rxfilename_ = model_path_str_ + "/graph/words.txt";
winfo_rxfilename_ = model_path_str_ + "/graph/phones/word_boundary.int";
carpa_rxfilename_ = model_path_str_ + "/rescore/G.carpa";
std_fst_rxfilename_ = model_path_str_ + "/rescore/G.fst";
}
void Model::ReadDataFiles()
{
struct stat buffer;
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()));
}
decodable_info_ = new nnet3::DecodableNnetSimpleLoopedInfo(decodable_opts_,
nnet_);
if (stat(hclg_fst_rxfilename_.c_str(), &buffer) == 0) {
KALDI_LOG << "Loading HCLG from " << hclg_fst_rxfilename_;
hclg_fst_ = fst::ReadFstKaldiGeneric(hclg_fst_rxfilename_);
hcl_fst_ = NULL;
g_fst_ = NULL;
} else {
KALDI_LOG << "Loading HCL and G from " << hcl_fst_rxfilename_ << " " << g_fst_rxfilename_;
hclg_fst_ = NULL;
hcl_fst_ = fst::StdFst::Read(hcl_fst_rxfilename_);
g_fst_ = fst::StdFst::Read(g_fst_rxfilename_);
ReadIntegerVectorSimple(disambig_rxfilename_, &disambig_);
}
word_syms_ = NULL;
if (hclg_fst_ && hclg_fst_->OutputSymbols()) {
word_syms_ = hclg_fst_->OutputSymbols();
} else if (g_fst_ && g_fst_->OutputSymbols()) {
word_syms_ = g_fst_->OutputSymbols();
}
if (!word_syms_) {
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_);
} else {
winfo_ = NULL;
}
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::PROJECT_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_);
} else {
std_lm_fst_ = NULL;
}
}
void Model::Ref()
{
ref_cnt_++;
}
void Model::Unref()
{
ref_cnt_--;
if (ref_cnt_ == 0) {
delete this;
}
}
Model::~Model() {
delete decodable_info_;
delete trans_model_;
delete nnet_;
delete winfo_;
delete hclg_fst_;
delete hcl_fst_;
delete g_fst_;
}
-87
View File
@@ -1,87 +0,0 @@
// Copyright 2019 Alpha Cephei Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef MODEL_H_
#define MODEL_H_
#include "base/kaldi-common.h"
#include "fstext/fstext-lib.h"
#include "fstext/fstext-utils.h"
#include "online2/onlinebin-util.h"
#include "online2/online-timing.h"
#include "online2/online-endpoint.h"
#include "online2/online-nnet3-decoding.h"
#include "online2/online-feature-pipeline.h"
#include "lat/lattice-functions.h"
#include "lat/sausages.h"
#include "lat/word-align-lattice.h"
#include "lm/const-arpa-lm.h"
#include "util/parse-options.h"
#include "nnet3/nnet-utils.h"
#include "rnnlm/rnnlm-utils.h"
using namespace kaldi;
using namespace std;
class KaldiRecognizer;
class Model {
public:
Model(const char *model_path);
void Ref();
void Unref();
protected:
~Model();
void ConfigureV1();
void ConfigureV2();
void ReadDataFiles();
friend class KaldiRecognizer;
string model_path_str_;
string nnet3_rxfilename_;
string hclg_fst_rxfilename_;
string hcl_fst_rxfilename_;
string g_fst_rxfilename_;
string disambig_rxfilename_;
string word_syms_rxfilename_;
string winfo_rxfilename_;
string carpa_rxfilename_;
string std_fst_rxfilename_;
kaldi::OnlineEndpointConfig endpoint_config_;
kaldi::LatticeFasterDecoderConfig nnet3_decoding_config_;
kaldi::nnet3::NnetSimpleLoopedComputationOptions decodable_opts_;
kaldi::OnlineNnet2FeaturePipelineInfo feature_info_;
kaldi::nnet3::DecodableNnetSimpleLoopedInfo *decodable_info_;
kaldi::TransitionModel *trans_model_;
kaldi::nnet3::AmNnetSimple *nnet_;
const fst::SymbolTable *word_syms_;
kaldi::WordBoundaryInfo *winfo_;
vector<int32> disambig_;
fst::Fst<fst::StdArc> *hclg_fst_;
fst::Fst<fst::StdArc> *hcl_fst_;
fst::Fst<fst::StdArc> *g_fst_;
fst::VectorFst<fst::StdArc> *std_lm_fst_;
kaldi::ConstArpaLm const_arpa_;
int ref_cnt_;
};
#endif /* MODEL_H_ */
+42
View File
@@ -0,0 +1,42 @@
// sherpa-onnx/csrc/offline-lm-config.cc
//
// Copyright (c) 2023 Xiaomi Corporation
#include "offline-lm-config.h"
#include <string>
#include "file-utils.h"
#include "macros.h"
namespace sherpa_onnx {
void OfflineLMConfig::Register(ParseOptions *po) {
po->Register("lm", &model, "Path to LM model.");
po->Register("lm-scale", &scale, "LM scale.");
po->Register("lm-num-threads", &lm_num_threads,
"Number of threads to run the neural network of LM model");
po->Register("lm-provider", &lm_provider,
"Specify a provider to LM model use: cpu, cuda, coreml");
}
bool OfflineLMConfig::Validate() const {
if (!FileExists(model)) {
SHERPA_ONNX_LOGE("%s does not exist", model.c_str());
return false;
}
return true;
}
std::string OfflineLMConfig::ToString() const {
std::ostringstream os;
os << "OfflineLMConfig(";
os << "model=\"" << model << "\", ";
os << "scale=" << scale << ")";
return os.str();
}
} // namespace sherpa_onnx

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