chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,3 @@
|
||||
demo/model
|
||||
demo/model-spk
|
||||
demo/test.wav
|
||||
@@ -0,0 +1,34 @@
|
||||
This is an FFI-NAPI wrapper for the Vosk library.
|
||||
|
||||
## Usage
|
||||
|
||||
Bindings mostly follow Vosk interface, some methods are not yet fully implemented.
|
||||
|
||||
See [demo folder](https://github.com/alphacep/vosk-api/tree/master/nodejs/demo) for
|
||||
details.
|
||||
|
||||
## About
|
||||
|
||||
Vosk is an offline open source speech recognition toolkit. It enables
|
||||
speech recognition for 20+ languages and dialects - English, Indian
|
||||
English, German, French, Spanish, Portuguese, Chinese, Russian, Turkish,
|
||||
Vietnamese, Italian, Dutch, Catalan, Arabic, Greek, Farsi, Filipino,
|
||||
Ukrainian, Kazakh, Swedish, Japanese, Esperanto, Hindi, Czech, Polish.
|
||||
More to come.
|
||||
|
||||
Vosk models are small (50 Mb) but provide continuous large vocabulary
|
||||
transcription, zero-latency response with streaming API, reconfigurable
|
||||
vocabulary and speaker identification.
|
||||
|
||||
Vosk supplies speech recognition for chatbots, smart home appliances,
|
||||
virtual assistants. It can also create subtitles for movies,
|
||||
transcription for lectures and interviews.
|
||||
|
||||
Vosk scales from small devices like Raspberry Pi or Android smartphone to
|
||||
big clusters.
|
||||
|
||||
# Documentation
|
||||
|
||||
For installation instructions, examples and documentation visit [Vosk
|
||||
Website](https://alphacephei.com/vosk). See also our project on
|
||||
[Github](https://github.com/alphacep/vosk-api).
|
||||
@@ -0,0 +1,33 @@
|
||||
var vosk = require('..')
|
||||
|
||||
const fs = require("fs");
|
||||
const { spawn } = require("child_process");
|
||||
|
||||
MODEL_PATH = "model"
|
||||
FILE_NAME = "test.wav"
|
||||
SAMPLE_RATE = 16000
|
||||
BUFFER_SIZE = 4000
|
||||
|
||||
if (!fs.existsSync(MODEL_PATH)) {
|
||||
console.log("Please download the model from https://alphacephei.com/vosk/models and unpack as " + MODEL_PATH + " in the current folder.")
|
||||
process.exit()
|
||||
}
|
||||
|
||||
if (process.argv.length > 2)
|
||||
FILE_NAME = process.argv[2]
|
||||
|
||||
vosk.setLogLevel(0);
|
||||
const model = new vosk.Model(MODEL_PATH);
|
||||
const rec = new vosk.Recognizer({model: model, sampleRate: SAMPLE_RATE});
|
||||
|
||||
const ffmpeg_run = spawn('ffmpeg', ['-loglevel', 'quiet', '-i', FILE_NAME,
|
||||
'-ar', String(SAMPLE_RATE) , '-ac', '1',
|
||||
'-f', 's16le', '-bufsize', String(BUFFER_SIZE) , '-']);
|
||||
|
||||
ffmpeg_run.stdout.on('data', (stdout) => {
|
||||
if (rec.acceptWaveform(stdout))
|
||||
console.log(rec.result());
|
||||
else
|
||||
console.log(rec.partialResult());
|
||||
console.log(rec.finalResult());
|
||||
});
|
||||
@@ -0,0 +1,46 @@
|
||||
var vosk = require('..')
|
||||
|
||||
const fs = require("fs");
|
||||
var mic = require("mic");
|
||||
|
||||
MODEL_PATH = "model"
|
||||
SAMPLE_RATE = 16000
|
||||
|
||||
if (!fs.existsSync(MODEL_PATH)) {
|
||||
console.log("Please download the model from https://alphacephei.com/vosk/models and unpack as " + MODEL_PATH + " in the current folder.")
|
||||
process.exit()
|
||||
}
|
||||
|
||||
vosk.setLogLevel(0);
|
||||
const model = new vosk.Model(MODEL_PATH);
|
||||
const rec = new vosk.Recognizer({model: model, sampleRate: SAMPLE_RATE});
|
||||
|
||||
var micInstance = mic({
|
||||
rate: String(SAMPLE_RATE),
|
||||
channels: '1',
|
||||
debug: false,
|
||||
device: 'default',
|
||||
});
|
||||
|
||||
var micInputStream = micInstance.getAudioStream();
|
||||
|
||||
micInputStream.on('data', data => {
|
||||
if (rec.acceptWaveform(data))
|
||||
console.log(rec.result());
|
||||
else
|
||||
console.log(rec.partialResult());
|
||||
});
|
||||
|
||||
micInputStream.on('audioProcessExitComplete', function() {
|
||||
console.log("Cleaning up");
|
||||
console.log(rec.finalResult());
|
||||
rec.free();
|
||||
model.free();
|
||||
});
|
||||
|
||||
process.on('SIGINT', function() {
|
||||
console.log("\nStopping");
|
||||
micInstance.stop();
|
||||
});
|
||||
|
||||
micInstance.start();
|
||||
@@ -0,0 +1,48 @@
|
||||
var vosk = require('..')
|
||||
|
||||
const fs = require("fs");
|
||||
const { Readable } = require("stream");
|
||||
const wav = require("wav");
|
||||
|
||||
MODEL_PATH = "model"
|
||||
FILE_NAME = "test.wav"
|
||||
|
||||
if (!fs.existsSync(MODEL_PATH)) {
|
||||
console.log("Please download the model from https://alphacephei.com/vosk/models and unpack as " + MODEL_PATH + " in the current folder.")
|
||||
process.exit()
|
||||
}
|
||||
|
||||
if (process.argv.length > 2)
|
||||
FILE_NAME = process.argv[2]
|
||||
|
||||
vosk.setLogLevel(0);
|
||||
const model = new vosk.Model(MODEL_PATH);
|
||||
|
||||
const wfReader = new wav.Reader();
|
||||
const wfReadable = new Readable().wrap(wfReader);
|
||||
|
||||
wfReader.on('format', async ({ audioFormat, sampleRate, channels }) => {
|
||||
if (audioFormat != 1 || channels != 1) {
|
||||
console.error("Audio file must be WAV format mono PCM.");
|
||||
process.exit(1);
|
||||
}
|
||||
const rec = new vosk.Recognizer({model: model, sampleRate: sampleRate});
|
||||
rec.setMaxAlternatives(10);
|
||||
rec.setWords(true);
|
||||
rec.setPartialWords(true);
|
||||
for await (const data of wfReadable) {
|
||||
const end_of_speech = rec.acceptWaveform(data);
|
||||
if (end_of_speech) {
|
||||
console.log(JSON.stringify(rec.result(), null, 4));
|
||||
} else {
|
||||
console.log(JSON.stringify(rec.partialResult(), null, 4));
|
||||
}
|
||||
}
|
||||
console.log(JSON.stringify(rec.finalResult(rec), null, 4));
|
||||
rec.free();
|
||||
});
|
||||
|
||||
fs.createReadStream(FILE_NAME, {'highWaterMark': 4096}).pipe(wfReader).on('finish',
|
||||
function (err) {
|
||||
model.free();
|
||||
});
|
||||
@@ -0,0 +1,46 @@
|
||||
var vosk = require('..')
|
||||
|
||||
const async = require("async");
|
||||
const fs = require("fs");
|
||||
const { Readable } = require("stream");
|
||||
const wav = require("wav");
|
||||
|
||||
MODEL_PATH = "model"
|
||||
|
||||
if (!fs.existsSync(MODEL_PATH)) {
|
||||
console.log("Please download the model from https://alphacephei.com/vosk/models and unpack as " + MODEL_PATH + " in the current folder.")
|
||||
process.exit()
|
||||
}
|
||||
|
||||
// Process file 4 times in parallel with a single model
|
||||
files = Array(10).fill("test.wav")
|
||||
const model = new vosk.Model(MODEL_PATH)
|
||||
|
||||
async.filter(files, function(filePath, callback) {
|
||||
const wfReader = new wav.Reader();
|
||||
const wfReadable = new Readable().wrap(wfReader);
|
||||
|
||||
wfReader.on('format', async ({ audioFormat, sampleRate, channels }) => {
|
||||
const rec = new vosk.Recognizer({model: model, sampleRate: sampleRate});
|
||||
if (audioFormat != 1 || channels != 1) {
|
||||
console.error("Audio file must be WAV format mono PCM.");
|
||||
process.exit(1);
|
||||
}
|
||||
for await (const data of wfReadable) {
|
||||
const end_of_speech = await rec.acceptWaveformAsync(data);
|
||||
if (end_of_speech) {
|
||||
console.log(rec.result());
|
||||
}
|
||||
}
|
||||
console.log(rec.finalResult(rec));
|
||||
rec.free();
|
||||
// Signal we are done without errors
|
||||
callback(null, true);
|
||||
});
|
||||
|
||||
fs.createReadStream(filePath, {'highWaterMark': 4096}).pipe(wfReader);
|
||||
|
||||
}, function(err, results) {
|
||||
model.free();
|
||||
console.log("Done!!!!!");
|
||||
});
|
||||
@@ -0,0 +1,53 @@
|
||||
const vosk = require('..');
|
||||
|
||||
const fs = require("fs");
|
||||
const { Readable } = require("stream");
|
||||
const wav = require("wav");
|
||||
|
||||
MODEL_PATH = "model"
|
||||
SPEAKER_MODEL_PATH = "model-spk"
|
||||
FILE_NAME = "test.wav"
|
||||
|
||||
if (!fs.existsSync(MODEL_PATH)) {
|
||||
console.log("Please download the model from https://alphacephei.com/vosk/models and unpack as " + MODEL_PATH + " in the current folder.")
|
||||
process.exit()
|
||||
}
|
||||
|
||||
if (!fs.existsSync(SPEAKER_MODEL_PATH)) {
|
||||
console.log("Please download the speaker model from https://alphacephei.com/vosk/models and unpack as " + SPEAKER_MODEL_PATH + " in the current folder.")
|
||||
process.exit()
|
||||
}
|
||||
|
||||
if (process.argv.length > 2)
|
||||
FILE_NAME = process.argv[2]
|
||||
|
||||
const model = new vosk.Model(MODEL_PATH);
|
||||
const speakerModel = new vosk.SpeakerModel(SPEAKER_MODEL_PATH);
|
||||
|
||||
const wfReader = new wav.Reader();
|
||||
const wfReadable = new Readable().wrap(wfReader);
|
||||
|
||||
wfReader.on('format', async ({ audioFormat, sampleRate, channels }) => {
|
||||
if (audioFormat != 1 || channels != 1) {
|
||||
console.error('Audio file must be WAV format mono PCM.');
|
||||
process.exit(1);
|
||||
}
|
||||
// const rec = new vosk.Recognizer({ model: model,
|
||||
// speakerModel: speakerModel,
|
||||
// sampleRate: sampleRate });
|
||||
const rec = new vosk.Recognizer({model: model, sampleRate: sampleRate});
|
||||
rec.setSpkModel(speakerModel);
|
||||
for await (const data of wfReadable) {
|
||||
const end_of_speech = rec.acceptWaveform(data);
|
||||
if (end_of_speech) {
|
||||
console.log(rec.finalResult());
|
||||
}
|
||||
}
|
||||
console.log(rec.finalResult());
|
||||
rec.free();
|
||||
});
|
||||
|
||||
fs.createReadStream(FILE_NAME, { highWaterMark: 4096 }).pipe(wfReader).on('finish', function (err) {
|
||||
model.free();
|
||||
speakerModel.free();
|
||||
});
|
||||
@@ -0,0 +1,84 @@
|
||||
var vosk = require('..')
|
||||
|
||||
const fs = require("fs");
|
||||
const { spawn } = require("child_process");
|
||||
const { stringifySync } = require('subtitle')
|
||||
|
||||
MODEL_PATH = "model"
|
||||
FILE_NAME = "test.wav"
|
||||
SAMPLE_RATE = 16000
|
||||
BUFFER_SIZE = 4000
|
||||
|
||||
if (!fs.existsSync(MODEL_PATH)) {
|
||||
console.log("Please download the model from https://alphacephei.com/vosk/models and unpack as " + MODEL_PATH + " in the current folder.")
|
||||
process.exit()
|
||||
}
|
||||
|
||||
if (process.argv.length > 2)
|
||||
FILE_NAME = process.argv[2]
|
||||
|
||||
vosk.setLogLevel(-1);
|
||||
const model = new vosk.Model(MODEL_PATH);
|
||||
const rec = new vosk.Recognizer({model: model, sampleRate: SAMPLE_RATE});
|
||||
rec.setWords(true);
|
||||
|
||||
const ffmpeg_run = spawn('ffmpeg', ['-loglevel', 'quiet', '-i', FILE_NAME,
|
||||
'-ar', String(SAMPLE_RATE) , '-ac', '1',
|
||||
'-f', 's16le', '-bufsize', String(BUFFER_SIZE), '-']);
|
||||
|
||||
WORDS_PER_LINE = 7
|
||||
const subs = []
|
||||
const results = []
|
||||
ffmpeg_run.stdout.on('data', (stdout) => {
|
||||
if (rec.acceptWaveform(stdout))
|
||||
results.push(rec.result());
|
||||
results.push(rec.finalResult());
|
||||
});
|
||||
|
||||
ffmpeg_run.on('exit', code => {
|
||||
rec.free();
|
||||
model.free();
|
||||
results.forEach(element =>{
|
||||
if (!element.hasOwnProperty('result'))
|
||||
return;
|
||||
const words = element.result;
|
||||
if (words.length == 1) {
|
||||
subs.push({
|
||||
type: 'cue',
|
||||
data: {
|
||||
start: words[0].start * 1000,
|
||||
end: words[0].end * 1000,
|
||||
text: words[0].word
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
var start_index = 0;
|
||||
var text = words[0].word + " ";
|
||||
for (let i = 1; i < words.length; i++) {
|
||||
text += words[i].word + " ";
|
||||
if (i % WORDS_PER_LINE == 0) {
|
||||
subs.push({
|
||||
type: 'cue',
|
||||
data: {
|
||||
start: words[start_index].start * 1000,
|
||||
end: words[i].end * 1000,
|
||||
text: text.slice(0, text.length-1)
|
||||
}
|
||||
});
|
||||
start_index = i;
|
||||
text = "";
|
||||
}
|
||||
}
|
||||
if (start_index != words.length - 1)
|
||||
subs.push({
|
||||
type: 'cue',
|
||||
data: {
|
||||
start: words[start_index].start * 1000,
|
||||
end: words[words.length-1].end * 1000,
|
||||
text: text
|
||||
}
|
||||
});
|
||||
});
|
||||
console.log(stringifySync(subs, {format: "SRT"}));
|
||||
});
|
||||
+445
@@ -0,0 +1,445 @@
|
||||
// @ts-check
|
||||
'use strict'
|
||||
|
||||
/**
|
||||
* @module vosk
|
||||
*/
|
||||
|
||||
const os = require('os');
|
||||
const path = require('path');
|
||||
/** @type {import('ffi-napi')} */
|
||||
const ffi = require('ffi-napi');
|
||||
/** @type {import('ref-napi')} */
|
||||
const ref = require('ref-napi');
|
||||
|
||||
const vosk_model = ref.types.void;
|
||||
const vosk_model_ptr = ref.refType(vosk_model);
|
||||
const vosk_spk_model = ref.types.void;
|
||||
const vosk_spk_model_ptr = ref.refType(vosk_spk_model);
|
||||
const vosk_recognizer = ref.types.void;
|
||||
const vosk_recognizer_ptr = ref.refType(vosk_recognizer);
|
||||
|
||||
/**
|
||||
* @typedef {Object} WordResult
|
||||
* @property {number} conf The confidence rate in the detection. 0 For unlikely, and 1 for totally accurate.
|
||||
* @property {number} start The start of the timeframe when the word is pronounced in seconds
|
||||
* @property {number} end The end of the timeframe when the word is pronounced in seconds
|
||||
* @property {string} word The word detected
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Object} RecognitionResults
|
||||
* @property {WordResult[]} result Details about the words that have been detected
|
||||
* @property {string} text The complete sentence that have been detected
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Object} SpeakerResults
|
||||
* @property {number[]} spk A floating vector representing speaker identity. It is usually about 128 numbers which uniquely represent speaker voice.
|
||||
* @property {number} spk_frames The number of frames used to extract speaker vector. The more frames you have the more reliable is speaker vector.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Object} BaseRecognizerParam
|
||||
* @property {Model} model The language model to be used
|
||||
* @property {number} sampleRate The sample rate. Most models are trained at 16kHz
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Object} GrammarRecognizerParam
|
||||
* @property {string[]} grammar The list of sentences to be recognized.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Object} SpeakerRecognizerParam
|
||||
* @property {SpeakerModel} speakerModel The SpeakerModel that will enable speaker identification
|
||||
*/
|
||||
|
||||
/**
|
||||
* @template {SpeakerRecognizerParam | GrammarRecognizerParam} T
|
||||
* @typedef {T extends SpeakerRecognizerParam ? SpeakerResults & RecognitionResults : RecognitionResults} Result
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Object} PartialResults
|
||||
* @property {string} partial The partial sentence that have been detected until now
|
||||
*/
|
||||
|
||||
/** @typedef {string[]} Grammar The list of strings to be recognized */
|
||||
|
||||
let soname;
|
||||
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 = dllDirectory + path.delimiter + currentPath;
|
||||
|
||||
soname = path.join(__dirname, 'lib', 'win-x86_64', 'libvosk.dll');
|
||||
} else if (os.platform() == 'darwin') {
|
||||
soname = path.join(__dirname, 'lib', 'osx-universal', 'libvosk.dylib');
|
||||
} else if (os.platform() == 'linux' && os.arch() == 'arm64') {
|
||||
soname = path.join(__dirname, 'lib', 'linux-arm64', 'libvosk.so');
|
||||
} else {
|
||||
soname = path.join(__dirname, 'lib', 'linux-x86_64', 'libvosk.so');
|
||||
}
|
||||
|
||||
const libvosk = ffi.Library(soname, {
|
||||
'vosk_set_log_level': ['void', ['int']],
|
||||
'vosk_model_new': [vosk_model_ptr, ['string']],
|
||||
'vosk_model_free': ['void', [vosk_model_ptr]],
|
||||
'vosk_spk_model_new': [vosk_spk_model_ptr, ['string']],
|
||||
'vosk_spk_model_free': ['void', [vosk_spk_model_ptr]],
|
||||
'vosk_recognizer_new': [vosk_recognizer_ptr, [vosk_model_ptr, 'float']],
|
||||
'vosk_recognizer_new_spk': [vosk_recognizer_ptr, [vosk_model_ptr, 'float', vosk_spk_model_ptr]],
|
||||
'vosk_recognizer_new_grm': [vosk_recognizer_ptr, [vosk_model_ptr, 'float', 'string']],
|
||||
'vosk_recognizer_free': ['void', [vosk_recognizer_ptr]],
|
||||
'vosk_recognizer_set_max_alternatives': ['void', [vosk_recognizer_ptr, 'int']],
|
||||
'vosk_recognizer_set_words': ['void', [vosk_recognizer_ptr, 'bool']],
|
||||
'vosk_recognizer_set_partial_words': ['void', [vosk_recognizer_ptr, 'bool']],
|
||||
'vosk_recognizer_set_spk_model': ['void', [vosk_recognizer_ptr, vosk_spk_model_ptr]],
|
||||
'vosk_recognizer_accept_waveform': ['bool', [vosk_recognizer_ptr, 'pointer', 'int']],
|
||||
'vosk_recognizer_result': ['string', [vosk_recognizer_ptr]],
|
||||
'vosk_recognizer_final_result': ['string', [vosk_recognizer_ptr]],
|
||||
'vosk_recognizer_partial_result': ['string', [vosk_recognizer_ptr]],
|
||||
'vosk_recognizer_reset': ['void', [vosk_recognizer_ptr]],
|
||||
});
|
||||
|
||||
/**
|
||||
* Set log level for Kaldi messages
|
||||
* @param {number} level The higher, the more verbose. 0 for infos and errors. Less than 0 for silence.
|
||||
*/
|
||||
function setLogLevel(level) {
|
||||
libvosk.vosk_set_log_level(level);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a Model from a model file.
|
||||
* @see models [models](https://alphacephei.com/vosk/models)
|
||||
*/
|
||||
class Model {
|
||||
/**
|
||||
* Build a Model to be used with the voice recognition. Each language should have it's own Model
|
||||
* for the speech recognition to work.
|
||||
* @param {string} modelPath The abstract pathname to the model
|
||||
* @see models [models](https://alphacephei.com/vosk/models)
|
||||
*/
|
||||
constructor(modelPath) {
|
||||
/**
|
||||
* Store the handle.
|
||||
* For internal use only
|
||||
* @type {unknown}
|
||||
*/
|
||||
this.handle = libvosk.vosk_model_new(modelPath);
|
||||
if (!this.handle) {
|
||||
throw new Error('Failed to create a model.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
free() {
|
||||
libvosk.vosk_model_free(this.handle);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a Speaker Model from a speaker model file.
|
||||
* The Speaker Model enables speaker identification.
|
||||
* @see models [models](https://alphacephei.com/vosk/models)
|
||||
*/
|
||||
class SpeakerModel {
|
||||
/**
|
||||
* Loads speaker model data from the file and returns the model object
|
||||
*
|
||||
* @param {string} modelPath the path of the model on the filesystem
|
||||
* @see models [models](https://alphacephei.com/vosk/models)
|
||||
*/
|
||||
constructor(modelPath) {
|
||||
/**
|
||||
* Store the handle.
|
||||
* For internal use only
|
||||
* @type {unknown}
|
||||
*/
|
||||
this.handle = libvosk.vosk_spk_model_new(modelPath);
|
||||
if (!this.handle) {
|
||||
throw new Error('Failed to create a speaker model.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
free() {
|
||||
libvosk.vosk_spk_model_free(this.handle);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to narrow down type while using `hasOwnProperty`.
|
||||
* @see hasOwnProperty [typescript issue](https://fettblog.eu/typescript-hasownproperty/)
|
||||
* @template {Object} Obj
|
||||
* @template {PropertyKey} Key
|
||||
* @param {Obj} obj
|
||||
* @param {Key} prop
|
||||
* @returns {obj is Obj & Record<Key, unknown>}
|
||||
*/
|
||||
function hasOwnProperty(obj, prop) {
|
||||
return obj.hasOwnProperty(prop)
|
||||
}
|
||||
|
||||
/**
|
||||
* @template T
|
||||
* @template U
|
||||
* @typedef {{ [P in Exclude<keyof T, keyof U>]?: never }} Without
|
||||
*/
|
||||
|
||||
/**
|
||||
* @template T
|
||||
* @template U
|
||||
* @typedef {(T | U) extends object ? (Without<T, U> & U) | (Without<U, T> & T) : T | U} XOR
|
||||
*/
|
||||
|
||||
/**
|
||||
* Create a Recognizer that will be able to transform audio streams into text using a Model.
|
||||
* @template {XOR<SpeakerRecognizerParam, Partial<GrammarRecognizerParam>>} T extra parameter
|
||||
* @see Model
|
||||
*/
|
||||
class Recognizer {
|
||||
/**
|
||||
* Create a Recognizer that will handle speech to text recognition.
|
||||
* @constructor
|
||||
* @param {T & BaseRecognizerParam} param The Recognizer parameters
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
constructor(param) {
|
||||
const { model, sampleRate } = param
|
||||
// Prevent the user to receive unpredictable results
|
||||
if (hasOwnProperty(param, 'speakerModel') && hasOwnProperty(param, 'grammar')) {
|
||||
throw new Error('grammar and speakerModel cannot be used together for now.')
|
||||
}
|
||||
/**
|
||||
* Store the handle.
|
||||
* For internal use only
|
||||
* @type {unknown}
|
||||
*/
|
||||
this.handle = hasOwnProperty(param, 'speakerModel')
|
||||
? libvosk.vosk_recognizer_new_spk(model.handle, sampleRate, param.speakerModel.handle)
|
||||
: hasOwnProperty(param, 'grammar')
|
||||
? libvosk.vosk_recognizer_new_grm(model.handle, sampleRate, JSON.stringify(param.grammar))
|
||||
: libvosk.vosk_recognizer_new(model.handle, sampleRate);
|
||||
|
||||
if (!this.handle) {
|
||||
throw new Error('Failed to create a recognizer.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
free() {
|
||||
libvosk.vosk_recognizer_free(this.handle);
|
||||
}
|
||||
|
||||
/** Configures recognizer to output n-best results
|
||||
*
|
||||
* <pre>
|
||||
* {
|
||||
* "alternatives": [
|
||||
* { "text": "one two three four five", "confidence": 0.97 },
|
||||
* { "text": "one two three for five", "confidence": 0.03 },
|
||||
* ]
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* @param max_alternatives - maximum alternatives to return from recognition results
|
||||
*/
|
||||
setMaxAlternatives(max_alternatives) {
|
||||
libvosk.vosk_recognizer_set_max_alternatives(this.handle, max_alternatives);
|
||||
}
|
||||
|
||||
/** Configures recognizer to output words with times
|
||||
*
|
||||
* <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
|
||||
*/
|
||||
setWords(words) {
|
||||
libvosk.vosk_recognizer_set_words(this.handle, words);
|
||||
}
|
||||
|
||||
/** Same as above, but for partial results*/
|
||||
setPartialWords(partial_words) {
|
||||
libvosk.vosk_recognizer_set_partial_words(this.handle, partial_words);
|
||||
}
|
||||
|
||||
/** Adds speaker recognition model to already created recognizer. Helps to initialize
|
||||
* speaker recognition for grammar-based recognizer.
|
||||
*
|
||||
* @param spk_model Speaker recognition model
|
||||
*/
|
||||
setSpkModel(spk_model) {
|
||||
libvosk.vosk_recognizer_set_spk_model(this.handle, spk_model.handle);
|
||||
}
|
||||
|
||||
/**
|
||||
* Accept voice data
|
||||
*
|
||||
* accept and process new chunk of voice data
|
||||
*
|
||||
* @param {Buffer} data audio data in PCM 16-bit mono format
|
||||
* @returns true if silence is occured and you can retrieve a new utterance with result method
|
||||
*/
|
||||
acceptWaveform(data) {
|
||||
return libvosk.vosk_recognizer_accept_waveform(this.handle, data, data.length);
|
||||
};
|
||||
|
||||
/**
|
||||
* Accept voice data
|
||||
*
|
||||
* accept and process new chunk of voice data
|
||||
*
|
||||
* @param {Buffer} data audio data in PCM 16-bit mono format
|
||||
* @returns true if silence is occured and you can retrieve a new utterance with result method
|
||||
*/
|
||||
acceptWaveformAsync(data) {
|
||||
return new Promise((resolve, reject) => {
|
||||
libvosk.vosk_recognizer_accept_waveform.async(this.handle, data, data.length, function(err, result) {
|
||||
if (err) {
|
||||
reject(err);
|
||||
} else {
|
||||
resolve(result);
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
/** Returns speech recognition result in a string
|
||||
*
|
||||
* @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>
|
||||
*/
|
||||
resultString() {
|
||||
return libvosk.vosk_recognizer_result(this.handle);
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns speech recognition results
|
||||
* @returns {Result<T>} The results
|
||||
*/
|
||||
result() {
|
||||
return JSON.parse(libvosk.vosk_recognizer_result(this.handle));
|
||||
};
|
||||
|
||||
/**
|
||||
* speech recognition text which is not yet finalized.
|
||||
* result may change as recognizer process more data.
|
||||
*
|
||||
* @returns {PartialResults} The partial results
|
||||
*/
|
||||
partialResult() {
|
||||
return JSON.parse(libvosk.vosk_recognizer_partial_result(this.handle));
|
||||
};
|
||||
|
||||
/**
|
||||
* 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 {Result<T>} speech result.
|
||||
*/
|
||||
finalResult() {
|
||||
return JSON.parse(libvosk.vosk_recognizer_final_result(this.handle));
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* Resets current results so the recognition can continue from scratch
|
||||
*/
|
||||
reset() {
|
||||
libvosk.vosk_recognizer_reset(this.handle);
|
||||
}
|
||||
}
|
||||
|
||||
exports.setLogLevel = setLogLevel
|
||||
exports.Model = Model
|
||||
exports.SpeakerModel = SpeakerModel
|
||||
exports.Recognizer = Recognizer
|
||||
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"name": "vosk",
|
||||
"version": "0.3.75",
|
||||
"description": "Node binding for continuous offline voice recoginition with Vosk library.",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git://github.com/alphacep/vosk-api.git"
|
||||
},
|
||||
"main": "index.js",
|
||||
"keywords": [
|
||||
"speech",
|
||||
"speech recognition",
|
||||
"voice"
|
||||
],
|
||||
"author": "Alpha Cephei Inc.",
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
"node": ">= 12.x.x"
|
||||
},
|
||||
"dependencies": {
|
||||
"async": "^3.2.0",
|
||||
"ffi-napi": "^4.0.3",
|
||||
"mic": "^2.1.2",
|
||||
"ref-napi": ">=2.0.0",
|
||||
"wav": "^1.0.2"
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user