Merge worker.js file so this truly has one file, status: tested, not working
This commit is contained in:
@@ -8,11 +8,11 @@
|
|||||||
| ```Promise<Model> makeModel(path: string, url: string, id: string)```<br><br>```Promise<SpkModel> makeSpkModel(path: string, url: string, id: string)``` | Make a ```Model``` or ```SpkModel```<br>- If **path** contains valid model files and **id** is the same, there will not be a fetch from **url**.<br>- If **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**. |
|
| ```Promise<Model> makeModel(path: string, url: string, id: string)```<br><br>```Promise<SpkModel> makeSpkModel(path: string, url: string, id: string)``` | Make a ```Model``` or ```SpkModel```<br>- If **path** contains valid model files and **id** is the same, there will not be a fetch from **url**.<br>- If **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**. |
|
||||||
| ```Promise<Recognizer> makeRecognizer(model: Model, sampleRate: float)``` | Make a ```Recognizer```, it will use **model**'s thread if it's the first to use **model**, else it will use a new thread.
|
| ```Promise<Recognizer> makeRecognizer(model: Model, sampleRate: float)``` | Make a ```Recognizer```, it will use **model**'s thread if it's the first to use **model**, else it will use a new thread.
|
||||||
| ```setLogLevel(lvl: int)``` | Set Vosk's log level (default: -1) <br>- 2: Error<br>- 1: Warning<br>- 0: Info <br>- 1: Verbose<br>- 2: More verbose<br>- 3: Debug |
|
| ```setLogLevel(lvl: int)``` | Set Vosk's log level (default: -1) <br>- 2: Error<br>- 1: Warning<br>- 0: Info <br>- 1: Verbose<br>- 2: More verbose<br>- 3: Debug |
|
||||||
| ```deleteAll()``` | Call ```delete()``` on all objects, it is recommended to run this at the API usage end to automatically clean up everything. See [why](https://emscripten.org/docs/getting_started/FAQ.html#what-does-exiting-the-runtime-mean-why-don-t-atexit-s-run).|
|
| ```cleanUp()``` | Call ```delete()``` on all objects and revoke all Blob URLs. |
|
||||||
|
|
||||||
| Function signature (all objects) | Description
|
| Function signature (all objects) | Description
|
||||||
|---|---|
|
|---|---|
|
||||||
| ```delete()``` | Delete this object
|
| ```delete()``` | Delete this object, 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 .
|
||||||
## ```Recognizer``` object
|
## ```Recognizer``` object
|
||||||
| Function signature | Description |
|
| Function signature | Description |
|
||||||
|---|---|
|
|---|---|
|
||||||
|
|||||||
@@ -88,4 +88,7 @@ em++ -pthread -O3 -flto -Wno-deprecated -I. -I$KALDI/src -I$OPENFST/include $VOS
|
|||||||
emar -rcs vosk.a ${VOSK_FILES//.cc/.o} &&
|
emar -rcs vosk.a ${VOSK_FILES//.cc/.o} &&
|
||||||
|
|
||||||
cd $SRC &&
|
cd $SRC &&
|
||||||
em++ -O3 global.cc genericModel.cc model.cc spkModel.cc recognizer.cc bindings.cc -sWASMFS -sWASM_BIGINT -sSINGLE_FILE -sEMBIND_STD_STRING_IS_UTF8 -sEXPORTED_RUNTIME_METHODS=UTF8ToString,stringToUTF8OnStack -sMODULARIZE -sEXPORTED_FUNCTIONS=_malloc,_free,_main -sSUPPORT_LONGJMP=0 -sTRUSTED_TYPES -sEXPORT_NAME=loadBR -sENVIRONMENT=web,worker -sINITIAL_MEMORY=$MAX_MEMORY -sPTHREAD_POOL_SIZE=$MAX_THREADS -sPTHREAD_POOL_SIZE_STRICT=0 -sALLOW_BLOCKING_ON_MAIN_THREAD=1 -sPOLYFILL=0 -sPTHREAD_POOL_DELAY_LOAD --pre-js pre.js -I. -I$LIBARCHIVE/include -I$VOSK/src -L$LIBARCHIVE/lib -larchive -L$ZSTD/lib -lzstd -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 -lopfs.js -lembind -pthread -flto -o ../test/BrowserRecognizer.js
|
em++ -O3 global.cc genericModel.cc model.cc spkModel.cc recognizer.cc bindings.cc -sWASMFS -sWASM_BIGINT -sSINGLE_FILE -sEMBIND_STD_STRING_IS_UTF8 -sEXPORTED_RUNTIME_METHODS=UTF8ToString,stringToUTF8OnStack -sMODULARIZE -sEXPORTED_FUNCTIONS=_malloc,_free,_main -sSUPPORT_LONGJMP=0 -sTRUSTED_TYPES -sEXPORT_NAME=loadBR -sENVIRONMENT=web,worker -sINITIAL_MEMORY=$MAX_MEMORY -sPTHREAD_POOL_SIZE=$MAX_THREADS -sPTHREAD_POOL_SIZE_STRICT=0 -sEXIT_RUNTIME -sALLOW_BLOCKING_ON_MAIN_THREAD=1 -sPOLYFILL=0 -sPTHREAD_POOL_DELAY_LOAD --pre-js pre.js -I. -I$LIBARCHIVE/include -I$VOSK/src -L$LIBARCHIVE/lib -larchive -L$ZSTD/lib -lzstd -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 -lopfs.js -lembind -pthread -flto ../BrowserRecognizer.js
|
||||||
|
cd .. &&
|
||||||
|
rm -f BrowserRecognizer.worker.js &&
|
||||||
|
sed -i 's/locateFile("BrowserRecognizer.worker.js")/pthreadUrl/g' BrowserRecognizer.js
|
||||||
@@ -1,13 +1,22 @@
|
|||||||
#include "genericModel.h"
|
#include "genericModel.h"
|
||||||
genericModel::genericModel(const std::string& storepath, const std::string &id, int index) : storepath(storepath), id(id), index(index) {
|
genericModel::genericModel(const std::string& storepath, const std::string &id, int index) : storepath(storepath), id(id), index(index) {
|
||||||
fs::current_path("/opfs");
|
fs::current_path("/opfs", tank);
|
||||||
fs::create_directories(storepath);
|
if(tank.value() != 0) {
|
||||||
fs::current_path(storepath);
|
throwJS("Unable to change to OPFS directory");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(!fs::create_directories(storepath, tank)) {
|
||||||
|
throwJS("Unable to make model directory");
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
fs::current_path(storepath, tank);
|
||||||
|
if(tank.value() != 0) {
|
||||||
|
throwJS("Unable to change to model directory");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
bool genericModel::checkModel() {
|
bool genericModel::checkModel() {
|
||||||
if(!checkModelFiles()) return false;
|
if(!checkModelFiles()) return false;
|
||||||
static std::error_code c{};
|
if(!fs::exists("id", tank)) return false;
|
||||||
if(!fs::exists("id", c)) return false;
|
|
||||||
std::ifstream file {"id", std::ifstream::binary};
|
std::ifstream file {"id", std::ifstream::binary};
|
||||||
if(!file.is_open()) return false;
|
if(!file.is_open()) return false;
|
||||||
long long size {file.seekg(0, std::ios::end).tellg()};
|
long long size {file.seekg(0, std::ios::end).tellg()};
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
#include "global.h"
|
#include "global.h"
|
||||||
|
|
||||||
static pthread_t selfTID{pthread_self()};
|
|
||||||
void throwJS(const char* msg, bool err) {
|
void throwJS(const char* msg, bool err) {
|
||||||
EM_ASM({
|
EM_ASM({
|
||||||
if($1) {
|
if($1) {
|
||||||
|
|||||||
@@ -6,8 +6,10 @@
|
|||||||
#include <emscripten/console.h>
|
#include <emscripten/console.h>
|
||||||
#include <emscripten/em_asm.h>
|
#include <emscripten/em_asm.h>
|
||||||
#include <emscripten/proxying.h>
|
#include <emscripten/proxying.h>
|
||||||
|
|
||||||
using namespace emscripten;
|
using namespace emscripten;
|
||||||
|
|
||||||
|
static pthread_t selfTID{pthread_self()};
|
||||||
|
static std::error_code tank{};
|
||||||
void throwJS(const char* msg, bool err = false);
|
void throwJS(const char* msg, bool err = false);
|
||||||
void fireEv(const char *type, const char *content, int index);
|
void fireEv(const char *type, const char *content, int index);
|
||||||
int main();
|
int main();
|
||||||
|
|||||||
27
src/model.cc
27
src/model.cc
@@ -28,18 +28,17 @@ void model::load(bool newThrd) {
|
|||||||
t.detach();
|
t.detach();
|
||||||
}
|
}
|
||||||
bool model::checkModelFiles() {
|
bool model::checkModelFiles() {
|
||||||
static std::error_code c{};
|
return fs::exists("am/final.mdl", tank) &&
|
||||||
return fs::exists("am/final.mdl", c) &&
|
fs::exists("conf/mfcc.conf", tank) &&
|
||||||
fs::exists("conf/mfcc.conf", c) &&
|
fs::exists("conf/model.conf", tank) &&
|
||||||
fs::exists("conf/model.conf", c) &&
|
fs::exists("graph/phones/word_boundary.int", tank) &&
|
||||||
fs::exists("graph/phones/word_boundary.int", c) &&
|
fs::exists("graph/Gr.fst", tank) &&
|
||||||
fs::exists("graph/Gr.fst", c) &&
|
fs::exists("graph/HCLr.fst", tank) &&
|
||||||
fs::exists("graph/HCLr.fst", c) &&
|
fs::exists("graph/disambig_tid.int", tank) &&
|
||||||
fs::exists("graph/disambig_tid.int", c) &&
|
fs::exists("ivector/final.dubm", tank) &&
|
||||||
fs::exists("ivector/final.dubm", c) &&
|
fs::exists("ivector/final.ie", tank) &&
|
||||||
fs::exists("ivector/final.ie", c) &&
|
fs::exists("ivector/final.mat", tank) &&
|
||||||
fs::exists("ivector/final.mat", c) &&
|
fs::exists("ivector/global_cmvn.stats", tank) &&
|
||||||
fs::exists("ivector/global_cmvn.stats", c) &&
|
fs::exists("ivector/online_cmvn.conf", tank) &&
|
||||||
fs::exists("ivector/online_cmvn.conf", c) &&
|
fs::exists("ivector/splice.conf", tank);
|
||||||
fs::exists("ivector/splice.conf", c);
|
|
||||||
}
|
}
|
||||||
216
src/pre.js
216
src/pre.js
@@ -1,4 +1,25 @@
|
|||||||
let objs = []
|
let objs = []
|
||||||
|
let processorUrl = URL.createObjectURL(new Blob([
|
||||||
|
(() => {
|
||||||
|
registerProcessor("BRProcessor", class extends AudioWorkletProcessor {
|
||||||
|
constructor(options) {
|
||||||
|
super(options)
|
||||||
|
this.done = false
|
||||||
|
this.port.onmessage = (ev) => {
|
||||||
|
if(ev.cmd === "deinit") this.done = false
|
||||||
|
}
|
||||||
|
this.ptr = options.processorOptions.ptr
|
||||||
|
}
|
||||||
|
process(inputs, outputs, params) {
|
||||||
|
if(this.done) return false;
|
||||||
|
this.wasmMem.set(inputs[0].getChannelData(this.channel));
|
||||||
|
this.recognizerPort.postMessage(".")
|
||||||
|
outputs = inputs
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}).toString()
|
||||||
|
], {type : "text/javascript"}))
|
||||||
class Recognizer extends EventTarget {
|
class Recognizer extends EventTarget {
|
||||||
constructor() {
|
constructor() {
|
||||||
super()
|
super()
|
||||||
@@ -69,11 +90,13 @@ class SpkModel extends EventTarget {
|
|||||||
this.obj.delete()
|
this.obj.delete()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Module.deleteAll = () => {
|
|
||||||
objs.forEach(obj => obj.delete())
|
|
||||||
}
|
|
||||||
Module.makeModel = async (url, storepath, id) => {
|
Module.makeModel = async (url, storepath, id) => {
|
||||||
let mdl = new Model(storepath, id)
|
try {
|
||||||
|
let mdl = new Model(storepath, id)
|
||||||
|
}
|
||||||
|
catch(e) {
|
||||||
|
return Promise.reject(e)
|
||||||
|
}
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
mdl.addEventListener("_continue", (ev) => {
|
mdl.addEventListener("_continue", (ev) => {
|
||||||
if(ev.detail === ".") {
|
if(ev.detail === ".") {
|
||||||
@@ -99,7 +122,12 @@ Module.makeModel = async (url, storepath, id) => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
Module.makeSpkModel = async (url, storepath, id) => {
|
Module.makeSpkModel = async (url, storepath, id) => {
|
||||||
let mdl = new SpkModel(storepath, id)
|
try {
|
||||||
|
let mdl = new SpkModel(storepath, id)
|
||||||
|
}
|
||||||
|
catch(e) {
|
||||||
|
return Promise.reject(e)
|
||||||
|
}
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
mdl.addEventListener("_continue", (ev) => {
|
mdl.addEventListener("_continue", (ev) => {
|
||||||
if(ev.detail === ".") {
|
if(ev.detail === ".") {
|
||||||
@@ -139,3 +167,181 @@ Module.makeRecognizer = (model, sampleRate) => {
|
|||||||
rec._init(model.obj, sampleRate)
|
rec._init(model.obj, sampleRate)
|
||||||
return retval
|
return retval
|
||||||
}
|
}
|
||||||
|
// Taken from the worker.js file
|
||||||
|
let pthreadUrl = URL.createObjectURL(new Blob([
|
||||||
|
(() => {
|
||||||
|
/**
|
||||||
|
* @license
|
||||||
|
* Copyright 2015 The Emscripten Authors
|
||||||
|
* SPDX-License-Identifier: MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Pthread Web Worker startup routine:
|
||||||
|
// This is the entry point file that is loaded first by each Web Worker
|
||||||
|
// that executes pthreads on the Emscripten application.
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var Module = {};
|
||||||
|
|
||||||
|
// Thread-local guard variable for one-time init of the JS state
|
||||||
|
var initializedJS = false;
|
||||||
|
|
||||||
|
function assert(condition, text) {
|
||||||
|
if (!condition) abort('Assertion failed: ' + text);
|
||||||
|
}
|
||||||
|
|
||||||
|
function threadPrintErr() {
|
||||||
|
var text = Array.prototype.slice.call(arguments).join(' ');
|
||||||
|
console.error(text);
|
||||||
|
}
|
||||||
|
function threadAlert() {
|
||||||
|
var text = Array.prototype.slice.call(arguments).join(' ');
|
||||||
|
postMessage({cmd: 'alert', text, threadId: Module['_pthread_self']()});
|
||||||
|
}
|
||||||
|
// We don't need out() for now, but may need to add it if we want to use it
|
||||||
|
// here. Or, if this code all moves into the main JS, that problem will go
|
||||||
|
// away. (For now, adding it here increases code size for no benefit.)
|
||||||
|
var out = () => { throw 'out() is not defined in worker.js.'; }
|
||||||
|
var err = threadPrintErr;
|
||||||
|
self.alert = threadAlert;
|
||||||
|
var dbg = threadPrintErr;
|
||||||
|
|
||||||
|
Module['instantiateWasm'] = (info, receiveInstance) => {
|
||||||
|
// Instantiate from the module posted from the main thread.
|
||||||
|
// We can just use sync instantiation in the worker.
|
||||||
|
var module = Module['wasmModule'];
|
||||||
|
// We don't need the module anymore; new threads will be spawned from the main thread.
|
||||||
|
Module['wasmModule'] = null;
|
||||||
|
var instance = new WebAssembly.Instance(module, info);
|
||||||
|
// TODO: Due to Closure regression https://github.com/google/closure-compiler/issues/3193,
|
||||||
|
// the above line no longer optimizes out down to the following line.
|
||||||
|
// When the regression is fixed, we can remove this if/else.
|
||||||
|
return receiveInstance(instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Turn unhandled rejected promises into errors so that the main thread will be
|
||||||
|
// notified about them.
|
||||||
|
self.onunhandledrejection = (e) => {
|
||||||
|
throw e.reason || e;
|
||||||
|
};
|
||||||
|
|
||||||
|
function handleMessage(e) {
|
||||||
|
try {
|
||||||
|
if (e.data.cmd === 'load') { // Preload command that is called once per worker to parse and load the Emscripten code.
|
||||||
|
|
||||||
|
// Until we initialize the runtime, queue up any further incoming messages.
|
||||||
|
let messageQueue = [];
|
||||||
|
self.onmessage = (e) => messageQueue.push(e);
|
||||||
|
|
||||||
|
// And add a callback for when the runtime is initialized.
|
||||||
|
self.startWorker = (instance) => {
|
||||||
|
Module = instance;
|
||||||
|
// Notify the main thread that this thread has loaded.
|
||||||
|
postMessage({ 'cmd': 'loaded' });
|
||||||
|
// Process any messages that were queued before the thread was ready.
|
||||||
|
for (let msg of messageQueue) {
|
||||||
|
handleMessage(msg);
|
||||||
|
}
|
||||||
|
// Restore the real message handler.
|
||||||
|
self.onmessage = handleMessage;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Module and memory were sent from main thread
|
||||||
|
Module['wasmModule'] = e.data.wasmModule;
|
||||||
|
|
||||||
|
// Use `const` here to ensure that the variable is scoped only to
|
||||||
|
// that iteration, allowing safe reference from a closure.
|
||||||
|
for (const handler of e.data.handlers) {
|
||||||
|
Module[handler] = (...args) => {
|
||||||
|
postMessage({ cmd: 'callHandler', handler, args: args });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Module['wasmMemory'] = e.data.wasmMemory;
|
||||||
|
|
||||||
|
Module['buffer'] = Module['wasmMemory'].buffer;
|
||||||
|
|
||||||
|
Module['workerID'] = e.data.workerID;
|
||||||
|
|
||||||
|
Module['ENVIRONMENT_IS_PTHREAD'] = true;
|
||||||
|
|
||||||
|
if (typeof e.data.urlOrBlob == 'string') {
|
||||||
|
if (typeof self.trustedTypes != 'undefined' && self.trustedTypes.createPolicy) {
|
||||||
|
var p = self.trustedTypes.createPolicy('emscripten#workerPolicy3', { createScriptURL: (ignored) => e.data.urlOrBlob });
|
||||||
|
importScripts(p.createScriptURL('ignored'));
|
||||||
|
} else
|
||||||
|
importScripts(e.data.urlOrBlob);
|
||||||
|
} else {
|
||||||
|
var objectUrl = URL.createObjectURL(e.data.urlOrBlob);
|
||||||
|
if (typeof self.trustedTypes != 'undefined' && self.trustedTypes.createPolicy) {
|
||||||
|
var p = self.trustedTypes.createPolicy('emscripten#workerPolicy3', { createScriptURL: (ignored) => objectUrl });
|
||||||
|
importScripts(p.createScriptURL('ignored'));
|
||||||
|
} else
|
||||||
|
importScripts(objectUrl);
|
||||||
|
URL.revokeObjectURL(objectUrl);
|
||||||
|
}
|
||||||
|
loadBR(Module);
|
||||||
|
} else if (e.data.cmd === 'run') {
|
||||||
|
// Pass the thread address to wasm to store it for fast access.
|
||||||
|
Module['__emscripten_thread_init'](e.data.pthread_ptr, /*is_main=*/0, /*is_runtime=*/0, /*can_block=*/1);
|
||||||
|
|
||||||
|
// Await mailbox notifications with `Atomics.waitAsync` so we can start
|
||||||
|
// using the fast `Atomics.notify` notification path.
|
||||||
|
Module['__emscripten_thread_mailbox_await'](e.data.pthread_ptr);
|
||||||
|
|
||||||
|
assert(e.data.pthread_ptr);
|
||||||
|
// Also call inside JS module to set up the stack frame for this pthread in JS module scope
|
||||||
|
Module['establishStackSpace']();
|
||||||
|
Module['PThread'].receiveObjectTransfer(e.data);
|
||||||
|
Module['PThread'].threadInitTLS();
|
||||||
|
|
||||||
|
if (!initializedJS) {
|
||||||
|
// Embind must initialize itself on all threads, as it generates support JS.
|
||||||
|
// We only do this once per worker since they get reused
|
||||||
|
Module['__embind_initialize_bindings']();
|
||||||
|
initializedJS = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
Module['invokeEntryPoint'](e.data.start_routine, e.data.arg);
|
||||||
|
} catch(ex) {
|
||||||
|
if (ex != 'unwind') {
|
||||||
|
// The pthread "crashed". Do not call `_emscripten_thread_exit` (which
|
||||||
|
// would make this thread joinable). Instead, re-throw the exception
|
||||||
|
// and let the top level handler propagate it back to the main thread.
|
||||||
|
throw ex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (e.data.cmd === 'cancel') { // Main thread is asking for a pthread_cancel() on this thread.
|
||||||
|
if (Module['_pthread_self']()) {
|
||||||
|
Module['__emscripten_thread_exit'](-1);
|
||||||
|
}
|
||||||
|
} else if (e.data.target === 'setimmediate') {
|
||||||
|
// no-op
|
||||||
|
} else if (e.data.cmd === 'checkMailbox') {
|
||||||
|
if (initializedJS) {
|
||||||
|
Module['checkMailbox']();
|
||||||
|
}
|
||||||
|
} else if (e.data.cmd) {
|
||||||
|
// The received message looks like something that should be handled by this message
|
||||||
|
// handler, (since there is a e.data.cmd field present), but is not one of the
|
||||||
|
// recognized commands:
|
||||||
|
err(`worker.js received unknown command ${e.data.cmd}`);
|
||||||
|
err(e.data);
|
||||||
|
}
|
||||||
|
} catch(ex) {
|
||||||
|
err(`worker.js onmessage() captured an uncaught exception: ${ex}`);
|
||||||
|
if (ex?.stack) err(ex.stack);
|
||||||
|
Module['__emscripten_thread_crashed']?.();
|
||||||
|
throw ex;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
self.onmessage = handleMessage;
|
||||||
|
}).toString()
|
||||||
|
], {type : "text/javascript"}))
|
||||||
|
Module.cleanUp = () => {
|
||||||
|
objs.forEach(obj => obj.delete())
|
||||||
|
URL.revokeObjectURL(pthreadUrl)
|
||||||
|
URL.revokeObjectURL(processorUrl)
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,26 +1 @@
|
|||||||
// A copy and pass processor
|
// A copy and pass processor
|
||||||
registerProcessor("BRProcessor", class extends AudioWorkletProcessor {
|
|
||||||
constructor(options) {
|
|
||||||
super(options)
|
|
||||||
this.done = false
|
|
||||||
this.port.onmessage = (ev) => {
|
|
||||||
switch(ev.cmd) {
|
|
||||||
case "init":
|
|
||||||
this.recognizerPort = ev.ports[0]
|
|
||||||
this.wasmMem = new Float32Array(WebAssembly.Memory.buffer).subarray(ev.ptr, ev.ptr+512)
|
|
||||||
this.channel = ev.channel
|
|
||||||
|
|
||||||
break
|
|
||||||
case "deinit":
|
|
||||||
this.done = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
process(inputs, outputs, params) {
|
|
||||||
if(this.done) return false;
|
|
||||||
this.wasmMem.set(inputs[0].getChannelData(this.channel));
|
|
||||||
this.recognizerPort.postMessage(".")
|
|
||||||
outputs = inputs
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
})
|
|
||||||
@@ -32,8 +32,8 @@ void spkModel::load(bool newThrd) {
|
|||||||
t.detach();
|
t.detach();
|
||||||
}
|
}
|
||||||
bool spkModel::checkModelFiles() {
|
bool spkModel::checkModelFiles() {
|
||||||
return fs::exists("mfcc.conf") &&
|
return fs::exists("mfcc.conf", tank) &&
|
||||||
fs::exists("final.ext.raw") &&
|
fs::exists("final.ext.raw", tank) &&
|
||||||
fs::exists("mean.vec") &&
|
fs::exists("mean.vec", tank) &&
|
||||||
fs::exists("transform.mat");
|
fs::exists("transform.mat", tank);
|
||||||
}
|
}
|
||||||
File diff suppressed because one or more lines are too long
@@ -1,169 +0,0 @@
|
|||||||
/**
|
|
||||||
* @license
|
|
||||||
* Copyright 2015 The Emscripten Authors
|
|
||||||
* SPDX-License-Identifier: MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Pthread Web Worker startup routine:
|
|
||||||
// This is the entry point file that is loaded first by each Web Worker
|
|
||||||
// that executes pthreads on the Emscripten application.
|
|
||||||
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
var Module = {};
|
|
||||||
|
|
||||||
// Thread-local guard variable for one-time init of the JS state
|
|
||||||
var initializedJS = false;
|
|
||||||
|
|
||||||
function assert(condition, text) {
|
|
||||||
if (!condition) abort('Assertion failed: ' + text);
|
|
||||||
}
|
|
||||||
|
|
||||||
function threadPrintErr() {
|
|
||||||
var text = Array.prototype.slice.call(arguments).join(' ');
|
|
||||||
console.error(text);
|
|
||||||
}
|
|
||||||
function threadAlert() {
|
|
||||||
var text = Array.prototype.slice.call(arguments).join(' ');
|
|
||||||
postMessage({cmd: 'alert', text, threadId: Module['_pthread_self']()});
|
|
||||||
}
|
|
||||||
// We don't need out() for now, but may need to add it if we want to use it
|
|
||||||
// here. Or, if this code all moves into the main JS, that problem will go
|
|
||||||
// away. (For now, adding it here increases code size for no benefit.)
|
|
||||||
var out = () => { throw 'out() is not defined in worker.js.'; }
|
|
||||||
var err = threadPrintErr;
|
|
||||||
self.alert = threadAlert;
|
|
||||||
var dbg = threadPrintErr;
|
|
||||||
|
|
||||||
Module['instantiateWasm'] = (info, receiveInstance) => {
|
|
||||||
// Instantiate from the module posted from the main thread.
|
|
||||||
// We can just use sync instantiation in the worker.
|
|
||||||
var module = Module['wasmModule'];
|
|
||||||
// We don't need the module anymore; new threads will be spawned from the main thread.
|
|
||||||
Module['wasmModule'] = null;
|
|
||||||
var instance = new WebAssembly.Instance(module, info);
|
|
||||||
// TODO: Due to Closure regression https://github.com/google/closure-compiler/issues/3193,
|
|
||||||
// the above line no longer optimizes out down to the following line.
|
|
||||||
// When the regression is fixed, we can remove this if/else.
|
|
||||||
return receiveInstance(instance);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Turn unhandled rejected promises into errors so that the main thread will be
|
|
||||||
// notified about them.
|
|
||||||
self.onunhandledrejection = (e) => {
|
|
||||||
throw e.reason || e;
|
|
||||||
};
|
|
||||||
|
|
||||||
function handleMessage(e) {
|
|
||||||
try {
|
|
||||||
if (e.data.cmd === 'load') { // Preload command that is called once per worker to parse and load the Emscripten code.
|
|
||||||
|
|
||||||
// Until we initialize the runtime, queue up any further incoming messages.
|
|
||||||
let messageQueue = [];
|
|
||||||
self.onmessage = (e) => messageQueue.push(e);
|
|
||||||
|
|
||||||
// And add a callback for when the runtime is initialized.
|
|
||||||
self.startWorker = (instance) => {
|
|
||||||
Module = instance;
|
|
||||||
// Notify the main thread that this thread has loaded.
|
|
||||||
postMessage({ 'cmd': 'loaded' });
|
|
||||||
// Process any messages that were queued before the thread was ready.
|
|
||||||
for (let msg of messageQueue) {
|
|
||||||
handleMessage(msg);
|
|
||||||
}
|
|
||||||
// Restore the real message handler.
|
|
||||||
self.onmessage = handleMessage;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Module and memory were sent from main thread
|
|
||||||
Module['wasmModule'] = e.data.wasmModule;
|
|
||||||
|
|
||||||
// Use `const` here to ensure that the variable is scoped only to
|
|
||||||
// that iteration, allowing safe reference from a closure.
|
|
||||||
for (const handler of e.data.handlers) {
|
|
||||||
Module[handler] = (...args) => {
|
|
||||||
postMessage({ cmd: 'callHandler', handler, args: args });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Module['wasmMemory'] = e.data.wasmMemory;
|
|
||||||
|
|
||||||
Module['buffer'] = Module['wasmMemory'].buffer;
|
|
||||||
|
|
||||||
Module['workerID'] = e.data.workerID;
|
|
||||||
|
|
||||||
Module['ENVIRONMENT_IS_PTHREAD'] = true;
|
|
||||||
|
|
||||||
if (typeof e.data.urlOrBlob == 'string') {
|
|
||||||
if (typeof self.trustedTypes != 'undefined' && self.trustedTypes.createPolicy) {
|
|
||||||
var p = self.trustedTypes.createPolicy('emscripten#workerPolicy3', { createScriptURL: (ignored) => e.data.urlOrBlob });
|
|
||||||
importScripts(p.createScriptURL('ignored'));
|
|
||||||
} else
|
|
||||||
importScripts(e.data.urlOrBlob);
|
|
||||||
} else {
|
|
||||||
var objectUrl = URL.createObjectURL(e.data.urlOrBlob);
|
|
||||||
if (typeof self.trustedTypes != 'undefined' && self.trustedTypes.createPolicy) {
|
|
||||||
var p = self.trustedTypes.createPolicy('emscripten#workerPolicy3', { createScriptURL: (ignored) => objectUrl });
|
|
||||||
importScripts(p.createScriptURL('ignored'));
|
|
||||||
} else
|
|
||||||
importScripts(objectUrl);
|
|
||||||
URL.revokeObjectURL(objectUrl);
|
|
||||||
}
|
|
||||||
loadBR(Module);
|
|
||||||
} else if (e.data.cmd === 'run') {
|
|
||||||
// Pass the thread address to wasm to store it for fast access.
|
|
||||||
Module['__emscripten_thread_init'](e.data.pthread_ptr, /*is_main=*/0, /*is_runtime=*/0, /*can_block=*/1);
|
|
||||||
|
|
||||||
// Await mailbox notifications with `Atomics.waitAsync` so we can start
|
|
||||||
// using the fast `Atomics.notify` notification path.
|
|
||||||
Module['__emscripten_thread_mailbox_await'](e.data.pthread_ptr);
|
|
||||||
|
|
||||||
assert(e.data.pthread_ptr);
|
|
||||||
// Also call inside JS module to set up the stack frame for this pthread in JS module scope
|
|
||||||
Module['establishStackSpace']();
|
|
||||||
Module['PThread'].receiveObjectTransfer(e.data);
|
|
||||||
Module['PThread'].threadInitTLS();
|
|
||||||
|
|
||||||
if (!initializedJS) {
|
|
||||||
// Embind must initialize itself on all threads, as it generates support JS.
|
|
||||||
// We only do this once per worker since they get reused
|
|
||||||
Module['__embind_initialize_bindings']();
|
|
||||||
initializedJS = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
Module['invokeEntryPoint'](e.data.start_routine, e.data.arg);
|
|
||||||
} catch(ex) {
|
|
||||||
if (ex != 'unwind') {
|
|
||||||
// The pthread "crashed". Do not call `_emscripten_thread_exit` (which
|
|
||||||
// would make this thread joinable). Instead, re-throw the exception
|
|
||||||
// and let the top level handler propagate it back to the main thread.
|
|
||||||
throw ex;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (e.data.cmd === 'cancel') { // Main thread is asking for a pthread_cancel() on this thread.
|
|
||||||
if (Module['_pthread_self']()) {
|
|
||||||
Module['__emscripten_thread_exit'](-1);
|
|
||||||
}
|
|
||||||
} else if (e.data.target === 'setimmediate') {
|
|
||||||
// no-op
|
|
||||||
} else if (e.data.cmd === 'checkMailbox') {
|
|
||||||
if (initializedJS) {
|
|
||||||
Module['checkMailbox']();
|
|
||||||
}
|
|
||||||
} else if (e.data.cmd) {
|
|
||||||
// The received message looks like something that should be handled by this message
|
|
||||||
// handler, (since there is a e.data.cmd field present), but is not one of the
|
|
||||||
// recognized commands:
|
|
||||||
err(`worker.js received unknown command ${e.data.cmd}`);
|
|
||||||
err(e.data);
|
|
||||||
}
|
|
||||||
} catch(ex) {
|
|
||||||
err(`worker.js onmessage() captured an uncaught exception: ${ex}`);
|
|
||||||
if (ex?.stack) err(ex.stack);
|
|
||||||
Module['__emscripten_thread_crashed']?.();
|
|
||||||
throw ex;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
self.onmessage = handleMessage;
|
|
||||||
@@ -35,4 +35,8 @@ fi
|
|||||||
export PATH=:$PATH:$EMSDK/upstream/bin &&
|
export PATH=:$PATH:$EMSDK/upstream/bin &&
|
||||||
|
|
||||||
cd $SRC &&
|
cd $SRC &&
|
||||||
em++ -O0 global.cc genericModel.cc model.cc spkModel.cc recognizer.cc bindings.cc -sWASMFS -sWASM_BIGINT -sSINGLE_FILE -sEMBIND_STD_STRING_IS_UTF8 -sEXPORTED_RUNTIME_METHODS=UTF8ToString,stringToUTF8OnStack -sMODULARIZE -sEXPORTED_FUNCTIONS=_malloc,_free,_main -sSUPPORT_LONGJMP=0 -sTRUSTED_TYPES -sEXPORT_NAME=loadBR -sENVIRONMENT=web,worker -sINITIAL_MEMORY=$MAX_MEMORY -sPTHREAD_POOL_SIZE=$MAX_THREADS -sPTHREAD_POOL_SIZE_STRICT=0 -sEXIT_RUNTIME -sALLOW_BLOCKING_ON_MAIN_THREAD=1 -sPOLYFILL=0 -sPTHREAD_POOL_DELAY_LOAD --pre-js pre.js -I. -I$LIBARCHIVE/include -I$VOSK/src -L$LIBARCHIVE/lib -larchive -L$ZSTD/lib -lzstd -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 -lopfs.js -lembind -pthread -flto -o ../test/BrowserRecognizer.js
|
em++ -O0 global.cc genericModel.cc model.cc spkModel.cc recognizer.cc bindings.cc -sWASMFS -sWASM_BIGINT -sSINGLE_FILE -sEMBIND_STD_STRING_IS_UTF8 -sEXPORTED_RUNTIME_METHODS=UTF8ToString,stringToUTF8OnStack -sMODULARIZE -sEXPORTED_FUNCTIONS=_malloc,_free,_main -sSUPPORT_LONGJMP=0 -sTRUSTED_TYPES -sEXPORT_NAME=loadBR -sENVIRONMENT=web,worker -sINITIAL_MEMORY=$MAX_MEMORY -sPTHREAD_POOL_SIZE=$MAX_THREADS -sPTHREAD_POOL_SIZE_STRICT=0 -sEXIT_RUNTIME -sALLOW_BLOCKING_ON_MAIN_THREAD=1 -sPOLYFILL=0 -sPTHREAD_POOL_DELAY_LOAD --pre-js pre.js -I. -I$LIBARCHIVE/include -I$VOSK/src -L$LIBARCHIVE/lib -larchive -L$ZSTD/lib -lzstd -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 -lopfs.js -lembind -pthread -flto -o ../test/BrowserRecognizer.js &&
|
||||||
|
cd ../test &&
|
||||||
|
rm -f BrowserRecognizer.worker.js &&
|
||||||
|
sed -i "s/locateFile('BrowserRecognizer.worker.js')/pthreadUrl/g" BrowserRecognizer.js &&
|
||||||
|
sed -i 's/locateFile("BrowserRecognizer.worker.js")/pthreadUrl/g' BrowserRecognizer.js
|
||||||
Reference in New Issue
Block a user