Compare commits

...

55 Commits

Author SHA1 Message Date
Nickolay Shmyrev d497c3a0a5 Move to 3-value endpointer config 2023-12-13 04:29:21 +01:00
Nickolay Shmyrev f37ee638a4 Add endpointer delays parameter 2023-11-27 15:02:55 +01:00
Nickolay Shmyrev e0b29bb17f Implement more endpointer modes 2023-11-10 01:58:31 +01: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
134 changed files with 20221 additions and 582 deletions
+26 -38
View File
@@ -4,62 +4,50 @@ buildscript {
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:4.2.0'
classpath 'com.vanniktech:gradle-maven-publish-plugin:0.18.0'
classpath 'com.android.tools.build:gradle:7.4.0'
classpath 'com.vanniktech:gradle-maven-publish-plugin:0.24.0'
}
}
allprojects {
version = '0.3.42'
version = '0.3.47'
}
subprojects {
apply plugin: 'com.android.library'
apply plugin: 'maven-publish'
apply plugin: 'com.vanniktech.maven.publish'
plugins.withId('com.vanniktech.maven.publish') {
mavenPublish {
group = 'com.alphacephei'
version = version
sonatypeHost = 's01'
androidVariantToPublish = 'release'
}
}
repositories {
google()
mavenCentral()
}
publishing {
publications {
aar(MavenPublication) {
groupId 'com.alphacephei'
version version
pom {
url = 'http://www.alphacephei.com.com/vosk/'
licenses {
license {
name = 'The Apache License, Version 2.0'
url = 'http://www.apache.org/licenses/LICENSE-2.0.txt'
}
}
developers {
developer {
id = 'com.alphacephei'
name = 'Alpha Cephei Inc'
email = 'contact@alphacephei.com'
}
}
scm {
connection = 'scm:git:git://github.com/alphacep/vosk-api.git'
url = 'https://github.com/alphacep/vosk-api/'
}
mavenPublishing {
publishToMavenCentral(com.vanniktech.maven.publish.SonatypeHost.S01, false)
signAllPublications()
}
mavenPublishing {
pom {
url = 'http://www.alphacephei.com.com/vosk/'
licenses {
license {
name = 'The Apache License, Version 2.0'
url = 'http://www.apache.org/licenses/LICENSE-2.0.txt'
}
}
developers {
developer {
id = 'com.alphacephei'
name = 'Alpha Cephei Inc'
email = 'contact@alphacephei.com'
}
}
scm {
connection = 'scm:git:git://github.com/alphacep/vosk-api.git'
url = 'https://github.com/alphacep/vosk-api/'
}
}
}
}
+11 -27
View File
@@ -3,14 +3,15 @@ def pomName = "Vosk Android"
def pomDescription = "Vosk speech recognition library for Android"
android {
compileSdkVersion 29
namespace 'org.vosk'
compileSdkVersion 33
defaultConfig {
minSdkVersion 21
targetSdkVersion 29
versionCode 6
targetSdkVersion 33
versionCode 10
versionName = version
archivesBaseName = archiveName
ndkVersion = "22.1.7171670"
ndkVersion = "25.2.9519653"
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
@@ -24,32 +25,15 @@ task buildVosk(type: Exec) {
}
dependencies {
api 'net.java.dev.jna:jna:4.4.0@aar'
api 'net.java.dev.jna:jna:5.13.0@aar'
}
//preBuild.dependsOn buildVosk
publishing {
publications {
aar(MavenPublication) {
artifactId = archiveName
artifact("$buildDir/outputs/aar/$archiveName-release.aar")
pom {
name = pomName
description = pomDescription
}
//generate pom nodes for dependencies
pom.withXml {
def dependenciesNode = asNode().appendNode('dependencies')
configurations.implementation.allDependencies.each { dependency ->
if (dependency.name != 'unspecified') {
def dependencyNode = dependenciesNode.appendNode('dependency')
dependencyNode.appendNode('groupId', dependency.group)
dependencyNode.appendNode('artifactId', dependency.name)
dependencyNode.appendNode('version', dependency.version)
}
}
}
}
mavenPublishing {
coordinates("com.alphacephei", archiveName, version)
pom {
name = pomName
description = pomDescription
}
}
+1 -1
View File
@@ -1,3 +1,3 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="org.vosk">
<manifest>
</manifest>
@@ -52,10 +52,20 @@ public class LibVosk {
public static native String vosk_recognizer_partial_result(Pointer recognizer);
public static native void vosk_recognizer_set_grm(Pointer recognizer, String grammar);
public static native void vosk_recognizer_reset(Pointer recognizer);
public static native void vosk_recognizer_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());
}
@@ -1,13 +1,18 @@
package org.vosk;
import java.io.IOException;
import com.sun.jna.PointerType;
public class Model extends PointerType implements AutoCloseable {
public Model() {
}
public Model(String path) {
public Model(String path) throws IOException {
super(LibVosk.vosk_model_new(path));
if (getPointer() == null) {
throw new IOException("Failed to create a model");
}
}
@Override
@@ -1,36 +1,163 @@
package org.vosk;
import com.sun.jna.PointerType;
import java.io.IOException;
public class Recognizer extends PointerType implements AutoCloseable {
public Recognizer(Model model, float sampleRate) {
/**
* 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");
}
}
public Recognizer(Model model, float sampleRate, SpeakerModel spkModel) {
/**
* Creates the recognizer object with speaker recognition.
*
* With the speaker recognition mode the recognizer not just recognize
* text but also return speaker vectors one can use for speaker identification
*
* @param model VoskModel containing static data for recognizer. Model can be
* shared across recognizers, even running in different threads.
* @param sampleRate The sample rate of the audio you are going to feed into the recognizer.
* Make sure this rate matches the audio content, it is a common
* issue causing accuracy problems.
* @param spkModel speaker model for speaker identification
* @throws IOException if the recognizer could not be created
*/
public Recognizer(Model model, float sampleRate, SpeakerModel spkModel) throws IOException {
super(LibVosk.vosk_recognizer_new_spk(model.getPointer(), sampleRate, spkModel.getPointer()));
if (getPointer() == null) {
throw new IOException("Failed to create a recognizer");
}
}
public Recognizer(Model model, float sampleRate, String grammar) {
/**
* Creates the recognizer object with the phrase list.
*
* Sometimes when you want to improve recognition accuracy and when you don't need
* to recognize large vocabulary you can specify a list of phrases to recognize. This
* will improve recognizer speed and accuracy but might return [unk] if user said
* something different.
*
* Only recognizers with lookahead models support this type of quick configuration.
* Precompiled HCLG graph models are not supported.
*
* @param model VoskModel containing static data for recognizer. Model can be
* shared across recognizers, even running in different threads.
* @param sampleRate The sample rate of the audio you are going to feed into the recognizer.
* Make sure this rate matches the audio content, it is a common
* issue causing accuracy problems.
* @param grammar The string with the list of phrases to recognize as JSON array of strings,
* for example "["one two three four five", "[unk]"]".
* @throws IOException if the recognizer could not be created
*/
public Recognizer(Model model, float sampleRate, String grammar) throws IOException {
super(LibVosk.vosk_recognizer_new_grm(model.getPointer(), sampleRate, grammar));
if (getPointer() == null) {
throw new IOException("Failed to create a recognizer");
}
}
/**
* Configures recognizer to output n-best results.
*
* <pre>
* {
* "alternatives": [
* { "text": "one two three four five", "confidence": 0.97 },
* { "text": "one two three for five", "confidence": 0.03 },
* ]
* }
* </pre>
*
* @param maxAlternatives - maximum alternatives to return from recognition results
*/
public void setMaxAlternatives(int maxAlternatives) {
LibVosk.vosk_recognizer_set_max_alternatives(this.getPointer(), maxAlternatives);
}
/** Enables words with times in the output
*
* <pre>
* "result" : [{
* "conf" : 1.000000,
* "end" : 1.110000,
* "start" : 0.870000,
* "word" : "what"
* }, {
* "conf" : 1.000000,
* "end" : 1.530000,
* "start" : 1.110000,
* "word" : "zero"
* }, {
* "conf" : 1.000000,
* "end" : 1.950000,
* "start" : 1.530000,
* "word" : "zero"
* }, {
* "conf" : 1.000000,
* "end" : 2.340000,
* "start" : 1.950000,
* "word" : "zero"
* }, {
* "conf" : 1.000000,
* "end" : 2.610000,
* "start" : 2.340000,
* "word" : "one"
* }],
* </pre>
*
* @param words - boolean value
*/
public void setWords(boolean words) {
LibVosk.vosk_recognizer_set_words(this.getPointer(), words);
}
/**
* Like above return words and confidences in partial results.
*
* @param partial_words - boolean value
*/
public void setPartialWords(boolean partial_words) {
LibVosk.vosk_recognizer_set_partial_words(this.getPointer(), partial_words);
}
/**
* Adds speaker model to already initialized recognizer.
*
* Can add speaker recognition model to already created recognizer.
* Helps to initialize speaker recognition for grammar-based recognizer.
*
* @param spkModel Speaker recognition model
*/
public void setSpeakerModel(SpeakerModel spkModel) {
LibVosk.vosk_recognizer_set_spk_model(this.getPointer(), spkModel.getPointer());
}
/**
* Accept and process new chunk of voice data.
*
* @param data - audio data in PCM 16-bit mono format
* @param len - length of the audio data
* @return 1 if silence is occurred and you can retrieve a new utterance with result method
* 0 if decoding continues
* -1 if exception occurred
*/
public boolean acceptWaveForm(byte[] data, int len) {
return LibVosk.vosk_recognizer_accept_waveform(this.getPointer(), data, len);
}
@@ -43,22 +170,76 @@ public class Recognizer extends PointerType implements AutoCloseable {
return LibVosk.vosk_recognizer_accept_waveform_f(this.getPointer(), data, len);
}
/**
* Returns speech recognition result
*
* @return the result in JSON format which contains decoded line, decoded
* words, times in seconds and confidences. You can parse this result
* with any json parser
*
* <pre>
* {
* "text" : "what zero zero zero one"
* }
* </pre>
*
* If alternatives enabled it returns result with alternatives, see also #setMaxAlternatives().
*
* If word times enabled returns word time, see also #setWordTimes().
*/
public String getResult() {
return LibVosk.vosk_recognizer_result(this.getPointer());
}
/**
* Returns partial speech recognition.
*
* @return partial speech recognition text which is not yet finalized.
* result may change as recognizer process more data.
*
* <pre>
* {
* "partial" : "cyril one eight zero"
* }
* </pre>
*/
public String getPartialResult() {
return LibVosk.vosk_recognizer_partial_result(this.getPointer());
}
/**
* Returns speech recognition result. Same as result, but doesn't wait for silence.
* You usually call it in the end of the stream to get final bits of audio. It
* flushes the feature pipeline, so all remaining audio chunks got processed.
*
* @return speech result in JSON format.
*/
public String getFinalResult() {
return LibVosk.vosk_recognizer_final_result(this.getPointer());
}
/**
* Reconfigures recognizer to use grammar.
*
* @param grammar Set of phrases in JSON array of strings or "[]" to use default model graph.
* @see #Recognizer(Model, float, String)
*/
public void setGrammar(String grammar) {
LibVosk.vosk_recognizer_set_grm(this.getPointer(), grammar);
}
/**
* Resets the recognizer.
* Resets current results so the recognition can continue from scratch.
*/
public void reset() {
LibVosk.vosk_recognizer_reset(this.getPointer());
}
/**
* Releases recognizer object.
* Underlying model is also unreferenced and if needed, released.
*/
@Override
public void close() {
LibVosk.vosk_recognizer_free(this.getPointer());
@@ -1,13 +1,36 @@
package org.vosk;
import com.sun.jna.PointerType;
import java.io.IOException;
/**
* Helps to initialize speaker recognition for grammar-based recognizer.
*/
public class SpeakerModel extends PointerType implements AutoCloseable {
public SpeakerModel() {
}
public SpeakerModel(String path) {
/**
* Loads speaker model data from the file.
*
* The path must contain:
* - a config file: mfcc.conf
* - kaldi nnet: final.ext.raw
* - mean.vec
* - transform.mat
*
* @param path the path of the model on the filesystem
* @throws IOException if the model could not be created
*
* @see <a href="http://kaldi-asr.org/doc/structkaldi_1_1MfccOptions.html">Kaldi MfccOptions</a>
* @see <a href="http://kaldi-asr.org/doc/classkaldi_1_1nnet3_1_1Nnet.html">Kaldi Nnet</a>
*/
public SpeakerModel(String path) throws IOException {
super(LibVosk.vosk_spk_model_new(path));
if (getPointer() == null) {
throw new IOException("Failed to create a speaker model");
}
}
@Override
@@ -19,9 +19,9 @@ import android.media.AudioRecord;
import android.media.MediaRecorder.AudioSource;
import android.os.Handler;
import android.os.Looper;
import android.annotation.SuppressLint;
import org.vosk.Recognizer;
import java.io.IOException;
/**
@@ -48,6 +48,7 @@ public class SpeechService {
*
* @throws IOException thrown if audio recorder can not be created for some reason.
*/
@SuppressLint("MissingPermission")
public SpeechService(Recognizer recognizer, float sampleRate) throws IOException {
this.recognizer = recognizer;
this.sampleRate = (int) sampleRate;
+9 -13
View File
@@ -3,11 +3,12 @@ def pomName = "Vosk English Model"
def pomDescription = "Small English model for Android"
android {
compileSdkVersion 29
namespace "org.vosk"
compileSdkVersion 33
defaultConfig {
minSdkVersion 21
targetSdkVersion 29
versionCode 6
targetSdkVersion 33
versionCode 10
versionName = version
archivesBaseName = archiveName
}
@@ -33,15 +34,10 @@ tasks.register('genUUID') {
preBuild.dependsOn(genUUID)
publishing {
publications {
aar(MavenPublication) {
artifactId = archiveName
artifact("$buildDir/outputs/aar/$archiveName-release.aar")
pom {
name = pomName
description = pomDescription
}
}
mavenPublishing {
coordinates("com.alphacephei", archiveName, version)
pom {
name = pomName
description = pomDescription
}
}
@@ -1,3 +1,2 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.vosk.model.en">
<manifest>
</manifest>
+1 -1
View File
@@ -11,7 +11,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Vosk" Version="0.3.42" />
<PackageReference Include="Vosk" Version="0.3.45" />
</ItemGroup>
</Project>
+1 -1
View File
@@ -2,7 +2,7 @@
<package>
<metadata>
<id>Vosk</id>
<version>0.3.42</version>
<version>0.3.45</version>
<authors>Alpha Cephei Inc</authors>
<owners>Alpha Cephei Inc</owners>
<license type="expression">Apache-2.0</license>
+55
View File
@@ -0,0 +1,55 @@
using System;
namespace Vosk
{
public class BatchModel : global::System.IDisposable
{
private global::System.Runtime.InteropServices.HandleRef handle;
internal BatchModel(global::System.IntPtr cPtr)
{
handle = new global::System.Runtime.InteropServices.HandleRef(this, cPtr);
}
internal static global::System.Runtime.InteropServices.HandleRef getCPtr(BatchModel obj)
{
return (obj == null) ? new global::System.Runtime.InteropServices.HandleRef(null, global::System.IntPtr.Zero) : obj.handle;
}
~BatchModel()
{
Dispose(false);
}
public void Dispose()
{
Dispose(true);
global::System.GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
lock (this)
{
if (handle.Handle != global::System.IntPtr.Zero)
{
VoskPINVOKE.delete_BatchModel(handle);
handle = new global::System.Runtime.InteropServices.HandleRef(null, global::System.IntPtr.Zero);
}
}
}
public BatchModel(string model_path) : this(VoskPINVOKE.new_BatchModel(model_path))
{
}
public void WaitForCompletion()
{
if (handle.Handle != global::System.IntPtr.Zero)
{
VoskPINVOKE.wait_BatchModel(handle);
}
}
}
}
+88
View File
@@ -0,0 +1,88 @@
using System;
namespace Vosk
{
public class VoskBatchRecognizer : System.IDisposable
{
private System.Runtime.InteropServices.HandleRef handle;
internal VoskBatchRecognizer(System.IntPtr cPtr)
{
handle = new System.Runtime.InteropServices.HandleRef(this, cPtr);
}
internal static System.Runtime.InteropServices.HandleRef getCPtr(VoskBatchRecognizer obj)
{
return (obj == null) ? new System.Runtime.InteropServices.HandleRef(null, System.IntPtr.Zero) : obj.handle;
}
~VoskBatchRecognizer()
{
Dispose(false);
}
public void Dispose()
{
Dispose(true);
System.GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
lock (this)
{
if (handle.Handle != System.IntPtr.Zero)
{
VoskPINVOKE.delete_VoskBatchRecognizer(handle);
handle = new System.Runtime.InteropServices.HandleRef(null, System.IntPtr.Zero);
}
}
}
public VoskBatchRecognizer(BatchModel model, float sample_rate) : this(VoskPINVOKE.new_VoskBatchRecognizer(BatchModel.getCPtr(model), sample_rate))
{
}
public bool AcceptWaveform(byte[] data, int len)
{
return VoskPINVOKE.VoskBatchRecognizer_AcceptWaveform(handle, data, len);
}
private static string PtrToStringUTF8(System.IntPtr ptr)
{
int len = 0;
while (System.Runtime.InteropServices.Marshal.ReadByte(ptr, len) != 0)
len++;
byte[] array = new byte[len];
System.Runtime.InteropServices.Marshal.Copy(ptr, array, 0, len);
return System.Text.Encoding.UTF8.GetString(array);
}
public string FrontResult()
{
return PtrToStringUTF8(VoskPINVOKE.VoskBatchRecognizer_FrontResult(handle));
}
public string Result()
{
string result = PtrToStringUTF8(VoskPINVOKE.VoskBatchRecognizer_FrontResult(handle));
VoskPINVOKE.VoskBatchRecognizer_Pop(handle);
return result;
}
public int GetNumPendingChunks()
{
return VoskPINVOKE.VoskBatchRecognizer_GetPendingChunks(handle);
}
public void FinishStream()
{
VoskPINVOKE.VoskBatchRecognizer_FinishStream(handle);
}
public void SetNLSML(bool nlsml)
{
VoskPINVOKE.VoskBatchRecognizer_SetNLSML(handle, Convert.ToInt32(nlsml));
}
}
}
+36 -1
View File
@@ -73,6 +73,41 @@ class VoskPINVOKE {
[global::System.Runtime.InteropServices.DllImport("libvosk", EntryPoint="vosk_gpu_thread_init")]
public static extern void GpuThreadInit();
}
[global::System.Runtime.InteropServices.DllImport("libvosk", EntryPoint = "vosk_batch_model_new")]
public static extern global::System.IntPtr new_BatchModel(string jarg1);
[global::System.Runtime.InteropServices.DllImport("libvosk", EntryPoint = "vosk_batch_model_free")]
public static extern void delete_BatchModel(global::System.Runtime.InteropServices.HandleRef jarg1);
[global::System.Runtime.InteropServices.DllImport("libvosk", EntryPoint = "vosk_batch_model_wait")]
public static extern void wait_BatchModel(global::System.Runtime.InteropServices.HandleRef jarg1);
[global::System.Runtime.InteropServices.DllImport("libvosk", EntryPoint = "vosk_batch_recognizer_new")]
public static extern global::System.IntPtr new_VoskBatchRecognizer(global::System.Runtime.InteropServices.HandleRef jarg1, float jarg2);
[global::System.Runtime.InteropServices.DllImport("libvosk", EntryPoint = "vosk_batch_recognizer_free")]
public static extern void delete_VoskBatchRecognizer(global::System.Runtime.InteropServices.HandleRef jarg1);
[global::System.Runtime.InteropServices.DllImport("libvosk", EntryPoint = "vosk_batch_recognizer_accept_waveform")]
public static extern bool VoskBatchRecognizer_AcceptWaveform(global::System.Runtime.InteropServices.HandleRef jarg1, [global::System.Runtime.InteropServices.In, global::System.Runtime.InteropServices.MarshalAs(global::System.Runtime.InteropServices.UnmanagedType.LPArray)] byte[] jarg2, int jarg3);
[global::System.Runtime.InteropServices.DllImport("libvosk", EntryPoint = "vosk_batch_recognizer_set_nlsml")]
public static extern void VoskBatchRecognizer_SetNLSML(global::System.Runtime.InteropServices.HandleRef jarg1, int jarg2);
[global::System.Runtime.InteropServices.DllImport("libvosk", EntryPoint = "vosk_batch_recognizer_finish_stream")]
public static extern void VoskBatchRecognizer_FinishStream(global::System.Runtime.InteropServices.HandleRef jarg1);
[global::System.Runtime.InteropServices.DllImport("libvosk", EntryPoint = "vosk_batch_recognizer_front_result")]
public static extern global::System.IntPtr VoskBatchRecognizer_FrontResult(global::System.Runtime.InteropServices.HandleRef jarg1);
[global::System.Runtime.InteropServices.DllImport("libvosk", EntryPoint = "vosk_batch_recognizer_pop")]
public static extern void VoskBatchRecognizer_Pop(global::System.Runtime.InteropServices.HandleRef jarg1);
[global::System.Runtime.InteropServices.DllImport("libvosk", EntryPoint = "vosk_batch_recognizer_get_pending_chunks")]
public static extern int VoskBatchRecognizer_GetPendingChunks(global::System.Runtime.InteropServices.HandleRef jarg1);
}
}
+7 -7
View File
@@ -5,13 +5,13 @@ On Linux (we download library and set LD_LIBRARY_PATH)
```
git clone https://github.com/alphacep/vosk-api
cd vosk-api/go/example
wget https://github.com/alphacep/vosk-api/releases/download/v0.3.42/vosk-linux-x86_64-0.3.42.zip
unzip vosk-linux-x86_64-0.3.42.zip
wget https://github.com/alphacep/vosk-api/releases/download/v0.3.45/vosk-linux-x86_64-0.3.45.zip
unzip vosk-linux-x86_64-0.3.45.zip
wget https://alphacephei.com/vosk/models/vosk-model-small-en-us-0.15.zip
unzip vosk-model-small-en-us-0.15.zip
mv vosk-model-small-en-us-0.15 model
cp ../../python/example/test.wav .
VOSK_PATH=`pwd`/vosk-linux-x86_64-0.3.42 LD_LIBRARY_PATH=$VOSK_PATH CGO_CPPFLAGS="-I $VOSK_PATH" CGO_LDFLAGS="-L $VOSK_PATH" go run . -f test.wav
VOSK_PATH=`pwd`/vosk-linux-x86_64-0.3.45 LD_LIBRARY_PATH=$VOSK_PATH CGO_CPPFLAGS="-I $VOSK_PATH" CGO_LDFLAGS="-L $VOSK_PATH" go run . -f test.wav
```
for Windows (we place DLLs in current folder where linker finds them):
@@ -19,10 +19,10 @@ for Windows (we place DLLs in current folder where linker finds them):
```
git clone https://github.com/alphacep/vosk-api
cd vosk-api/go/example
wget https://github.com/alphacep/vosk-api/releases/download/v0.3.42/vosk-linux-x86_64-0.3.42.zip
unzip vosk-linux-x86_64-0.3.42.zip
cp vosk-linux-x86_64-0.3.42/*.dll .
cp vosk-linux-x86_64-0.3.42/*.h .
wget https://github.com/alphacep/vosk-api/releases/download/v0.3.45/vosk-linux-x86_64-0.3.45.zip
unzip vosk-linux-x86_64-0.3.45.zip
cp vosk-linux-x86_64-0.3.45/*.dll .
cp vosk-linux-x86_64-0.3.45/*.h .
wget https://alphacephei.com/vosk/models/vosk-model-small-en-us-0.15.zip
unzip vosk-model-small-en-us-0.15.zip
mv vosk-model-small-en-us-0.15 model
+10 -5
View File
@@ -1,7 +1,7 @@
package main
import (
"bufio"
"encoding/json"
"flag"
"fmt"
"io"
@@ -21,6 +21,9 @@ func main() {
log.Fatal(err)
}
// we can check if word is in the vocabulary
// fmt.Println(model.FindWord("air"))
sampleRate := 16000.0
rec, err := vosk.NewRecognizer(model, sampleRate)
if err != nil {
@@ -34,11 +37,10 @@ func main() {
}
defer file.Close()
reader := bufio.NewReader(file)
buf := make([]byte, 4096)
for {
_, err := reader.Read(buf)
_, err := file.Read(buf)
if err != nil {
if err != io.EOF {
log.Fatal(err)
@@ -48,9 +50,12 @@ func main() {
}
if rec.AcceptWaveform(buf) != 0 {
fmt.Println(string(rec.Result()))
fmt.Println(rec.Result())
}
}
fmt.Println(string(rec.FinalResult()))
// Unmarshal example for final result
var jres map[string]interface{}
json.Unmarshal([]byte(rec.FinalResult()), &jres)
fmt.Println(jres["text"])
}
+33 -16
View File
@@ -5,6 +5,7 @@ package vosk
// #include <stdlib.h>
// #include <vosk_api.h>
import "C"
import "unsafe"
// VoskModel contains a reference to the C VoskModel
type VoskModel struct {
@@ -13,7 +14,9 @@ type VoskModel struct {
// NewModel creates a new VoskModel instance
func NewModel(modelPath string) (*VoskModel, error) {
internal := C.vosk_model_new(C.CString(modelPath))
cmodelPath := C.CString(modelPath)
defer C.free(unsafe.Pointer(cmodelPath))
internal := C.vosk_model_new(cmodelPath)
model := &VoskModel{model: internal}
return model, nil
}
@@ -29,10 +32,10 @@ func freeModel(model *VoskModel) {
// FindWord checks if a word can be recognized by the model.
// Returns the word symbol if the word exists inside the model or
// -1 otherwise.
func (m *VoskModel) FindWord(word []byte) int {
cbuf := C.CBytes(word)
defer C.free(cbuf)
i := C.vosk_model_find_word(m.model, (*C.char)(cbuf))
func (m *VoskModel) FindWord(word string) int {
cstr := C.CString(word)
defer C.free(unsafe.Pointer(cstr))
i := C.vosk_model_find_word(m.model, cstr)
return int(i)
}
@@ -43,7 +46,9 @@ type VoskSpkModel struct {
// NewSpkModel creates a new VoskSpkModel instance
func NewSpkModel(spkModelPath string) (*VoskSpkModel, error) {
internal := C.vosk_spk_model_new(C.CString(spkModelPath))
cspkModelPath := C.CString(spkModelPath)
defer C.free(unsafe.Pointer(cspkModelPath))
internal := C.vosk_spk_model_new(cspkModelPath)
spkModel := &VoskSpkModel{spkModel: internal}
return spkModel, nil
}
@@ -84,10 +89,10 @@ func NewRecognizerSpk(model *VoskModel, sampleRate float64, spkModel *VoskSpkMod
}
// NewRecognizerGrm creates a new VoskRecognizer instance with the phrase list.
func NewRecognizerGrm(model *VoskModel, sampleRate float64, grammer []byte) (*VoskRecognizer, error) {
cbuf := C.CBytes(grammer)
defer C.free(cbuf)
internal := C.vosk_recognizer_new_grm(model.model, C.float(sampleRate), (*C.char)(cbuf))
func NewRecognizerGrm(model *VoskModel, sampleRate float64, grammar string) (*VoskRecognizer, error) {
cgrammar := C.CString(grammar)
defer C.free(unsafe.Pointer(cgrammar))
internal := C.vosk_recognizer_new_grm(model.model, C.float(sampleRate), cgrammar)
rec := &VoskRecognizer{rec: internal}
return rec, nil
}
@@ -97,6 +102,13 @@ func (r *VoskRecognizer) SetSpkModel(spkModel *VoskSpkModel) {
C.vosk_recognizer_set_spk_model(r.rec, spkModel.spkModel)
}
// SetGrm sets which phrases to recognize on an already initialized recognizer.
func (r *VoskRecognizer) SetGrm(grammar string) {
cgrammar := C.CString(grammar)
defer C.free(unsafe.Pointer(cgrammar))
C.vosk_recognizer_set_grm(r.rec, cgrammar)
}
// SetMaxAlternatives configures the recognizer to output n-best results.
func (r *VoskRecognizer) SetMaxAlternatives(maxAlternatives int) {
C.vosk_recognizer_set_max_alternatives(r.rec, C.int(maxAlternatives))
@@ -107,6 +119,11 @@ func (r *VoskRecognizer) SetWords(words int) {
C.vosk_recognizer_set_words(r.rec, C.int(words))
}
// SetPartialWords enables words with times in the partial ouput.
func (r *VoskRecognizer) SetPartialWords(words int) {
C.vosk_recognizer_set_partial_words(r.rec, C.int(words))
}
// AcceptWaveform accepts and processes a new chunk of the voice data.
func (r *VoskRecognizer) AcceptWaveform(buffer []byte) int {
cbuf := C.CBytes(buffer)
@@ -116,19 +133,19 @@ func (r *VoskRecognizer) AcceptWaveform(buffer []byte) int {
}
// Result returns a speech recognition result.
func (r *VoskRecognizer) Result() []byte {
return []byte(C.GoString(C.vosk_recognizer_result(r.rec)))
func (r *VoskRecognizer) Result() string {
return C.GoString(C.vosk_recognizer_result(r.rec))
}
// PartialResult returns a partial speech recognition result.
func (r *VoskRecognizer) PartialResult() []byte {
return []byte(C.GoString(C.vosk_recognizer_partial_result(r.rec)))
func (r *VoskRecognizer) PartialResult() string {
return C.GoString(C.vosk_recognizer_partial_result(r.rec))
}
// FinalResult returns a speech recognition result. Same as result, but doesn't wait
// for silence.
func (r *VoskRecognizer) FinalResult() []byte {
return []byte(C.GoString(C.vosk_recognizer_final_result(r.rec)))
func (r *VoskRecognizer) FinalResult() string {
return C.GoString(C.vosk_recognizer_final_result(r.rec))
}
// Reset resets the recognizer.
+1 -2
View File
@@ -11,6 +11,5 @@ repositories {
}
dependencies {
implementation group: 'net.java.dev.jna', name: 'jna', version: '5.7.0'
implementation group: 'com.alphacephei', name: 'vosk', version: '0.3.42+'
implementation group: 'com.alphacephei', name: 'vosk', version: '0.3.45'
}
+2 -2
View File
@@ -16,7 +16,7 @@ repositories {
archivesBaseName = 'vosk'
group = 'com.alphacephei'
version = '0.3.42'
version = '0.3.45'
mavenPublish {
group = 'com.alphacephei'
@@ -25,7 +25,7 @@ mavenPublish {
}
dependencies {
api group: 'net.java.dev.jna', name: 'jna', version: '5.7.0'
api group: 'net.java.dev.jna', name: 'jna', version: '5.13.0'
testImplementation 'junit:junit:4.13'
}
+14 -4
View File
@@ -1,7 +1,6 @@
package org.vosk;
import com.sun.jna.Native;
import com.sun.jna.Library;
import com.sun.jna.Platform;
import com.sun.jna.Pointer;
import java.io.File;
@@ -13,8 +12,9 @@ import java.nio.file.StandardCopyOption;
public class LibVosk {
private static void unpackDll(File targetDir, String lib) throws IOException {
InputStream source = LibVosk.class.getResourceAsStream("/win32-x86-64/" + lib + ".dll");
Files.copy(source, new File(targetDir, lib + ".dll").toPath(), StandardCopyOption.REPLACE_EXISTING);
try (InputStream source = LibVosk.class.getResourceAsStream("/win32-x86-64/" + lib + ".dll")) {
Files.copy(source, new File(targetDir, lib + ".dll").toPath(), StandardCopyOption.REPLACE_EXISTING);
}
}
static {
@@ -23,7 +23,7 @@ public class LibVosk {
// We have to unpack dependencies
try {
// To get a tmp folder we unpack small library and mark it for deletion
File tmpFile = Native.extractFromResourcePath("/win32-x86-64/empty");
File tmpFile = Native.extractFromResourcePath("/win32-x86-64/empty", LibVosk.class.getClassLoader());
File tmpDir = tmpFile.getParentFile();
new File(tmpDir, tmpFile.getName() + ".x").createNewFile();
@@ -78,10 +78,20 @@ public class LibVosk {
public static native String vosk_recognizer_partial_result(Pointer recognizer);
public static native void vosk_recognizer_set_grm(Pointer recognizer, String grammar);
public static native void vosk_recognizer_reset(Pointer recognizer);
public static native void vosk_recognizer_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());
}
+178 -2
View File
@@ -4,6 +4,17 @@ 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));
@@ -12,30 +23,141 @@ public class Recognizer extends PointerType implements AutoCloseable {
}
}
public Recognizer(Model model, float sampleRate, SpeakerModel spkModel) {
/**
* Creates the recognizer object with speaker recognition.
*
* With the speaker recognition mode the recognizer not just recognize
* text but also return speaker vectors one can use for speaker identification
*
* @param model VoskModel containing static data for recognizer. Model can be
* shared across recognizers, even running in different threads.
* @param sampleRate The sample rate of the audio you are going to feed into the recognizer.
* Make sure this rate matches the audio content, it is a common
* issue causing accuracy problems.
* @param spkModel speaker model for speaker identification
* @throws IOException if the recognizer could not be created
*/
public Recognizer(Model model, float sampleRate, SpeakerModel spkModel) throws IOException {
super(LibVosk.vosk_recognizer_new_spk(model.getPointer(), sampleRate, spkModel.getPointer()));
if (getPointer() == null) {
throw new IOException("Failed to create a recognizer");
}
}
public Recognizer(Model model, float sampleRate, String grammar) {
/**
* Creates the recognizer object with the phrase list.
*
* Sometimes when you want to improve recognition accuracy and when you don't need
* to recognize large vocabulary you can specify a list of phrases to recognize. This
* will improve recognizer speed and accuracy but might return [unk] if user said
* something different.
*
* Only recognizers with lookahead models support this type of quick configuration.
* Precompiled HCLG graph models are not supported.
*
* @param model VoskModel containing static data for recognizer. Model can be
* shared across recognizers, even running in different threads.
* @param sampleRate The sample rate of the audio you are going to feed into the recognizer.
* Make sure this rate matches the audio content, it is a common
* issue causing accuracy problems.
* @param grammar The string with the list of phrases to recognize as JSON array of strings,
* for example "["one two three four five", "[unk]"]".
* @throws IOException if the recognizer could not be created
*/
public Recognizer(Model model, float sampleRate, String grammar) throws IOException {
super(LibVosk.vosk_recognizer_new_grm(model.getPointer(), sampleRate, grammar));
if (getPointer() == null) {
throw new IOException("Failed to create a recognizer");
}
}
/**
* Configures recognizer to output n-best results.
*
* <pre>
* {
* "alternatives": [
* { "text": "one two three four five", "confidence": 0.97 },
* { "text": "one two three for five", "confidence": 0.03 },
* ]
* }
* </pre>
*
* @param maxAlternatives - maximum alternatives to return from recognition results
*/
public void setMaxAlternatives(int maxAlternatives) {
LibVosk.vosk_recognizer_set_max_alternatives(this.getPointer(), maxAlternatives);
}
/** Enables words with times in the output
*
* <pre>
* "result" : [{
* "conf" : 1.000000,
* "end" : 1.110000,
* "start" : 0.870000,
* "word" : "what"
* }, {
* "conf" : 1.000000,
* "end" : 1.530000,
* "start" : 1.110000,
* "word" : "zero"
* }, {
* "conf" : 1.000000,
* "end" : 1.950000,
* "start" : 1.530000,
* "word" : "zero"
* }, {
* "conf" : 1.000000,
* "end" : 2.340000,
* "start" : 1.950000,
* "word" : "zero"
* }, {
* "conf" : 1.000000,
* "end" : 2.610000,
* "start" : 2.340000,
* "word" : "one"
* }],
* </pre>
*
* @param words - boolean value
*/
public void setWords(boolean words) {
LibVosk.vosk_recognizer_set_words(this.getPointer(), words);
}
/**
* Like above return words and confidences in partial results.
*
* @param partial_words - boolean value
*/
public void setPartialWords(boolean partial_words) {
LibVosk.vosk_recognizer_set_partial_words(this.getPointer(), partial_words);
}
/**
* Adds speaker model to already initialized recognizer.
*
* Can add speaker recognition model to already created recognizer.
* Helps to initialize speaker recognition for grammar-based recognizer.
*
* @param spkModel Speaker recognition model
*/
public void setSpeakerModel(SpeakerModel spkModel) {
LibVosk.vosk_recognizer_set_spk_model(this.getPointer(), spkModel.getPointer());
}
/**
* Accept and process new chunk of voice data.
*
* @param data - audio data in PCM 16-bit mono format
* @param len - length of the audio data
* @return 1 if silence is occurred and you can retrieve a new utterance with result method
* 0 if decoding continues
* -1 if exception occurred
*/
public boolean acceptWaveForm(byte[] data, int len) {
return LibVosk.vosk_recognizer_accept_waveform(this.getPointer(), data, len);
}
@@ -48,22 +170,76 @@ public class Recognizer extends PointerType implements AutoCloseable {
return LibVosk.vosk_recognizer_accept_waveform_f(this.getPointer(), data, len);
}
/**
* Returns speech recognition result
*
* @return the result in JSON format which contains decoded line, decoded
* words, times in seconds and confidences. You can parse this result
* with any json parser
*
* <pre>
* {
* "text" : "what zero zero zero one"
* }
* </pre>
*
* If alternatives enabled it returns result with alternatives, see also #setMaxAlternatives().
*
* If word times enabled returns word time, see also #setWordTimes().
*/
public String getResult() {
return LibVosk.vosk_recognizer_result(this.getPointer());
}
/**
* Returns partial speech recognition.
*
* @return partial speech recognition text which is not yet finalized.
* result may change as recognizer process more data.
*
* <pre>
* {
* "partial" : "cyril one eight zero"
* }
* </pre>
*/
public String getPartialResult() {
return LibVosk.vosk_recognizer_partial_result(this.getPointer());
}
/**
* Returns speech recognition result. Same as result, but doesn't wait for silence.
* You usually call it in the end of the stream to get final bits of audio. It
* flushes the feature pipeline, so all remaining audio chunks got processed.
*
* @return speech result in JSON format.
*/
public String getFinalResult() {
return LibVosk.vosk_recognizer_final_result(this.getPointer());
}
/**
* Reconfigures recognizer to use grammar.
*
* @param grammar Set of phrases in JSON array of strings or "[]" to use default model graph.
* @see #Recognizer(Model, float, String)
*/
public void setGrammar(String grammar) {
LibVosk.vosk_recognizer_set_grm(this.getPointer(), grammar);
}
/**
* Resets the recognizer.
* Resets current results so the recognition can continue from scratch.
*/
public void reset() {
LibVosk.vosk_recognizer_reset(this.getPointer());
}
/**
* Releases recognizer object.
* Underlying model is also unreferenced and if needed, released.
*/
@Override
public void close() {
LibVosk.vosk_recognizer_free(this.getPointer());
@@ -3,10 +3,28 @@ package org.vosk;
import com.sun.jna.PointerType;
import java.io.IOException;
/**
* Helps to initialize speaker recognition for grammar-based recognizer.
*/
public class SpeakerModel extends PointerType implements AutoCloseable {
public SpeakerModel() {
}
/**
* Loads speaker model data from the file.
*
* The path must contain:
* - a config file: mfcc.conf
* - kaldi nnet: final.ext.raw
* - mean.vec
* - transform.mat
*
* @param path the path of the model on the filesystem
* @throws IOException if the model could not be created
*
* @see <a href="http://kaldi-asr.org/doc/structkaldi_1_1MfccOptions.html">Kaldi MfccOptions</a>
* @see <a href="http://kaldi-asr.org/doc/classkaldi_1_1nnet3_1_1Nnet.html">Kaldi Nnet</a>
*/
public SpeakerModel(String path) throws IOException {
super(LibVosk.vosk_spk_model_new(path));
+3
View File
@@ -0,0 +1,3 @@
.gradle/
.idea/
build/
+3
View File
@@ -0,0 +1,3 @@
# Default ignored files
/shelf/
/workspace.xml
+1
View File
@@ -0,0 +1 @@
vosk-api-kotlin
+127
View File
@@ -0,0 +1,127 @@
<component name="ProjectCodeStyleConfiguration">
<code_scheme name="Project" version="173">
<JetCodeStyleSettings>
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
</JetCodeStyleSettings>
<codeStyleSettings language="XML">
<option name="FORCE_REARRANGE_MODE" value="1" />
<indentOptions>
<option name="CONTINUATION_INDENT_SIZE" value="4" />
</indentOptions>
<arrangement>
<rules>
<section>
<rule>
<match>
<AND>
<NAME>xmlns:android</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>xmlns:.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:id</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:name</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>name</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>style</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
<order>ANDROID_ATTRIBUTE_ORDER</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>.*</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
</rules>
</arrangement>
</codeStyleSettings>
<codeStyleSettings language="kotlin">
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
<indentOptions>
<option name="USE_TAB_CHARACTER" value="true" />
<option name="SMART_TABS" value="true" />
</indentOptions>
</codeStyleSettings>
</code_scheme>
</component>
+5
View File
@@ -0,0 +1,5 @@
<component name="ProjectCodeStyleConfiguration">
<state>
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
</state>
</component>
+6
View File
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CompilerConfiguration">
<bytecodeTargetLevel target="11" />
</component>
</project>
+6
View File
@@ -0,0 +1,6 @@
<component name="CopyrightManager">
<copyright>
<option name="notice" value="Copyright &amp;#36;today.year Alpha Cephei Inc.&#10;&#10;Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);&#10;you may not use this file except in compliance with the License.&#10;You may obtain a copy of the License at&#10;&#10; http://www.apache.org/licenses/LICENSE-2.0&#10;&#10;Unless required by applicable law or agreed to in writing, software&#10;distributed under the License is distributed on an &quot;AS IS&quot; BASIS,&#10;WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.&#10;See the License for the specific language governing permissions and&#10;limitations under the License." />
<option name="myName" value="Apache-2.0" />
</copyright>
</component>
+7
View File
@@ -0,0 +1,7 @@
<component name="CopyrightManager">
<settings default="Apache-2.0">
<module2copyright>
<element module="All" copyright="Apache-2.0" />
</module2copyright>
</settings>
</component>
+18
View File
@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="GradleMigrationSettings" migrationVersion="1" />
<component name="GradleSettings">
<option name="linkedExternalProjectsSettings">
<GradleProjectSettings>
<option name="testRunner" value="GRADLE" />
<option name="distributionType" value="DEFAULT_WRAPPED" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="modules">
<set>
<option value="$PROJECT_DIR$" />
</set>
</option>
</GradleProjectSettings>
</option>
</component>
</project>
+25
View File
@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="RemoteRepositoriesConfiguration">
<remote-repository>
<option name="id" value="central" />
<option name="name" value="Maven Central repository" />
<option name="url" value="https://repo1.maven.org/maven2" />
</remote-repository>
<remote-repository>
<option name="id" value="jboss.community" />
<option name="name" value="JBoss Community repository" />
<option name="url" value="https://repository.jboss.org/nexus/content/repositories/public/" />
</remote-repository>
<remote-repository>
<option name="id" value="MavenRepo" />
<option name="name" value="MavenRepo" />
<option name="url" value="https://repo.maven.apache.org/maven2/" />
</remote-repository>
<remote-repository>
<option name="id" value="Google" />
<option name="name" value="Google" />
<option name="url" value="https://dl.google.com/dl/android/maven2/" />
</remote-repository>
</component>
</project>
+10
View File
@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="ProjectRootManager" version="2" languageLevel="JDK_11" default="true" project-jdk-name="Embedded JDK" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/build/classes" />
</component>
<component name="ProjectType">
<option name="id" value="Android" />
</component>
</project>
+6
View File
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$/.." vcs="Git" />
</component>
</project>
+1
View File
@@ -0,0 +1 @@
Doomsdayrs doomsdayrs@gmail.com
+8
View File
@@ -0,0 +1,8 @@
# Maintenance
To maintain this module, please ensure the following.
1. Kotlin version is kept up to date.
This will be found in the plugins block of the [build.gradle.kts](./build.gradle.kts).
Ensure both multiplatform and serialization have the same value.
2. Ensure dependencies are up to date
3. Ensure that the android min & target sdks are up to date.
+63
View File
@@ -0,0 +1,63 @@
# vosk-api-kotlin
The vosk-api wrapped using Kotlin Multiplatform.
## Usage
The following are ways to use this wrapper.
### JVM
For Java & Android targets.
```kotlin
dependencoes {
val voskVersion = "0.4.0-alpha0"
// Generic
implementation("com.alphacephei:vosk-api-kotlin:$voskVersion")
// Android
implementation("com.alphacephei:vosk-api-kotlin-android:$voskVersion")
}
```
## Building
To build this project, follow the following steps.
1. Install `libvosk`.
This can be done from [source][source install] (parent monorepo)
or [downloaded][download].
2. Download a [Vosk model](https://alphacephei.com/vosk/models) to use.
- It is suggested to use a small model to speed up tests.
3. Once both are downloaded and placed into a proper location (hopefully following UNIX specification).
Set the following environment variables:
- `VOSK_MODEL` to the path of the model.
- `VOSK_PATH` to the path of `libvosk`.
These are used by the various tests to operate.
4. Now that the required steps are complete, one can run `./gradlew build`.
## Kotlin/Native
Currently, the native target is disabled due to a lack of vosk-api packaging on platforms.
Further worsened by the fact that installing the vosk-api on Linux systems is a chore.
First, either install from [source][source install]
or [download][download] and install into the proper unix directories as expected in
[libvosk.def](./src/nativeInterop/cinterop/libvosk.def).
To enable development for Kotlin/Native, do either for the following.
- Add `NATIVE_EXPERIMENT=true` to your environment.
- Go into [build.gradle.kts](./build.gradle.kts) & find `enableNative` & set the right side to true.
Afterwards, when syncing the project, the native source sets will become available to work on.
It is suggested to run `cinteropLibvoskNative` to generate the Kotlin C bindings to work with.
## Future
- Possibly target Kotlin/JS
- Possibly target Kotlin/Objective-C (?)
[source install]: https://alphacephei.com/vosk/install
[download]: https://github.com/alphacep/vosk-api/releases/latest
+190
View File
@@ -0,0 +1,190 @@
import org.jetbrains.dokka.gradle.DokkaTask
import org.jetbrains.kotlin.config.JvmTarget
/*
* Copyright 2020 Alpha Cephei Inc. & Doomsdayrs
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
plugins {
kotlin("multiplatform") version "1.8.10"
id("com.android.library")
`maven-publish`
id("org.jetbrains.dokka") version "1.7.20"
kotlin("plugin.serialization") version "1.8.10"
}
group = "com.alphacephei"
version = "0.4.0-alpha0"
repositories {
google()
mavenCentral()
}
val dokkaOutputDir = "$buildDir/dokka"
tasks.getByName<DokkaTask>("dokkaHtml") {
outputDirectory.set(file(dokkaOutputDir))
}
val deleteDokkaOutputDir by tasks.register<Delete>("deleteDokkaOutputDirectory") {
delete(dokkaOutputDir)
}
val javadocJar = tasks.register<Jar>("javadocJar") {
dependsOn(deleteDokkaOutputDir, tasks.dokkaHtml)
archiveClassifier.set("javadoc")
from(dokkaOutputDir)
}
fun org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension.native(
configure: org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTargetWithHostTests.() -> Unit = {}
) {
when {
org.jetbrains.kotlin.konan.target.HostManager.hostIsMingw -> mingwX64("native")
org.jetbrains.kotlin.konan.target.HostManager.hostIsLinux -> linuxX64("native")
org.jetbrains.kotlin.konan.target.HostManager.hostIsMac -> if (org.jetbrains.kotlin.konan.target.HostManager.hostArch() == "arm64") {
macosArm64("native")
} else {
macosX64("native")
}
else -> error("Unsupported Host OS: ${org.jetbrains.kotlin.konan.target.HostManager.hostOs()}")
}.apply(configure)
}
kotlin {
jvm {
compilations.all {
kotlinOptions.jvmTarget = JvmTarget.JVM_11.description
}
testRuns["test"].executionTask.configure {
useJUnitPlatform()
environment("MODEL", "VOSK_MODEL")
//environment("MODEL", "/home/doomsdayrs/Downloads/vosk-model-small-en-us-0.15/")
environment("LIBRARY", "VOSK_PATH")
//environment("LIBRARY", "/usr/local/lib64/libvosk/libvosk.so")
environment("AUDIO", "$projectDir/../python/example/test.wav")
}
}
android {
publishAllLibraryVariants()
}
/**
* If native target should be enabled or not.
*
* Currently disabled as there is no proper packaging distribution currently.
*/
@Suppress("SimplifyBooleanWithConstants") // Ignore, the false is for overrides
val enableNative = System.getenv("NATIVE_EXPERIMENT") == "true" || false
if (enableNative)
native {
val main by compilations.getting
val libvosk by main.cinterops.creating
binaries {
sharedLib()
}
}
publishing {
publications {
withType<MavenPublication> {
artifact(javadocJar)
pom {
url.set("http://www.alphacephei.com.com/vosk/")
licenses {
license {
name.set("The Apache License, Version 2.0")
url.set("http://www.apache.org/licenses/LICENSE-2.0.txt")
}
}
developers {
developer {
id.set("com.alphacephei")
name.set("Alpha Cephei Inc")
email.set("contact@alphacephei.com")
}
}
scm {
connection.set("scm:git:git://github.com/alphacep/vosk-api.git")
url.set("https://github.com/alphacep/vosk-api/")
}
}
}
}
}
val jna_version = "5.13.0"
val coroutines_version = "1.6.4"
sourceSets {
val commonMain by getting {
dependencies {
api("org.jetbrains.kotlinx:kotlinx-serialization-json:1.4.1")
api("org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutines_version")
}
}
val commonTest by getting {
dependencies {
implementation(kotlin("test"))
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:$coroutines_version")
}
}
val jvmMain by getting {
dependencies {
api("net.java.dev.jna:jna:$jna_version")
}
}
val jvmTest by getting
if (enableNative) {
val nativeMain by getting
}
val androidMain by getting {
dependsOn(jvmMain)
dependencies {
api("net.java.dev.jna:jna:$jna_version@aar")
}
}
val androidTest by getting {
dependencies {
implementation("junit:junit:4.13.2")
}
}
}
}
android {
compileSdk = 33
sourceSets["main"].manifest.srcFile("src/androidMain/AndroidManifest.xml")
defaultConfig {
minSdk = 24
targetSdk = 33
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}
publishing {
multipleVariants {
withSourcesJar()
withJavadocJar()
allVariants()
}
}
}
+31
View File
@@ -0,0 +1,31 @@
/*
* Copyright 2020 Alpha Cephei Inc. & Doomsdayrs
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
pluginManagement {
repositories {
google()
gradlePluginPortal()
mavenCentral()
}
resolutionStrategy {
eachPlugin {
if (requested.id.namespace == "com.android") {
useModule("com.android.tools.build:gradle:7.3.0")
}
}
}
}
rootProject.name = "vosk-api-kotlin"
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.alphacephei.library">
<uses-permission android:name="android.permission.RECORD_AUDIO" />
</manifest>
@@ -0,0 +1,46 @@
/*
* Copyright 2023 Alpha Cephei Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.vosk.android
/**
* Interface to receive recognition results
*/
interface RecognitionListener {
/**
* Called when partial recognition result is available.
*/
fun onPartialResult(hypothesis: String)
/**
* Called after silence occured.
*/
fun onResult(hypothesis: String)
/**
* Called after stream end.
*/
fun onFinalResult(hypothesis: String)
/**
* Called when an error occurs.
*/
fun onError(exception: Exception)
/**
* Called after timeout expired
*/
fun onTimeout()
}
@@ -0,0 +1,241 @@
/*
* Copyright 2023 Alpha Cephei Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.vosk.android
import android.annotation.SuppressLint
import android.media.AudioFormat
import android.media.AudioRecord
import android.media.MediaRecorder.AudioSource
import android.os.Handler
import android.os.Looper
import org.vosk.Recognizer
import java.io.IOException
import kotlin.math.roundToInt
/**
* Service that records audio in a thread, passes it to a recognizer and emits
* recognition results. Recognition events are passed to a client using
* [RecognitionListener]
*/
class SpeechService @Throws(IOException::class) constructor(
private val recognizer: Recognizer,
sampleRate: Float
) {
private val sampleRate: Int
private val bufferSize: Int
private val recorder: AudioRecord
private var recognizerThread: RecognizerThread? = null
private val mainHandler = Handler(Looper.getMainLooper())
/**
* Creates speech service. Service holds the AudioRecord object, so you
* need to call [.shutdown] in order to properly finalize it.
*
* @throws IOException thrown if audio recorder can not be created for some reason.
*/
init {
this.sampleRate = sampleRate.toInt()
bufferSize = (this.sampleRate * BUFFER_SIZE_SECONDS).roundToInt()
@SuppressLint("MissingPermission")
recorder = AudioRecord(
AudioSource.VOICE_RECOGNITION, this.sampleRate,
AudioFormat.CHANNEL_IN_MONO,
AudioFormat.ENCODING_PCM_16BIT, bufferSize * 2
)
if (recorder.state == AudioRecord.STATE_UNINITIALIZED) {
recorder.release()
throw IOException(
"Failed to initialize recorder. Microphone might be already in use."
)
}
}
/**
* Starts recognition. Does nothing if recognition is active.
*
* @return true if recognition was actually started
*/
fun startListening(listener: RecognitionListener): Boolean {
if (null != recognizerThread) return false
recognizerThread = RecognizerThread(listener)
recognizerThread!!.start()
return true
}
/**
* Starts recognition. After specified timeout listening stops and the
* endOfSpeech signals about that. Does nothing if recognition is active.
*
*
* timeout - timeout in milliseconds to listen.
*
* @return true if recognition was actually started
*/
fun startListening(listener: RecognitionListener, timeout: Int): Boolean {
if (null != recognizerThread) return false
recognizerThread = RecognizerThread(listener, timeout)
recognizerThread!!.start()
return true
}
private fun stopRecognizerThread(): Boolean {
if (null == recognizerThread) return false
try {
recognizerThread!!.interrupt()
recognizerThread!!.join()
} catch (e: InterruptedException) {
// Restore the interrupted status.
Thread.currentThread().interrupt()
}
recognizerThread = null
return true
}
/**
* Stops recognition. Listener should receive final result if there is
* any. Does nothing if recognition is not active.
*
* @return true if recognition was actually stopped
*/
fun stop(): Boolean {
return stopRecognizerThread()
}
/**
* Cancel recognition. Do not post any new events, simply cancel processing.
* Does nothing if recognition is not active.
*
* @return true if recognition was actually stopped
*/
fun cancel(): Boolean {
if (recognizerThread != null) {
recognizerThread!!.setPause(true)
}
return stopRecognizerThread()
}
/**
* Shutdown the recognizer and release the recorder
*/
fun shutdown() {
recorder.release()
}
fun setPause(paused: Boolean) {
if (recognizerThread != null) {
recognizerThread!!.setPause(paused)
}
}
/**
* Resets recognizer in a thread, starts recognition over again
*/
fun reset() {
if (recognizerThread != null) {
recognizerThread!!.reset()
}
}
private inner class RecognizerThread @JvmOverloads constructor(
var listener: RecognitionListener,
timeout: Int = Companion.NO_TIMEOUT
) : Thread() {
private var remainingSamples: Int
private val timeoutSamples: Int
@Volatile
private var paused = false
@Volatile
private var reset = false
init {
timeoutSamples = if (timeout != Companion.NO_TIMEOUT) {
timeout * sampleRate / 1000
} else {
Companion.NO_TIMEOUT
}
remainingSamples = timeoutSamples
}
/**
* When we are paused, don't process audio by the recognizer and don't emit
* any listener results
*
* @param paused the status of pause
*/
fun setPause(paused: Boolean) {
this.paused = paused
}
/**
* Set reset state to signal reset of the recognizer and start over
*/
fun reset() {
reset = true
}
override fun run() {
recorder.startRecording()
if (recorder.recordingState == AudioRecord.RECORDSTATE_STOPPED) {
recorder.stop()
val ioe = IOException(
"Failed to start recording. Microphone might be already in use."
)
mainHandler.post { listener.onError(ioe) }
}
val buffer = ShortArray(bufferSize)
while (!interrupted()
&& (timeoutSamples == Companion.NO_TIMEOUT || remainingSamples > 0)
) {
val nread = recorder.read(buffer, 0, buffer.size)
if (paused) {
continue
}
if (reset) {
recognizer.reset()
reset = false
}
if (nread < 0) throw RuntimeException("error reading audio buffer")
if (recognizer.acceptWaveform(buffer)) {
val result = recognizer.result
mainHandler.post { listener.onResult(result) }
} else {
val partialResult = recognizer.partialResult
mainHandler.post { listener.onPartialResult(partialResult) }
}
if (timeoutSamples != NO_TIMEOUT) {
remainingSamples -= nread
}
}
recorder.stop()
if (!paused) {
// If we met timeout signal that speech ended
if (timeoutSamples != NO_TIMEOUT && remainingSamples <= 0) {
mainHandler.post { listener.onTimeout() }
} else {
val finalResult = recognizer.finalResult
mainHandler.post { listener.onFinalResult(finalResult) }
}
}
}
}
companion object {
private const val NO_TIMEOUT = -1
private const val BUFFER_SIZE_SECONDS = 0.2f
}
}
@@ -0,0 +1,151 @@
/*
* Copyright 2023 Alpha Cephei Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.vosk.android
import android.os.Handler
import android.os.Looper
import org.vosk.Recognizer
import java.io.IOException
import java.io.InputStream
import kotlin.math.roundToInt
/**
* Service that recognizes stream audio in a thread, passes it to a recognizer and emits
* recognition results. Recognition events are passed to a client using
* [RecognitionListener]
*/
class SpeechStreamService(
private val recognizer: Recognizer,
inputStream: InputStream,
sampleRate: Float
) {
private val inputStream: InputStream
private val sampleRate: Int
private val bufferSize: Int
private var recognizerThread: Thread? = null
private val mainHandler = Handler(Looper.getMainLooper())
/**
* Creates speech service.
*/
init {
this.sampleRate = sampleRate.toInt()
this.inputStream = inputStream
bufferSize = (this.sampleRate * BUFFER_SIZE_SECONDS * 2).roundToInt()
}
/**
* Starts recognition. Does nothing if recognition is active.
*
* @return true if recognition was actually started
*/
fun start(listener: RecognitionListener): Boolean {
if (null != recognizerThread) return false
recognizerThread = RecognizerThread(listener)
recognizerThread!!.start()
return true
}
/**
* Starts recognition. After specified timeout listening stops and the
* endOfSpeech signals about that. Does nothing if recognition is active.
*
*
* timeout - timeout in milliseconds to listen.
*
* @return true if recognition was actually started
*/
fun start(listener: RecognitionListener, timeout: Int): Boolean {
if (null != recognizerThread) return false
recognizerThread = RecognizerThread(listener, timeout)
recognizerThread!!.start()
return true
}
/**
* Stops recognition. All listeners should receive final result if there is
* any. Does nothing if recognition is not active.
*
* @return true if recognition was actually stopped
*/
fun stop(): Boolean {
if (null == recognizerThread) return false
try {
recognizerThread!!.interrupt()
recognizerThread!!.join()
} catch (e: InterruptedException) {
// Restore the interrupted status.
Thread.currentThread().interrupt()
}
recognizerThread = null
return true
}
private inner class RecognizerThread @JvmOverloads constructor(
var listener: RecognitionListener,
timeout: Int = Companion.NO_TIMEOUT
) : Thread() {
private var remainingSamples: Int
private val timeoutSamples: Int
init {
if (timeout != Companion.NO_TIMEOUT) timeoutSamples =
timeout * sampleRate / 1000 else timeoutSamples = Companion.NO_TIMEOUT
remainingSamples = timeoutSamples
}
override fun run() {
val buffer = ByteArray(bufferSize)
while (!interrupted()
&& (timeoutSamples == Companion.NO_TIMEOUT || remainingSamples > 0)
) {
try {
val nread = inputStream.read(buffer, 0, buffer.size)
if (nread < 0) {
break
} else {
val isSilence: Boolean = recognizer.acceptWaveform(buffer)
if (isSilence) {
val result = recognizer.result
mainHandler.post { listener.onResult(result) }
} else {
val partialResult = recognizer.partialResult
mainHandler.post { listener.onPartialResult(partialResult) }
}
}
if (timeoutSamples != NO_TIMEOUT) {
remainingSamples -= nread
}
} catch (e: IOException) {
mainHandler.post { listener.onError(e) }
}
}
// If we met timeout signal that speech ended
if (timeoutSamples != NO_TIMEOUT && remainingSamples <= 0) {
mainHandler.post { listener.onTimeout() }
} else {
val finalResult = recognizer.finalResult
mainHandler.post { listener.onFinalResult(finalResult) }
}
}
}
companion object {
private const val NO_TIMEOUT = -1
private const val BUFFER_SIZE_SECONDS = 0.2f
}
}
@@ -0,0 +1,138 @@
/*
* Copyright 2023 Alpha Cephei Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.vosk.android
import android.content.Context
import android.content.res.AssetManager
import android.os.Environment
import android.os.Handler
import android.os.Looper
import android.util.Log
import org.vosk.Model
import java.io.*
import java.util.concurrent.Executor
import java.util.concurrent.Executors
import java.util.function.Consumer
/**
* Provides utility methods to sync model files to external storage to allow
* C++ code access them. Relies on file named "uuid" to track updates.
*/
object StorageService {
private val TAG = StorageService::class.simpleName
@JvmStatic
fun unpack(
context: Context,
sourcePath: String,
targetPath: String,
completeCallback: Consumer<Model>,
errorCallback: Consumer<IOException>
) {
val executor: Executor =
Executors.newSingleThreadExecutor() // change according to your requirements
val handler = Handler(Looper.getMainLooper())
executor.execute {
try {
val outputPath = sync(context, sourcePath, targetPath)
val model = Model(outputPath)
handler.post { completeCallback.accept(model) }
} catch (e: IOException) {
handler.post { errorCallback.accept(e) }
}
}
}
@JvmStatic
@Throws(IOException::class)
fun sync(context: Context, sourcePath: String, targetPath: String): String {
val assetManager = context.assets
val externalFilesDir = context.getExternalFilesDir(null)
?: throw IOException(
"cannot get external files dir, "
+ "external storage state is " + Environment.getExternalStorageState()
)
val targetDir = File(externalFilesDir, targetPath)
val resultPath = File(targetDir, sourcePath).absolutePath
val sourceUUID = readLine(assetManager.open("$sourcePath/uuid"))
try {
val targetUUID = readLine(FileInputStream(File(targetDir, "$sourcePath/uuid")))
if (targetUUID == sourceUUID) return resultPath
} catch (e: FileNotFoundException) {
// ignore
}
deleteContents(targetDir)
copyAssets(assetManager, sourcePath, targetDir)
// Copy uuid
copyFile(assetManager, "$sourcePath/uuid", targetDir)
return resultPath
}
@Throws(IOException::class)
private fun readLine(inputStream: InputStream): String {
return BufferedReader(InputStreamReader(inputStream)).use { it.readLine() }
}
private fun deleteContents(dir: File): Boolean {
val files = dir.listFiles()
var success = true
if (files != null) {
for (file in files) {
if (file.isDirectory) {
success = success and deleteContents(file)
}
if (!file.delete()) {
success = false
}
}
}
return success
}
@Throws(IOException::class)
private fun copyAssets(assetManager: AssetManager, path: String, outPath: File) {
val assets = assetManager.list(path) ?: return
if (assets.isEmpty()) {
if (!path.endsWith("uuid")) copyFile(assetManager, path, outPath)
} else {
val dir = File(outPath, path)
if (!dir.exists()) {
Log.v(TAG, "Making directory " + dir.absolutePath)
if (!dir.mkdirs()) {
Log.v(TAG, "Failed to create directory " + dir.absolutePath)
}
}
for (asset in assets) {
copyAssets(assetManager, "$path/$asset", outPath)
}
}
}
@Throws(IOException::class)
private fun copyFile(assetManager: AssetManager, fileName: String, outPath: File) {
Log.v(TAG, "Copy $fileName to $outPath")
assetManager.open(fileName).use { inputStream ->
FileOutputStream("$outPath/$fileName").use { out ->
val buffer = ByteArray(4000)
var read: Int
while (inputStream.read(buffer).also { read = it } != -1) {
out.write(buffer, 0, read)
}
}
}
}
}
@@ -0,0 +1,22 @@
/*
* Copyright 2023 Alpha Cephei Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.vosk
/**
* Thrown when a [Recognizer] cannot accept a given waveform
*/
class AcceptWaveformException(data: Any) : Exception("Could not accept waveform: $data")
@@ -0,0 +1,38 @@
/*
* Copyright 2023 Alpha Cephei Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.vosk
import org.vosk.exception.ModelException
/**
* Batch model object
*
* @since 26 / 12 / 2022
* @constructor Creates the batch recognizer object
*/
expect class BatchModel @Throws(ModelException::class) constructor(path: String) : Freeable {
/**
* Releases batch model object
*/
override fun free()
/**
* Wait for the processing
*/
fun await()
}
@@ -0,0 +1,62 @@
/*
* Copyright 2023 Alpha Cephei Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.vosk
/**
* Batch recognizer object
*
* @since 26 / 12 / 2022
* @constructor Creates batch recognizer object
*/
expect class BatchRecognizer constructor(model: BatchModel, sampleRate: Float) : Freeable {
/**
* Releases batch recognizer object
*/
override fun free()
/**
* Accept batch voice data
*/
fun acceptWaveform(data: ByteArray)
/**
* Set NLSML output
* @param nlsml - boolean value
*/
fun setNLSML(nlsml: Boolean)
/**
* Closes the stream
*/
fun finishStream()
/**
* Return results
*/
val frontResult: String
/**
* Release and free first retrieved result
*/
fun pop()
/**
* Get amount of pending chunks for more intelligent waiting
*/
val pendingChunks: Int
}
@@ -0,0 +1,31 @@
/*
* Copyright 2023 Alpha Cephei Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.vosk
/**
* Denotes an object that must be freed afterwards.
*
* On JVM, This is done via AutoClosable.
*/
@Suppress("SpellCheckingInspection")
interface Freeable {
/**
* Dereference the related object
*/
fun free()
}
@@ -0,0 +1,40 @@
/*
* Copyright 2023 Alpha Cephei Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.vosk
/**
* Log level for Kaldi messages.
*
* @since 26 / 12 / 2022
*/
enum class LogLevel(val value: Int) {
/**
* Don't print info messages
*/
WARNINGS(-1),
/**
* Default value to print info and error messages but no debug
*/
INFO(0),
/**
* More verbose mode
*/
DEBUG(1);
}
@@ -0,0 +1,52 @@
/*
* Copyright 2023 Alpha Cephei Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.vosk
import org.vosk.exception.IOException
/**
* Model stores all the data required for recognition
*
* It contains static data and can be shared across processing
* threads.
*
* @since 26 / 12 / 2022
* @constructor Loads model data from the file and returns the model object
* @param path the path of the model on the filesystem
* @throws IOException if the path provided is invalid
*/
expect class Model @Throws(IOException::class) constructor(path: String) : Freeable {
/**
* Check if a word can be recognized by the model
* @param word: the word
* @returns the word symbol if @param word exists inside the model
* or -1 otherwise.
* Reminding that word symbol 0 is for <epsilon>
*/
fun findWord(word: String): Int
/**
* Releases the model memory
*
* The model object is reference-counted so if some recognizer
* depends on this model, model might still stay alive. When
* last recognizer is released, model will be released too.
*/
override fun free()
}
@@ -0,0 +1,255 @@
/*
* Copyright 2023 Alpha Cephei Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.vosk
import org.vosk.exception.RecognizerException
/**
* Recognizer object is the main object which processes data.
*
* Each recognizer usually runs in own thread and takes audio as input.
* Once audio is processed recognizer returns JSON object as a string
* which represent decoded information - words, confidences, times, n-best lists,
* speaker information and so on
*
* @since 26 / 12 / 2022
*/
expect class Recognizer : Freeable {
/**
* 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 going to feed into the recognizer.
* Make sure this rate matches the audio content, it is a common
* issue causing accuracy problems.
* @throws RecognizerException if a problem occurred
*/
@Throws(RecognizerException::class)
constructor(model: Model, sampleRate: Float)
/**
* Creates the recognizer object with speaker recognition
*
* With the speaker recognition mode the recognizer not just recognize
* text but also return speaker vectors one can use for speaker identification
*
* @param model VoskModel containing static data for recognizer. Model can be
* shared across recognizers, even running in different threads.
* @param sampleRate The sample rate of the audio you going to feed into the recognizer.
* Make sure this rate matches the audio content, it is a common
* issue causing accuracy problems.
* @param speakerModel speaker model for speaker identification
* @throws RecognizerException if a problem occurred
*/
@Throws(RecognizerException::class)
constructor(model: Model, sampleRate: Float, speakerModel: SpeakerModel)
/**
* Creates the recognizer object with the phrase list
*
* Sometimes when you want to improve recognition accuracy and when you don't need
* to recognize large vocabulary you can specify a list of phrases to recognize. This
* will improve recognizer speed and accuracy but might return [unk] if user said
* something different.
*
* Only recognizers with lookahead models support this type of quick configuration.
* Precompiled HCLG graph models are not supported.
*
* @param model VoskModel containing static data for recognizer. Model can be
* shared across recognizers, even running in different threads.
* @param sampleRate The valuesample rate of the audio you going to feed into the recognizer.
* Make sure this rate matches the audio content, it is a common
* issue causing accuracy problems.
* @param grammar The string with the list of phrases to recognize as JSON array of strings,
* for example "["one two three four five", "[unk]"]".
*
* @throws RecognizerException if a problem occurred
*/
@Throws(RecognizerException::class)
constructor(model: Model, sampleRate: Float, grammar: String)
/**
* Adds speaker model to already initialized recognizer
*
* Can add speaker recognition model to already created recognizer. Helps to initialize
* speaker recognition for grammar-based recognizer.
*
* @param speakerModel Speaker recognition model
*/
fun setSpeakerModel(speakerModel: SpeakerModel)
/**
* Reconfigures recognizer to use grammar
*
* @param grammar Set of phrases in JSON array of strings or "[]" to use default model graph.
* See also vosk_recognizer_new_grm
*/
fun setGrammar(grammar: String)
/**
* Configures recognizer to output n-best results
*
* <pre>
* {
* "alternatives": [
* { "text": "one two three four five", "confidence": 0.97 },
* { "text": "one two three for five", "confidence": 0.03 },
* ]
* }
* </pre>
*
* @param maxAlternatives - maximum alternatives to return from recognition results
*/
fun setMaxAlternatives(maxAlternatives: Int)
/**
* Enables words with times in the output
*
* <pre>
* "result" : [{
* "conf" : 1.000000,
* "end" : 1.110000,
* "start" : 0.870000,
* "word" : "what"
* }, {
* "conf" : 1.000000,
* "end" : 1.530000,
* "start" : 1.110000,
* "word" : "zero"
* }, {
* "conf" : 1.000000,
* "end" : 1.950000,
* "start" : 1.530000,
* "word" : "zero"
* }, {
* "conf" : 1.000000,
* "end" : 2.340000,
* "start" : 1.950000,
* "word" : "zero"
* }, {
* "conf" : 1.000000,
* "end" : 2.610000,
* "start" : 2.340000,
* "word" : "one"
* }],
* </pre>
*
* @param words - boolean value
*/
fun setOutputWordTimes(words: Boolean)
/**
* Like [setOutputWordTimes] return words and confidences in partial results
*
* @param partialWords - boolean value
*/
fun setPartialWords(partialWords: Boolean)
/**
* Set NLSML output
* @param nlsml - boolean value
*/
fun setNLSML(nlsml: Boolean)
/**
* Accept voice data
*
* accept and process new chunk of voice data
*
* @param data Audio data in PCM 16-bit mono format.
* @param length Length of the audio data.
* @returns
* 1 - If silence is occurred and you can retrieve a new utterance with result method
* 0 - If decoding continues
* -1 - If exception occurred
*/
@Throws(AcceptWaveformException::class)
fun acceptWaveform(data: ByteArray): Boolean
/**
* Same as [acceptWaveform] but the version with the short data for language bindings where you have
* audio as array of shorts
*/
@Throws(AcceptWaveformException::class)
fun acceptWaveform(data: ShortArray): Boolean
/**
* Same as [acceptWaveform] but the version with the float data for language bindings where you have
* audio as array of floats
*/
@Throws(AcceptWaveformException::class)
fun acceptWaveform(data: FloatArray): Boolean
/**
* Returns speech recognition result
*
* @returns the result in JSON format which contains decoded line, decoded
* words, times in seconds and confidences. You can parse this result
* with any json parser
*
* <pre>
* {
* "text" : "what zero zero zero one"
* }
* </pre>
*
* If alternatives enabled it returns result with alternatives, see also [setMaxAlternatives].
*
* If word times enabled returns word time, see also [setOutputWordTimes].
*/
val result: String
/**
* Returns partial speech recognition
*
* @returns partial speech recognition text which is not yet finalized.
* result may change as recognizer process more data.
*
* <pre>
* {
* "partial" : "cyril one eight zero"
* }
* </pre>
*/
val finalResult: String
/**
* Returns speech recognition result. Same as result, but doesn't wait for silence
* You usually call it in the end of the stream to get final bits of audio. It
* flushes the feature pipeline, so all remaining audio chunks got processed.
*
* @returns speech result in JSON format.
*/
val partialResult: String
/**
* Resets the recognizer
*
* Resets current results so the recognition can continue from scratch
*/
fun reset()
/**
* Releases recognizer object
*
* Underlying model is also unreferenced and if needed released
*/
override fun free()
}
@@ -0,0 +1,40 @@
/*
* Copyright 2023 Alpha Cephei Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.vosk
import org.vosk.exception.ModelException
/**
* Speaker model is the same as model but contains the data
* for speaker identification.
*
* @since 26 / 12 / 2022
* @constructor Loads speaker model data from the file and returns the model object
* @param path the path of the model on the filesystem
* @throws ModelException if the path provided is invalid
*/
expect class SpeakerModel @Throws(ModelException::class) constructor(path: String) : Freeable {
/**
* Releases the model memory
*
* The model object is reference-counted so if some recognizer
* depends on this model, model might still stay alive. When
* last recognizer is released, model will be released too.
*/
override fun free()
}
@@ -0,0 +1,46 @@
/*
* Copyright 2023 Alpha Cephei Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.vosk
/**
* 26 / 12 / 2022
*
* Control overarching features of libvosk.
*/
expect object Vosk {
/**
* Set log level for Kaldi messages
*
* @param logLevel the level
*/
fun setLogLevel(logLevel: LogLevel)
/**
* Init, automatically select a CUDA device and allow multithreading.
* Must be called once from the main thread.
* Has no effect if HAVE_CUDA flag is not set.
*/
fun gpuInit()
/**
* Init CUDA device in a multi-threaded environment.
* Must be called for each thread.
* Has no effect if HAVE_CUDA flag is not set.
*/
fun gpuThreadInit()
}
@@ -0,0 +1,22 @@
/*
* Copyright 2023 Alpha Cephei Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.vosk.exception
/**
* Internal common IO exception. On JVM this is just a type alias.
*/
expect open class IOException(message: String? = null) : Exception
@@ -0,0 +1,22 @@
/*
* Copyright 2023 Alpha Cephei Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.vosk.exception
/**
* Thrown when there is an exception creating a model.
*/
class ModelException(path: String): IOException("Failed to find model: $path")
@@ -0,0 +1,22 @@
/*
* Copyright 2023 Alpha Cephei Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.vosk.exception
/**
* Thrown when the recognizer fails to be created
*/
class RecognizerException: IOException("Failed to create recognizer.")
@@ -0,0 +1,31 @@
/*
* Copyright 2023 Alpha Cephei Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.vosk.json
import kotlinx.serialization.Serializable
/**
* Represents an alternative result.
*
* @since 26 / 12 / 2022
*/
@Serializable
data class Alternative(
val confidence: Double,
val result: List<Result> = emptyList(),
val text: String
)
@@ -0,0 +1,61 @@
/*
* Copyright 2023 Alpha Cephei Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.vosk.json
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.encodeToJsonElement
import org.vosk.Recognizer
import org.vosk.Model
import org.vosk.exception.RecognizerException
/*
* 26 / 12 / 2022
*/
/**
* Vosk JSON encoder
*/
val voskJson = Json { encodeDefaults = true }
/**
* Get the result as a JSON object
*/
fun Recognizer.resultAsJson(): ResultOutput =
voskJson.decodeFromString(result)
/**
* Get the final result as a JSON object
*/
fun Recognizer.finalResultAsJson(): ResultOutput =
voskJson.decodeFromString(finalResult)
/**
* Get the partial result as a JSON object
*/
fun Recognizer.partialResultAsJson(): PartialResultOutput =
voskJson.decodeFromString(partialResult)
/**
* Create a [Recognizer], but using a list for grammar instead.
*
* The grammar list is converted into a JSON array
*/
@Throws(RecognizerException::class)
fun Recognizer(model: Model, sampleRate: Float, grammar: List<String>) =
Recognizer(model, sampleRate, voskJson.encodeToJsonElement(grammar).toString())
@@ -0,0 +1,32 @@
/*
* Copyright 2023 Alpha Cephei Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.vosk.json
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
/**
* Represents a partial result
*
* @since 26 / 12 / 2022
*/
@Serializable
data class PartialResultOutput(
val partial: String,
@SerialName("partial_result")
val partialResult: List<Result> = emptyList(),
)
@@ -0,0 +1,32 @@
/*
* Copyright 2023 Alpha Cephei Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.vosk.json
import kotlinx.serialization.Serializable
/**
* Represents a result for any given word.
*
* @since 26 / 12 / 2022
*/
@Serializable
data class Result(
val conf: Double? = null,
val end: Double,
val start: Double,
val word: String,
)
@@ -0,0 +1,31 @@
/*
* Copyright 2023 Alpha Cephei Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.vosk.json
import kotlinx.serialization.Serializable
/**
* Result output
*
* @since 26 / 12 / 2022
*/
@Serializable
data class ResultOutput(
val alternatives: List<Alternative> = emptyList(),
val result: List<Result> = emptyList(),
val text: String? = null
)
@@ -0,0 +1,38 @@
/*
* Copyright 2023 Alpha Cephei Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.vosk.json
/**
* For extension functions transforming (input streams->recognizers) into flows.
*/
sealed interface WaveformResult {
/**
* A result made after a period of silence.
*/
data class Result(val result: ResultOutput) : WaveformResult
/**
* A partial result that is being made in progress.
*/
data class PartialResult(val result: PartialResultOutput) : WaveformResult
/**
* A final result made after there is no more content to feed.
*/
data class FinalResult(val result: ResultOutput) : WaveformResult
}
@@ -0,0 +1,83 @@
/*
* Copyright 2023 Alpha Cephei Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.vosk
import com.sun.jna.PointerType
import org.vosk.exception.ModelException
import java.io.File
import java.nio.file.Path
import kotlin.io.path.absolutePathString
/**
* Batch model object
*
* @since 26 / 12 / 2022
*/
actual class BatchModel : Freeable, PointerType, AutoCloseable {
/**
* Empty constructor for JNA
*/
constructor()
/**
* Creates the batch recognizer object
*/
@Throws(ModelException::class)
actual constructor(path: String) : super(
LibVosk.vosk_batch_model_new(path) ?: throw ModelException(path)
)
/**
* Constructor using a Path, will retrieve absolutePath
*
* @param path to batch model
*/
@Throws(ModelException::class)
constructor(path: Path) : this(path.absolutePathString())
/**
* Constructor using a File, will retrieve absolutePath
*
* @param file to batch model
*/
@Throws(ModelException::class)
constructor(file: File) : this(file.absolutePath)
/**
* Releases batch model object
*/
actual override fun free() {
LibVosk.vosk_batch_model_free(this)
}
/**
* Wait for the processing
*/
actual fun await() {
LibVosk.vosk_batch_model_wait(this)
}
/**
* @see free
*/
override fun close() {
free()
}
}
@@ -0,0 +1,95 @@
/*
* Copyright 2023 Alpha Cephei Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.vosk
import com.sun.jna.PointerType
/**
* Batch recognizer object
*
* @since 26 / 12 / 2022
* @constructor Creates batch recognizer object
*/
actual class BatchRecognizer : Freeable, PointerType, AutoCloseable {
/**
* Empty constructor for JNA
*/
constructor()
/**
* Creates batch recognizer object
*/
actual constructor(model: BatchModel, sampleRate: Float) :
super(LibVosk.vosk_batch_recognizer_new(model, sampleRate))
/**
* Releases batch recognizer object
*/
actual override fun free() {
LibVosk.vosk_batch_recognizer_free(this);
}
/**
* Accept batch voice data
*/
actual fun acceptWaveform(data: ByteArray) {
LibVosk.vosk_batch_recognizer_accept_waveform(this, data, data.size)
}
/**
* Set NLSML output
* @param nlsml - boolean value
*/
actual fun setNLSML(nlsml: Boolean) {
LibVosk.vosk_batch_recognizer_set_nlsml(this, nlsml)
}
/**
* Closes the stream
*/
actual fun finishStream() {
LibVosk.vosk_batch_recognizer_finish_stream(this)
}
/**
* Return results
*/
actual val frontResult: String
get() = LibVosk.vosk_batch_recognizer_front_result(this)
/**
* Release and free first retrieved result
*/
actual fun pop() {
LibVosk.vosk_batch_recognizer_pop(this)
}
/**
* Get amount of pending chunks for more intelligent waiting
*/
actual val pendingChunks: Int
get() = LibVosk.vosk_batch_recognizer_get_pending_chunks(this)
/**
* @see free
*/
override fun close() {
free()
}
}
@@ -0,0 +1,197 @@
/*
* Copyright 2023 Alpha Cephei Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.vosk
import com.sun.jna.Native
import com.sun.jna.Platform
import com.sun.jna.Pointer
import java.io.File
import java.io.IOException
import java.io.InputStream
import java.nio.file.Files
import java.nio.file.StandardCopyOption
/**
* 26 / 12 / 2022
*/
@Suppress("FunctionName")
internal object LibVosk {
@JvmStatic
@Deprecated(
"LibVosk is now for internal JNA, use Vosk instead",
ReplaceWith("Vosk.setLogLevel(logLevel)", "org.vosk.Vosk")
)
fun setLogLevel(logLevel: LogLevel) {
Vosk.setLogLevel(logLevel)
}
@Throws(IOException::class)
private fun unpackDll(targetDir: File, lib: String) {
val source: InputStream =
Vosk::class.java.getResourceAsStream("/win32-x86-64/$lib.dll")!!
Files.copy(
source,
File(targetDir, "$lib.dll").toPath(),
StandardCopyOption.REPLACE_EXISTING
)
}
init {
when {
Platform.isAndroid() -> {
Native.register(LibVosk::class.java, "vosk")
}
Platform.isWindows() -> {
// We have to unpack dependencies
try {
// To get a tmp folder we unpack small library and mark it for deletion
val tmpFile: File = Native.extractFromResourcePath(
"/win32-x86-64/empty",
LibVosk::class.java.classLoader
)
val tmpDir = tmpFile.parentFile!!
File(tmpDir, tmpFile.name + ".x").createNewFile()
// Now unpack dependencies
unpackDll(tmpDir, "libwinpthread-1");
unpackDll(tmpDir, "libgcc_s_seh-1");
unpackDll(tmpDir, "libstdc++-6");
} catch (e: IOException) {
// Nothing for now, it will fail on next step
} finally {
Native.register(LibVosk::class.java, "libvosk");
}
}
else -> {
Native.register(LibVosk::class.java, "vosk");
}
}
}
external fun vosk_model_new(path: String): Pointer?
external fun vosk_model_free(model: Model)
external fun vosk_model_find_word(model: Model, word: String): Int
external fun vosk_spk_model_new(path: String): Pointer?
external fun vosk_spk_model_free(model: SpeakerModel)
external fun vosk_recognizer_new(model: Model, sampleRate: Float): Pointer?
external fun vosk_recognizer_new_spk(
model: Model,
sampleRate: Float,
spkModel: SpeakerModel
): Pointer?
external fun vosk_recognizer_new_grm(
model: Model,
sampleRate: Float,
grammar: String?
): Pointer?
external fun vosk_recognizer_set_spk_model(recognizer: Recognizer, spk_model: SpeakerModel)
external fun vosk_recognizer_set_grm(recognizer: Recognizer, grammar: String)
external fun vosk_recognizer_set_max_alternatives(recognizer: Recognizer, maxAlternatives: Int)
external fun vosk_recognizer_set_words(recognizer: Recognizer, words: Boolean)
external fun vosk_recognizer_set_partial_words(recognizer: Recognizer, partial_words: Boolean)
external fun vosk_recognizer_set_nlsml(recognizer: Recognizer, nlsml: Boolean)
external fun vosk_recognizer_accept_waveform(
recognizer: Recognizer,
data: ByteArray?,
len: Int
): Int
external fun vosk_recognizer_accept_waveform_s(
recognizer: Recognizer,
data: ShortArray?,
len: Int
): Int
external fun vosk_recognizer_accept_waveform_f(
recognizer: Recognizer,
data: FloatArray?,
len: Int
): Int
external fun vosk_recognizer_result(recognizer: Recognizer): String
external fun vosk_recognizer_final_result(recognizer: Recognizer): String
external fun vosk_recognizer_partial_result(recognizer: Recognizer): String
external fun vosk_recognizer_reset(recognizer: Recognizer)
external fun vosk_recognizer_free(recognizer: Recognizer)
external fun vosk_set_log_level(level: Int)
external fun vosk_gpu_init()
external fun vosk_gpu_thread_init()
external fun vosk_batch_model_new(path: String): Pointer?
external fun vosk_batch_model_free(model: BatchModel)
external fun vosk_batch_model_wait(model: BatchModel)
external fun vosk_batch_recognizer_new(batchModel: BatchModel, sampleRate: Float): Pointer
external fun vosk_batch_recognizer_free(recognizer: BatchRecognizer)
external fun vosk_batch_recognizer_accept_waveform(
recognizer: BatchRecognizer,
data: ByteArray?,
length: Int
)
external fun vosk_batch_recognizer_set_nlsml(
recognizer: BatchRecognizer,
nlsml: Boolean
)
external fun vosk_batch_recognizer_finish_stream(
recognizer: BatchRecognizer
)
external fun vosk_batch_recognizer_front_result(
recognizer: BatchRecognizer
): String
external fun vosk_batch_recognizer_pop(recognizer: BatchRecognizer)
external fun vosk_batch_recognizer_get_pending_chunks(recognizer: BatchRecognizer): Int
}
@@ -0,0 +1,95 @@
/*
* Copyright 2023 Alpha Cephei Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.vosk
import com.sun.jna.PointerType
import org.vosk.exception.IOException
import org.vosk.exception.ModelException
import java.io.File
import java.nio.file.Path
import kotlin.io.path.absolutePathString
/**
* Model stores all the data required for recognition
*
* It contains static data and can be shared across processing
* threads.
*
* @since 26 / 12 / 2022
* @constructor Loads model data from the file and returns the model object
* @throws IOException if the path provided is invalid
*/
actual class Model : Freeable, PointerType, AutoCloseable {
/**
* Empty constructor for JNA
*/
constructor()
/**
* Loads model data from the file and returns the model object
*/
@Throws(IOException::class)
actual constructor(path: String) : super(
LibVosk.vosk_model_new(path) ?: throw ModelException(path)
)
/**
* Constructor using a Path, will retrieve absolutePath
*
* @param path to batch model
*/
@Throws(IOException::class)
constructor(path: Path) : this(path.absolutePathString())
/**
* Constructor using a File, will retrieve absolutePath
*
* @param file to batch model
*/
@Throws(IOException::class)
constructor(file: File) : this(file.absolutePath)
/**
* Check if a word can be recognized by the model
* @param word: the word
* @returns the word symbol if @param word exists inside the model
* or -1 otherwise.
* Reminding that word symbol 0 is for <epsilon>
*/
actual fun findWord(word: String): Int =
LibVosk.vosk_model_find_word(this, word)
/**
* Releases the model memory
*
* The model object is reference-counted so if some recognizer
* depends on this model, model might still stay alive. When
* last recognizer is released, model will be released too.
*/
actual override fun free() {
LibVosk.vosk_model_free(this)
}
/**
* @see free
*/
override fun close() {
free()
}
}
@@ -0,0 +1,330 @@
/*
* Copyright 2023 Alpha Cephei Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.vosk
import com.sun.jna.PointerType
import org.vosk.exception.RecognizerException
/**
* Recognizer object is the main object which processes data.
*
* Each recognizer usually runs in own thread and takes audio as input.
* Once audio is processed recognizer returns JSON object as a string
* which represent decoded information - words, confidences, times, n-best lists,
* speaker information and so on
*
* @since 26 / 12 / 2022
*/
actual class Recognizer : Freeable, PointerType, AutoCloseable {
/**
* Empty constructor for JNA
*/
constructor()
/**
* 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 going to feed into the recognizer.
* Make sure this rate matches the audio content, it is a common
* issue causing accuracy problems.
* @throws RecognizerException if a problem occurred
*/
@Throws(RecognizerException::class)
actual constructor(model: Model, sampleRate: Float) :
super(LibVosk.vosk_recognizer_new(model, sampleRate) ?: throw RecognizerException())
/**
* Creates the recognizer object with speaker recognition
*
* With the speaker recognition mode the recognizer not just recognize
* text but also return speaker vectors one can use for speaker identification
*
* @param model VoskModel containing static data for recognizer. Model can be
* shared across recognizers, even running in different threads.
* @param sampleRate The sample rate of the audio you going to feed into the recognizer.
* Make sure this rate matches the audio content, it is a common
* issue causing accuracy problems.
* @param speakerModel speaker model for speaker identification
* @throws RecognizerException if a problem occurred
*/
@Throws(RecognizerException::class)
actual constructor(
model: Model,
sampleRate: Float,
speakerModel: SpeakerModel
) : super(
LibVosk.vosk_recognizer_new_spk(model, sampleRate, speakerModel)
?: throw RecognizerException()
)
/**
* Creates the recognizer object with the phrase list
*
* Sometimes when you want to improve recognition accuracy and when you don't need
* to recognize large vocabulary you can specify a list of phrases to recognize. This
* will improve recognizer speed and accuracy but might return [unk] if user said
* something different.
*
* Only recognizers with lookahead models support this type of quick configuration.
* Precompiled HCLG graph models are not supported.
*
* @param model VoskModel containing static data for recognizer. Model can be
* shared across recognizers, even running in different threads.
* @param sampleRate The valuesample rate of the audio you going to feed into the recognizer.
* Make sure this rate matches the audio content, it is a common
* issue causing accuracy problems.
* @param grammar The string with the list of phrases to recognize as JSON array of strings,
* for example "["one two three four five", "[unk]"]".
*
* @throws RecognizerException if a problem occurred
*/
@Throws(RecognizerException::class)
actual constructor(
model: Model,
sampleRate: Float,
grammar: String
) : super(
LibVosk.vosk_recognizer_new_grm(model, sampleRate, grammar) ?: throw RecognizerException()
)
/**
* JVM analog of Kotlin extension
* @see [org.vosk.json.Recognizer]
*/
@Throws(RecognizerException::class)
constructor(
model: Model,
sampleRate: Float,
grammar: List<String>
) : super(
// We need the full qualifer to avoid any build issues
@Suppress("RemoveRedundantQualifierName")
org.vosk.json.Recognizer(model, sampleRate, grammar).pointer
)
/**
* Adds speaker model to already initialized recognizer
*
* Can add speaker recognition model to already created recognizer. Helps to initialize
* speaker recognition for grammar-based recognizer.
*
* @param speakerModel Speaker recognition model
*/
actual fun setSpeakerModel(speakerModel: SpeakerModel) {
LibVosk.vosk_recognizer_set_spk_model(this, speakerModel)
}
/**
* Reconfigures recognizer to use grammar
*
* @param grammar Set of phrases in JSON array of strings or "[]" to use default model graph.
* See also vosk_recognizer_new_grm
*/
actual fun setGrammar(grammar: String) {
LibVosk.vosk_recognizer_set_grm(this, grammar)
}
/**
* Configures recognizer to output n-best results
*
* <pre>
* {
* "alternatives": [
* { "text": "one two three four five", "confidence": 0.97 },
* { "text": "one two three for five", "confidence": 0.03 },
* ]
* }
* </pre>
*
* @param maxAlternatives - maximum alternatives to return from recognition results
*/
actual fun setMaxAlternatives(maxAlternatives: Int) {
LibVosk.vosk_recognizer_set_max_alternatives(this, maxAlternatives)
}
/**
* Enables words with times in the output
*
* <pre>
* "result" : [{
* "conf" : 1.000000,
* "end" : 1.110000,
* "start" : 0.870000,
* "word" : "what"
* }, {
* "conf" : 1.000000,
* "end" : 1.530000,
* "start" : 1.110000,
* "word" : "zero"
* }, {
* "conf" : 1.000000,
* "end" : 1.950000,
* "start" : 1.530000,
* "word" : "zero"
* }, {
* "conf" : 1.000000,
* "end" : 2.340000,
* "start" : 1.950000,
* "word" : "zero"
* }, {
* "conf" : 1.000000,
* "end" : 2.610000,
* "start" : 2.340000,
* "word" : "one"
* }],
* </pre>
*
* @param words - boolean value
*/
actual fun setOutputWordTimes(words: Boolean) {
LibVosk.vosk_recognizer_set_words(this, words)
}
/**
* Like [setOutputWordTimes] return words and confidences in partial results
*
* @param partialWords - boolean value
*/
actual fun setPartialWords(partialWords: Boolean) {
LibVosk.vosk_recognizer_set_partial_words(this, partialWords)
}
/**
* Set NLSML output
* @param nlsml - boolean value
*/
actual fun setNLSML(nlsml: Boolean) {
LibVosk.vosk_recognizer_set_nlsml(this, nlsml)
}
/**
* Accept voice data
*
* accept and process new chunk of voice data
*
* @param data Audio data in PCM 16-bit mono format.
* @param length Length of the audio data.
* @returns
* 1 - If silence is occurred and you can retrieve a new utterance with result method
* 0 - If decoding continues
* -1 - If exception occurred
*/
@Throws(AcceptWaveformException::class)
actual fun acceptWaveform(data: ByteArray): Boolean {
val result = LibVosk.vosk_recognizer_accept_waveform(this, data, data.size)
if (result == -1) throw AcceptWaveformException(data)
return result == 1
}
/**
* Same as [acceptWaveform] but the version with the short data for language bindings where you have
* audio as array of shorts
*/
@Throws(AcceptWaveformException::class)
actual fun acceptWaveform(data: ShortArray): Boolean {
val result = LibVosk.vosk_recognizer_accept_waveform_s(this, data, data.size)
if (result == -1) throw AcceptWaveformException(data)
return result == 1
}
/**
* Same as [acceptWaveform] but the version with the float data for language bindings where you have
* audio as array of floats
*/
@Throws(AcceptWaveformException::class)
actual fun acceptWaveform(data: FloatArray): Boolean {
val result = LibVosk.vosk_recognizer_accept_waveform_f(this, data, data.size)
if (result == -1) throw AcceptWaveformException(data)
return result == 1
}
/**
* Returns speech recognition result
*
* @returns the result in JSON format which contains decoded line, decoded
* words, times in seconds and confidences. You can parse this result
* with any json parser
*
* <pre>
* {
* "text" : "what zero zero zero one"
* }
* </pre>
*
* If alternatives enabled it returns result with alternatives, see also [setMaxAlternatives].
*
* If word times enabled returns word time, see also [setOutputWordTimes].
*/
actual val result: String
get() = LibVosk.vosk_recognizer_result(this)
/**
* Returns partial speech recognition
*
* @returns partial speech recognition text which is not yet finalized.
* result may change as recognizer process more data.
*
* <pre>
* {
* "partial" : "cyril one eight zero"
* }
* </pre>
*/
actual val finalResult: String
get() = LibVosk.vosk_recognizer_final_result(this)
/**
* Returns speech recognition result. Same as result, but doesn't wait for silence
* You usually call it in the end of the stream to get final bits of audio. It
* flushes the feature pipeline, so all remaining audio chunks got processed.
*
* @returns speech result in JSON format.
*/
actual val partialResult: String
get() = LibVosk.vosk_recognizer_partial_result(this)
/**
* Resets the recognizer
*
* Resets current results so the recognition can continue from scratch
*/
actual fun reset() {
LibVosk.vosk_recognizer_reset(this)
}
/**
* Releases recognizer object
*
* Underlying model is also unreferenced and if needed released
*/
actual override fun free() {
LibVosk.vosk_recognizer_free(this)
}
/**
* @see free
*/
override fun close() {
free()
}
}
@@ -0,0 +1,84 @@
/*
* Copyright 2023 Alpha Cephei Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.vosk
import com.sun.jna.PointerType
import org.vosk.exception.ModelException
import java.io.File
import java.nio.file.Path
import kotlin.io.path.absolutePathString
/**
* Speaker model is the same as model but contains the data
* for speaker identification.
*
* @since 26 / 12 / 2022
* @constructor Loads speaker model data from the file and returns the model object
* @throws ModelException if the path provided is invalid
*/
actual class SpeakerModel : Freeable, PointerType, AutoCloseable {
/**
* Empty constructor for JNA
*/
constructor()
/**
* Loads speaker model data from the file and returns the model object
*
* @param path the path of the model on the filesystem
*/
@Throws(ModelException::class)
actual constructor(path: String) : super(
LibVosk.vosk_spk_model_new(path) ?: throw ModelException(path)
)
/**
* Constructor using a Path, will retrieve absolutePath
*
* @param path to batch model
*/
@Throws(ModelException::class)
constructor(path: Path) : this(path.absolutePathString())
/**
* Constructor using a File, will retrieve absolutePath
*
* @param file to batch model
*/
@Throws(ModelException::class)
constructor(file: File) : this(file.absolutePath)
/**
* Releases the model memory
*
* The model object is reference-counted so if some recognizer
* depends on this model, model might still stay alive. When
* last recognizer is released, model will be released too.
*/
actual override fun free() {
LibVosk.vosk_spk_model_free(this)
}
/**
* @see free
*/
override fun close() {
free()
}
}
@@ -0,0 +1,55 @@
/*
* Copyright 2023 Alpha Cephei Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.vosk
/**
* 26 / 12 / 2022
*
* Control overarching features of libvosk.
*/
actual object Vosk {
/**
* Set log level for Kaldi messages
*
* @param logLevel the level
*/
@JvmStatic
actual fun setLogLevel(logLevel: LogLevel) {
LibVosk.vosk_set_log_level(logLevel.value)
}
/**
* Init, automatically select a CUDA device and allow multithreading.
* Must be called once from the main thread.
* Has no effect if HAVE_CUDA flag is not set.
*/
@JvmStatic
actual fun gpuInit() {
LibVosk.vosk_gpu_init()
}
/**
* Init CUDA device in a multi-threaded environment.
* Must be called for each thread.
* Has no effect if HAVE_CUDA flag is not set.
*/
@JvmStatic
actual fun gpuThreadInit() {
LibVosk.vosk_gpu_thread_init()
}
}
@@ -0,0 +1,55 @@
/*
* Copyright 2023 Alpha Cephei Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.vosk
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
import org.vosk.json.WaveformResult
import org.vosk.json.finalResultAsJson
import org.vosk.json.partialResultAsJson
import org.vosk.json.resultAsJson
/**
* Feed an [Flow] of [ByteArray] into a [Recognizer].
*
* The returned flow will emit an [WaveformResult] for each result parsed.
*
* This expects a null terminator to signify the end of a stream.
*
* This will not close the [recognizer] at the end of reading.
*
* Any exceptions will be fed into the flow,
* and should be collected via [kotlinx.coroutines.flow.catch].
*
* Flows on [Dispatchers.IO] to prevent blocking the main thread.
*
* @see [Recognizer.acceptWaveform]
*/
fun Flow<ByteArray?>.feed(recognizer: Recognizer): Flow<WaveformResult> =
map {
if (it != null) {
if (recognizer.acceptWaveform(it)) {
WaveformResult.Result(recognizer.resultAsJson())
} else {
WaveformResult.PartialResult(recognizer.partialResultAsJson())
}
} else {
WaveformResult.FinalResult(recognizer.finalResultAsJson())
}
}.flowOn(Dispatchers.IO)
@@ -0,0 +1,22 @@
/*
* Copyright 2023 Alpha Cephei Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.vosk.exception
/**
* Analog of [java.io.IOException]
*/
actual typealias IOException = java.io.IOException
+212
View File
@@ -0,0 +1,212 @@
/*
* Copyright 2023 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.
*/
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.test.runTest
import org.vosk.*
import java.io.BufferedInputStream
import java.io.FileInputStream
import java.io.IOException
import java.nio.ByteBuffer
import java.nio.ByteOrder
import javax.sound.sampled.AudioSystem
import javax.sound.sampled.UnsupportedAudioFileException
import kotlin.test.Test
class DecoderTest {
val modelPath = System.getenv("MODEL")
val testFile = System.getenv("AUDIO")
init {
System.load(System.getenv("LIBRARY"))
Vosk.setLogLevel(LogLevel.DEBUG)
}
@Test
fun grammarList() {
Model(modelPath).use { model ->
Recognizer(model, 16000f, listOf("one")).apply {
setMaxAlternatives(10)
setOutputWordTimes(true)
setPartialWords(true)
}
}
}
@Test
@Throws(IOException::class, UnsupportedAudioFileException::class)
fun decoderTest() {
Model(modelPath).use { model ->
AudioSystem.getAudioInputStream(BufferedInputStream(FileInputStream(testFile)))
.use { ais ->
Recognizer(model, 16000f).apply {
setMaxAlternatives(10)
setOutputWordTimes(true)
setPartialWords(true)
}.use { recognizer ->
val b = ByteArray(4096)
while (ais.read(b) >= 0) {
if (recognizer.acceptWaveform(b)) {
println(recognizer.result)
} else {
println(recognizer.partialResult)
}
}
println(recognizer.finalResult)
}
}
}
}
@OptIn(ExperimentalCoroutinesApi::class)
@Test
@Throws(IOException::class, UnsupportedAudioFileException::class)
fun decoderTestFlow() = runTest {
Model(modelPath).use { model ->
Recognizer(model, 16000f).apply {
setMaxAlternatives(10)
setOutputWordTimes(true)
setPartialWords(true)
}.use { recognizer ->
AudioSystem.getAudioInputStream(BufferedInputStream(FileInputStream(testFile)))
.use { ais ->
flow {
val b = ByteArray(4096)
while (ais.read(b) >= 0) {
emit(b)
}
emit(null)
}.flowOn(Dispatchers.IO)
.feed(recognizer)
.collect {
println(it)
}
}
}
}
}
/**
* This test aims to simulate the situation for Dicio,
* In which we can receive the audio input stream before the recognizer is setup.
*
* It is recommended to use a large model on desktop to properly see how long it takes to load up.
*/
@Test
@Throws(IOException::class, UnsupportedAudioFileException::class)
fun decoderTestFlowBuffered() {
// Scope to buffer into
val scope = CoroutineScope(Dispatchers.IO)
AudioSystem.getAudioInputStream(BufferedInputStream(FileInputStream(testFile))).use { ais ->
val byteFlow = flow {
val b = ByteArray(4096)
while (ais.read(b) >= 0) {
emit(b)
}
emit(null)
}.flowOn(Dispatchers.IO)
.shareIn(scope, SharingStarted.Eagerly, 100)
// Tell us the current buffer size
println("Buffered size:" + byteFlow.replayCache.size)
var startTime = System.currentTimeMillis()
Model(modelPath).use { model ->
var resultTime = System.currentTimeMillis() - startTime
println("Model initialized in: $resultTime")
startTime = System.currentTimeMillis()
Recognizer(model, 16000f).apply {
setMaxAlternatives(10)
setOutputWordTimes(true)
setPartialWords(true)
}.use { recognizer ->
resultTime = System.currentTimeMillis() - startTime
println("Recognizer initialized in: $resultTime")
println("Buffered size:" + byteFlow.replayCache.size)
runBlocking {
byteFlow
.feed(recognizer)
.take(byteFlow.replayCache.size)
.collect {
println(it)
}
}
}
}
}
}
@Test
@Throws(IOException::class, UnsupportedAudioFileException::class)
fun decoderTestShort() {
Model(modelPath).use { model ->
AudioSystem.getAudioInputStream(BufferedInputStream(FileInputStream(testFile)))
.use { ais ->
Recognizer(model, 16000f).use { recognizer ->
val b = ByteArray(4096)
val s = ShortArray(2048)
while (ais.read(b) >= 0) {
ByteBuffer.wrap(b).order(ByteOrder.LITTLE_ENDIAN).asShortBuffer().get(s)
if (recognizer.acceptWaveform(s)) {
println(recognizer.result)
} else {
println(recognizer.partialResult)
}
}
println(recognizer.finalResult)
}
}
}
}
@Test
@Throws(IOException::class, UnsupportedAudioFileException::class)
fun decoderTestGrammar() {
Model(modelPath).use { model ->
AudioSystem.getAudioInputStream(BufferedInputStream(FileInputStream(testFile)))
.use { ais ->
Recognizer(
model, 16000f, "[\"one two three four five six seven eight nine zero oh\"]"
).use { recognizer ->
val b = ByteArray(4096)
while (ais.read(b) >= 0) {
if (recognizer.acceptWaveform(b)) {
println(recognizer.result)
} else {
println(recognizer.partialResult)
}
}
println(recognizer.finalResult)
}
}
}
}
@Test
fun decoderTestException() {
try {
val model = Model("model_missing")
assert(false)
} catch (e: IOException) {
assert(true)
}
}
}
@@ -0,0 +1,25 @@
headers = libvosk/vosk_api.h
package = libvosk
noStringConversion = \
vosk_batch_recognizer_accept_waveform \
vosk_recognizer_accept_waveform
compilerOpts.linux = \
-I/usr/include/libvosk/ \
-I/usr/local/include/libvosk/ \
-I/usr/include/ \
-I/usr/local/include/
compilerOpts.linux_x64 = \
-I/usr/lib64/libvosk/ \
-I/usr/local/lib64/libvosk/
linkerOpts.linux = \
-L/usr/lib/ \
-L/usr/local/lib/ \
-llibvosk
linkerOpts.linux_x64 = \
-L/usr/lib64/ \
-L/usr/local/lib64/
@@ -0,0 +1,50 @@
/*
* Copyright 2020 Alpha Cephei Inc. & Doomsdayrs
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.vosk
import cnames.structs.VoskBatchModel
import kotlinx.cinterop.CPointer
import libvosk.vosk_batch_model_free
import libvosk.vosk_batch_model_new
import libvosk.vosk_batch_model_wait
/**
* Batch model object
*
* 26 / 12 / 2022
*/
actual class BatchModel(val pointer: CPointer<VoskBatchModel>) : Freeable {
/**
* Creates the batch recognizer object
*/
@Throws(IOException::class)
actual constructor(path: String) : this(vosk_batch_model_new(path) ?: throw ioException(path))
/**
* Releases batch model object
*/
actual override fun free() {
vosk_batch_model_free(pointer)
}
/**
* Wait for the processing
*/
actual fun await() {
vosk_batch_model_wait(pointer)
}
}
@@ -0,0 +1,86 @@
/*
* Copyright 2020 Alpha Cephei Inc. & Doomsdayrs
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.vosk
import cnames.structs.VoskBatchRecognizer
import kotlinx.cinterop.CPointer
import kotlinx.cinterop.toCValues
import kotlinx.cinterop.toKString
import libvosk.*
/**
* Batch recognizer object
*
* 26 / 12 / 2022
*/
actual class BatchRecognizer(val pointer: CPointer<VoskBatchRecognizer>) : Freeable {
/**
* Creates batch recognizer object
*/
actual constructor(
model: BatchModel,
sampleRate: Float
) : this(vosk_batch_recognizer_new(model.pointer, sampleRate)!!)
/**
* Releases batch recognizer object
*/
actual override fun free() {
vosk_batch_recognizer_free(pointer)
}
/**
* Accept batch voice data
*/
actual fun acceptWaveform(data: ByteArray) {
vosk_batch_recognizer_accept_waveform(pointer, data.toCValues(), data.size)
}
/**
* Set NLSML output
* @param nlsml - boolean value
*/
actual fun setNLSML(nlsml: Boolean) {
vosk_batch_recognizer_set_nlsml(pointer, nlsml.toInt())
}
/**
* Closes the stream
*/
actual fun finishStream() {
vosk_batch_recognizer_finish_stream(pointer)
}
/**
* Return results
*/
actual val frontResult: String
get() = vosk_batch_recognizer_front_result(pointer)!!.toKString()
/**
* Release and free first retrieved result
*/
actual fun pop() {
vosk_batch_recognizer_pop(pointer)
}
/**
* Get amount of pending chunks for more intelligent waiting
*/
actual val pendingChunks: Int
get() = vosk_batch_recognizer_get_pending_chunks(pointer)
}
@@ -0,0 +1,23 @@
/*
* Copyright 2020 Alpha Cephei Inc. & Doomsdayrs
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.vosk
/**
* Use a [Freeable], then immediately free it and return any values.
*/
fun <T : Freeable, R> T.use(block: (T) -> R): R =
block(this).also { free() }
@@ -0,0 +1,3 @@
package org.vosk
actual class IOException actual constructor(message: String?) : Exception()
@@ -0,0 +1,63 @@
/*
* Copyright 2020 Alpha Cephei Inc. & Doomsdayrs
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.vosk
import cnames.structs.VoskModel
import kotlinx.cinterop.CPointer
import libvosk.vosk_model_find_word
import libvosk.vosk_model_free
import libvosk.vosk_model_new
/**
* Model stores all the data required for recognition
*
* It contains static data and can be shared across processing
* threads.
*
* 26 / 12 / 2022
*/
actual class Model(val pointer: CPointer<VoskModel>) : Freeable {
/**
* Loads model data from the file and returns the model object
*
* @param path: the path of the model on the filesystem
* @returns model object or NULL if problem occured
*/
@Throws(IOException::class)
actual constructor(path: String) : this(vosk_model_new(path) ?: throw ioException(path))
/**
* Check if a word can be recognized by the model
* @param word: the word
* @returns the word symbol if @param word exists inside the model
* or -1 otherwise.
* Reminding that word symbol 0 is for <epsilon>
*/
actual fun findWord(word: String): Int =
vosk_model_find_word(pointer, word)
/**
* Releases the model memory
*
* The model object is reference-counted so if some recognizer
* depends on this model, model might still stay alive. When
* last recognizer is released, model will be released too.
*/
actual override fun free() {
vosk_model_free(pointer)
}
}
@@ -0,0 +1,310 @@
/*
* Copyright 2020 Alpha Cephei Inc. & Doomsdayrs
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.vosk
import kotlinx.cinterop.CPointer
import kotlinx.cinterop.toCValues
import kotlinx.cinterop.toKString
import libvosk.*
/**
* Recognizer object is the main object which processes data.
*
* Each recognizer usually runs in own thread and takes audio as input.
* Once audio is processed recognizer returns JSON object as a string
* which represent decoded information - words, confidences, times, n-best lists,
* speaker information and so on
*
* 26 / 12 / 2022
*/
actual class Recognizer(val pointer: CPointer<VoskRecognizer>) : Freeable {
/**
* 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 going to feed into the recognizer.
* Make sure this rate matches the audio content, it is a common
* issue causing accuracy problems.
* @returns recognizer object or NULL if problem occured
*/
actual constructor(
model: Model,
sampleRate: Float
) : this(
vosk_recognizer_new(
model.pointer,
sampleRate
)!!
)
/**
* Creates the recognizer object with speaker recognition
*
* With the speaker recognition mode the recognizer not just recognize
* text but also return speaker vectors one can use for speaker identification
*
* @param model VoskModel containing static data for recognizer. Model can be
* shared across recognizers, even running in different threads.
* @param sampleRate The sample rate of the audio you going to feed into the recognizer.
* Make sure this rate matches the audio content, it is a common
* issue causing accuracy problems.
* @param speakerModel speaker model for speaker identification
* @returns recognizer object or NULL if problem occured
*/
actual constructor(
model: Model,
sampleRate: Float,
speakerModel: SpeakerModel
) : this(
vosk_recognizer_new_spk(
model.pointer,
sampleRate,
speakerModel.pointer
)!!
)
/**
* Creates the recognizer object with the phrase list
*
* Sometimes when you want to improve recognition accuracy and when you don't need
* to recognize large vocabulary you can specify a list of phrases to recognize. This
* will improve recognizer speed and accuracy but might return [unk] if user said
* something different.
*
* Only recognizers with lookahead models support this type of quick configuration.
* Precompiled HCLG graph models are not supported.
*
* @param model VoskModel containing static data for recognizer. Model can be
* shared across recognizers, even running in different threads.
* @param sampleRate The sample rate of the audio you going to feed into the recognizer.
* Make sure this rate matches the audio content, it is a common
* issue causing accuracy problems.
* @param grammar The string with the list of phrases to recognize as JSON array of strings,
* for example "["one two three four five", "[unk]"]".
*
* @returns recognizer object or NULL if problem occured
*/
actual constructor(
model: Model,
sampleRate: Float,
grammar: String
) : this(
vosk_recognizer_new_grm(
model.pointer,
sampleRate,
grammar
)!!
)
/**
* Adds speaker model to already initialized recognizer
*
* Can add speaker recognition model to already created recognizer. Helps to initialize
* speaker recognition for grammar-based recognizer.
*
* @param speakerModel Speaker recognition model
*/
actual fun setSpeakerModel(speakerModel: SpeakerModel) {
vosk_recognizer_set_spk_model(pointer, speakerModel.pointer)
}
/**
* Reconfigures recognizer to use grammar
*
* @param grammar Set of phrases in JSON array of strings or "[]" to use default model graph.
* See also vosk_recognizer_new_grm
*/
actual fun setGrammar(grammar: String) {
vosk_recognizer_set_grm(pointer, grammar)
}
/**
* Configures recognizer to output n-best results
*
* <pre>
* {
* "alternatives": [
* { "text": "one two three four five", "confidence": 0.97 },
* { "text": "one two three for five", "confidence": 0.03 },
* ]
* }
* </pre>
*
* @param maxAlternatives - maximum alternatives to return from recognition results
*/
actual fun setMaxAlternatives(maxAlternatives: Int) {
vosk_recognizer_set_max_alternatives(pointer, maxAlternatives)
}
/**
* Enables words with times in the output
*
* <pre>
* "result" : [{
* "conf" : 1.000000,
* "end" : 1.110000,
* "start" : 0.870000,
* "word" : "what"
* }, {
* "conf" : 1.000000,
* "end" : 1.530000,
* "start" : 1.110000,
* "word" : "zero"
* }, {
* "conf" : 1.000000,
* "end" : 1.950000,
* "start" : 1.530000,
* "word" : "zero"
* }, {
* "conf" : 1.000000,
* "end" : 2.340000,
* "start" : 1.950000,
* "word" : "zero"
* }, {
* "conf" : 1.000000,
* "end" : 2.610000,
* "start" : 2.340000,
* "word" : "one"
* }],
* </pre>
*
* @param words - boolean value
*/
actual fun setWords(words: Boolean) {
vosk_recognizer_set_words(pointer, words.toInt())
}
/**
* Like above return words and confidences in partial results
*
* @param partialWords - boolean value
*/
actual fun setPartialWords(partialWords: Boolean) {
vosk_recognizer_set_partial_words(pointer, partialWords.toInt())
}
/**
* Set NLSML output
* @param nlsml - boolean value
*/
actual fun setNLSML(nlsml: Boolean) {
vosk_recognizer_set_nlsml(pointer, nlsml.toInt())
}
/**
* Accept voice data
*
* accept and process new chunk of voice data
*
* @param data - audio data in PCM 16-bit mono format
* @param length - length of the audio data
* @returns 1 if silence is occured and you can retrieve a new utterance with result method
* 0 if decoding continues
* -1 if exception occured
*/
@Throws(AcceptWaveformException::class)
actual fun acceptWaveform(data: ByteArray): Boolean {
val result = vosk_recognizer_accept_waveform(pointer, data.toCValues(), data.size)
if (result == -1) throw AcceptWaveformException(data)
return result == 1
}
/**
* Same as above but the version with the short data for language bindings where you have
* audio as array of shorts
*/
@Throws(AcceptWaveformException::class)
actual fun acceptWaveform(data: ShortArray): Boolean {
val result = vosk_recognizer_accept_waveform_s(pointer, data.toCValues(), data.size)
if (result == -1) throw AcceptWaveformException(data)
return result == 1
}
/**
* Same as above but the version with the float data for language bindings where you have
* audio as array of floats
*/
@Throws(AcceptWaveformException::class)
actual fun acceptWaveform(data: FloatArray): Boolean {
val result = vosk_recognizer_accept_waveform_f(pointer, data.toCValues(), data.size)
if (result == -1) throw AcceptWaveformException(data)
return result == 1
}
/**
* Returns speech recognition result
*
* @returns the result in JSON format which contains decoded line, decoded
* words, times in seconds and confidences. You can parse this result
* with any json parser
*
* <pre>
* {
* "text" : "what zero zero zero one"
* }
* </pre>
*
* If alternatives enabled it returns result with alternatives, see also vosk_recognizer_set_max_alternatives().
*
* If word times enabled returns word time, see also vosk_recognizer_set_word_times().
*/
actual val result: String
get() = vosk_recognizer_result(pointer)!!.toKString()
/**
* Returns partial speech recognition
*
* @returns partial speech recognition text which is not yet finalized.
* result may change as recognizer process more data.
*
* <pre>
* {
* "partial" : "cyril one eight zero"
* }
* </pre>
*/
actual val finalResult: String
get() = vosk_recognizer_result(pointer)!!.toKString()
/**
* Returns speech recognition result. Same as result, but doesn't wait for silence
* You usually call it in the end of the stream to get final bits of audio. It
* flushes the feature pipeline, so all remaining audio chunks got processed.
*
* @returns speech result in JSON format.
*/
actual val partialResult: String
get() = vosk_recognizer_partial_result(pointer)!!.toKString()
/**
* Resets the recognizer
*
* Resets current results so the recognition can continue from scratch */
actual fun reset() {
vosk_recognizer_reset(pointer)
}
/**
* Releases recognizer object
*
* Underlying model is also unreferenced and if needed released */
actual override fun free() {
vosk_recognizer_free(pointer)
}
}
@@ -0,0 +1,50 @@
/*
* Copyright 2020 Alpha Cephei Inc. & Doomsdayrs
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.vosk
import cnames.structs.VoskSpkModel
import kotlinx.cinterop.CPointer
import libvosk.vosk_spk_model_free
import libvosk.vosk_spk_model_new
/**
* Speaker model is the same as model but contains the data
* for speaker identification.
*
* 26 / 12 / 2022
*/
actual class SpeakerModel(val pointer: CPointer<VoskSpkModel>) : Freeable {
/**
* Loads speaker model data from the file and returns the model object
*
* @param path: the path of the model on the filesystem
* @returns model object or NULL if problem occurred
*/
@Throws(IOException::class)
actual constructor(path: String) : this(vosk_spk_model_new(path) ?: throw ioException(path))
/**
* Releases the model memory
*
* The model object is reference-counted so if some recognizer
* depends on this model, model might still stay alive. When
* last recognizer is released, model will be released too.
*/
actual override fun free() {
vosk_spk_model_free(pointer)
}
}
@@ -0,0 +1,26 @@
/*
* Copyright 2020 Alpha Cephei Inc. & Doomsdayrs
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.vosk
/*
* 26 / 12 / 2022
*/
/**
* Converts a boolean to an int
*/
internal inline fun Boolean.toInt() = if (this) 1 else 0
@@ -0,0 +1,52 @@
/*
* Copyright 2020 Alpha Cephei Inc. & Doomsdayrs
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.vosk
import libvosk.vosk_set_log_level
import libvosk.vosk_gpu_init
import libvosk.vosk_gpu_thread_init
/**
* 26 / 12 / 2022
*/
actual object Vosk {
/** Set log level for Kaldi messages
*
* @param logLevel the level
*/
actual fun setLogLevel(logLevel: LogLevel) {
vosk_set_log_level(logLevel.value)
}
/**
* Init, automatically select a CUDA device and allow multithreading.
* Must be called once from the main thread.
* Has no effect if HAVE_CUDA flag is not set.
*/
actual fun gpuInit() {
vosk_gpu_init()
}
/**
* Init CUDA device in a multi-threaded environment.
* Must be called for each thread.
* Has no effect if HAVE_CUDA flag is not set.
*/
actual fun gpuThreadInit() {
vosk_gpu_thread_init()
}
}
+1 -1
View File
@@ -72,7 +72,7 @@ if (os.platform() == 'win32') {
// Update path to load dependent dlls
let currentPath = process.env.Path;
let dllDirectory = path.resolve(path.join(__dirname, "lib", "win-x86_64"));
process.env.Path = currentPath + path.delimiter + dllDirectory;
process.env.Path = dllDirectory + path.delimiter + currentPath;
soname = path.join(__dirname, "lib", "win-x86_64", "libvosk.dll")
} else if (os.platform() == 'darwin') {
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "vosk",
"version": "0.3.42",
"version": "0.3.45",
"description": "Node binding for continuous offline voice recoginition with Vosk library.",
"repository": {
"type": "git",
+639
View File
@@ -0,0 +1,639 @@
[MAIN]
# Analyse import fallback blocks. This can be used to support both Python 2 and
# 3 compatible code, which means that the block might have code that exists
# only in one or another interpreter, leading to false positives when analysed.
analyse-fallback-blocks=no
# Load and enable all available extensions. Use --list-extensions to see a list
# all available extensions.
#enable-all-extensions=
# In error mode, messages with a category besides ERROR or FATAL are
# suppressed, and no reports are done by default. Error mode is compatible with
# disabling specific errors.
#errors-only=
# Always return a 0 (non-error) status code, even if lint errors are found.
# This is primarily useful in continuous integration scripts.
#exit-zero=
# A comma-separated list of package or module names from where C extensions may
# be loaded. Extensions are loading into the active Python interpreter and may
# run arbitrary code.
extension-pkg-allow-list=
# A comma-separated list of package or module names from where C extensions may
# be loaded. Extensions are loading into the active Python interpreter and may
# run arbitrary code. (This is an alternative name to extension-pkg-allow-list
# for backward compatibility.)
extension-pkg-whitelist=
# Return non-zero exit code if any of these messages/categories are detected,
# even if score is above --fail-under value. Syntax same as enable. Messages
# specified are enabled, while categories only check already-enabled messages.
fail-on=
# Specify a score threshold under which the program will exit with error.
fail-under=10
# Interpret the stdin as a python script, whose filename needs to be passed as
# the module_or_package argument.
#from-stdin=
# Files or directories to be skipped. They should be base names, not paths.
ignore=CVS
# Add files or directories matching the regular expressions patterns to the
# ignore-list. The regex matches against paths and can be in Posix or Windows
# format. Because '\' represents the directory delimiter on Windows systems, it
# can't be used as an escape character.
ignore-paths=
# Files or directories matching the regular expression patterns are skipped.
# The regex matches against base names, not paths. The default value ignores
# Emacs file locks
ignore-patterns=^\.#
# List of module names for which member attributes should not be checked
# (useful for modules/projects where namespaces are manipulated during runtime
# and thus existing member attributes cannot be deduced by static analysis). It
# supports qualified module names, as well as Unix pattern matching.
ignored-modules=
# Python code to execute, usually for sys.path manipulation such as
# pygtk.require().
#init-hook=
# Use multiple processes to speed up Pylint. Specifying 0 will auto-detect the
# number of processors available to use, and will cap the count on Windows to
# avoid hangs.
jobs=1
# Control the amount of potential inferred values when inferring a single
# object. This can help the performance when dealing with large functions or
# complex, nested conditions.
limit-inference-results=100
# List of plugins (as comma separated values of python module names) to load,
# usually to register additional checkers.
load-plugins=
# Pickle collected data for later comparisons.
persistent=yes
# Minimum Python version to use for version dependent checks. Will default to
# the version used to run pylint.
py-version=3.7
# Discover python modules and packages in the file system subtree.
recursive=no
# When enabled, pylint would attempt to guess common misconfiguration and emit
# user-friendly hints instead of false-positive error messages.
suggestion-mode=yes
# Allow loading of arbitrary C extensions. Extensions are imported into the
# active Python interpreter and may run arbitrary code.
unsafe-load-any-extension=no
# In verbose mode, extra non-checker-related info will be displayed.
#verbose=
[REPORTS]
# Python expression which should return a score less than or equal to 10. You
# have access to the variables 'fatal', 'error', 'warning', 'refactor',
# 'convention', and 'info' which contain the number of messages in each
# category, as well as 'statement' which is the total number of statements
# analyzed. This score is used by the global evaluation report (RP0004).
evaluation=max(0, 0 if fatal else 10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10))
# Template used to display messages. This is a python new-style format string
# used to format the message information. See doc for all details.
msg-template=
# Set the output format. Available formats are text, parseable, colorized, json
# and msvs (visual studio). You can also give a reporter class, e.g.
# mypackage.mymodule.MyReporterClass.
#output-format=
# Tells whether to display a full report or only the messages.
reports=no
# Activate the evaluation score.
score=yes
[MESSAGES CONTROL]
# Only show warnings with the listed confidence levels. Leave empty to show
# all. Valid levels: HIGH, CONTROL_FLOW, INFERENCE, INFERENCE_FAILURE,
# UNDEFINED.
confidence=HIGH,
CONTROL_FLOW,
INFERENCE,
INFERENCE_FAILURE,
UNDEFINED
# Disable the message, report, category or checker with the given id(s). You
# can either give multiple identifiers separated by comma (,) or put this
# option multiple times (only on the command line, not in the configuration
# file where it should appear only once). You can also use "--disable=all" to
# disable everything first and then re-enable specific checks. For example, if
# you want to run only the similarities checker, you can use "--disable=all
# --enable=similarities". If you want to run only the classes checker, but have
# no Warning level messages displayed, use "--disable=all --enable=classes
# --disable=W".
disable=raw-checker-failed,
bad-inline-option,
locally-disabled,
file-ignored,
suppressed-message,
useless-suppression,
deprecated-pragma,
use-symbolic-message-instead,
missing-module-docstring, # added
missing-class-docstring, # added
missing-function-docstring, # added
invalid-name, # added
broad-except, # added
wrong-import-order, # added
consider-using-f-string, # added
logging-format-interpolation, # added
no-member, # added
consider-using-with, # added
unused-argument,
no-else-return, # added
too-few-public-methods, # added
import-error, # added
protected-access, # added
unused-variable, # added
R0801 # added (similarity of two scripts)
# Enable the message, report, category or checker with the given id(s). You can
# either give multiple identifier separated by comma (,) or put this option
# multiple time (only on the command line, not in the configuration file where
# it should appear only once). See also the "--disable" option for examples.
enable=c-extension-no-member
[BASIC]
# Naming style matching correct argument names.
argument-naming-style=snake_case
# Regular expression matching correct argument names. Overrides argument-
# naming-style. If left empty, argument names will be checked with the set
# naming style.
#argument-rgx=
# Naming style matching correct attribute names.
attr-naming-style=snake_case
# Regular expression matching correct attribute names. Overrides attr-naming-
# style. If left empty, attribute names will be checked with the set naming
# style.
#attr-rgx=
# Bad variable names which should always be refused, separated by a comma.
bad-names=foo,
bar,
baz,
toto,
tutu,
tata
# Bad variable names regexes, separated by a comma. If names match any regex,
# they will always be refused
bad-names-rgxs=
# Naming style matching correct class attribute names.
class-attribute-naming-style=any
# Regular expression matching correct class attribute names. Overrides class-
# attribute-naming-style. If left empty, class attribute names will be checked
# with the set naming style.
#class-attribute-rgx=
# Naming style matching correct class constant names.
class-const-naming-style=UPPER_CASE
# Regular expression matching correct class constant names. Overrides class-
# const-naming-style. If left empty, class constant names will be checked with
# the set naming style.
#class-const-rgx=
# Naming style matching correct class names.
class-naming-style=PascalCase
# Regular expression matching correct class names. Overrides class-naming-
# style. If left empty, class names will be checked with the set naming style.
#class-rgx=
# Naming style matching correct constant names.
const-naming-style=UPPER_CASE
# Regular expression matching correct constant names. Overrides const-naming-
# style. If left empty, constant names will be checked with the set naming
# style.
#const-rgx=
# Minimum line length for functions/classes that require docstrings, shorter
# ones are exempt.
docstring-min-length=-1
# Naming style matching correct function names.
function-naming-style=snake_case
# Regular expression matching correct function names. Overrides function-
# naming-style. If left empty, function names will be checked with the set
# naming style.
#function-rgx=
# Good variable names which should always be accepted, separated by a comma.
good-names=i,
j,
k,
ex,
Run,
_,
x,
y,
nx,
ny
# Good variable names regexes, separated by a comma. If names match any regex,
# they will always be accepted
good-names-rgxs=
# Include a hint for the correct naming format with invalid-name.
include-naming-hint=no
# Naming style matching correct inline iteration names.
inlinevar-naming-style=any
# Regular expression matching correct inline iteration names. Overrides
# inlinevar-naming-style. If left empty, inline iteration names will be checked
# with the set naming style.
#inlinevar-rgx=
# Naming style matching correct method names.
method-naming-style=snake_case
# Regular expression matching correct method names. Overrides method-naming-
# style. If left empty, method names will be checked with the set naming style.
#method-rgx=
# Naming style matching correct module names.
module-naming-style=snake_case
# Regular expression matching correct module names. Overrides module-naming-
# style. If left empty, module names will be checked with the set naming style.
#module-rgx=
# Colon-delimited sets of names that determine each other's naming style when
# the name regexes allow several styles.
name-group=
# Regular expression which should only match function or class names that do
# not require a docstring.
no-docstring-rgx=^_
# List of decorators that produce properties, such as abc.abstractproperty. Add
# to this list to register other decorators that produce valid properties.
# These decorators are taken in consideration only for invalid-name.
property-classes=abc.abstractproperty
# Regular expression matching correct type variable names. If left empty, type
# variable names will be checked with the set naming style.
#typevar-rgx=
# Naming style matching correct variable names.
variable-naming-style=snake_case
# Regular expression matching correct variable names. Overrides variable-
# naming-style. If left empty, variable names will be checked with the set
# naming style.
#variable-rgx=
[VARIABLES]
# List of additional names supposed to be defined in builtins. Remember that
# you should avoid defining new builtins when possible.
additional-builtins=
# Tells whether unused global variables should be treated as a violation.
allow-global-unused-variables=yes
# List of names allowed to shadow builtins
allowed-redefined-builtins=
# List of strings which can identify a callback function by name. A callback
# name must start or end with one of those strings.
callbacks=cb_,
_cb
# A regular expression matching the name of dummy variables (i.e. expected to
# not be used).
dummy-variables-rgx=_+$|(_[a-zA-Z0-9_]*[a-zA-Z0-9]+?$)|dummy|^ignored_|^unused_
# Argument names that match this expression will be ignored.
ignored-argument-names=_.*|^ignored_|^unused_
# Tells whether we should check for unused import in __init__ files.
init-import=no
# List of qualified module names which can have objects that can redefine
# builtins.
redefining-builtins-modules=six.moves,past.builtins,future.builtins,builtins,io
[SIMILARITIES]
# Comments are removed from the similarity computation
ignore-comments=yes
# Docstrings are removed from the similarity computation
ignore-docstrings=yes
# Imports are removed from the similarity computation
ignore-imports=yes
# Signatures are removed from the similarity computation
ignore-signatures=yes
# Minimum lines number of a similarity.
min-similarity-lines=4
[MISCELLANEOUS]
# List of note tags to take in consideration, separated by a comma.
notes=FIXME,
XXX,
TODO
# Regular expression of note tags to take in consideration.
notes-rgx=
[STRING]
# This flag controls whether inconsistent-quotes generates a warning when the
# character used as a quote delimiter is used inconsistently within a module.
check-quote-consistency=no
# This flag controls whether the implicit-str-concat should generate a warning
# on implicit string concatenation in sequences defined over several lines.
check-str-concat-over-line-jumps=no
[IMPORTS]
# List of modules that can be imported at any level, not just the top level
# one.
allow-any-import-level=
# Allow wildcard imports from modules that define __all__.
allow-wildcard-with-all=no
# Deprecated modules which should not be used, separated by a comma.
deprecated-modules=
# Output a graph (.gv or any supported image format) of external dependencies
# to the given file (report RP0402 must not be disabled).
ext-import-graph=
# Output a graph (.gv or any supported image format) of all (i.e. internal and
# external) dependencies to the given file (report RP0402 must not be
# disabled).
import-graph=
# Output a graph (.gv or any supported image format) of internal dependencies
# to the given file (report RP0402 must not be disabled).
int-import-graph=
# Force import order to recognize a module as part of the standard
# compatibility libraries.
known-standard-library=
# Force import order to recognize a module as part of a third party library.
known-third-party=enchant
# Couples of modules and preferred modules, separated by a comma.
preferred-modules=
[EXCEPTIONS]
# Exceptions that will emit a warning when caught.
overgeneral-exceptions=BaseException,
Exception
[DESIGN]
# List of regular expressions of class ancestor names to ignore when counting
# public methods (see R0903)
exclude-too-few-public-methods=
# List of qualified class names to ignore when counting class parents (see
# R0901)
ignored-parents=
# Maximum number of arguments for function / method.
max-args=5
# Maximum number of attributes for a class (see R0902).
max-attributes=7
# Maximum number of boolean expressions in an if statement (see R0916).
max-bool-expr=5
# Maximum number of branch for function / method body.
max-branches=12
# Maximum number of locals for function / method body.
max-locals=15
# Maximum number of parents for a class (see R0901).
max-parents=7
# Maximum number of public methods for a class (see R0904).
max-public-methods=20
# Maximum number of return / yield for function / method body.
max-returns=6
# Maximum number of statements in function / method body.
max-statements=50
# Minimum number of public methods for a class (see R0903).
min-public-methods=2
[METHOD_ARGS]
# List of qualified names (i.e., library.method) which require a timeout
# parameter e.g. 'requests.api.get,requests.api.post'
timeout-methods=requests.api.delete,requests.api.get,requests.api.head,requests.api.options,requests.api.patch,requests.api.post,requests.api.put,requests.api.request
[LOGGING]
# The type of string formatting that logging methods do. `old` means using %
# formatting, `new` is for `{}` formatting.
logging-format-style=old
# Logging modules to check that the string format arguments are in logging
# function parameter format.
logging-modules=logging
[FORMAT]
# Expected format of line ending, e.g. empty (any line ending), LF or CRLF.
expected-line-ending-format=
# Regexp for a line that is allowed to be longer than the limit.
ignore-long-lines=^\s*(# )?<?https?://\S+>?$
# Number of spaces of indent required inside a hanging or continued line.
indent-after-paren=4
# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1
# tab).
indent-string=' '
# Maximum number of characters on a single line.
max-line-length=100
# Maximum number of lines in a module.
max-module-lines=1000
# Allow the body of a class to be on the same line as the declaration if body
# contains single statement.
single-line-class-stmt=no
# Allow the body of an if to be on the same line as the test if there is no
# else.
single-line-if-stmt=no
[REFACTORING]
# Maximum number of nested blocks for function / method body
max-nested-blocks=5
# Complete name of functions that never returns. When checking for
# inconsistent-return-statements if a never returning function is called then
# it will be considered as an explicit return statement and no message will be
# printed.
never-returning-functions=sys.exit,argparse.parse_error
[TYPECHECK]
# List of decorators that produce context managers, such as
# contextlib.contextmanager. Add to this list to register other decorators that
# produce valid context managers.
contextmanager-decorators=contextlib.contextmanager
# List of members which are set dynamically and missed by pylint inference
# system, and so shouldn't trigger E1101 when accessed. Python regular
# expressions are accepted.
generated-members=
# Tells whether to warn about missing members when the owner of the attribute
# is inferred to be None.
ignore-none=yes
# This flag controls whether pylint should warn about no-member and similar
# checks whenever an opaque object is returned when inferring. The inference
# can return multiple potential results while evaluating a Python object, but
# some branches might not be evaluated, which results in partial inference. In
# that case, it might be useful to still emit no-member and other checks for
# the rest of the inferred objects.
ignore-on-opaque-inference=yes
# List of symbolic message names to ignore for Mixin members.
ignored-checks-for-mixins=no-member,
not-async-context-manager,
not-context-manager,
attribute-defined-outside-init
# List of class names for which member attributes should not be checked (useful
# for classes with dynamically set attributes). This supports the use of
# qualified names.
ignored-classes=optparse.Values,thread._local,_thread._local,argparse.Namespace
# Show a hint with possible names when a member name was not found. The aspect
# of finding the hint is based on edit distance.
missing-member-hint=yes
# The minimum edit distance a name should have in order to be considered a
# similar match for a missing member name.
missing-member-hint-distance=1
# The total number of similar names that should be taken in consideration when
# showing a hint for a missing member.
missing-member-max-choices=1
# Regex pattern to define which classes are considered mixins.
mixin-class-rgx=.*[Mm]ixin
# List of decorators that change the signature of a decorated function.
signature-mutators=
[CLASSES]
# Warn about protected attribute access inside special methods
check-protected-access-in-special-methods=no
# List of method names used to declare (i.e. assign) instance attributes.
defining-attr-methods=__init__,
__new__,
setUp,
__post_init__
# List of member names, which should be excluded from the protected access
# warning.
exclude-protected=_asdict,
_fields,
_replace,
_source,
_make
# List of valid names for the first argument in a class method.
valid-classmethod-first-arg=cls
# List of valid names for the first argument in a metaclass class method.
valid-metaclass-classmethod-first-arg=cls
[SPELLING]
# Limits count of emitted suggestions for spelling mistakes.
max-spelling-suggestions=4
# Spelling dictionary name. Available dictionaries: none. To make it work,
# install the 'python-enchant' package.
spelling-dict=
# List of comma separated words that should be considered directives if they
# appear at the beginning of a comment and should not be checked.
spelling-ignore-comment-directives=fmt: on,fmt: off,noqa:,noqa,nosec,isort:skip,mypy:
# List of comma separated words that should not be checked.
spelling-ignore-words=
# A path to a file that contains the private dictionary; one word per line.
spelling-private-dict-file=
# Tells whether to store unknown words to the private dictionary (see the
# --spelling-private-dict-file option) instead of raising a message.
spelling-store-unknown-words=no
File diff suppressed because one or more lines are too long
+558
View File
@@ -0,0 +1,558 @@
{
"nbformat": 4,
"nbformat_minor": 0,
"metadata": {
"colab": {
"name": "Vosk Adaptation",
"provenance": [],
"collapsed_sections": []
},
"kernelspec": {
"name": "python3",
"display_name": "Python 3"
},
"language_info": {
"name": "python"
},
"accelerator": "GPU",
"gpuClass": "standard"
},
"cells": [
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "URzWMmv50-Ba",
"outputId": "0e096a99-74dd-42e2-efb1-9cba784c3664"
},
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"/content\n",
"--2022-08-17 09:48:52-- https://alphacephei.com/vosk-colab/kaldi.tar.gz\n",
"Resolving alphacephei.com (alphacephei.com)... 188.40.21.16, 2a01:4f8:13a:279f::2\n",
"Connecting to alphacephei.com (alphacephei.com)|188.40.21.16|:443... connected.\n",
"HTTP request sent, awaiting response... 200 OK\n",
"Length: 809174554 (772M) [application/octet-stream]\n",
"Saving to: kaldi.tar.gz\n",
"\n",
"kaldi.tar.gz 100%[===================>] 771.69M 20.3MB/s in 40s \n",
"\n",
"2022-08-17 09:49:33 (19.4 MB/s) - kaldi.tar.gz saved [809174554/809174554]\n",
"\n"
]
}
],
"source": [
"%cd /content\n",
"!wget -c https://alphacephei.com/vosk-colab/kaldi.tar.gz\n",
"!tar xzf kaldi.tar.gz"
]
},
{
"cell_type": "code",
"source": [
"%cd /content/kaldi/egs/ac\n",
"!wget -c https://alphacephei.com/vosk-colab/vosk-model-small-en-us-0.15-compile-colab.tar.gz\n",
"!rm -rf vosk-model-small-en-us-0.15-compile-colab\n",
"!tar xf vosk-model-small-en-us-0.15-compile-colab.tar.gz"
],
"metadata": {
"id": "-065p7WC2SHh",
"colab": {
"base_uri": "https://localhost:8080/"
},
"outputId": "241c7473-7464-48d5-b48d-dc6e3bf4971d"
},
"execution_count": 8,
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"/content/kaldi/egs/ac\n",
"--2022-08-17 10:28:26-- https://alphacephei.com/vosk-colab/vosk-model-small-en-us-0.15-compile-colab.tar.gz\n",
"Resolving alphacephei.com (alphacephei.com)... 188.40.21.16, 2a01:4f8:13a:279f::2\n",
"Connecting to alphacephei.com (alphacephei.com)|188.40.21.16|:443... connected.\n",
"HTTP request sent, awaiting response... 200 OK\n",
"Length: 59618100 (57M) [application/octet-stream]\n",
"Saving to: vosk-model-small-en-us-0.15-compile-colab.tar.gz\n",
"\n",
"vosk-model-small-en 100%[===================>] 56.86M 18.6MB/s in 3.6s \n",
"\n",
"2022-08-17 10:28:30 (15.7 MB/s) - vosk-model-small-en-us-0.15-compile-colab.tar.gz saved [59618100/59618100]\n",
"\n"
]
}
]
},
{
"cell_type": "code",
"source": [
"%cd /content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab\n",
"!ls\n",
"!cat compile-graph.sh\n",
"!bash compile-graph.sh"
],
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "wuDjvNbd2sf9",
"outputId": "34a1d2fe-d443-4574-e25d-824e38eb3a78"
},
"execution_count": 9,
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab\n",
"compile-graph.sh data_test decode.sh\texp\t local path.sh steps\n",
"conf\t\t db\t dict.py\tget_vocab.py mfcc RESULTS utils\n",
"#!/bin/bash\n",
"\n",
"set -x\n",
"\n",
". path.sh\n",
"\n",
"pip3 install phonetisaurus\n",
"\n",
"rm -rf data\n",
"rm -rf exp/tdnn/lgraph\n",
"rm -rf exp/tdnn/lgraph_orig\n",
"\n",
"mkdir -p data/dict\n",
"cp db/phone/* data/dict\n",
"./dict.py > data/dict/lexicon.txt\n",
"\n",
"python3 ./get_vocab.py > data/mix.vocab\n",
"ngramsymbols data/mix.vocab data/mix.syms\n",
"farcompilestrings --fst_type=compact --symbols=data/mix.syms --keep_symbols --unknown_symbol=\"[unk]\" db/extra.txt data/extra.far\n",
"ngramcount --order=3 data/extra.far - |\n",
" ngramprint --integers | grep -v \"<unk>\" | ngramread |\n",
" ngramshrink --method=count_prune --count_pattern=\"3+:3\" |\n",
" ngrammake --method=witten_bell - data/extra.mod\n",
"gunzip -c db/en-50k-0.4-android.lm.gz | ngramread --renormalize_arpa --ARPA --symbols=data/mix.syms - data/en-us.mod\n",
"ngrammerge --method=\"bayes_model_merge\" --normalize --alpha=0.95 --beta=0.05 data/en-us.mod data/extra.mod data/en-us-mix.mod\n",
"ngramprint --ARPA data/en-us-mix.mod | gzip -c > data/en-us-mix.lm.gz\n",
"\n",
"# Prune for the first stage if needed\n",
"# ngramshrink --method=relative_entropy --theta=2e-8 data/en-us-mix.mod data/en-us-mix-prune.mod\n",
"# ngramprint --ARPA data/en-us-mix-prune.mod | gzip -c > data/en-us-mix-small.lm.gz\n",
"\n",
"utils/prepare_lang.sh data/dict \"[unk]\" data/lang_local data/lang\n",
"utils/format_lm.sh data/lang db/en-50k-0.4-android.lm.gz data/dict/lexicon.txt data/lang_test\n",
"utils/format_lm.sh data/lang data/en-us-mix.lm.gz data/dict/lexicon.txt data/lang_test_adapt\n",
"\n",
"utils/mkgraph.sh --self-loop-scale 1.0 data/lang_test exp/tdnn exp/tdnn/graph\n",
"utils/mkgraph.sh --self-loop-scale 1.0 data/lang_test_adapt exp/tdnn exp/tdnn/graph_adapt\n",
"\n",
"# Lookahead part goes OOM\n",
"#utils/mkgraph_lookahead.sh \\\n",
"# --self-loop-scale 1.0 data/lang \\\n",
"# exp/tdnn data/en-us-mix.lm.gz exp/tdnn/lgraph\n",
"#utils/mkgraph_lookahead.sh \\\n",
"# --self-loop-scale 1.0 data/lang \\\n",
"# exp/tdnn db/en-50k-0.4-android.lm.gz exp/tdnn/lgraph_orig\n",
"+ . path.sh\n",
"+++ pwd\n",
"++ export KALDI_ROOT=/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../..\n",
"++ KALDI_ROOT=/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../..\n",
"++ export PATH=/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/utils:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../../src/bin:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../../tools/openfst/bin:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../../src/fstbin:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../../src/gmmbin:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../../src/featbin:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../../src/lm:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../../src/sgmmbin:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../../src/sgmm2bin:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../../src/fgmmbin:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../../src/latbin:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../../src/nnetbin:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../../src/nnet2bin:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../../src/online2bin:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../../src/ivectorbin:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../../src/lmbin:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../../src/chainbin:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../../src/nnet3bin:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab:/opt/bin:/usr/local/nvidia/bin:/usr/local/cuda/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/tools/node/bin:/tools/google-cloud-sdk/bin:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../../tools/sph2pipe_v2.5\n",
"++ PATH=/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/utils:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../../src/bin:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../../tools/openfst/bin:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../../src/fstbin:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../../src/gmmbin:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../../src/featbin:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../../src/lm:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../../src/sgmmbin:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../../src/sgmm2bin:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../../src/fgmmbin:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../../src/latbin:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../../src/nnetbin:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../../src/nnet2bin:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../../src/online2bin:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../../src/ivectorbin:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../../src/lmbin:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../../src/chainbin:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../../src/nnet3bin:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab:/opt/bin:/usr/local/nvidia/bin:/usr/local/cuda/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/tools/node/bin:/tools/google-cloud-sdk/bin:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../../tools/sph2pipe_v2.5\n",
"++ export PATH=/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../../tools/ngram-1.3.7/src/bin:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/utils:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../../src/bin:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../../tools/openfst/bin:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../../src/fstbin:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../../src/gmmbin:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../../src/featbin:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../../src/lm:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../../src/sgmmbin:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../../src/sgmm2bin:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../../src/fgmmbin:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../../src/latbin:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../../src/nnetbin:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../../src/nnet2bin:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../../src/online2bin:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../../src/ivectorbin:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../../src/lmbin:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../../src/chainbin:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../../src/nnet3bin:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab:/opt/bin:/usr/local/nvidia/bin:/usr/local/cuda/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/tools/node/bin:/tools/google-cloud-sdk/bin:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../../tools/sph2pipe_v2.5\n",
"++ PATH=/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../../tools/ngram-1.3.7/src/bin:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/utils:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../../src/bin:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../../tools/openfst/bin:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../../src/fstbin:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../../src/gmmbin:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../../src/featbin:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../../src/lm:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../../src/sgmmbin:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../../src/sgmm2bin:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../../src/fgmmbin:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../../src/latbin:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../../src/nnetbin:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../../src/nnet2bin:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../../src/online2bin:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../../src/ivectorbin:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../../src/lmbin:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../../src/chainbin:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../../src/nnet3bin:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab:/opt/bin:/usr/local/nvidia/bin:/usr/local/cuda/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/tools/node/bin:/tools/google-cloud-sdk/bin:/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../../tools/sph2pipe_v2.5\n",
"++ export LD_LIBRARY_PATH=/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../../tools/openfst/lib/fst/\n",
"++ LD_LIBRARY_PATH=/content/kaldi/egs/ac/vosk-model-small-en-us-0.15-compile-colab/../../../tools/openfst/lib/fst/\n",
"++ export LC_ALL=C\n",
"++ LC_ALL=C\n",
"+ pip3 install phonetisaurus\n",
"Looking in indexes: https://pypi.org/simple, https://us-python.pkg.dev/colab-wheels/public/simple/\n",
"Requirement already satisfied: phonetisaurus in /usr/local/lib/python3.7/dist-packages (0.3.0)\n",
"+ rm -rf data\n",
"+ rm -rf exp/tdnn/lgraph\n",
"+ rm -rf exp/tdnn/lgraph_orig\n",
"+ mkdir -p data/dict\n",
"+ cp db/phone/extra_questions.txt db/phone/nonsilence_phones.txt db/phone/optional_silence.txt db/phone/silence_phones.txt data/dict\n",
"+ ./dict.py\n",
"+ python3 ./get_vocab.py\n",
"+ ngramsymbols data/mix.vocab data/mix.syms\n",
"+ farcompilestrings --fst_type=compact --symbols=data/mix.syms --keep_symbols '--unknown_symbol=[unk]' db/extra.txt data/extra.far\n",
"+ ngramcount --order=3 data/extra.far -\n",
"+ ngrammake --method=witten_bell - data/extra.mod\n",
"+ ngramshrink --method=count_prune --count_pattern=3+:3\n",
"+ ngramread\n",
"+ ngramprint --integers\n",
"+ grep -v '<unk>'\n",
"+ ngramread --renormalize_arpa --ARPA --symbols=data/mix.syms - data/en-us.mod\n",
"+ gunzip -c db/en-50k-0.4-android.lm.gz\n",
"+ ngrammerge --method=bayes_model_merge --normalize --alpha=0.95 --beta=0.05 data/en-us.mod data/extra.mod data/en-us-mix.mod\n",
"+ ngramprint --ARPA data/en-us-mix.mod\n",
"+ gzip -c\n",
"+ utils/prepare_lang.sh data/dict '[unk]' data/lang_local data/lang\n",
"utils/prepare_lang.sh data/dict [unk] data/lang_local data/lang\n",
"Checking data/dict/silence_phones.txt ...\n",
"--> reading data/dict/silence_phones.txt\n",
"--> text seems to be UTF-8 or ASCII, checking whitespaces\n",
"--> text contains only allowed whitespaces\n",
"--> data/dict/silence_phones.txt is OK\n",
"\n",
"Checking data/dict/optional_silence.txt ...\n",
"--> reading data/dict/optional_silence.txt\n",
"--> text seems to be UTF-8 or ASCII, checking whitespaces\n",
"--> text contains only allowed whitespaces\n",
"--> data/dict/optional_silence.txt is OK\n",
"\n",
"Checking data/dict/nonsilence_phones.txt ...\n",
"--> reading data/dict/nonsilence_phones.txt\n",
"--> text seems to be UTF-8 or ASCII, checking whitespaces\n",
"--> text contains only allowed whitespaces\n",
"--> data/dict/nonsilence_phones.txt is OK\n",
"\n",
"Checking disjoint: silence_phones.txt, nonsilence_phones.txt\n",
"--> disjoint property is OK.\n",
"\n",
"Checking data/dict/lexicon.txt\n",
"--> reading data/dict/lexicon.txt\n",
"--> text seems to be UTF-8 or ASCII, checking whitespaces\n",
"--> text contains only allowed whitespaces\n",
"--> data/dict/lexicon.txt is OK\n",
"\n",
"Checking data/dict/extra_questions.txt ...\n",
"--> data/dict/extra_questions.txt is empty (this is OK)\n",
"--> SUCCESS [validating dictionary directory data/dict]\n",
"\n",
"**Creating data/dict/lexiconp.txt from data/dict/lexicon.txt\n",
"fstaddselfloops data/lang/phones/wdisambig_phones.int data/lang/phones/wdisambig_words.int \n",
"prepare_lang.sh: validating output directory\n",
"utils/validate_lang.pl data/lang\n",
"Checking existence of separator file\n",
"separator file data/lang/subword_separator.txt is empty or does not exist, deal in word case.\n",
"Checking data/lang/phones.txt ...\n",
"--> text seems to be UTF-8 or ASCII, checking whitespaces\n",
"--> text contains only allowed whitespaces\n",
"--> data/lang/phones.txt is OK\n",
"\n",
"Checking words.txt: #0 ...\n",
"--> text seems to be UTF-8 or ASCII, checking whitespaces\n",
"--> text contains only allowed whitespaces\n",
"--> data/lang/words.txt is OK\n",
"\n",
"Checking disjoint: silence.txt, nonsilence.txt, disambig.txt ...\n",
"--> silence.txt and nonsilence.txt are disjoint\n",
"--> silence.txt and disambig.txt are disjoint\n",
"--> disambig.txt and nonsilence.txt are disjoint\n",
"--> disjoint property is OK\n",
"\n",
"Checking sumation: silence.txt, nonsilence.txt, disambig.txt ...\n",
"--> found no unexplainable phones in phones.txt\n",
"\n",
"Checking data/lang/phones/context_indep.{txt, int, csl} ...\n",
"--> text seems to be UTF-8 or ASCII, checking whitespaces\n",
"--> text contains only allowed whitespaces\n",
"--> 10 entry/entries in data/lang/phones/context_indep.txt\n",
"--> data/lang/phones/context_indep.int corresponds to data/lang/phones/context_indep.txt\n",
"--> data/lang/phones/context_indep.csl corresponds to data/lang/phones/context_indep.txt\n",
"--> data/lang/phones/context_indep.{txt, int, csl} are OK\n",
"\n",
"Checking data/lang/phones/nonsilence.{txt, int, csl} ...\n",
"--> text seems to be UTF-8 or ASCII, checking whitespaces\n",
"--> text contains only allowed whitespaces\n",
"--> 156 entry/entries in data/lang/phones/nonsilence.txt\n",
"--> data/lang/phones/nonsilence.int corresponds to data/lang/phones/nonsilence.txt\n",
"--> data/lang/phones/nonsilence.csl corresponds to data/lang/phones/nonsilence.txt\n",
"--> data/lang/phones/nonsilence.{txt, int, csl} are OK\n",
"\n",
"Checking data/lang/phones/silence.{txt, int, csl} ...\n",
"--> text seems to be UTF-8 or ASCII, checking whitespaces\n",
"--> text contains only allowed whitespaces\n",
"--> 10 entry/entries in data/lang/phones/silence.txt\n",
"--> data/lang/phones/silence.int corresponds to data/lang/phones/silence.txt\n",
"--> data/lang/phones/silence.csl corresponds to data/lang/phones/silence.txt\n",
"--> data/lang/phones/silence.{txt, int, csl} are OK\n",
"\n",
"Checking data/lang/phones/optional_silence.{txt, int, csl} ...\n",
"--> text seems to be UTF-8 or ASCII, checking whitespaces\n",
"--> text contains only allowed whitespaces\n",
"--> 1 entry/entries in data/lang/phones/optional_silence.txt\n",
"--> data/lang/phones/optional_silence.int corresponds to data/lang/phones/optional_silence.txt\n",
"--> data/lang/phones/optional_silence.csl corresponds to data/lang/phones/optional_silence.txt\n",
"--> data/lang/phones/optional_silence.{txt, int, csl} are OK\n",
"\n",
"Checking data/lang/phones/disambig.{txt, int, csl} ...\n",
"--> text seems to be UTF-8 or ASCII, checking whitespaces\n",
"--> text contains only allowed whitespaces\n",
"--> 14 entry/entries in data/lang/phones/disambig.txt\n",
"--> data/lang/phones/disambig.int corresponds to data/lang/phones/disambig.txt\n",
"--> data/lang/phones/disambig.csl corresponds to data/lang/phones/disambig.txt\n",
"--> data/lang/phones/disambig.{txt, int, csl} are OK\n",
"\n",
"Checking data/lang/phones/roots.{txt, int} ...\n",
"--> text seems to be UTF-8 or ASCII, checking whitespaces\n",
"--> text contains only allowed whitespaces\n",
"--> 41 entry/entries in data/lang/phones/roots.txt\n",
"--> data/lang/phones/roots.int corresponds to data/lang/phones/roots.txt\n",
"--> data/lang/phones/roots.{txt, int} are OK\n",
"\n",
"Checking data/lang/phones/sets.{txt, int} ...\n",
"--> text seems to be UTF-8 or ASCII, checking whitespaces\n",
"--> text contains only allowed whitespaces\n",
"--> 41 entry/entries in data/lang/phones/sets.txt\n",
"--> data/lang/phones/sets.int corresponds to data/lang/phones/sets.txt\n",
"--> data/lang/phones/sets.{txt, int} are OK\n",
"\n",
"Checking data/lang/phones/extra_questions.{txt, int} ...\n",
"--> text seems to be UTF-8 or ASCII, checking whitespaces\n",
"--> text contains only allowed whitespaces\n",
"--> 9 entry/entries in data/lang/phones/extra_questions.txt\n",
"--> data/lang/phones/extra_questions.int corresponds to data/lang/phones/extra_questions.txt\n",
"--> data/lang/phones/extra_questions.{txt, int} are OK\n",
"\n",
"Checking data/lang/phones/word_boundary.{txt, int} ...\n",
"--> text seems to be UTF-8 or ASCII, checking whitespaces\n",
"--> text contains only allowed whitespaces\n",
"--> 166 entry/entries in data/lang/phones/word_boundary.txt\n",
"--> data/lang/phones/word_boundary.int corresponds to data/lang/phones/word_boundary.txt\n",
"--> data/lang/phones/word_boundary.{txt, int} are OK\n",
"\n",
"Checking optional_silence.txt ...\n",
"--> reading data/lang/phones/optional_silence.txt\n",
"--> data/lang/phones/optional_silence.txt is OK\n",
"\n",
"Checking disambiguation symbols: #0 and #1\n",
"--> data/lang/phones/disambig.txt has \"#0\" and \"#1\"\n",
"--> data/lang/phones/disambig.txt is OK\n",
"\n",
"Checking topo ...\n",
"\n",
"Checking word_boundary.txt: silence.txt, nonsilence.txt, disambig.txt ...\n",
"--> data/lang/phones/word_boundary.txt doesn't include disambiguation symbols\n",
"--> data/lang/phones/word_boundary.txt is the union of nonsilence.txt and silence.txt\n",
"--> data/lang/phones/word_boundary.txt is OK\n",
"\n",
"Checking word-level disambiguation symbols...\n",
"--> data/lang/phones/wdisambig.txt exists (newer prepare_lang.sh)\n",
"Checking word_boundary.int and disambig.int\n",
"--> generating a 98 word/subword sequence\n",
"--> resulting phone sequence from L.fst corresponds to the word sequence\n",
"--> L.fst is OK\n",
"--> generating a 49 word/subword sequence\n",
"--> resulting phone sequence from L_disambig.fst corresponds to the word sequence\n",
"--> L_disambig.fst is OK\n",
"\n",
"Checking data/lang/oov.{txt, int} ...\n",
"--> text seems to be UTF-8 or ASCII, checking whitespaces\n",
"--> text contains only allowed whitespaces\n",
"--> 1 entry/entries in data/lang/oov.txt\n",
"--> data/lang/oov.int corresponds to data/lang/oov.txt\n",
"--> data/lang/oov.{txt, int} are OK\n",
"\n",
"--> data/lang/L.fst is olabel sorted\n",
"--> data/lang/L_disambig.fst is olabel sorted\n",
"--> SUCCESS [validating lang directory data/lang]\n",
"+ utils/format_lm.sh data/lang db/en-50k-0.4-android.lm.gz data/dict/lexicon.txt data/lang_test\n",
"Converting 'db/en-50k-0.4-android.lm.gz' to FST\n",
"arpa2fst --disambig-symbol=#0 --read-symbol-table=data/lang_test/words.txt - data/lang_test/G.fst \n",
"LOG (arpa2fst[5.5.1046~1-76cd5]:Read():arpa-file-parser.cc:94) Reading \\data\\ section.\n",
"LOG (arpa2fst[5.5.1046~1-76cd5]:Read():arpa-file-parser.cc:149) Reading \\1-grams: section.\n",
"LOG (arpa2fst[5.5.1046~1-76cd5]:Read():arpa-file-parser.cc:149) Reading \\2-grams: section.\n",
"LOG (arpa2fst[5.5.1046~1-76cd5]:Read():arpa-file-parser.cc:149) Reading \\3-grams: section.\n",
"LOG (arpa2fst[5.5.1046~1-76cd5]:RemoveRedundantStates():arpa-lm-compiler.cc:359) Reduced num-states from 1217362 to 185036\n",
"fstisstochastic data/lang_test/G.fst \n",
"0.476411 -3.03779\n",
"Succeeded in formatting LM: 'db/en-50k-0.4-android.lm.gz'\n",
"+ utils/format_lm.sh data/lang data/en-us-mix.lm.gz data/dict/lexicon.txt data/lang_test_adapt\n",
"Converting 'data/en-us-mix.lm.gz' to FST\n",
"arpa2fst --disambig-symbol=#0 --read-symbol-table=data/lang_test_adapt/words.txt - data/lang_test_adapt/G.fst \n",
"LOG (arpa2fst[5.5.1046~1-76cd5]:Read():arpa-file-parser.cc:94) Reading \\data\\ section.\n",
"LOG (arpa2fst[5.5.1046~1-76cd5]:Read():arpa-file-parser.cc:149) Reading \\1-grams: section.\n",
"LOG (arpa2fst[5.5.1046~1-76cd5]:Read():arpa-file-parser.cc:149) Reading \\2-grams: section.\n",
"LOG (arpa2fst[5.5.1046~1-76cd5]:Read():arpa-file-parser.cc:149) Reading \\3-grams: section.\n",
"LOG (arpa2fst[5.5.1046~1-76cd5]:RemoveRedundantStates():arpa-lm-compiler.cc:359) Reduced num-states from 1217646 to 185095\n",
"fstisstochastic data/lang_test_adapt/G.fst \n",
"6.81902e-07 -3.03779\n",
"Succeeded in formatting LM: 'data/en-us-mix.lm.gz'\n",
"+ utils/mkgraph.sh --self-loop-scale 1.0 data/lang_test exp/tdnn exp/tdnn/graph\n",
"tree-info exp/tdnn/tree \n",
"tree-info exp/tdnn/tree \n",
"fstdeterminizestar --use-log=true \n",
"fsttablecompose data/lang_test/L_disambig.fst data/lang_test/G.fst \n",
"fstminimizeencoded \n",
"fstpushspecial \n",
"fstisstochastic data/lang_test/tmp/LG.fst \n",
"-0.145498 -0.146281\n",
"[info]: LG not stochastic.\n",
"fstcomposecontext --context-size=2 --central-position=1 --read-disambig-syms=data/lang_test/phones/disambig.int --write-disambig-syms=data/lang_test/tmp/disambig_ilabels_2_1.int data/lang_test/tmp/ilabels_2_1.905 data/lang_test/tmp/LG.fst \n",
"fstisstochastic data/lang_test/tmp/CLG_2_1.fst \n",
"-0.145498 -0.146281\n",
"[info]: CLG not stochastic.\n",
"make-h-transducer --disambig-syms-out=exp/tdnn/graph/disambig_tid.int --transition-scale=1.0 data/lang_test/tmp/ilabels_2_1 exp/tdnn/tree exp/tdnn/final.mdl \n",
"fstrmepslocal \n",
"fsttablecompose exp/tdnn/graph/Ha.fst data/lang_test/tmp/CLG_2_1.fst \n",
"fstdeterminizestar --use-log=true \n",
"fstminimizeencoded \n",
"fstrmsymbols exp/tdnn/graph/disambig_tid.int \n",
"fstisstochastic exp/tdnn/graph/HCLGa.fst \n",
"-0.109817 -0.571742\n",
"HCLGa is not stochastic\n",
"add-self-loops --self-loop-scale=1.0 --reorder=true exp/tdnn/final.mdl exp/tdnn/graph/HCLGa.fst \n",
"fstisstochastic exp/tdnn/graph/HCLG.fst \n",
"1.90465e-09 -0.415046\n",
"[info]: final HCLG is not stochastic.\n",
"+ utils/mkgraph.sh --self-loop-scale 1.0 data/lang_test_adapt exp/tdnn exp/tdnn/graph_adapt\n",
"tree-info exp/tdnn/tree \n",
"tree-info exp/tdnn/tree \n",
"fstdeterminizestar --use-log=true \n",
"fsttablecompose data/lang_test_adapt/L_disambig.fst data/lang_test_adapt/G.fst \n",
"fstminimizeencoded \n",
"fstpushspecial \n",
"fstisstochastic data/lang_test_adapt/tmp/LG.fst \n",
"-0.148474 -0.149181\n",
"[info]: LG not stochastic.\n",
"fstcomposecontext --context-size=2 --central-position=1 --read-disambig-syms=data/lang_test_adapt/phones/disambig.int --write-disambig-syms=data/lang_test_adapt/tmp/disambig_ilabels_2_1.int data/lang_test_adapt/tmp/ilabels_2_1.979 data/lang_test_adapt/tmp/LG.fst \n",
"fstisstochastic data/lang_test_adapt/tmp/CLG_2_1.fst \n",
"-0.148474 -0.149181\n",
"[info]: CLG not stochastic.\n",
"make-h-transducer --disambig-syms-out=exp/tdnn/graph_adapt/disambig_tid.int --transition-scale=1.0 data/lang_test_adapt/tmp/ilabels_2_1 exp/tdnn/tree exp/tdnn/final.mdl \n",
"fstrmepslocal \n",
"fsttablecompose exp/tdnn/graph_adapt/Ha.fst data/lang_test_adapt/tmp/CLG_2_1.fst \n",
"fstdeterminizestar --use-log=true \n",
"fstminimizeencoded \n",
"fstrmsymbols exp/tdnn/graph_adapt/disambig_tid.int \n",
"fstisstochastic exp/tdnn/graph_adapt/HCLGa.fst \n",
"-0.113907 -0.5857\n",
"HCLGa is not stochastic\n",
"add-self-loops --self-loop-scale=1.0 --reorder=true exp/tdnn/final.mdl exp/tdnn/graph_adapt/HCLGa.fst \n",
"fstisstochastic exp/tdnn/graph_adapt/HCLG.fst \n",
"1.90465e-09 -0.423618\n",
"[info]: final HCLG is not stochastic.\n"
]
}
]
},
{
"cell_type": "code",
"source": [
"!cat decode.sh\n",
"!bash decode.sh"
],
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "Sl3QBI1MXpc-",
"outputId": "affac8a3-782f-4000-e31f-81bfed47a37a"
},
"execution_count": 10,
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"#!/bin/bash\n",
"\n",
". path.sh\n",
"\n",
"steps/make_mfcc.sh --nj 10 data_test/test_small exp/make_mfcc/test mfcc\n",
"steps/compute_cmvn_stats.sh data_test/test_small exp/make_mfcc/test mfcc\n",
"utils/fix_data_dir.sh data_test/test_small\n",
"\n",
"steps/online/nnet2/extract_ivectors_online.sh --nj 4 \\\n",
" data_test/test_small exp/extractor \\\n",
" exp/ivectors_test\n",
"\n",
"steps/nnet3/decode.sh --nj 4 \\\n",
" --acwt 1.0 --post-decode-acwt 10.0 \\\n",
" --online-ivector-dir exp/ivectors_test \\\n",
" exp/tdnn/graph_adapt data_test/test_small exp/tdnn/decode_test_adapt\n",
"\n",
"steps/nnet3/decode.sh --nj 4 \\\n",
" --acwt 1.0 --post-decode-acwt 10.0 \\\n",
" --online-ivector-dir exp/ivectors_test \\\n",
" exp/tdnn/graph data_test/test_small exp/tdnn/decode_test\n",
"\n",
"#steps/nnet3/decode_lookahead.sh --nj 4 \\\n",
"# --acwt 1.0 --post-decode-acwt 10.0 \\\n",
"# --online-ivector-dir exp/ivectors_test \\\n",
"# exp/tdnn/lgraph data_test/test_small exp/tdnn/decode_test_adapt\n",
"#steps/nnet3/decode_lookahead.sh --nj 4 \\\n",
"# --acwt 1.0 --post-decode-acwt 10.0 \\\n",
"# --online-ivector-dir exp/ivectors_test \\\n",
"# exp/tdnn/lgraph_orig data_test/test_small exp/tdnn/decode_test\n",
"steps/make_mfcc.sh --nj 10 data_test/test_small exp/make_mfcc/test mfcc\n",
"steps/make_mfcc.sh: moving data_test/test_small/feats.scp to data_test/test_small/.backup\n",
"utils/validate_data_dir.sh: Successfully validated data-directory data_test/test_small\n",
"steps/make_mfcc.sh: [info]: no segments file exists: assuming wav.scp indexed by utterance.\n",
"steps/make_mfcc.sh: Succeeded creating MFCC features for test_small\n",
"steps/compute_cmvn_stats.sh data_test/test_small exp/make_mfcc/test mfcc\n",
"Succeeded creating CMVN stats for test_small\n",
"fix_data_dir.sh: kept all 50 utterances.\n",
"fix_data_dir.sh: old files are kept in data_test/test_small/.backup\n",
"steps/online/nnet2/extract_ivectors_online.sh --nj 4 data_test/test_small exp/extractor exp/ivectors_test\n",
"steps/online/nnet2/extract_ivectors_online.sh: extracting iVectors\n",
"steps/online/nnet2/extract_ivectors_online.sh: combining iVectors across jobs\n",
"steps/online/nnet2/extract_ivectors_online.sh: done extracting (online) iVectors to exp/ivectors_test using the extractor in exp/extractor.\n",
"steps/nnet3/decode.sh --nj 4 --acwt 1.0 --post-decode-acwt 10.0 --online-ivector-dir exp/ivectors_test exp/tdnn/graph_adapt data_test/test_small exp/tdnn/decode_test_adapt\n",
"steps/nnet2/check_ivectors_compatible.sh: WARNING: One of the directories do not contain iVector ID.\n",
"steps/nnet2/check_ivectors_compatible.sh: WARNING: That means it's you who's reponsible for keeping \n",
"steps/nnet2/check_ivectors_compatible.sh: WARNING: the directories compatible\n",
"steps/nnet3/decode.sh: feature type is raw\n",
"steps/diagnostic/analyze_lats.sh --cmd run.pl --iter final exp/tdnn/graph_adapt exp/tdnn/decode_test_adapt\n",
"steps/diagnostic/analyze_lats.sh: see stats in exp/tdnn/decode_test_adapt/log/analyze_alignments.log\n",
"Overall, lattice depth (10,50,90-percentile)=(1,1,4) and mean=2.4\n",
"steps/diagnostic/analyze_lats.sh: see stats in exp/tdnn/decode_test_adapt/log/analyze_lattice_depth_stats.log\n",
"score best paths\n",
"local/score.sh --cmd run.pl data_test/test_small exp/tdnn/graph_adapt exp/tdnn/decode_test_adapt\n",
"local/score.sh: scoring with word insertion penalty=0.0,0.5,1.0\n",
"score confidence and timing with sclite\n",
"Decoding done.\n",
"steps/nnet3/decode.sh --nj 4 --acwt 1.0 --post-decode-acwt 10.0 --online-ivector-dir exp/ivectors_test exp/tdnn/graph data_test/test_small exp/tdnn/decode_test\n",
"steps/nnet2/check_ivectors_compatible.sh: WARNING: One of the directories do not contain iVector ID.\n",
"steps/nnet2/check_ivectors_compatible.sh: WARNING: That means it's you who's reponsible for keeping \n",
"steps/nnet2/check_ivectors_compatible.sh: WARNING: the directories compatible\n",
"steps/nnet3/decode.sh: feature type is raw\n",
"steps/diagnostic/analyze_lats.sh --cmd run.pl --iter final exp/tdnn/graph exp/tdnn/decode_test\n",
"steps/diagnostic/analyze_lats.sh: see stats in exp/tdnn/decode_test/log/analyze_alignments.log\n",
"Overall, lattice depth (10,50,90-percentile)=(1,5,23) and mean=10.4\n",
"steps/diagnostic/analyze_lats.sh: see stats in exp/tdnn/decode_test/log/analyze_lattice_depth_stats.log\n",
"score best paths\n",
"local/score.sh --cmd run.pl data_test/test_small exp/tdnn/graph exp/tdnn/decode_test\n",
"local/score.sh: scoring with word insertion penalty=0.0,0.5,1.0\n",
"score confidence and timing with sclite\n",
"Decoding done.\n"
]
}
]
},
{
"cell_type": "code",
"source": [
"!bash RESULTS"
],
"metadata": {
"id": "ABtcNyUDX4S8",
"outputId": "d5e50be7-3293-4a59-94b8-9bfa46736481",
"colab": {
"base_uri": "https://localhost:8080/"
}
},
"execution_count": 11,
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"%WER 11.77 [ 107 / 909, 13 ins, 7 del, 87 sub ] exp/tdnn/decode_test/wer_7_1.0\n",
"%WER 0.22 [ 2 / 909, 0 ins, 1 del, 1 sub ] exp/tdnn/decode_test_adapt/wer_10_1.0\n"
]
}
]
}
]
}
File diff suppressed because it is too large Load Diff
File diff suppressed because one or more lines are too long
+5 -5
View File
@@ -1,17 +1,17 @@
#!/usr/bin/env python3
from vosk import Model, KaldiRecognizer, SetLogLevel
import sys
import os
import wave
import sys
import json
from vosk import Model, KaldiRecognizer, SetLogLevel
SetLogLevel(0)
wf = wave.open(sys.argv[1], "rb")
if wf.getnchannels() != 1 or wf.getsampwidth() != 2 or wf.getcomptype() != "NONE":
print ("Audio file must be WAV format mono PCM.")
exit (1)
print("Audio file must be WAV format mono PCM.")
sys.exit(1)
model = Model(lang="en-us")
rec = KaldiRecognizer(model, wf.getframerate())
+3 -3
View File
@@ -1,11 +1,11 @@
#!/usr/bin/env python3
from vosk import Model, KaldiRecognizer
import sys
import json
from vosk import Model, KaldiRecognizer
model = Model(lang="en-us")
rec = KaldiRecognizer(model, 8000)
res = json.loads(rec.FinalResult())
print (res)
print(res)
+55
View File
@@ -0,0 +1,55 @@
#!/usr/bin/env python3
import wave
import sys
from vosk import Model, KaldiRecognizer, SetLogLevel, EndpointerMode
# 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(lang="en-us")
# You can also init model by name or with a folder path
# model = Model(model_name="vosk-model-en-us-0.21")
# model = Model("models/en")
rec = KaldiRecognizer(model, wf.getframerate())
rec.SetWords(True)
rec.SetPartialWords(True)
rec.SetEndpointerMode(EndpointerMode.VERY_LONG)
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())
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)
rec.SetEndpointerDelays(300, 500, 2000)
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())
+18 -18
View File
@@ -1,29 +1,29 @@
#!/usr/bin/env python3
from vosk import Model, KaldiRecognizer, SetLogLevel
import sys
import os
import wave
import subprocess
import sys
from vosk import Model, KaldiRecognizer, SetLogLevel
SAMPLE_RATE = 16000
SetLogLevel(0)
sample_rate=16000
model = Model(lang="en-us")
rec = KaldiRecognizer(model, sample_rate)
rec = KaldiRecognizer(model, SAMPLE_RATE)
process = subprocess.Popen(['ffmpeg', '-loglevel', 'quiet', '-i',
with subprocess.Popen(["ffmpeg", "-loglevel", "quiet", "-i",
sys.argv[1],
'-ar', str(sample_rate) , '-ac', '1', '-f', 's16le', '-'],
stdout=subprocess.PIPE)
"-ar", str(SAMPLE_RATE) , "-ac", "1", "-f", "s16le", "-"],
stdout=subprocess.PIPE) as process:
while True:
data = process.stdout.read(4000)
if len(data) == 0:
break
if rec.AcceptWaveform(data):
print(rec.Result())
else:
print(rec.PartialResult())
while True:
data = process.stdout.read(4000)
if len(data) == 0:
break
if rec.AcceptWaveform(data):
print(rec.Result())
else:
print(rec.PartialResult())
print(rec.FinalResult())
print(rec.FinalResult())
+22 -20
View File
@@ -1,26 +1,25 @@
#!/usr/bin/env python3
import sys
import os
import wave
from time import sleep
import json
from timeit import default_timer as timer
from vosk import BatchModel, BatchRecognizer, GpuInit
from timeit import default_timer as timer
TOT_SAMPLES = 0
GpuInit()
model = BatchModel()
model = BatchModel("model")
with open(sys.argv[1]) as fn:
fnames = fn.readlines()
fds = [open(x.strip(), "rb") for x in fnames]
uids = [fname.strip().split("/")[-1][:-4] for fname in fnames]
recs = [BatchRecognizer(model, 16000) for x in fnames]
results = [""] * len(fnames)
fnames = open(sys.argv[1]).readlines()
fds = [open(x.strip(), "rb") for x in fnames]
uids = [fname.strip().split('/')[-1][:-4] for fname in fnames]
recs = [BatchRecognizer(model, 16000) for x in fnames]
results = [""] * len(fnames)
ended = set()
tot_samples = 0
start_time = timer()
@@ -36,24 +35,27 @@ while True:
ended.add(i)
continue
recs[i].AcceptWaveform(data)
tot_samples += len(data)
TOT_SAMPLES += len(data)
# Wait for results from CUDA
model.Wait()
# Retrieve and add results
for i, fd in enumerate(fds):
res = recs[i].Result()
if len(res) != 0:
results[i] = results[i] + " " + json.loads(res)['text']
res = recs[i].Result()
if len(res) != 0:
results[i] = results[i] + " " + json.loads(res)["text"]
if len(ended) == len(fds):
break
end_time = timer()
for i in range(len(results)):
print (uids[i], results[i].strip())
for i, res in enumerate(results):
print(uids[i], res.strip())
print ("Processed %.3f seconds of audio in %.3f seconds (%.3f xRT)" % (tot_samples / 16000.0 / 2, end_time - start_time,
(tot_samples / 16000.0 / 2 / (end_time - start_time))), file=sys.stderr)
print("Processed %.3f seconds of audio in %.3f seconds (%.3f xRT)"
% (TOT_SAMPLES / 16000.0 / 2,
end_time - start_time,
(TOT_SAMPLES / 16000.0 / 2 / (end_time - start_time))),
file=sys.stderr)
+40
View File
@@ -0,0 +1,40 @@
#!/usr/bin/env python3
import json
import gradio as gr
from vosk import KaldiRecognizer, Model
model = Model(lang="en-us")
def transcribe(data, state):
sample_rate, audio_data = data
audio_data = (audio_data >> 16).astype("int16").tobytes()
if state is None:
rec = KaldiRecognizer(model, sample_rate)
result = []
else:
rec, result = state
if rec.AcceptWaveform(audio_data):
text_result = json.loads(rec.Result())["text"]
if text_result != "":
result.append(text_result)
partial_result = ""
else:
partial_result = json.loads(rec.PartialResult())["partial"] + " "
return "\n".join(result) + "\n" + partial_result, (rec, result)
gr.Interface(
fn=transcribe,
inputs=[
gr.Audio(source="microphone", type="numpy", streaming=True),
"state"
],
outputs=[
"textbox",
"state"
],
live=True).launch(share=True)
+39 -30
View File
@@ -1,11 +1,15 @@
#!/usr/bin/env python3
# prerequisites: as described in https://alphacephei.com/vosk/install and also python module `sounddevice` (simply run command `pip install sounddevice`)
# Example usage using Dutch (nl) recognition model: `python test_microphone.py -m nl`
# For more help run: `python test_microphone.py -h`
import argparse
import os
import queue
import sounddevice as sd
import vosk
import sys
import sounddevice as sd
from vosk import Model, KaldiRecognizer
q = queue.Queue()
@@ -24,8 +28,8 @@ def callback(indata, frames, time, status):
parser = argparse.ArgumentParser(add_help=False)
parser.add_argument(
'-l', '--list-devices', action='store_true',
help='show list of audio devices and exit')
"-l", "--list-devices", action="store_true",
help="show list of audio devices and exit")
args, remaining = parser.parse_known_args()
if args.list_devices:
print(sd.query_devices())
@@ -35,46 +39,51 @@ parser = argparse.ArgumentParser(
formatter_class=argparse.RawDescriptionHelpFormatter,
parents=[parser])
parser.add_argument(
'-f', '--filename', type=str, metavar='FILENAME',
help='audio file to store recording to')
"-f", "--filename", type=str, metavar="FILENAME",
help="audio file to store recording to")
parser.add_argument(
'-d', '--device', type=int_or_str,
help='input device (numeric ID or substring)')
"-d", "--device", type=int_or_str,
help="input device (numeric ID or substring)")
parser.add_argument(
'-r', '--samplerate', type=int, help='sampling rate')
"-r", "--samplerate", type=int, help="sampling rate")
parser.add_argument(
"-m", "--model", type=str, help="language model; e.g. en-us, fr, nl; default is en-us")
args = parser.parse_args(remaining)
try:
if args.samplerate is None:
device_info = sd.query_devices(args.device, 'input')
device_info = sd.query_devices(args.device, "input")
# soundfile expects an int, sounddevice provides a float:
args.samplerate = int(device_info['default_samplerate'])
model = vosk.Model(lang="en-us")
args.samplerate = int(device_info["default_samplerate"])
if args.model is None:
model = Model(lang="en-us")
else:
model = Model(lang=args.model)
if args.filename:
dump_fn = open(args.filename, "wb")
else:
dump_fn = None
with sd.RawInputStream(samplerate=args.samplerate, blocksize = 8000, device=args.device, dtype='int16',
channels=1, callback=callback):
print('#' * 80)
print('Press Ctrl+C to stop the recording')
print('#' * 80)
with sd.RawInputStream(samplerate=args.samplerate, blocksize = 8000, device=args.device,
dtype="int16", channels=1, callback=callback):
print("#" * 80)
print("Press Ctrl+C to stop the recording")
print("#" * 80)
rec = vosk.KaldiRecognizer(model, args.samplerate)
while True:
data = q.get()
if rec.AcceptWaveform(data):
print(rec.Result())
else:
print(rec.PartialResult())
if dump_fn is not None:
dump_fn.write(data)
rec = KaldiRecognizer(model, args.samplerate)
while True:
data = q.get()
if rec.AcceptWaveform(data):
print(rec.Result())
else:
print(rec.PartialResult())
if dump_fn is not None:
dump_fn.write(data)
except KeyboardInterrupt:
print('\nDone')
print("\nDone")
parser.exit(0)
except Exception as e:
parser.exit(type(e).__name__ + ': ' + str(e))
parser.exit(type(e).__name__ + ": " + str(e))
+5 -5
View File
@@ -1,16 +1,16 @@
#!/usr/bin/env python3
from vosk import Model, KaldiRecognizer, SetLogLevel
import sys
import os
import wave
import sys
from vosk import Model, KaldiRecognizer, SetLogLevel
SetLogLevel(0)
wf = wave.open(sys.argv[1], "rb")
if wf.getnchannels() != 1 or wf.getsampwidth() != 2 or wf.getcomptype() != "NONE":
print ("Audio file must be WAV format mono PCM.")
exit (1)
print("Audio file must be WAV format mono PCM.")
sys.exit(1)
model = Model(lang="en-us")
rec = KaldiRecognizer(model, wf.getframerate())
+8 -8
View File
@@ -1,17 +1,17 @@
#!/usr/bin/env python3
from vosk import Model, KaldiRecognizer, SetLogLevel
import sys
import os
import wave
import sys
import json
from vosk import Model, KaldiRecognizer, SetLogLevel
SetLogLevel(0)
wf = wave.open(sys.argv[1], "rb")
if wf.getnchannels() != 1 or wf.getsampwidth() != 2 or wf.getcomptype() != "NONE":
print ("Audio file must be WAV format mono PCM.")
exit (1)
print("Audio file must be WAV format mono PCM.")
sys.exit(1)
model = Model(lang="en-us")
rec = KaldiRecognizer(model, wf.getframerate())
@@ -22,12 +22,12 @@ while True:
break
if rec.AcceptWaveform(data):
print(rec.Result())
break
sys.exit(1)
else:
jres = json.loads(rec.PartialResult())
print(jres)
if jres['partial'] == "one zero zero zero":
if jres["partial"] == "one zero zero zero":
print("We can reset recognizer here and start over")
rec.Reset();
rec.Reset()
+5 -5
View File
@@ -1,17 +1,17 @@
#!/usr/bin/env python3
from vosk import Model, KaldiRecognizer, SetLogLevel
import sys
import os
import wave
import sys
from vosk import Model, KaldiRecognizer, SetLogLevel
# You can set log level to -1 to disable debug messages
SetLogLevel(0)
wf = wave.open(sys.argv[1], "rb")
if wf.getnchannels() != 1 or wf.getsampwidth() != 2 or wf.getcomptype() != "NONE":
print ("Audio file must be WAV format mono PCM.")
exit (1)
print("Audio file must be WAV format mono PCM.")
sys.exit(1)
model = Model(lang="en-us")

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