Recognizer deleteion safety + data control, remove repeating UTF8ToString() call from event system.
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -1,6 +1,8 @@
|
||||
vosk
|
||||
kaldi
|
||||
.vscode
|
||||
Models
|
||||
index2.html
|
||||
clapack-wasm
|
||||
openfst
|
||||
emsdk
|
||||
|
||||
@@ -1,18 +1,13 @@
|
||||
<h1 style="text-align:center;">API Reference</h1>
|
||||
# API Reference
|
||||
|
||||
## JS ```window``` object
|
||||
| Function/Object | Description |
|
||||
|---|---|
|
||||
|-|-|
|
||||
| ```Promise<Module> loadVosklet()``` | Load Vosklet module interface |
|
||||
|
||||
## Shared interface
|
||||
| Function/Object | Description |
|
||||
|---|---|
|
||||
| ```delete()``` | Delete this object (call C++ destructor), see [why](https://emscripten.org/docs/getting_started/FAQ.html#what-does-exiting-the-runtime-mean-why-don-t-atexit-s-run) this is neccessary. For recognizers, make sure they finished recognizing before deleting them |
|
||||
|
||||
## ```Module``` object
|
||||
| Function/Object | Description |
|
||||
|---|---|
|
||||
|-|-|
|
||||
| ```Promise<Model> createModel(url: string, path: string, id: string)```<br><br>```Promise<SpkModel> createSpkModel(url: string, path: string, id: string)``` | Create a ```Model``` or ```SpkModel```, model files must be directly under the model root, and compressed model must be in ```.tar.gz```/```.tgz``` format. Tar format must be USTAR. If:<br>- ```path``` contains valid model files and ```id``` is the same, there will not be a fetch from ```url```.<br>- ```path``` doesn't contain valid model files, or if it contains valid model files but ```id``` is different, there will be a fetch from ```url```, and the model is stored with ```id```. Models are thread-safe and reusable across recognizers. |
|
||||
| ```Promise<Recognizer> createRecognizer(model: Model, sampleRate: float)```<br><br>```Promise<Recognizer> createRecognizerWithSpkModel(model: Model, spkModel: spkModel, sampleRate: float)```<br><br>```Promise<Recognizer> createRecognizerWithGrm(model: Model, grammar: string, sampleRate: float)``` | Create a ```Recognizer``` |
|
||||
| ```setLogLevel(lvl: int)``` | Set log level for Kaldi messages (default: ```0```: Info) <br>```-2```: Error<br>```-1```: Warning<br>```1```: Verbose<br>```2```: More verbose<br>```3```: Debug |
|
||||
@@ -22,13 +17,14 @@
|
||||
|
||||
## ```Model``` object
|
||||
| Function/Object | Description |
|
||||
|---|---|
|
||||
| ```int findWord(word: string)``` | Check if a word can be recognized by the model, return the word symbol if ```word``` exists inside the model or ```-1``` otherwise. Word symbol ```0``` is for epsilon. |
|
||||
|-|-|
|
||||
| ```int findWord(word: string)``` | Check if a word can be recognized by the model, return the word symbol if ```word``` exists inside the model or ```-1``` otherwise. Word symbol ```0``` is for epsilon |
|
||||
| ```delete()``` | Delete the model |
|
||||
|
||||
## ```Recognizer``` object
|
||||
| Function/Object | Description |
|
||||
|---|---|
|
||||
| ```acceptWaveform(audioData: Float32Array)``` | Accept voice data in a ```Float32Array``` with elements from ```-1.0``` to ```1.0```. |
|
||||
|-|-|
|
||||
| ```acceptWaveform(audioData: Float32Array)``` | Enqueue an audio block for recognition as a ```Float32Array``` of numbers between ```-1.0``` and ```1.0``` |
|
||||
| ```setWords(words: bool)``` | Enables words with times in the output (default: ```false```) |
|
||||
| ```setPartialWords(partialWords: bool)``` | Like above return words and confidences in partial results (default: ```false```) |
|
||||
| ```setNLSML(nlsml: bool)``` | Set NLSML output (default: ```false```) |
|
||||
@@ -37,13 +33,15 @@
|
||||
| ```setSpkModel(mdl: SpkModel)``` | Adds speaker model to already initialized recognizer |
|
||||
| ```setEndpointerMode(mode: EpMode)``` | Set endpointer scaling factor (default: ```ANSWER_DEFAULT```) |
|
||||
| ```setEndpointerDelays(tStartMax: float, tEnd: float, tMax: float)``` | Set endpointer delays |
|
||||
| ```Promise<void> delete(processCurrent: bool)``` | Delete the recognizer right after it processes its current block if ```processCurrent``` is ```true```; or after processing all remaining blocks if ```processCurrent``` is ```false``` (default: ```false```) |
|
||||
|
||||
| Event | Description |
|
||||
|---|---|
|
||||
| ```partialResult``` | There is a partial recognition result, check the event's ```detail``` property |
|
||||
| ```result``` | There is a full recognition result, check the event's ```detail``` property |
|
||||
<br>
|
||||
<h1 style="text-align:center;">HTTP Remarks</h1>
|
||||
|-|-|
|
||||
| ```partialResult``` | Partial recognition result (stored in the event's ```detail``` property). This is fired while silence is not detected, ie. still speaking. Its words maybe updated and change in later ```partialResult```s until a ```result``` is fired. |
|
||||
| ```result``` | Full, finalized recognition result (stored in the event's ```detail``` property). This is fired when silence is detected. Its words are finalized and won't ever change |
|
||||
|
||||
---
|
||||
# HTTP Remarks
|
||||
|
||||
## HTTPS
|
||||
Vosklet is available only in [secure contexts](https://developer.mozilla.org/en-US/docs/Web/Security/Secure_Contexts) (HTTPS)
|
||||
@@ -51,14 +49,13 @@ Vosklet is available only in [secure contexts](https://developer.mozilla.org/en-
|
||||
SharedArrayBuffer is necessary to share data between workers, so these response headers must be set:
|
||||
- ```Cross-Origin-Embedder-Policy``` ⟶ ```require-corp```
|
||||
- ```Cross-Origin-Opener-Policy``` ⟶ ```same-origin```
|
||||
<br>If you can't set them, you may use a hacky workaround in *AddCOI.js*.
|
||||
<br>If you can't set them, you may use a hacky workaround in *AddCOI.js*
|
||||
|
||||
## Content Security Policy (CSP)
|
||||
Wasm worker construction will be from a blob so the CSP:
|
||||
- ```worker-src``` must include ```blob:```
|
||||
For those who are using CSP, Vosklet's Wasm worker construction will be from a ```Blob``` which require the CSP ```worker-src``` to include ```blob:```
|
||||
|
||||
<br>
|
||||
<h1 style="border:2px ;text-align:center;">Compilation</h1>
|
||||
---
|
||||
# Compilation
|
||||
- Requires all Autotools commands in PATH, ```make```, and ```pkg-config```. For example, installing with ```apt``` would be:
|
||||
|
||||
```sudo apt install autotools-dev autoconf libtool make pkg-config```
|
||||
@@ -71,8 +68,8 @@ cd Vosklet/src &&
|
||||
# Example: INITIAL_MEMORY=350mb MAX_THREADS=3 ./make
|
||||
```
|
||||
| Option | Description | Default value |
|
||||
|---|---|---|
|
||||
| INITIAL_MEMORY | Set inital memory, valid suffixes: kb, mb, gb, tb or none (bytes) | ```300mb``` as [recommended](https://alphacephei.com/vosk/models). This memory will grow if usage exceeds this value. |
|
||||
|-|-|-|
|
||||
| INITIAL_MEMORY | Set inital memory, valid suffixes: kb, mb, gb, tb or none (bytes) | ```315mb``` as [recommended](https://alphacephei.com/vosk/models) plus a bit of leeway. This memory will grow if usage exceeds this value. |
|
||||
| MAX_THREADS | Set the max number of threads (>=1), this should be equal to the number of recognizers used in the program | ```1``` |
|
||||
| JOBS | Set the number of jobs (threads) when building | ```$(nproc)``` |
|
||||
| EMSDK | Set EMSDK's path (will install EMSDK in root folder if unset) | ```../emsdk``` |
|
||||
| EMSDK | Set EMSDK's path (will install EMSDK in root folder if unset) | ```../emsdk``` |
|
||||
@@ -1,7 +1,7 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<script src="https://cdn.jsdelivr.net/gh/msqr1/Vosklet@1.1.3/Examples/Vosklet.min.js" async defer></script>
|
||||
<script src="https://cdn.jsdelivr.net/gh/msqr1/Vosklet@1.1.4/Examples/Vosklet.min.js" async defer></script>
|
||||
<script>
|
||||
async function start() {
|
||||
// Make sure sample rate matches that in the training data
|
||||
@@ -1,7 +1,7 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<script src="https://cdn.jsdelivr.net/gh/msqr1/Vosklet@1.1.3/Examples/Vosklet.min.js" async defer></script>
|
||||
<script src="https://cdn.jsdelivr.net/gh/msqr1/Vosklet@1.1.4/Examples/Vosklet.min.js" async defer></script>
|
||||
<script>
|
||||
async function start() {
|
||||
// Make sure sample rate matches that in the training data
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<script src="https://cdn.jsdelivr.net/gh/msqr1/Vosklet@1.1.3/Examples/Vosklet.min.js" async defer></script>
|
||||
<script src="https://cdn.jsdelivr.net/gh/msqr1/Vosklet@1.1.4/Examples/Vosklet.min.js" async defer></script>
|
||||
<script>
|
||||
async function start() {
|
||||
// Make sure sample rate matches that in the training data
|
||||
|
||||
@@ -21,7 +21,8 @@ EMSCRIPTEN_BINDINGS() {
|
||||
.constructor<int, float, CommonModel*>(allow_raw_pointers())
|
||||
.constructor<int, float, CommonModel*, CommonModel*>(allow_raw_pointers())
|
||||
.constructor<int, float, CommonModel*, std::string, int>(allow_raw_pointers())
|
||||
.function("pushData", &Recognizer::pushData, allow_raw_pointers())
|
||||
.function("safeDelete", &Recognizer::safeDelete, allow_raw_pointers())
|
||||
.function("acceptWaveform", &Recognizer::acceptWaveform, allow_raw_pointers())
|
||||
.function("reset", &Recognizer::reset, allow_raw_pointers())
|
||||
.function("setEndpointerMode", &Recognizer::setEndpointerMode, allow_raw_pointers())
|
||||
.function("setEndpointerDelays", &Recognizer::setEndpointerDelays, allow_raw_pointers())
|
||||
|
||||
@@ -14,25 +14,25 @@ void CommonModel::extractAndLoad(unsigned char* tar, int tarSize) {
|
||||
free(tar);
|
||||
switch(res) {
|
||||
case IncorrectFormat:
|
||||
fireEv(index, "Untar: Incorrect tar format, must be USTAR");
|
||||
fireEv(index, Event::status, "Untar: Incorrect tar format, must be USTAR");
|
||||
return;
|
||||
case IncorrectFiletype:
|
||||
fireEv(index, "Untar: Not a directory or regular file");
|
||||
fireEv(index, Event::status, "Untar: Not a directory or regular file");
|
||||
return;
|
||||
case FailedOpen:
|
||||
fireEv(index, "Untar: Unable to open file for write");
|
||||
fireEv(index, Event::status, "Untar: Unable to open file for write");
|
||||
return;
|
||||
case FailedWrite:
|
||||
fireEv(index, "Untar: Unable to write file");
|
||||
fireEv(index, Event::status, "Untar: Unable to write file");
|
||||
return;
|
||||
case FailedClose:
|
||||
fireEv(index, "Untar: Unable to close file after write");
|
||||
fireEv(index, Event::status, "Untar: Unable to close file after write");
|
||||
return;
|
||||
};
|
||||
if(normalMdl) mdl = vosk_model_new(storepath.c_str());
|
||||
else mdl = vosk_spk_model_new(storepath.c_str());
|
||||
if(normalMdl ? std::get<VoskModel*>(mdl) != nullptr : std::get<VoskSpkModel*>(mdl) != nullptr) fireEv(index, "0");
|
||||
else fireEv(index, "Unable to load model for recognition");
|
||||
if(normalMdl ? std::get<VoskModel*>(mdl) != nullptr : std::get<VoskSpkModel*>(mdl) != nullptr) fireEv(index, status);
|
||||
else fireEv(index, status, "Unable to load model for recognition");
|
||||
fs::remove_all(storepath);
|
||||
}
|
||||
int CommonModel::findWord(std::string word) {
|
||||
|
||||
@@ -9,6 +9,7 @@ struct CommonModel {
|
||||
std::string storepath;
|
||||
std::string id;
|
||||
std::variant<VoskModel*, VoskSpkModel*> mdl;
|
||||
|
||||
void extractAndLoad(unsigned char* tarStart, int tarSize);
|
||||
int findWord(std::string word);
|
||||
CommonModel(int index, bool normalMdl, std::string storepath, std::string id, int tarStart, int tarSize);
|
||||
|
||||
@@ -1,57 +1,73 @@
|
||||
#include "Recognizer.h"
|
||||
#include <atomic>
|
||||
|
||||
Recognizer::Recognizer(int index, float sampleRate, CommonModel* model) :
|
||||
index{index},
|
||||
rec{vosk_recognizer_new(std::get<VoskModel*>(model->mdl), sampleRate)}
|
||||
{
|
||||
finishConstruction(model);
|
||||
rec{vosk_recognizer_new(std::get<VoskModel*>(model->mdl), sampleRate)} {
|
||||
if(rec == nullptr) fireEv(index, Event::status, "Unable to initialize recognizer");
|
||||
else globalPool.exec([this, index]{main(index);});
|
||||
}
|
||||
Recognizer::Recognizer(int index, float sampleRate, CommonModel* model, CommonModel* spkModel) :
|
||||
index{index},
|
||||
rec{vosk_recognizer_new_spk(std::get<VoskModel*>(model->mdl), sampleRate, std::get<VoskSpkModel*>(spkModel->mdl))} {
|
||||
finishConstruction(model, spkModel);
|
||||
if(rec == nullptr) fireEv(index, Event::status, "Unable to initialize recognizer");
|
||||
else globalPool.exec([this, index]{main(index);});
|
||||
}
|
||||
Recognizer::Recognizer(int index, float sampleRate, CommonModel* model, const std::string& grm, int) :
|
||||
index{index},
|
||||
rec{vosk_recognizer_new_grm(std::get<VoskModel*>(model->mdl), sampleRate, grm.c_str())} {
|
||||
finishConstruction(model);
|
||||
if(rec == nullptr) fireEv(index, Event::status, "Unable to initialize recognizer");
|
||||
else globalPool.exec([this, index]{main(index);});
|
||||
}
|
||||
Recognizer::~Recognizer() {
|
||||
done = true;
|
||||
void Recognizer::safeDelete(bool _processCurrent) {
|
||||
emscripten_atomic_store_u8(&processCurrent, _processCurrent);
|
||||
emscripten_atomic_store_u8(&done, true);
|
||||
emscripten_atomic_store_u32(&haveData, true);
|
||||
emscripten_atomic_notify(&haveData, 1);
|
||||
while(!dataQ.empty()) {
|
||||
free(dataQ.front().data);
|
||||
dataQ.pop();
|
||||
}
|
||||
vosk_recognizer_free(rec);
|
||||
}
|
||||
void Recognizer::finishConstruction(CommonModel* model, CommonModel* spkModel) {
|
||||
if(rec == nullptr) fireEv(index, "Unable to initialize recognizer");
|
||||
else globalPool.exec([this]{main();});
|
||||
}
|
||||
void Recognizer::main() {
|
||||
fireEv(index, "0");
|
||||
while(!done) {
|
||||
void Recognizer::main(int index) {
|
||||
fireEv(index, Event::status);
|
||||
AudioData* next;
|
||||
while(!emscripten_atomic_load_u8(&done)) {
|
||||
if(dataQ.empty()) {
|
||||
emscripten_atomic_store_u32(&haveData, false);
|
||||
emscripten_atomic_wait_u32(&haveData, false, -1);
|
||||
}
|
||||
else {
|
||||
AudioData& next = dataQ.front();
|
||||
switch(vosk_recognizer_accept_waveform_f(rec, next.data, next.len)) {
|
||||
next = &dataQ.front();
|
||||
switch(vosk_recognizer_accept_waveform_f(rec, next->data, next->len)) {
|
||||
case 0: [[likely]]
|
||||
fireEv(index, vosk_recognizer_partial_result(rec), "partialResult");
|
||||
fireEv(index, Event::partialResult, vosk_recognizer_partial_result(rec));
|
||||
break;
|
||||
case 1: [[unlikely]]
|
||||
fireEv(index, vosk_recognizer_result(rec), "result");
|
||||
fireEv(index, Event::result, vosk_recognizer_result(rec));
|
||||
}
|
||||
free(next.data);
|
||||
free(next->data);
|
||||
dataQ.pop();
|
||||
}
|
||||
}
|
||||
if(emscripten_atomic_load_u8(&processCurrent)) {
|
||||
while(!dataQ.empty()) {
|
||||
free(dataQ.front().data);
|
||||
dataQ.pop();
|
||||
}
|
||||
}
|
||||
else {
|
||||
while(!dataQ.empty()) {
|
||||
next = &dataQ.front();
|
||||
switch(vosk_recognizer_accept_waveform_f(rec, next->data, next->len)) {
|
||||
case 0: [[likely]]
|
||||
fireEv(index, Event::partialResult, vosk_recognizer_partial_result(rec));
|
||||
break;
|
||||
case 1: [[unlikely]]
|
||||
fireEv(index, Event::result, vosk_recognizer_result(rec));
|
||||
}
|
||||
free(next->data);
|
||||
dataQ.pop();
|
||||
}
|
||||
}
|
||||
fireEv(index, Event::result, vosk_recognizer_final_result(rec));
|
||||
vosk_recognizer_free(rec);
|
||||
fireEv(index, Event::status);
|
||||
}
|
||||
void Recognizer::pushData(int start, int len) {
|
||||
void Recognizer::acceptWaveform(int start, int len) {
|
||||
dataQ.emplace(start, len);
|
||||
emscripten_atomic_store_u32(&haveData, true);
|
||||
emscripten_atomic_notify(&haveData, 1);
|
||||
|
||||
@@ -4,18 +4,17 @@
|
||||
// Prevent naming conflicts with Vosk's Recognizer class
|
||||
#define Recognizer Recognizer_
|
||||
struct Recognizer {
|
||||
int haveData{};
|
||||
int haveData{};
|
||||
bool processCurrent{};
|
||||
bool done{};
|
||||
int index;
|
||||
VoskRecognizer* rec;
|
||||
std::queue<AudioData> dataQ;
|
||||
Recognizer(int index, float sampleRate, CommonModel* model);
|
||||
Recognizer(int index, float sampleRate, CommonModel* model, CommonModel* spkModel);
|
||||
Recognizer(int index, float sampleRate, CommonModel* model, const std::string& grm, int);
|
||||
~Recognizer();
|
||||
void finishConstruction(CommonModel* model, CommonModel* spkModel = nullptr);
|
||||
void main();
|
||||
void pushData(int start, int len);
|
||||
void main(int index);
|
||||
void safeDelete(bool _processCurrent);
|
||||
void acceptWaveform(int start, int len);
|
||||
void reset();
|
||||
void setEndpointerMode(VoskEndpointerMode mode);
|
||||
void setEndpointerDelays(float tStartMax, float tEnd, float tMax);
|
||||
|
||||
18
src/Util.cc
18
src/Util.cc
@@ -2,19 +2,13 @@
|
||||
#include <emscripten/em_js.h>
|
||||
#include <emscripten/wasm_worker.h>
|
||||
|
||||
EM_JS(void, _fireEv, (int index, int content, int type), {
|
||||
objs[index].dispatchEvent(new CustomEvent(type === 0 ? "0" : UTF8ToString(type), { "detail" : UTF8ToString(content) }));
|
||||
EM_JS(void, _fireEv, (int index, int typeIdx, int content), {
|
||||
objs[index].dispatchEvent(new CustomEvent(events[typeIdx], { "detail" : content == 0 ? null : UTF8ToString(content) }));
|
||||
})
|
||||
void fireEv(int index, const char* _content, const char* _type) {
|
||||
int content = reinterpret_cast<int>(_content);
|
||||
int type = reinterpret_cast<int>(_type);
|
||||
switch(emscripten_wasm_worker_self_id()) {
|
||||
case 0: [[unlikely]]
|
||||
_fireEv(index, content, type);
|
||||
break;
|
||||
case 1: [[likely]]
|
||||
emscripten_wasm_worker_post_function_viii(0, _fireEv, index, content, type);
|
||||
}
|
||||
void fireEv(int index, int typeIdx, const char* content) {
|
||||
int contentAddr{reinterpret_cast<int>(content)};
|
||||
if(emscripten_wasm_worker_self_id()) emscripten_wasm_worker_post_function_viii(0, _fireEv, index, typeIdx, contentAddr);
|
||||
else _fireEv(index, typeIdx, contentAddr);
|
||||
}
|
||||
int untar(unsigned char* tar, int tarSize, const std::string& storepath) {
|
||||
if(std::memcmp(tar + 257, "ustar", 5)) return IncorrectFormat;
|
||||
|
||||
10
src/Util.h
10
src/Util.h
@@ -13,6 +13,14 @@ struct AudioData {
|
||||
int len;
|
||||
AudioData(int start, int len) : data{reinterpret_cast<float*>(start)}, len{len} {}
|
||||
};
|
||||
enum Event {
|
||||
// Shared
|
||||
status,
|
||||
|
||||
// Recognizer
|
||||
partialResult,
|
||||
result,
|
||||
};
|
||||
enum UntarStatus {
|
||||
Successful,
|
||||
IncorrectFormat,
|
||||
@@ -39,7 +47,7 @@ struct WorkerPool {
|
||||
~WorkerPool();
|
||||
void exec(std::function<void()> fn);
|
||||
};
|
||||
void fireEv(int index, const char* _content, const char* _type = nullptr);
|
||||
void fireEv(int index, int typeIdx, const char* content = nullptr);
|
||||
int untar(unsigned char* tar, int tarSize, const std::string& storepath);
|
||||
|
||||
extern WorkerPool globalPool;
|
||||
@@ -1,4 +1,5 @@
|
||||
let objs = []
|
||||
let events = ["status", "partialResult", "result"]
|
||||
let processorURL = URL.createObjectURL(new Blob(['(', (() => {
|
||||
registerProcessor("VoskletTransferer", class extends AudioWorkletProcessor {
|
||||
constructor(opts) {
|
||||
@@ -21,8 +22,8 @@ let processorURL = URL.createObjectURL(new Blob(['(', (() => {
|
||||
})
|
||||
}).toString(), ')()'], { type : "text/javascript" }))
|
||||
|
||||
Module.cleanUp = () => {
|
||||
objs.forEach(obj => obj.obj.delete())
|
||||
Module.cleanUp = async () => {
|
||||
for(let obj of objs) await obj.delete()
|
||||
URL.revokeObjectURL(processorURL)
|
||||
}
|
||||
|
||||
@@ -49,15 +50,18 @@ class CommonModel extends EventTarget {
|
||||
super()
|
||||
objs.push(this)
|
||||
}
|
||||
delete() {
|
||||
this.obj.delete()
|
||||
}
|
||||
static async create(url, storepath, id, normalMdl) {
|
||||
let mdl = new CommonModel()
|
||||
let result = new Promise((resolve, reject) => {
|
||||
mdl.addEventListener("0", ev => {
|
||||
if(ev.detail == "0") {
|
||||
mdl.addEventListener("status", ev => {
|
||||
if(!ev.detail) {
|
||||
if(normalMdl) mdl.findWord = (word) => mdl.obj.findWord(word)
|
||||
return resolve(mdl)
|
||||
resolve(mdl)
|
||||
}
|
||||
reject(ev.detail)
|
||||
else reject(ev.detail)
|
||||
}, { once : true })
|
||||
})
|
||||
let tar
|
||||
@@ -101,21 +105,38 @@ Module.createSpkModel = async (url, storepath, id) => {
|
||||
}
|
||||
|
||||
class Recognizer extends EventTarget {
|
||||
constructor() {
|
||||
constructor() {
|
||||
super()
|
||||
objs.push(this)
|
||||
return new Proxy(this, {
|
||||
get(self, prop, _) {
|
||||
return self.obj && Object.keys(Object.getPrototypeOf(self.obj)).includes(prop) ? self.obj[prop].bind(self.obj) : self[prop] ? self[prop].bind ? self[prop].bind(self) : self[prop] : undefined
|
||||
if(self[prop] == undefined && self.obj[prop] == undefined) return undefined
|
||||
let p = self[prop]
|
||||
if(p) return p.bind ? p.bind(self) : p
|
||||
p = self.obj[prop]
|
||||
return p.bind ? p.bind(self.obj) : p
|
||||
}
|
||||
})
|
||||
}
|
||||
acceptWaveform(audioData) {
|
||||
let start = _malloc(audioData.length * 4)
|
||||
HEAPF32.set(audioData, start / 4)
|
||||
this.obj.acceptWaveform(start, audioData.length)
|
||||
}
|
||||
async delete(processCurrent = false) {
|
||||
let result = new Promise((resolve, _) => this.addEventListener("status", _ => {
|
||||
this.obj.delete()
|
||||
resolve()
|
||||
}, { once : true }))
|
||||
this.obj.safeDelete(processCurrent)
|
||||
return result;
|
||||
}
|
||||
static async create(model, sampleRate, mode, grammar, spkModel) {
|
||||
let rec = new Recognizer()
|
||||
let result = new Promise((resolve, reject) => {
|
||||
rec.addEventListener("0", ev => {
|
||||
if(ev.detail == "0") return resolve(rec)
|
||||
reject(ev.detail)
|
||||
rec.addEventListener("status", ev => {
|
||||
if(!ev.detail) resolve(rec)
|
||||
else reject(ev.detail)
|
||||
}, { once : true })
|
||||
})
|
||||
switch(mode) {
|
||||
@@ -130,11 +151,6 @@ class Recognizer extends EventTarget {
|
||||
}
|
||||
return result
|
||||
}
|
||||
acceptWaveform(audioData) {
|
||||
let start = _malloc(audioData.length * 4)
|
||||
HEAPF32.set(audioData, start / 4)
|
||||
this.obj.pushData(start, audioData.length)
|
||||
}
|
||||
}
|
||||
|
||||
Module.createRecognizer = (model, sampleRate) => {
|
||||
|
||||
2
src/make
2
src/make
@@ -1,5 +1,5 @@
|
||||
#!/bin/bash
|
||||
INITIAL_MEMORY=${INITIAL_MEMORY:-300mb}
|
||||
INITIAL_MEMORY=${INITIAL_MEMORY:-315mb}
|
||||
MAX_THREADS=${MAX_THREADS:-1}
|
||||
EMSDK=${EMSDK:-../emsdk}
|
||||
JOBS=${JOBS:-$(nproc)}
|
||||
|
||||
4
test
4
test
@@ -1,5 +1,5 @@
|
||||
#!/bin/bash
|
||||
INITIAL_MEMORY=${INITIAL_MEMORY:-800mb}
|
||||
INITIAL_MEMORY=${INITIAL_MEMORY:-315mb}
|
||||
MAX_THREADS=${MAX_THREADS:-1}
|
||||
EMSDK=${EMSDK:-emsdk}
|
||||
JOBS=${JOBS:-$(nproc)}
|
||||
@@ -36,7 +36,7 @@ OPENFST=$(realpath openfst)
|
||||
CLAPACK_WASM=$(realpath clapack-wasm)
|
||||
|
||||
cd src &&
|
||||
MODE=1 && # 0: Ultra debug info, 1: Optimized release, else custom
|
||||
MODE=0 && # 0: Ultra debug info, 1: Optimized release, else custom
|
||||
echo "Mode = $MODE" &&
|
||||
if [ "$MODE" = 0 ]; then
|
||||
em++ Util.cc CommonModel.cc Recognizer.cc Bindings.cc -O0 -DEMSCRIPTEN_HAS_UNBOUND_TYPE_NAMES=0 -fno-rtti -DMAX_WORKERS="$MAX_THREADS" -sWASMFS -sWASM_BIGINT -sMODULARIZE -sWASM_EXNREF -sTEXTDECODER=2 -sWASM_WORKERS=2 -sEVAL_CTORS=2 -sSTACK_OVERFLOW_CHECK=2 -sASSERTIONS=2 -sINITIAL_MEMORY="$INITIAL_MEMORY" -sALLOW_MEMORY_GROWTH -sRUNTIME_DEBUG -sPOLYFILL=0 -sEXIT_RUNTIME=0 -sINVOKE_RUN=0 -sDISABLE_EXCEPTION_CATCHING=0 -sEXPORTED_FUNCTIONS=_malloc -sEXPORT_NAME=loadVosklet -sMALLOC=emmalloc -sEXPORTED_RUNTIME_METHODS=UTF8ToString -sENVIRONMENT=web,worker -I. -I"$VOSK"/src -L"$KALDI"/src -l:online2/kaldi-online2.a -l:decoder/kaldi-decoder.a -l:ivector/kaldi-ivector.a -l:gmm/kaldi-gmm.a -l:tree/kaldi-tree.a -l:feat/kaldi-feat.a -l:cudamatrix/kaldi-cudamatrix.a -l:lat/kaldi-lat.a -l:lm/kaldi-lm.a -l:rnnlm/kaldi-rnnlm.a -l:hmm/kaldi-hmm.a -l:nnet3/kaldi-nnet3.a -l:transform/kaldi-transform.a -l:matrix/kaldi-matrix.a -l:fstext/kaldi-fstext.a -l:util/kaldi-util.a -l:base/kaldi-base.a -L"$OPENFST"/lib -l:libfst.a -l:libfstngram.a -L"$CLAPACK_WASM" -l:CBLAS/lib/cblas.a -l:CLAPACK-3.2.1/lapack.a -l:CLAPACK-3.2.1/libcblaswr.a -l:f2c_BLAS-3.8.0/blas.a -l:libf2c/libf2c.a -L"$VOSK"/src -l:vosk.a -lembind -flto -msimd128 -mreference-types -mnontrapping-fptoint -mextended-const -msign-ext -mmutable-globals --pre-js Wrapper.js -o ../test.js
|
||||
|
||||
Reference in New Issue
Block a user