Compare commits

..

5 Commits

Author SHA1 Message Date
Nickolay Shmyrev 8dd2166443 Another timestamp bugfix 2020-06-29 14:11:05 +02:00
Nickolay Shmyrev 7f3f25e170 Release memory when final result is received to reduce memory pressure. 2020-06-24 18:37:53 +02:00
Nickolay Shmyrev f48e4624bf Actually show the distance 2020-06-23 22:14:39 +02:00
Nickolay V. Shmyrev 856c935e92 Merge pull request #153 from He1nr1chK/master
Added cosine distance function
2020-06-23 21:54:47 +03:00
He1nr1chK 444123b37e Merge pull request #1 from He1nr1chK/He1nr1chK-Nodejs
Added cosine distance function
2020-06-23 20:11:22 +02:00
40 changed files with 172 additions and 1002 deletions
-3
View File
@@ -5,9 +5,6 @@
# Java class files
*.class
# Object files
*.o
# Gradle files
.gradle/
build/
+20 -19
View File
@@ -1,25 +1,26 @@
# About
Vosk is an offline open source speech recognition toolkit. It enables
speech recognition models for 16 languages and dialects - English, Indian
English, German, French, Spanish, Portuguese, Chinese, Russian, Turkish,
Vietnamese, Italian, Dutch, Catalan, Arabic, Greek, Farsi.
Vosk models are small (50 Mb) but provide continuous large vocabulary
transcription, zero-latency response with streaming API, reconfigurable
Vosk is an open source speech recognition toolkit which supports 9
languages - English, German, French, Spanish, Portuguese, Chinese,
Russian, Turkish, Vietnamese. Vosk works offline with small (50 Mb), but
accurate model, zero-latency response with streaming API, reconfigurable
vocabulary and speaker identification.
Speech recognition bindings implemented for various programming languages
like Python, Java, Node.JS, C#, C++ and others.
### Installation and usage
Vosk supplies speech recognition for chatbots, smart home appliances,
virtual assistants. It can also create subtitles for movies,
transcription for lectures and interviews.
For Vosk installation instructions, examples and turorial and documentation visit https://alphacephei.com/vosk
Vosk scales from small devices like Raspberry Pi or Android smartphone to
big clusters.
### Build
# Documentation
[![Build Status](https://travis-ci.com/alphacep/vosk-api.svg?branch=master)](https://travis-ci.com/alphacep/vosk-api)
For installation instructions, examples and documentation visit [Vosk
Website](https://alphacephei.com/vosk).
### Models for different languages
For information about models see [the documentation on available models](https://alphacephei.com/vosk/models.html).
### Contact Us
If you have any questions, feel free to:
* Post an issue here on github
* Send us an e-mail at [contact@alphacephei.com](mailto:contact@alphacephei.com)
* Join our group dedicated to speech recognition on Telegram [@speech_recognition](https://t.me/speech_recognition)
* We have a Wechat group which is pretty big, so it is invitation-only. Mail us to join the group and provide some information about yourself.
+2 -4
View File
@@ -22,8 +22,6 @@ set(LIB_ROOT "${PROJECT_SOURCE_DIR}/build/kaldi_${KALDI_SUFFIX}/local")
set(API_SOURCES
"${PROJECT_SOURCE_DIR}/../src/kaldi_recognizer.cc"
"${PROJECT_SOURCE_DIR}/../src/kaldi_recognizer.h"
"${PROJECT_SOURCE_DIR}/../src/language_model.cc"
"${PROJECT_SOURCE_DIR}/../src/language_model.h"
"${PROJECT_SOURCE_DIR}/../src/model.cc"
"${PROJECT_SOURCE_DIR}/../src/model.h"
"${PROJECT_SOURCE_DIR}/../src/spk_model.cc"
@@ -34,14 +32,14 @@ set(API_SOURCES
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O3 -DFST_NO_DYNAMIC_LINKING")
add_library( vosk_jni SHARED
add_library( kaldi_jni SHARED
build/generated-src/cpp/vosk_wrap.cc
${API_SOURCES}
)
include_directories("${PROJECT_SOURCE_DIR}/../src" "build/kaldi_${KALDI_SUFFIX}/kaldi/src" "build/kaldi_${KALDI_SUFFIX}/local/include")
target_link_libraries( vosk_jni
target_link_libraries( kaldi_jni
${KALDI_ROOT}/src/online2/kaldi-online2.a
${KALDI_ROOT}/src/decoder/kaldi-decoder.a
${KALDI_ROOT}/src/ivector/kaldi-ivector.a
+3 -45
View File
@@ -5,17 +5,9 @@ buildscript {
}
dependencies {
classpath 'com.android.tools.build:gradle:3.5.3'
classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.8.5'
}
}
plugins {
id "com.jfrog.bintray" version "1.8.5"
}
def archiveName = "vosk-android"
def libVersion = "0.3.15"
allprojects {
repositories {
google()
@@ -24,16 +16,15 @@ allprojects {
}
apply plugin: 'com.android.library'
apply plugin: 'maven-publish'
android {
compileSdkVersion 29
defaultConfig {
minSdkVersion 21
targetSdkVersion 29
versionCode 6
versionName = libVersion
archivesBaseName = archiveName
versionCode 5
versionName "5.2"
setProperty("archivesBaseName", "kaldi-android-$versionName")
externalNativeBuild {
cmake {
arguments "-DCMAKE_VERBOSE_MAKEFILE=ON", "-DANDROID_ARM_NEON=TRUE", "-DCMAKE_CXX_FLAGS_RELEASE=-O3"
@@ -55,39 +46,6 @@ android {
}
}
Properties properties = new Properties()
properties.load(project.rootProject.file('local.properties').newDataInputStream())
bintray {
user = properties.getProperty("bintray.user")
key = properties.getProperty("bintray.apikey")
pkg {
repo = 'vosk'
name = 'vosk-android'
userOrg = "alphacep"
licenses = ['Apache2.0']
websiteUrl = 'https://github.com/alphacep/vosk-api'
issueTrackerUrl = 'https://github.com/alphacep/vosk-api/issues'
vcsUrl = 'https://github.com/alphacep/vosk-api'
version {
name = libVersion
vcsTag = libVersion
}
}
publications = ['aar']
}
publishing {
publications {
aar(MavenPublication) {
groupId 'com.alphacep'
artifactId archiveName
version libVersion
artifact("$buildDir/outputs/aar/$archiveName-release.aar")
}
}
}
task swig {
doLast {
mkdir 'build/generated-src/java'
@@ -29,18 +29,19 @@ import android.os.Looper;
import android.util.Log;
/**
* Service that records audio in a thread, passes it to a recognizer and emits
* recognition results. Recognition events are passed to a client using
* Main class to access recognizer functions. After configuration this class
* starts a listener thread which records the data and recognizes it using
* VOSK engine. Recognition events are passed to a client using
* {@link RecognitionListener}
*
*
*/
public class SpeechService {
public class SpeechRecognizer {
protected static final String TAG = SpeechService.class.getSimpleName();
protected static final String TAG = SpeechRecognizer.class.getSimpleName();
private final KaldiRecognizer recognizer;
private final int sampleRate;
private final int sampleRate;
private final static float BUFFER_SIZE_SECONDS = 0.4f;
private int bufferSize;
private final AudioRecord recorder;
@@ -52,18 +53,33 @@ public class SpeechService {
private final Collection<RecognitionListener> listeners = new HashSet<RecognitionListener>();
/**
* Creates speech service. Service holds the AudioRecord object, so you
* Creates speech recognizer. Recognizer holds the AudioRecord object, so you
* need to call {@link release} in order to properly finalize it.
*
* @throws IOException thrown if audio recorder can not be created for some reason.
*/
public SpeechService(KaldiRecognizer recognizer, float sampleRate) throws IOException {
this.recognizer = recognizer;
this.sampleRate = (int)sampleRate;
bufferSize = Math.round(this.sampleRate * BUFFER_SIZE_SECONDS);
public SpeechRecognizer(Model model) throws IOException {
recognizer = new KaldiRecognizer(model, 16000.0f);
sampleRate = 16000;
bufferSize = Math.round(sampleRate * BUFFER_SIZE_SECONDS);
recorder = new AudioRecord(
AudioSource.VOICE_RECOGNITION, this.sampleRate,
AudioSource.VOICE_RECOGNITION, sampleRate,
AudioFormat.CHANNEL_IN_MONO,
AudioFormat.ENCODING_PCM_16BIT, bufferSize * 2);
if (recorder.getState() == AudioRecord.STATE_UNINITIALIZED) {
recorder.release();
throw new IOException(
"Failed to initialize recorder. Microphone might be already in use.");
}
}
public SpeechRecognizer(Model model, SpkModel spkModel) throws IOException {
recognizer = new KaldiRecognizer(model, spkModel, 16000.0f);
sampleRate = 16000;
bufferSize = Math.round(sampleRate * BUFFER_SIZE_SECONDS);
recorder = new AudioRecord(
AudioSource.VOICE_RECOGNITION, sampleRate,
AudioFormat.CHANNEL_IN_MONO,
AudioFormat.ENCODING_PCM_16BIT, bufferSize * 2);
@@ -171,9 +187,9 @@ public class SpeechService {
public void shutdown() {
recorder.release();
}
private final class RecognizerThread extends Thread {
private int remainingSamples;
private int timeoutSamples;
private final static int NO_TIMEOUT = -1;
-50
View File
@@ -1,50 +0,0 @@
KALDI_ROOT=$(HOME)/travis/kaldi
VOSK_SOURCES= \
../src/kaldi_recognizer.cc \
../src/language_model.cc \
../src/model.cc \
../src/spk_model.cc \
../src/vosk_api.cc
CFLAGS=-g -O2 -DFST_NO_DYNAMIC_LINKING -I../src -I$(KALDI_ROOT)/src -I$(KALDI_ROOT)/tools/openfst/include
LIBS= \
$(KALDI_ROOT)/src/online2/kaldi-online2.a \
$(KALDI_ROOT)/src/decoder/kaldi-decoder.a \
$(KALDI_ROOT)/src/ivector/kaldi-ivector.a \
$(KALDI_ROOT)/src/gmm/kaldi-gmm.a \
$(KALDI_ROOT)/src/nnet3/kaldi-nnet3.a \
$(KALDI_ROOT)/src/tree/kaldi-tree.a \
$(KALDI_ROOT)/src/feat/kaldi-feat.a \
$(KALDI_ROOT)/src/lat/kaldi-lat.a \
$(KALDI_ROOT)/src/lm/kaldi-lm.a \
$(KALDI_ROOT)/src/hmm/kaldi-hmm.a \
$(KALDI_ROOT)/src/transform/kaldi-transform.a \
$(KALDI_ROOT)/src/cudamatrix/kaldi-cudamatrix.a \
$(KALDI_ROOT)/src/matrix/kaldi-matrix.a \
$(KALDI_ROOT)/src/fstext/kaldi-fstext.a \
$(KALDI_ROOT)/src/util/kaldi-util.a \
$(KALDI_ROOT)/src/base/kaldi-base.a \
$(KALDI_ROOT)/tools/OpenBLAS/libopenblas.a \
$(KALDI_ROOT)/tools/openfst/lib/libfst.a \
$(KALDI_ROOT)/tools/openfst/lib/libfstngram.a
all: test_vosk test_vosk_speaker
test_vosk: test_vosk.o libvosk.a
g++ $^ -o $@ $(LIBS) -lgfortran -lpthread
test_vosk_speaker: test_vosk_speaker.o libvosk.a
g++ $^ -o $@ $(LIBS) -lgfortran -lpthread
libvosk.a: $(VOSK_SOURCES:.cc=.o)
ar rcs $@ $^
%.o: %.c
g++ $(CFLAGS) -c -o $@ $<
%.o: %.cc
g++ -std=c++11 $(CFLAGS) -c -o $@ $<
clean:
rm -f *.o *.a test_vosk
-28
View File
@@ -1,28 +0,0 @@
#include <vosk_api.h>
#include <stdio.h>
int main() {
FILE *wavin;
char buf[3200];
int nread, final;
VoskModel *model = vosk_model_new("model");
VoskRecognizer *recognizer = vosk_recognizer_new(model, 16000.0);
wavin = fopen("test.wav", "rb");
fseek(wavin, 44, SEEK_SET);
while (!feof(wavin)) {
nread = fread(buf, 1, sizeof(buf), wavin);
final = vosk_recognizer_accept_waveform(recognizer, buf, nread);
if (final) {
printf("%s\n", vosk_recognizer_result(recognizer));
} else {
printf("%s\n", vosk_recognizer_partial_result(recognizer));
}
}
printf("%s\n", vosk_recognizer_final_result(recognizer));
vosk_recognizer_free(recognizer);
vosk_model_free(model);
return 0;
}
-30
View File
@@ -1,30 +0,0 @@
#include <vosk_api.h>
#include <stdio.h>
int main() {
FILE *wavin;
char buf[3200];
int nread, final;
VoskModel *model = vosk_model_new("model");
VoskSpkModel *spk_model = vosk_spk_model_new("spk-model");
VoskRecognizer *recognizer = vosk_recognizer_new_spk(model, spk_model, 16000.0);
wavin = fopen("test.wav", "rb");
fseek(wavin, 44, SEEK_SET);
while (!feof(wavin)) {
nread = fread(buf, 1, sizeof(buf), wavin);
final = vosk_recognizer_accept_waveform(recognizer, buf, nread);
if (final) {
printf("%s\n", vosk_recognizer_result(recognizer));
} else {
printf("%s\n", vosk_recognizer_partial_result(recognizer));
}
}
printf("%s\n", vosk_recognizer_final_result(recognizer));
vosk_recognizer_free(recognizer);
vosk_spk_model_free(spk_model);
vosk_model_free(model);
return 0;
}
+10 -17
View File
@@ -20,12 +20,9 @@ KALDI_LIBS = \
${KALDI_ROOT}/src/util/kaldi-util.a \
${KALDI_ROOT}/src/base/kaldi-base.a \
${KALDI_ROOT}/tools/openfst/lib/libfst.a \
${KALDI_ROOT}/tools/openfst/lib/libfstngram.a
# On Linux
MATH_LIBS = ${KALDI_ROOT}/tools/OpenBLAS/libopenblas.a -lgfortran
# On OSX
# MATH_LIBS = -framework Accelerate
${KALDI_ROOT}/tools/openfst/lib/libfstngram.a \
${KALDI_ROOT}/tools/OpenBLAS/libopenblas.a \
-lgfortran -lstdc++
all: test.exe
@@ -35,20 +32,16 @@ test.exe: libkaldiwrap.so test.cs
VOSK_SOURCES = \
vosk_wrap.c \
../src/kaldi_recognizer.cc \
../src/language_model.cc \
../src/model.cc \
../src/spk_model.cc \
../src/vosk_api.cc
VOSK_HEADERS = \
../src/kaldi_recognizer.h \
../src/language_model.h \
../src/model.cc \
../src/model.h \
../src/vosk_api.h \
../src/spk_model.h
../src/spk_model.cc \
../src/spk_model.h \
../src/vosk_api.cc \
../src/vosk_api.h
libkaldiwrap.so: $(VOSK_SOURCES) $(VOSK_HEADERS)
$(CXX) -fpermissive $(CFLAGS) $(CPPFLAGS) -shared -o $@ $(VOSK_SOURCES) $(KALDI_LIBS) $(MATH_LIBS)
libkaldiwrap.so: $(VOSK_SOURCES)
$(CXX) -fpermissive $(CFLAGS) $(CPPFLAGS) -shared -o $@ $(VOSK_SOURCES) $(KALDI_LIBS)
vosk_wrap.c: ../src/vosk.i
mkdir -p gen
+1 -1
View File
@@ -1 +1 @@
See https://alphacephei.com/vosk/accuracy
See https://alphacephei.com/vosk/accuracy.html
+1 -1
View File
@@ -1 +1 @@
See https://alphacephei.com/vosk/adaptation
See https://alphacephei.com/vosk/adaptation.html
+1 -1
View File
@@ -1 +1 @@
See https://alphacephei.com/vosk/models
See https://alphacephei.com/vosk/models.html
+3 -165
View File
@@ -12,199 +12,37 @@
// See the License for the specific language governing permissions and
// limitations under the License.
/* This header contains the C API for Vosk speech recognition system */
#ifndef VOSK_API_H
#define VOSK_API_H
#ifndef _VOSK_API_H_
#define _VOSK_API_H_
#ifdef __cplusplus
extern "C" {
#endif
/** Model stores all the data required for recognition
* it contains static data and can be shared across processing
* threads. */
typedef struct VoskModel VoskModel;
/** Speaker model is the same as model but contains the data
* for speaker identification. */
typedef struct VoskSpkModel VoskSpkModel;
/** Recognizer object is the main object which processes data.
* Each recognizer usually runs in own thread and takes audio as input.
* Once audio is processed recognizer returns JSON object as a string
* which represent decoded information - words, confidences, times, n-best lists,
* speaker information and so on */
typedef struct VoskRecognizer VoskRecognizer;
/** Loads model data from the file and returns the model object
*
* @param model_path: the path of the model on the filesystem
@ @returns model object */
VoskModel *vosk_model_new(const char *model_path);
/** Releases the model memory
*
* The model object is reference-counted so if some recognizer
* depends on this model, model might still stay alive. When
* last recognizer is released, model will be released too. */
void vosk_model_free(VoskModel *model);
/** Loads speaker model data from the file and returns the model object
*
* @param model_path: the path of the model on the filesystem
* @returns model object */
VoskSpkModel *vosk_spk_model_new(const char *model_path);
/** Releases the model memory
*
* The model object is reference-counted so if some recognizer
* depends on this model, model might still stay alive. When
* last recognizer is released, model will be released too. */
void vosk_spk_model_free(VoskSpkModel *model);
/** Creates the recognizer object
*
* The recognizers process the speech and return text using shared model data
* @param sample_rate The sample rate of the audio you going to feed into the recognizer
* @returns recognizer object */
VoskRecognizer *vosk_recognizer_new(VoskModel *model, float sample_rate);
/** 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 spk_model speaker model for speaker identification
* @param sample_rate The sample rate of the audio you going to feed into the recognizer
* @returns recognizer object */
VoskRecognizer *vosk_recognizer_new_spk(VoskModel *model, VoskSpkModel *spk_model, float sample_rate);
/** Creates the recognizer object with the grammar
*
* Sometimes when you want to improve recognition accuracy and when you don't need
* to recognize large vocabulary you can specify a list of words 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 sample_rate The sample rate of the audio you going to feed into the recognizer
* @param grammar The string with the list of words to recognize, for example "one two three four five [unk]"
*
* @returns recognizer object */
VoskRecognizer *vosk_recognizer_new_grm(VoskModel *model, float sample_rate, const char *grammar);
/** 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 true if silence is occured and you can retrieve a new utterance with result method */
int vosk_recognizer_accept_waveform(VoskRecognizer *recognizer, const char *data, int length);
/** Same as above but the version with the short data for language bindings where you have
* audio as array of shorts */
int vosk_recognizer_accept_waveform_s(VoskRecognizer *recognizer, const short *data, int length);
/** Same as above but the version with the float data for language bindings where you have
* audio as array of floats */
int vosk_recognizer_accept_waveform_f(VoskRecognizer *recognizer, const float *data, int length);
/** Returns speech recognition result
*
* @returns the result in JSON format which contains decoded line, decoded
* words, times in seconds and confidences. You can parse this result
* with any json parser
*
* <pre>
* {
* "result" : [{
* "conf" : 1.000000,
* "end" : 1.110000,
* "start" : 0.870000,
* "word" : "what"
* }, {
* "conf" : 1.000000,
* "end" : 1.530000,
* "start" : 1.110000,
* "word" : "zero"
* }, {
* "conf" : 1.000000,
* "end" : 1.950000,
* "start" : 1.530000,
* "word" : "zero"
* }, {
* "conf" : 1.000000,
* "end" : 2.340000,
* "start" : 1.950000,
* "word" : "zero"
* }, {
* "conf" : 1.000000,
* "end" : 2.610000,
* "start" : 2.340000,
* "word" : "one"
* }],
* "text" : "what zero zero zero one"
* }
* </pre>
*/
const char *vosk_recognizer_result(VoskRecognizer *recognizer);
/** Returns partial speech recognition
*
* @returns partial speech recognition text which is not yet finalized.
* result may change as recognizer process more data.
*
* <pre>
* {
* "partial" : "cyril one eight zero"
* }
* </pre>
*/
const char *vosk_recognizer_partial_result(VoskRecognizer *recognizer);
/** Returns speech recognition result. Same as result, but doesn't wait for silence
* You usually call it in the end of the stream to get final bits of audio. It
* flushes the feature pipeline, so all remaining audio chunks got processed.
*
* @returns speech result in JSON format.
*/
const char *vosk_recognizer_final_result(VoskRecognizer *recognizer);
/** Releases recognizer object
*
* Underlying model is also unreferenced and if needed released */
void vosk_recognizer_free(VoskRecognizer *recognizer);
/** Set log level for Kaldi messages
*
* @param log_level the level
* 0 - default value to print info and error messages but no debug
* less than 0 - don't print info messages
* greather than 0 - more verbose mode
*/
void vosk_set_log_level(int log_level);
#ifdef __cplusplus
}
#endif
#endif /* VOSK_API_H */
#endif /* _VOSK_API_H_ */
-2
View File
@@ -30,8 +30,6 @@ VOSK_SOURCES = \
vosk_wrap.cc \
../src/kaldi_recognizer.cc \
../src/kaldi_recognizer.h \
../src/language_model.cc \
../src/language_model.h \
../src/model.cc \
../src/model.h \
../src/spk_model.cc \
+3
View File
@@ -14,6 +14,9 @@ import org.kaldi.SpkModel;
import org.kaldi.Vosk;
public class DecoderTest {
static {
System.loadLibrary("vosk_jni");
}
public static void main(String args[]) throws IOException {
Vosk.SetLogLevel(-10);
+1 -1
View File
@@ -9,7 +9,7 @@ Still, you need swig of newest version 4.0.1
Build like this
```
npm install --kaldi_root=/home/user/kaldi
npm install --kaldi_root=/home/suser/kaldi
```
Then test with
+9 -19
View File
@@ -5,32 +5,22 @@
'sources': [
'../src/kaldi_recognizer.cc',
'../src/model.cc',
'../src/language_model.cc',
'../src/spk_model.cc',
'../src/vosk_api.cc',
'vosk_wrap.cc',
],
'cflags': [
'-std=c++11',
'-DFST_NO_DYNAMIC_LINKING',
'-Wno-deprecated-declarations',
'-Wno-sign-compare',
'-Wno-unused-local-typedefs',
'-Wno-ignored-quaifiers',
'-Wno-extra',
'-std=c++11',
'-DFST_NO_DYNAMIC_LINKING',
'-Wno-deprecated-declarations',
'-Wno-sign-compare',
'-Wno-unused-local-typedefs',
'-Wno-ignored-quaifiers',
'-Wno-extra',
],
'cflags_cc!' : [
'-fno-rtti',
'-fno-exceptions',
],
'conditions': [
['OS == "mac"', {
'xcode_settings': {
'GCC_ENABLE_CPP_EXCEPTIONS': 'YES',
'GCC_ENABLE_CPP_RTTI': 'YES',
'CLANG_CXX_LANGUAGE_STANDARD': 'c++11'
}
}]
'-fno-rtti',
'-fno-exceptions',
],
'actions': [
{
+19 -19
View File
@@ -24,25 +24,25 @@ const wfReader = new wav.Reader()
const model = new Model('model')
const spkModel = new SpkModel('model-spk')
const spk_sig = [-0.56648, 0.030579, 1.730239, 0.239899, -1.194183,
0.251954, 0.540388, -0.971872, -0.020963, 0.085036, 0.563973, -0.019682,
-0.597381, 1.094719, -0.566738, 0.29819, 0.171165, 0.370341, -0.033539,
-0.09757, 1.228286, 0.485949, 0.427826, 0.147762, -0.015112, 0.599513,
-2.040655, -0.490882, 0.440161, -0.072991, 0.835955, -0.496124, 0.952978,
0.85356, -1.096116, 0.107764, -0.385486, 1.410305, 0.609147, -0.457014,
-1.542864, 0.343669, 0.171913, -0.627281, -1.281781, -1.134276,
-0.639895, 1.190183, -0.700537, 1.063457, 0.206946, 0.342198, -1.165625,
1.475955, -0.089007, -2.555155, 0.551438, -0.212736, 1.025625, -1.631965,
-0.716256, -1.295995, 1.554956, -1.866009, -1.010782, -1.43231, 0.109027,
2.123925, 1.703283, -0.784997, 2.730568, 0.755113, 0.0617, 0.128955,
-0.054047, 1.359119, -0.611666, -1.105754, -0.631353, 0.052109, 0.729386,
-0.769876, 1.250235, -1.463298, 0.648176, -0.73239, -0.385239,
-1.661856, 0.602106, -0.45567, -1.438431, -0.836673, 0.033557, 0.373597,
-1.343341, -0.181095, 0.237287, -0.522005, -1.722836, 0.932333,
-0.092861, -0.219254, 0.476182, 1.033803, -1.633563, -0.874341, 1.039064,
-1.758573, -0.838422, -0.324336, -0.924634, 1.962594, 2.152814, 1.2521,
-0.46172, -1.50271, 1.685691, 0.403097, -0.819042, 0.866403, -0.591716,
-0.578645, -0.553839, 0.381861, -1.051647, -1.477578, 0.524005, 0.925245]
const spk_sig = [4.658117, 1.277387, 3.346158, -1.473036, -2.15727,
2.461757, 3.76756, -1.241252, 2.333765, 0.642588, -2.848165, 1.229534,
3.907015, 1.726496, -1.188692, 1.16322, -0.668811, -0.623309, 4.628018,
0.407197, 0.089955, 0.920438, 1.47237, -0.311365, -0.437051, -0.531738,
-1.591781, 3.095415, 0.439524, -0.274787, 4.03165, 2.665864, 4.815553,
1.581063, 1.078242, 5.017717, -0.089395, -3.123428, 5.34038, 0.456982,
2.465727, 2.131833, 4.056272, 1.178392, -2.075712, -1.568503, 0.847139,
0.409214, 1.84727, 0.986758, 4.222116, 2.235512, 1.369377, 4.283126,
2.278125, -1.467577, -0.999971, 3.070041, 1.462214, 0.423204, 2.143578,
0.567174, -2.294655, 1.864723, 4.307356, 2.610872, -1.238721, 0.551861,
2.861954, 0.59613, -0.715396, -1.395357, 2.706177, -2.004444, 2.055255,
0.458283, 1.231968, 3.48234, 2.993858, 0.402819, 0.940885, 0.360162,
-2.173674, -2.504609, 0.329541, 3.653913, 3.638025, -1.406409, 2.14059,
1.662765, -0.991323, 0.770921, 0.010094, 3.775469, 1.847511, 2.074432,
-1.928593, 0.807414, 2.964505, 0.128597, 1.297962, 2.645227, 0.136405,
-2.543087, 0.932246, 2.405783, -2.122267, 3.044013, 0.486728, 4.395338,
0.474267, 0.781297, 1.694144, -0.831078, -0.462362, -0.964715, 3.187863,
6.008708, 1.725954, 3.667886, -1.467623, 3.370667, 2.72555, -0.796541,
2.416543, 0.675401, -0.737634, -1.709676]
function dotp(x, y) {
function dotp_sum(a, b) {
+2 -2
View File
@@ -1,7 +1,7 @@
{
"name": "vosk",
"version": "0.3.15",
"description": "Node binding for continuous offline voice recoginition with Vosk library.",
"version": "0.3.8",
"description": "Node binding for continuous voice recoginition through pocketsphinx.",
"repository": {
"type": "git",
"url": "git://github.com/alphacep/vosk-api.git"
+2 -22
View File
@@ -1,23 +1,3 @@
This is a Python module for Vosk.
Python module for vosk-api
Vosk is an offline open source speech recognition toolkit. It enables
speech recognition models for 16 languages and dialects - English, Indian
English, German, French, Spanish, Portuguese, Chinese, Russian, Turkish,
Vietnamese, Italian, Dutch, Catalan, Arabic, Greek, Farsi.
Vosk models are small (50 Mb) but provide continuous large vocabulary
transcription, zero-latency response with streaming API, reconfigurable
vocabulary and speaker identification.
Vosk supplies speech recognition for chatbots, smart home appliances,
virtual assistants. It can also create subtitles for movies,
transcription for lectures and interviews.
Vosk scales from small devices like Raspberry Pi or Android smartphone to
big clusters.
# Documentation
For installation instructions, examples and documentation visit [Vosk
Website](https://alphacephei.com/vosk). See also our project on
[Github](https://github.com/alphacep/vosk-api).
See for details https://github.com/alphacep/vosk-api
+1 -1
View File
@@ -9,7 +9,7 @@ import subprocess
SetLogLevel(0)
if not os.path.exists("model"):
print ("Please download the model from https://alphacephei.com/vosk/models and unpack as 'model' in the current folder.")
print ("Please download the model from https://github.com/alphacep/vosk-api/blob/master/doc/models.md and unpack as 'model' in the current folder.")
exit (1)
sample_rate=16000
+1 -1
View File
@@ -4,7 +4,7 @@ from vosk import Model, KaldiRecognizer
import os
if not os.path.exists("model"):
print ("Please download the model from https://alphacephei.com/vosk/models and unpack as 'model' in the current folder.")
print ("Please download the model from https://github.com/alphacep/vosk-api/blob/master/doc/models.md and unpack as 'model' in the current folder.")
exit (1)
import pyaudio
+1 -1
View File
@@ -8,7 +8,7 @@ import wave
SetLogLevel(0)
if not os.path.exists("model"):
print ("Please download the model from https://alphacephei.com/vosk/models and unpack as 'model' in the current folder.")
print ("Please download the model from https://github.com/alphacep/vosk-api/blob/master/doc/models.md and unpack as 'model' in the current folder.")
exit (1)
wf = wave.open(sys.argv[1], "rb")
+5 -8
View File
@@ -11,11 +11,11 @@ model_path = "model"
spk_model_path = "model-spk"
if not os.path.exists(model_path):
print ("Please download the model from https://alphacephei.com/vosk/models and unpack as {} in the current folder.".format(model_path))
print ("Please download the model from https://github.com/alphacep/vosk-api/blob/master/doc/models.md and unpack as {} in the current folder.".format(model_path))
exit (1)
if not os.path.exists(spk_model_path):
print ("Please download the speaker model from https://alphacephei.com/vosk/models and unpack as {} in the current folder.".format(spk_model_path))
print ("Please download the speaker model from https://github.com/alphacep/vosk-api/blob/master/doc/models.md and unpack as {} in the current folder.".format(spk_model_path))
exit (1)
wf = wave.open(sys.argv[1], "rb")
@@ -30,7 +30,7 @@ rec = KaldiRecognizer(model, spk_model, wf.getframerate())
# We compare speakers with cosine distance. We can keep one or several fingerprints for the speaker in a database
# to distingusih among users.
spk_sig = [-1.110417,0.09703002,1.35658,0.7798632,-0.305457,-0.339204,0.6186931,-0.4521213,0.3982236,-0.004530723,0.7651616,0.6500852,-0.6664245,0.1361499,0.1358056,-0.2887807,-0.1280468,-0.8208137,-1.620276,-0.4628615,0.7870904,-0.105754,0.9739769,-0.3258137,-0.7322628,-0.6212429,-0.5531687,-0.7796484,0.7035915,1.056094,-0.4941756,-0.6521456,-0.2238328,-0.003737517,0.2165709,1.200186,-0.7737719,0.492015,1.16058,0.6135428,-0.7183084,0.3153541,0.3458071,-1.418189,-0.9624157,0.4168292,-1.627305,0.2742135,-0.6166027,0.1962581,-0.6406527,0.4372789,-0.4296024,0.4898657,-0.9531326,-0.2945702,0.7879696,-1.517101,-0.9344181,-0.5049928,-0.005040941,-0.4637912,0.8223695,-1.079849,0.8871287,-0.9732434,-0.5548235,1.879138,-1.452064,-0.1975368,1.55047,0.5941782,-0.52897,1.368219,0.6782904,1.202505,-0.9256122,-0.9718158,-0.9570228,-0.5563112,-1.19049,-1.167985,2.606804,-2.261825,0.01340385,0.2526799,-1.125458,-1.575991,-0.363153,0.3270262,1.485984,-1.769565,1.541829,0.7293826,0.1743717,-0.4759418,1.523451,-2.487134,-1.824067,-0.626367,0.7448186,-1.425648,0.3524166,-0.9903384,3.339342,0.4563958,-0.2876643,1.521635,0.9508078,-0.1398541,0.3867955,-0.7550205,0.6568405,0.09419366,-1.583935,1.306094,-0.3501927,0.1794427,-0.3768163,0.9683866,-0.2442541,-1.696921,-1.8056,-0.6803037,-1.842043,0.3069353,0.9070363,-0.486526]
spk_sig = [4.658117, 1.277387, 3.346158, -1.473036, -2.15727, 2.461757, 3.76756, -1.241252, 2.333765, 0.642588, -2.848165, 1.229534, 3.907015, 1.726496, -1.188692, 1.16322, -0.668811, -0.623309, 4.628018, 0.407197, 0.089955, 0.920438, 1.47237, -0.311365, -0.437051, -0.531738, -1.591781, 3.095415, 0.439524, -0.274787, 4.03165, 2.665864, 4.815553, 1.581063, 1.078242, 5.017717, -0.089395, -3.123428, 5.34038, 0.456982, 2.465727, 2.131833, 4.056272, 1.178392, -2.075712, -1.568503, 0.847139, 0.409214, 1.84727, 0.986758, 4.222116, 2.235512, 1.369377, 4.283126, 2.278125, -1.467577, -0.999971, 3.070041, 1.462214, 0.423204, 2.143578, 0.567174, -2.294655, 1.864723, 4.307356, 2.610872, -1.238721, 0.551861, 2.861954, 0.59613, -0.715396, -1.395357, 2.706177, -2.004444, 2.055255, 0.458283, 1.231968, 3.48234, 2.993858, 0.402819, 0.940885, 0.360162, -2.173674, -2.504609, 0.329541, 3.653913, 3.638025, -1.406409, 2.14059, 1.662765, -0.991323, 0.770921, 0.010094, 3.775469, 1.847511, 2.074432, -1.928593, 0.807414, 2.964505, 0.128597, 1.297962, 2.645227, 0.136405, -2.543087, 0.932246, 2.405783, -2.122267, 3.044013, 0.486728, 4.395338, 0.474267, 0.781297, 1.694144, -0.831078, -0.462362, -0.964715, 3.187863, 6.008708, 1.725954, 3.667886, -1.467623, 3.370667, 2.72555, -0.796541, 2.416543, 0.675401, -0.737634, -1.709676]
def cosine_dist(x, y):
nx = np.array(x)
@@ -45,12 +45,9 @@ while True:
res = json.loads(rec.Result())
print ("Text:", res['text'])
print ("X-vector:", res['spk'])
print ("Speaker distance:", cosine_dist(spk_sig, res['spk']), "based on", res['spk_frames'], "frames")
print ("Note that second distance is not very reliable because utterance is too short. Utterances longer than 4 seconds give better xvector")
print ("Speaker distance:", cosine_dist(spk_sig, res['spk']))
res = json.loads(rec.FinalResult())
print ("Text:", res['text'])
print ("X-vector:", res['spk'])
print ("Speaker distance:", cosine_dist(spk_sig, res['spk']), "based on", res['spk_frames'], "frames")
print ("Speaker distance:", cosine_dist(spk_sig, res['spk']))
-49
View File
@@ -1,49 +0,0 @@
#!/usr/bin/env python3
from vosk import Model, KaldiRecognizer, SetLogLevel
import sys
import os
import wave
import subprocess
import srt
import json
import datetime
SetLogLevel(-1)
if not os.path.exists("model"):
print ("Please download the model from https://alphacephei.com/vosk/models and unpack as 'model' in the current folder.")
exit (1)
sample_rate=16000
model = Model("model")
rec = KaldiRecognizer(model, sample_rate)
process = subprocess.Popen(['ffmpeg', '-loglevel', 'quiet', '-i',
sys.argv[1],
'-ar', str(sample_rate) , '-ac', '1', '-f', 's16le', '-'],
stdout=subprocess.PIPE)
def transcribe():
results = []
subs = []
while True:
data = process.stdout.read(4000)
if len(data) == 0:
break
if rec.AcceptWaveform(data):
results.append(rec.Result())
results.append(rec.FinalResult())
for i, res in enumerate(results):
jres = json.loads(res)
s = srt.Subtitle(index=i,
content=jres['text'],
start=datetime.timedelta(seconds=jres['result'][0]['start']),
end=datetime.timedelta(seconds=jres['result'][-1]['end']))
subs.append(s)
return subs
print (srt.compose(transcribe()))
+1 -1
View File
@@ -6,7 +6,7 @@ import json
import os
if not os.path.exists("model"):
print ("Please download the model from https://alphacephei.com/vosk/models and unpack as 'model' in the current folder.")
print ("Please download the model from https://github.com/alphacep/vosk-api/blob/master/doc/models.md and unpack as 'model' in the current folder.")
exit (1)
+3 -4
View File
@@ -6,7 +6,7 @@ import os
import wave
if not os.path.exists("model"):
print ("Please download the model from https://alphacephei.com/vosk/models and unpack as 'model' in the current folder.")
print ("Please download the model from https://github.com/alphacep/vosk-api/blob/master/doc/models.md and unpack as 'model' in the current folder.")
exit (1)
wf = wave.open(sys.argv[1], "rb")
@@ -15,9 +15,8 @@ if wf.getnchannels() != 1 or wf.getsampwidth() != 2 or wf.getcomptype() != "NONE
exit (1)
model = Model("model")
# You can also specify the possible word or phrase list as JSON list, the order doesn't have to be strict
rec = KaldiRecognizer(model, wf.getframerate(), '["oh one two three four five six seven eight nine zero", "[unk]"]')
# You can also specify the possible word list
rec = KaldiRecognizer(model, wf.getframerate(), "zero oh one two three four five six seven eight nine")
while True:
data = wf.readframes(4000)
+5 -5
View File
@@ -52,14 +52,14 @@ kaldi_libraries = []
if sys.platform.startswith('darwin'):
kaldi_link_args.extend(['-Wl,-undefined,dynamic_lookup', '-framework', 'Accelerate'])
elif kaldi_mkl == "1":
elif kaldi_mkl != None:
kaldi_link_args.extend(['-L/opt/intel/mkl/lib/intel64', '-Wl,-rpath=/opt/intel/mkl/lib/intel64'])
kaldi_libraries.extend(['mkl_rt', 'mkl_intel_lp64', 'mkl_core', 'mkl_sequential'])
else:
kaldi_static_libs.append('tools/OpenBLAS/libopenblas.a')
kaldi_libraries.append('gfortran')
sources = ['kaldi_recognizer.cc', 'model.cc', 'spk_model.cc', 'vosk_api.cc', 'language_model.cc', 'vosk.i']
sources = ['kaldi_recognizer.cc', 'model.cc', 'spk_model.cc', 'vosk_api.cc', 'vosk.i']
vosk_ext = Extension('vosk._vosk',
define_macros = [('FST_NO_DYNAMIC_LINKING', '1')],
@@ -72,11 +72,11 @@ vosk_ext = Extension('vosk._vosk',
extra_compile_args = ['-std=c++11', '-Wno-sign-compare', '-Wno-unused-variable', '-Wno-unused-local-typedefs'])
setuptools.setup(
name="vosk",
version="0.3.15",
name="vosk", # Replace with your own username
version="0.3.10",
author="Alpha Cephei Inc",
author_email="contact@alphacephei.com",
description="Offline open source speech recognition API based on Kaldi and Vosk",
description="API for Kaldi and Vosk",
long_description=long_description,
long_description_content_type="text/markdown",
url="https://github.com/alphacep/vosk-api",
+31 -92
View File
@@ -16,7 +16,6 @@
#include "json.h"
#include "fstext/fstext-utils.h"
#include "lat/sausages.h"
#include "language_model.h"
using namespace fst;
using namespace kaldi::nnet3;
@@ -58,49 +57,28 @@ KaldiRecognizer::KaldiRecognizer(Model *model, float sample_frequency, char cons
feature_pipeline_ = new kaldi::OnlineNnet2FeaturePipeline (model_->feature_info_);
silence_weighting_ = new kaldi::OnlineSilenceWeighting(*model_->trans_model_, model_->feature_info_.silence_weighting_config, 3);
g_fst_ = new StdVectorFst();
if (model_->hcl_fst_) {
json::JSON obj;
obj = json::JSON::Load(grammar);
g_fst_->AddState();
g_fst_->SetStart(0);
g_fst_->AddState();
g_fst_->SetFinal(1, fst::TropicalWeight::One());
g_fst_->AddArc(1, StdArc(0, 0, fst::TropicalWeight::One(), 0));
if (obj.length() <= 0) {
KALDI_WARN << "Expecting array of strings, got: '" << grammar << "'";
} else {
KALDI_LOG << obj;
// Create simple word loop FST
stringstream ss(grammar);
string token;
LanguageModelOptions opts;
opts.ngram_order = 2;
opts.discount = 0.5;
LanguageModelEstimator estimator(opts);
for (int i = 0; i < obj.length(); i++) {
bool ok;
string line = obj[i].ToString(ok);
if (!ok) {
KALDI_ERR << "Expecting array of strings, got: '" << obj << "'";
}
std::vector<int32> sentence;
stringstream ss(line);
string token;
while (getline(ss, token, ' ')) {
int32 id = model_->word_syms_->Find(token);
if (id == kNoSymbol) {
KALDI_WARN << "Ignoring word missing in vocabulary: '" << token << "'";
} else {
sentence.push_back(id);
}
}
estimator.AddCounts(sentence);
}
g_fst_ = new StdVectorFst();
estimator.Estimate(g_fst_);
decode_fst_ = LookaheadComposeFst(*model_->hcl_fst_, *g_fst_, model_->disambig_);
while (getline(ss, token, ' ')) {
int32 id = model_->word_syms_->Find(token);
g_fst_->AddArc(0, StdArc(id, id, fst::TropicalWeight::One(), 1));
}
ArcSort(g_fst_, ILabelCompare<StdArc>());
decode_fst_ = LookaheadComposeFst(*model_->hcl_fst_, *g_fst_, model_->disambig_);
} else {
decode_fst_ = NULL;
KALDI_WARN << "Runtime graphs are not supported by this model";
KALDI_ERR << "Can't create decoding graph";
}
decoder_ = new kaldi::SingleUtteranceNnet3Decoder(model_->nnet3_decoding_config_,
@@ -186,6 +164,11 @@ void KaldiRecognizer::CleanUp()
delete silence_weighting_;
silence_weighting_ = new kaldi::OnlineSilenceWeighting(*model_->trans_model_, model_->feature_info_.silence_weighting_config, 3);
if (spk_model_) {
delete spk_feature_;
spk_feature_ = new OnlineMfcc(spk_model_->spkvector_mfcc_opts);
}
if (decoder_)
frame_offset_ += decoder_->NumFramesDecoded();
@@ -209,11 +192,6 @@ void KaldiRecognizer::CleanUp()
*model_->decodable_info_,
model_->hclg_fst_ ? *model_->hclg_fst_ : *decode_fst_,
feature_pipeline_);
if (spk_model_) {
delete spk_feature_;
spk_feature_ = new OnlineMfcc(spk_model_->spkvector_mfcc_opts);
}
} else {
decoder_->InitDecoding(frame_offset_);
}
@@ -316,9 +294,7 @@ static void RunNnetComputation(const MatrixBase<BaseFloat> &features,
xvector->CopyFromVec(cu_output.Row(0));
}
#define MIN_SPK_FEATS 50
bool KaldiRecognizer::GetSpkVector(Vector<BaseFloat> &out_xvector, int *num_spk_frames)
void KaldiRecognizer::GetSpkVector(Vector<BaseFloat> &xvector)
{
vector<int32> nonsilence_frames;
if (silence_weighting_->Active() && feature_pipeline_->NumFramesReady() > 0) {
@@ -328,36 +304,17 @@ bool KaldiRecognizer::GetSpkVector(Vector<BaseFloat> &out_xvector, int *num_spk_
&nonsilence_frames);
}
int num_frames = spk_feature_->NumFramesReady() - frame_offset_ * 3;
int num_frames = spk_feature_->NumFramesReady();
Matrix<BaseFloat> mfcc(num_frames, spk_feature_->Dim());
// Not very efficient, would be nice to have faster search
int num_nonsilence_frames = 0;
Vector<BaseFloat> feat(spk_feature_->Dim());
for (int i = 0; i < num_frames; ++i) {
if (std::find(nonsilence_frames.begin(),
nonsilence_frames.end(), i / 3) == nonsilence_frames.end()) {
nonsilence_frames.end(), i % 3) == nonsilence_frames.end())
continue;
}
spk_feature_->GetFrame(i + frame_offset_ * 3, &feat);
mfcc.CopyRowFromVec(feat, num_nonsilence_frames);
num_nonsilence_frames++;
Vector<BaseFloat> feat(spk_feature_->Dim());
spk_feature_->GetFrame(i, &feat);
mfcc.CopyRowFromVec(feat, i);
}
*num_spk_frames = num_nonsilence_frames;
// Don't extract vector if not enough data
if (num_nonsilence_frames < MIN_SPK_FEATS) {
return false;
}
mfcc.Resize(num_nonsilence_frames, spk_feature_->Dim(), kCopyData);
SlidingWindowCmnOptions cmvn_opts;
cmvn_opts.center = true;
cmvn_opts.cmn_window = 300;
Matrix<BaseFloat> features(mfcc.NumRows(), mfcc.NumCols(), kUndefined);
SlidingWindowCmn(cmvn_opts, mfcc, &features);
@@ -365,22 +322,7 @@ bool KaldiRecognizer::GetSpkVector(Vector<BaseFloat> &out_xvector, int *num_spk_
nnet3::CachingOptimizingCompilerOptions compiler_config;
nnet3::CachingOptimizingCompiler compiler(spk_model_->speaker_nnet, opts.optimize_config, compiler_config);
Vector<BaseFloat> xvector;
RunNnetComputation(features, spk_model_->speaker_nnet, &compiler, &xvector);
// Whiten the vector with global mean and transform and normalize mean
xvector.AddVec(-1.0, spk_model_->mean);
out_xvector.Resize(spk_model_->transform.NumRows(), kSetZero);
out_xvector.AddMatVec(1.0, spk_model_->transform, kNoTrans, xvector, 0.0);
BaseFloat norm = out_xvector.Norm(2.0);
BaseFloat ratio = norm / sqrt(out_xvector.Dim()); // how much larger it is
// than it would be, in
// expectation, if normally
out_xvector.Scale(1.0 / ratio);
return true;
}
const char* KaldiRecognizer::GetResult()
@@ -415,7 +357,7 @@ const char* KaldiRecognizer::GetResult()
DeterminizeLattice(composed_lat1, &clat);
}
fst::ScaleLattice(fst::GraphLatticeScale(0.9), &clat); // Apply rescoring weight
fst::ScaleLattice(fst::LatticeScale(9.0, 10.0), &clat);
CompactLattice aligned_lat;
if (model_->winfo_) {
WordAlignLattice(clat, *model_->trans_model_, *model_->winfo_, 0, &aligned_lat);
@@ -452,12 +394,9 @@ const char* KaldiRecognizer::GetResult()
if (spk_model_) {
Vector<BaseFloat> xvector;
int num_spk_frames;
if (GetSpkVector(xvector, &num_spk_frames)) {
for (int i = 0; i < xvector.Dim(); i++) {
obj["spk"].append(xvector(i));
}
obj["spk_frames"] = num_spk_frames;
GetSpkVector(xvector);
for (int i = 0; i < xvector.Dim(); i++) {
obj["spk"].append(xvector(i));
}
}
+1 -6
View File
@@ -12,9 +12,6 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef VOSK_KALDI_RECOGNIZER_H
#define VOSK_KALDI_RECOGNIZER_H
#include "base/kaldi-common.h"
#include "util/common-utils.h"
#include "fstext/fstext-lib.h"
@@ -58,7 +55,7 @@ class KaldiRecognizer {
void CleanUp();
void UpdateSilenceWeights();
bool AcceptWaveform(Vector<BaseFloat> &wdata);
bool GetSpkVector(Vector<BaseFloat> &out_xvector, int *frames);
void GetSpkVector(Vector<BaseFloat> &xvector);
const char *GetResult();
const char *StoreReturn(const string &res);
@@ -83,5 +80,3 @@ class KaldiRecognizer {
KaldiRecognizerState state_;
string last_result_;
};
#endif /* VOSK_KALDI_RECOGNIZER_H */
-211
View File
@@ -1,211 +0,0 @@
// Copyright 2015 Johns Hopkins University (author: Daniel Povey)
// See ../../COPYING for clarification regarding multiple authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
// WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
// MERCHANTABLITY OR NON-INFRINGEMENT.
// See the Apache 2 License for the specific language governing permissions and
// limitations under the License.
// A modified version from chain/language-model.cc for static backoff
#include <algorithm>
#include <numeric>
#include "language_model.h"
using namespace kaldi;
void LanguageModelEstimator::AddCounts(const std::vector<int32> &sentence) {
KALDI_ASSERT(opts_.ngram_order >= 2 && "--ngram-order must be >= 2");
int32 order = opts_.ngram_order;
// 0 is used for left-context at the beginning of the file.. treat it as BOS.
std::vector<int32> history(0);
std::vector<int32>::const_iterator iter = sentence.begin(),
end = sentence.end();
for (; iter != end; ++iter) {
KALDI_ASSERT(*iter != 0);
IncrementCount(history, *iter);
history.push_back(*iter);
if (history.size() >= order)
history.erase(history.begin());
}
// Probability of end of sentence. This will end up getting ignored later, but
// it still makes a difference for probability-normalization reasons.
IncrementCount(history, 0);
}
void LanguageModelEstimator::IncrementCount(const std::vector<int32> &history,
int32 next_phone) {
int32 lm_state_index = FindOrCreateLmStateIndexForHistory(history);
if (lm_states_[lm_state_index].tot_count == 0) {
num_active_lm_states_++;
}
lm_states_[lm_state_index].AddCount(next_phone, 1);
}
void LanguageModelEstimator::SetParentCounts() {
int32 num_lm_states = lm_states_.size();
for (int32 l = 0; l < num_lm_states; l++) {
int32 l_iter = lm_states_[l].backoff_lmstate_index;
while (l_iter != -1) {
lm_states_[l_iter].Add(lm_states_[l]);
l_iter = lm_states_[l_iter].backoff_lmstate_index;
}
}
}
int32 LanguageModelEstimator::FindLmStateIndexForHistory(
const std::vector<int32> &hist) const {
MapType::const_iterator iter = hist_to_lmstate_index_.find(hist);
if (iter == hist_to_lmstate_index_.end())
return -1;
else
return iter->second;
}
int32 LanguageModelEstimator::FindNonzeroLmStateIndexForHistory(
std::vector<int32> hist) const {
while (1) {
int32 l = FindLmStateIndexForHistory(hist);
if (l == -1 || lm_states_[l].tot_count == 0) {
// no such state or state has zero count.
if (hist.empty())
KALDI_ERR << "Error looking up LM state index for history "
<< "(likely code bug)";
hist.erase(hist.begin()); // back off.
} else {
return l;
}
}
}
int32 LanguageModelEstimator::FindOrCreateLmStateIndexForHistory(
const std::vector<int32> &hist) {
MapType::const_iterator iter = hist_to_lmstate_index_.find(hist);
if (iter != hist_to_lmstate_index_.end())
return iter->second;
int32 ans = lm_states_.size(); // index of next element
// next statement relies on default construct of LmState.
lm_states_.resize(lm_states_.size() + 1);
lm_states_.back().history = hist;
hist_to_lmstate_index_[hist] = ans;
// make sure backoff_lmstate_index is set
if (hist.size() > 0) {
std::vector<int32> backoff_hist(hist.begin() + 1,
hist.end());
int32 backoff_lm_state = FindOrCreateLmStateIndexForHistory(backoff_hist);
lm_states_[ans].backoff_lmstate_index = backoff_lm_state;
}
return ans;
}
void LanguageModelEstimator::LmState::AddCount(int32 phone, int32 count) {
std::map<int32, int32>::iterator iter = phone_to_count.find(phone);
if (iter == phone_to_count.end())
phone_to_count[phone] = count;
else
iter->second += count;
tot_count += count;
}
void LanguageModelEstimator::LmState::Add(const LmState &other) {
KALDI_ASSERT(&other != this);
std::map<int32, int32>::const_iterator iter = other.phone_to_count.begin(),
end = other.phone_to_count.end();
for (; iter != end; ++iter)
AddCount(iter->first, iter->second);
}
int32 LanguageModelEstimator::AssignFstStates() {
int32 num_lm_states = lm_states_.size();
int32 current_fst_state = 0;
for (int32 l = 0; l < num_lm_states; l++) {
if (lm_states_[l].tot_count != 0) {
lm_states_[l].fst_state = current_fst_state++;
}
}
KALDI_ASSERT(current_fst_state == num_active_lm_states_);
return current_fst_state;
}
void LanguageModelEstimator::Estimate(fst::StdVectorFst *fst) {
KALDI_LOG << "Estimating language model with ngram-order="
<< opts_.ngram_order << ", discount="
<< opts_.discount;
SetParentCounts();
int32 num_fst_states = AssignFstStates();
OutputToFst(num_fst_states, fst);
}
int32 LanguageModelEstimator::FindInitialFstState() const {
std::vector<int32> history(0);
int32 l = FindNonzeroLmStateIndexForHistory(history);
KALDI_ASSERT(l != -1 && lm_states_[l].fst_state != -1);
return lm_states_[l].fst_state;
}
void LanguageModelEstimator::OutputToFst(
int32 num_states,
fst::StdVectorFst *fst) const {
KALDI_ASSERT(num_states == num_active_lm_states_);
fst->DeleteStates();
for (int32 i = 0; i < num_states; i++)
fst->AddState();
fst->SetStart(FindInitialFstState());
int64 tot_count = 0;
double tot_logprob = 0.0;
int32 num_lm_states = lm_states_.size();
// note: not all lm-states end up being 'active'.
for (int32 l = 0; l < num_lm_states; l++) {
const LmState &lm_state = lm_states_[l];
if (lm_state.fst_state == -1) {
continue;
}
int32 state_count = lm_state.tot_count;
KALDI_ASSERT(state_count != 0);
std::map<int32, int32>::const_iterator
iter = lm_state.phone_to_count.begin(),
end = lm_state.phone_to_count.end();
for (; iter != end; ++iter) {
int32 phone = iter->first, count = iter->second;
BaseFloat logprob = log(count * opts_.discount / state_count);
tot_count += count;
tot_logprob += logprob * count;
if (phone == 0) { // Go to final state
fst->SetFinal(lm_state.fst_state, fst::TropicalWeight(-logprob));
} else { // It becomes a transition.
std::vector<int32> next_history(lm_state.history);
next_history.push_back(phone);
int32 dest_lm_state = FindNonzeroLmStateIndexForHistory(next_history),
dest_fst_state = lm_states_[dest_lm_state].fst_state;
KALDI_ASSERT(dest_fst_state != -1);
fst->AddArc(lm_state.fst_state,
fst::StdArc(phone, phone, fst::TropicalWeight(-logprob),
dest_fst_state));
}
}
if (lm_state.backoff_lmstate_index >= 0) {
fst->AddArc(lm_state.fst_state, fst::StdArc(0, 0, fst::TropicalWeight(-log(1 - opts_.discount)), lm_states_[lm_state.backoff_lmstate_index].fst_state));
}
}
fst::Connect(fst);
// Make sure that Connect does not delete any states.
int32 num_states_connected = fst->NumStates();
KALDI_ASSERT(num_states_connected == num_states);
// arc-sort. ilabel or olabel doesn't matter, it's an acceptor.
fst::ArcSort(fst, fst::ILabelCompare<fst::StdArc>());
KALDI_LOG << "Created language model with " << num_states
<< " states and " << fst::NumArcs(*fst) << " arcs.";
}
-150
View File
@@ -1,150 +0,0 @@
// Copyright 2015 Johns Hopkins University (Author: Daniel Povey)
// See ../../COPYING for clarification regarding multiple authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
// WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
// MERCHANTABLITY OR NON-INFRINGEMENT.
// See the Apache 2 License for the specific language governing permissions and
// limitations under the License.
#ifndef VOSK_LANGUAGE_MODEL_H
#define VOSK_LANGUAGE_MODEL_H
#include <vector>
#include <map>
#include "base/kaldi-common.h"
#include "util/common-utils.h"
#include "fstext/fstext-lib.h"
#include "lat/kaldi-lattice.h"
using namespace kaldi;
// Very simply lm construction with absolute discounting
struct LanguageModelOptions {
int32 ngram_order; // you might want to tune this
BaseFloat discount; // discount for backoff
LanguageModelOptions():
ngram_order(3),
discount(0.5)
{ }
void Register(OptionsItf *opts) {
opts->Register("ngram-order", &ngram_order, "n-gram order for the phone "
"language model used for the 'denominator model'");
opts->Register("discount", &discount, "Discount for backoff");
}
};
class LanguageModelEstimator {
public:
LanguageModelEstimator(LanguageModelOptions &opts): opts_(opts),
num_active_lm_states_(0) {
KALDI_ASSERT(opts.ngram_order >= 1);
}
// Adds counts for this sentence. Basically does: for each n-gram in the
// sentence, count[n-gram] += 1. The only constraint on 'sentence' is that it
// should contain no zeros.
void AddCounts(const std::vector<int32> &sentence);
// Estimates the LM and outputs it as an FST. Note: there is
// no concept here of backoff arcs.
void Estimate(fst::StdVectorFst *fst);
protected:
struct LmState {
// the phone history associated with this state (length can vary).
std::vector<int32> history;
// maps from
std::map<int32, int32> phone_to_count;
// total count of this state. As we back off states to lower-order states
// (and note that this is a hard backoff where we completely remove un-needed
// states) this tot_count may become zero.
int32 tot_count;
// LM-state index of the backoff LM state (if it exists, else -1)...
// provided for convenience.
int32 backoff_lmstate_index;
// this is only set after we decide on the FST state numbering (at the end).
// If not set, it's -1.
int32 fst_state;
void AddCount(int32 phone, int32 count);
// Add the contents of another LmState.
void Add(const LmState &other);
LmState(): tot_count(0), backoff_lmstate_index(-1),
fst_state(-1) { }
LmState(const LmState &other):
history(other.history), phone_to_count(other.phone_to_count),
tot_count(other.tot_count),
backoff_lmstate_index(other.backoff_lmstate_index),
fst_state(other.fst_state) { }
};
// maps from history to int32
typedef unordered_map<std::vector<int32>, int32, VectorHasher<int32> > MapType;
LanguageModelOptions opts_;
MapType hist_to_lmstate_index_;
std::vector<LmState> lm_states_; // indexed by lmstate_index, the LmStates.
// Keeps track of the number of lm states that have nonzero counts.
int32 num_active_lm_states_;
// adds the counts for this ngram (called from AddCounts()).
inline void IncrementCount(const std::vector<int32> &history,
int32 next_phone);
// sets up tot_count_with_parents in all the lm-states
void SetParentCounts();
// Finds and returns an LM-state index for a history -- or -1 if it doesn't
// exist. No backoff is done.
int32 FindLmStateIndexForHistory(const std::vector<int32> &hist) const;
// Finds and returns an LM-state index for a history -- and creates one if
// it doesn't exist -- and also creates any backoff states needed, down
// to history-length no_prune_ngram_order - 1.
int32 FindOrCreateLmStateIndexForHistory(const std::vector<int32> &hist);
// Finds and returns the most specific LM-state index for a history or
// backed-off versions of it, that exists and has nonzero count. Will die if
// there is no such history. [e.g. if there is no unigram backoff state,
// which generally speaking there won't be.]
int32 FindNonzeroLmStateIndexForHistory(std::vector<int32> hist) const;
// after all backoff has been done, assigns FST state indexes to all states
// that exist and have nonzero count. Returns the number of states.
int32 AssignFstStates();
// find the FST index of the initial-state, and returns it.
int32 FindInitialFstState() const;
// Write to an FST
void OutputToFst(
int32 num_fst_states,
fst::StdVectorFst *fst) const;
};
#endif
-2
View File
@@ -225,8 +225,6 @@ void Model::ReadDataFiles()
ivector_extraction_opts.global_cmvn_stats_rxfilename = model_path_str_ + "/ivector/global_cmvn.stats";
ivector_extraction_opts.diag_ubm_rxfilename = model_path_str_ + "/ivector/final.dubm";
ivector_extraction_opts.ivector_extractor_rxfilename = model_path_str_ + "/ivector/final.ie";
ivector_extraction_opts.max_count = 100;
feature_info_.use_ivectors = true;
feature_info_.ivector_extractor_info.Init(ivector_extraction_opts);
} else {
+3 -3
View File
@@ -12,8 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef VOSK_MODEL_H
#define VOSK_MODEL_H
#ifndef MODEL_H_
#define MODEL_H_
#include "base/kaldi-common.h"
#include "fstext/fstext-lib.h"
@@ -86,4 +86,4 @@ protected:
int ref_cnt_;
};
#endif /* VOSK_MODEL_H */
#endif /* MODEL_H_ */
-3
View File
@@ -25,9 +25,6 @@ SpkModel::SpkModel(const char *speaker_path) {
SetDropoutTestMode(true, &speaker_nnet);
CollapseModel(nnet3::CollapseModelConfig(), &speaker_nnet);
ReadKaldiObject(speaker_path_str + "/mean.vec", &mean);
ReadKaldiObject(speaker_path_str + "/transform.mat", &transform);
ref_cnt_ = 1;
}
+3 -6
View File
@@ -12,8 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef VOSK_SPK_MODEL_H
#define VOSK_SPK_MODEL_H
#ifndef SPK_MODEL_H_
#define SPK_MODEL_H_
#include "base/kaldi-common.h"
#include "online2/online-feature-pipeline.h"
@@ -35,12 +35,9 @@ protected:
~SpkModel() {};
kaldi::nnet3::Nnet speaker_nnet;
kaldi::Vector<BaseFloat> mean;
kaldi::Matrix<BaseFloat> transform;
MfccOptions spkvector_mfcc_opts;
int ref_cnt_;
};
#endif /* VOSK_SPK_MODEL_H */
#endif /* SPK_MODEL_H_ */
-5
View File
@@ -34,11 +34,6 @@ import java.nio.ByteOrder;
return AcceptWaveform(bdata, bdata.length);
}
%}
%pragma(java) jniclasscode=%{
static {
System.loadLibrary("vosk_jni");
}
%}
#endif
#if SWIGCSHARP
+6 -7
View File
@@ -14,8 +14,8 @@
/* This header contains the C API for Vosk speech recognition system */
#ifndef VOSK_API_H
#define VOSK_API_H
#ifndef _VOSK_API_H_
#define _VOSK_API_H_
#ifdef __cplusplus
extern "C" {
@@ -88,10 +88,10 @@ VoskRecognizer *vosk_recognizer_new(VoskModel *model, float sample_rate);
VoskRecognizer *vosk_recognizer_new_spk(VoskModel *model, VoskSpkModel *spk_model, float sample_rate);
/** Creates the recognizer object with the phrase list
/** Creates the recognizer object with the grammar
*
* 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
* to recognize large vocabulary you can specify a list of words to recognize. This
* will improve recognizer speed and accuracy but might return [unk] if user said
* something different.
*
@@ -99,8 +99,7 @@ VoskRecognizer *vosk_recognizer_new_spk(VoskModel *model, VoskSpkModel *spk_mode
* Precompiled HCLG graph models are not supported.
*
* @param sample_rate The sample rate of the audio you going to feed into the recognizer
* @param grammar The string with the list of phrases to recognize as JSON array of strings,
* for example "["one two three four five", "[unk]"]".
* @param grammar The string with the list of words to recognize, for example "one two three four five [unk]"
*
* @returns recognizer object */
VoskRecognizer *vosk_recognizer_new_grm(VoskModel *model, float sample_rate, const char *grammar);
@@ -208,4 +207,4 @@ void vosk_set_log_level(int log_level);
}
#endif
#endif /* VOSK_API_H */
#endif /* _VOSK_API_H_ */
+1 -1
View File
@@ -90,7 +90,7 @@ RUN cd /opt \
&& cd kaldi/tools \
&& git clone -b v0.3.7 --single-branch https://github.com/xianyi/OpenBLAS \
&& make PREFIX=$(pwd)/OpenBLAS/install TARGET="${OPENBLAS_ARCH}" HOSTCC=gcc USE_LOCKING=1 USE_THREAD=0 -C OpenBLAS all install \
&& git clone -b old-gcc --single-branch https://github.com/alphacep/openfst openfst \
&& git clone https://github.com/alphacep/openfst openfst \
&& cd openfst \
&& autoreconf -i \
&& ./configure --prefix=/opt/kaldi/tools/openfst --enable-static --enable-shared --enable-far --enable-ngram-fsts --enable-lookahead-fsts --with-pic --disable-bin --host=${CROSS_TRIPLE} --build=x86-linux-gnu \
+2 -2
View File
@@ -24,7 +24,7 @@ RUN cd /opt \
&& git clone -b lookahead --single-branch https://github.com/alphacep/kaldi \
&& cd kaldi/tools \
&& git clone -b v0.3.7 --single-branch https://github.com/xianyi/OpenBLAS \
&& make PREFIX=$(pwd)/OpenBLAS/install DYNAMIC_ARCH=1 USE_LOCKING=1 USE_THREAD=0 -C OpenBLAS all install \
&& make PREFIX=$(pwd)/OpenBLAS/install TARGET=NEHALEM USE_LOCKING=1 USE_THREAD=0 -C OpenBLAS all install \
&& git clone https://github.com/alphacep/openfst openfst \
&& cd openfst \
&& autoreconf -i \
@@ -32,5 +32,5 @@ RUN cd /opt \
&& make -j 10 && make install \
&& cd /opt/kaldi/src \
&& ./configure --mathlib=OPENBLAS --shared --use-cuda=no \
&& make -j 10 online2 lm rnnlm \
&& make -j 10 online2 lm \
&& find /opt/kaldi -name "*.o" -exec rm {} \;